我总结了几种经典的类型,列个目录吧:
1.Strange apk:firda dump正在运行的dex文件
2.app3:怎么打开.ab文件,jeb动态调试
3.ph0en1x_100:jeb动态调试
4.easydex:so层静态分析,idc脚本
首先看一下java层
(这里插一句,JEB谁用谁说好,我感觉这个比as好用,动态调试的时候不用操作太多,还可以修改参数类型和名称,比jadx,jdgui好用好分析,还可以保存上次没有分析完的程序)
先看manifest文件,找到程序的入口点:
sctf.demo.myapplication.t这个是程序的入口点
但是找不到啊,这里我猜测应该就是在app运行过程中动态释放出来的
那就用frida_dump试试吧
在指定文件夹里找到dump下的dex文件进行分析就可以了
只有第一个dex文件能找到了.t文件
在这个。t文件的oncreate又定位到了这里:
因为程序一开始分析的是没有sctf.demo.myapplication.t文件了 xml文件里只有sctf.demo.myapplication.t和sctf.demo.myapplication.s文件,所以这个MAIN应该是sctf.demo.myapplication.s文件里面的,所以就要分析sctf.demo.myapplication.s文件了
然后修复一下代码,修改一下变量的名称,加一些注释便于分析:
通过这段代码的分析可以得到前半部分的flag:sctf{W3lc0me
总结一下就是通过base64解密得到的(详细分析过程在代码注释)
返回.t文件对str2字符串进程分析
修复一下代码:
这个是中间的encode的函数(代码修复之后的)分析:
通过分析后半部分代码得到了:~t0_An4r0id-w0rld}
总结一下就是md5解密,然后分析encode函数,得到flag的后半部分
这道题的关键所在就是,找到程序的入口点,dump出正在运行的dex文件,字符串加密简单分析即可得到
打开之后一看是.ab文件(在对安卓手机进行取证时,经常需要备份手机的应用程序数据,备份后得到的数据文件为ab格式,ab文件一般分两种,一种是没有加密,这种文件前面有24字节的文件头,文件头包含none标志,文件头之后就是数据;一种是加密的备份文件,它的文件头就比较复杂了,文件头包含AES-256标志。)
一般的话.ab文件是用这个文件进行操作的
我总结出来一般的话使用这两条指令中的一条:
java -jar abe-all.jar unpack app3.ab app3.jar
java -jar abe-all.jar unpack app3.ab app3.tar
将.ab文件转化为.tar文件
解压缩之后发现了apk文件,就可以进行分析了:
先从程序的入口点开始分析:
这个代码是调用了这个方法:
最后这个代码这里,由于这里是给数据库传参数的函数,所以就是要分析这个,一开始的那个tar文件里面解压之后发现了.db文件,就是数据库文件,所以我猜测,这里传入的参数应该是登录数据库的密码,重点就是要分析这个函数了
先是外层调用了v1.a的这个方法:
然后又内层调用了这个方法:
先分析这个函数的返回值
跟进去分析:
继续跟进去分析b方法:
其实也没有什么,就是md5加密和SHA-1加密,
这里我就用动态调试的方法吧:
先获得这个函数的返回值:
跟进去下个断点:
附上进程:
把v0的类型改为string类型是这样的:
v0 = 44e2e4457d4e252ca5b9fe9d20b3fea5
接下来就要看看这个函数的返回值了
v1.a(v2 + v1.b(v2, v0.getAsString("password")))
继续下断点:
v0 = "yaphetshan"
然后分析这个:
(v1.a(v2 + v1.b(v2, v0.getAsString("password"))).substring(0, 7))
下断点,再调:
v0 = "ae56f99638285eb0743d8bf76d2b0c80e5cbb096"
然后取前七位:ae56f99(这就是登录数据库的密码了,其实直接从最后一步那里下断点就行了,我想一步步的看看加密算法的结果)
用ae56f99登录数据库找到:VGN0ZntIM2xsMF9Eb19ZMHVfTG92M19UZW5jM250IX0=
这一眼就是base64编码,直接解码就行了
Tctf{H3ll0_Do_Y0u_Lov3_Tenc3nt!}
既然JEB动态调试这么好用,那就再来一个题(这个题就非常简单了,直接一步到位)嘿嘿
关键代码就是这里,修复一下代码在分析就是这样的:
如果这个if分支返回为真,就能让程序正确运行
然后equals的两个参数在传入之前都调用了getSecret()函数,所以这个函数就不再用分析了
先看一下getflag()函数:
再看一下encrypt()函数:
终于可以用ida分析了:
上ida导入.h文件在改一下参数:
分析一下encrypt函数,发现逻辑很简单,就是将传进来的字符串的每个字符的ASCII码减一
对于getFlag函数直接用JEB动态调试该函数得到返回值即可:
下好断点,附加进程:
string@4175:"ekfz@q2^x/t^fn0mF^6/^rb
qanqntfg^E`hq|"
直接上脚本就行了:
apk没有加固:
首先看看apk的java层:
发现只有一些资源文件,并没有发现dex文件,所以dex文件应该也是动态加载出来的
这里我尝试用frida-dexdump想dump下正在运行的dex文件,但是啥也没有:
所以就要分析so层了:
我想着先从jnionload或者java开始的来着,但是都没有,然后我就想着从这个函数分析吧:
一看这么多逻辑,先动态看看程序是怎么走的吧:
先在这里下个断点,可以找出程序在系统中(经过了异或运算)的释放的dex文件的位置
然后接着动态分析就能分析出程序的流程:
这个是我修复之后的代码:
这个31分支这里就是解密dex文件的位置:
下面就是对解密出来的dex文件(从byte_7004开始,长度为0x35A0C是加密后的数据)进行的操作:
这里有两种方法:
1.在删除dex文件操作之前下个断点,然后将手机中的dex文件pull到电脑上
2.使用idc脚本直接解密出dex文件:
然后就可以得到dex文件进行分析了:
但是再dex文件中并没有发现明显的逻辑:
然后将字符串解密:
然后再源apk文件中发现了two fish算法............(这道题的关键应该是so层的分析,这个twofish算法我还是第一次听说)
然后找个在线解密网站就行了:
protected void onCreate(Bundle arg5) {
super
.onCreate(arg5);
this.setContentView(
0x7F09001D
);
View input_str
=
this.findViewById(
0x7F070022
);
/
/
输入字符串
this.findViewById(
0x7F07008A
);
((Button)input_str).setOnClickListener(new View$OnClickListener(this.findViewById(
0x7F070037
)) {
public void onClick(View arg9) {
String str1
=
"";
/
/
两个字符串临时存储变量
String str2
=
"";
int
t
=
0
;
/
/
临时变量
String
str
=
this.val$ed.getText().toString();
int
num_30
=
30
;
if
(
str
.length()
=
=
num_30) {
/
/
输入的字符串的长度为
30
while
(t <
12
) {
str1
=
str1
+
str
.charAt(t);
/
/
将前
12
个输入的字符存储到str1里面
+
+
t;
}
str1
=
f.sctf(str1);
/
/
跟进去发现是将前
12
个字符进行base64加密
while
(t < num_30) {
str2
=
str2
+
str
.charAt(t);
/
/
将后
12
个字符存储到str2字符串里面
+
+
t;
}
if
(str1.equals(
"c2N0ZntXM2xjMG1l"
)) {
/
/
根据字符串
1
和该字符串相匹配,直接就能解出来前
12
个字符sctf{W3lc0me
Intent v4_1
=
new Intent();
v4_1.putExtra(
"data_return"
, str2);
/
/
返回.t文件中分析str2字符串的加密操作
s.this.setResult(
-
1
, v4_1);
s.this.finish();
}
else
{
Toast.makeText(s.this.getApplicationContext(),
"something wrong"
,
1
).show();
}
}
else
{
Toast.makeText(s.this.getApplicationContext(),
"something wrong"
,
1
).show();
}
}
});
}
}
protected void onCreate(Bundle arg5) {
super
.onCreate(arg5);
this.setContentView(
0x7F09001D
);
View input_str
=
this.findViewById(
0x7F070022
);
/
/
输入字符串
this.findViewById(
0x7F07008A
);
((Button)input_str).setOnClickListener(new View$OnClickListener(this.findViewById(
0x7F070037
)) {
public void onClick(View arg9) {
String str1
=
"";
/
/
两个字符串临时存储变量
String str2
=
"";
int
t
=
0
;
/
/
临时变量
String
str
=
this.val$ed.getText().toString();
int
num_30
=
30
;
if
(
str
.length()
=
=
num_30) {
/
/
输入的字符串的长度为
30
while
(t <
12
) {
str1
=
str1
+
str
.charAt(t);
/
/
将前
12
个输入的字符存储到str1里面
+
+
t;
}
str1
=
f.sctf(str1);
/
/
跟进去发现是将前
12
个字符进行base64加密
while
(t < num_30) {
str2
=
str2
+
str
.charAt(t);
/
/
将后
12
个字符存储到str2字符串里面
+
+
t;
}
if
(str1.equals(
"c2N0ZntXM2xjMG1l"
)) {
/
/
根据字符串
1
和该字符串相匹配,直接就能解出来前
12
个字符sctf{W3lc0me
Intent v4_1
=
new Intent();
v4_1.putExtra(
"data_return"
, str2);
/
/
返回.t文件中分析str2字符串的加密操作
s.this.setResult(
-
1
, v4_1);
s.this.finish();
}
else
{
Toast.makeText(s.this.getApplicationContext(),
"something wrong"
,
1
).show();
}
}
else
{
Toast.makeText(s.this.getApplicationContext(),
"something wrong"
,
1
).show();
}
}
});
}
}
public
class
t extends AppCompatActivity {
public t() {
super
();
}
protected void onActivityResult(
int
arg9,
int
arg10, Intent arg11) {
View v0
=
this.findViewById(
0x7F07008B
);
View v1
=
this.findViewById(
0x7F070023
);
if
(arg9
=
=
1
&& arg10
=
=
-
1
) {
try
{
MessageDigest
str
=
MessageDigest.getInstance(
"MD5"
);
str
.update(
"syclover"
.getBytes());
/
/
将syclover字符串进行md5加密
String temp_str
=
new BigInteger(
1
,
str
.digest()).toString(
16
);
/
/
将加密后的字符转化为字符串
8bfc8af07bca146c937f283b8ec768d4
}
catch(Exception v5) {
v5.printStackTrace();
}
if
(f.encode(arg11.getStringExtra(
"data_return"
), temp_str).equals(
"~8t808_8A8n848r808i8d8-8w808r8l8d8}8"
)) {
((TextView)v0).setVisibility(
0
);
((Button)v1).setVisibility(
4
);
/
/
去除所有的
8
之后剩下~t0_An4r0id
-
w0rld}
}
else
{
Toast.makeText(this.getApplicationContext(),
"one more step"
,
1
).show();
}
}
}
public
class
t extends AppCompatActivity {
public t() {
super
();
}
protected void onActivityResult(
int
arg9,
int
arg10, Intent arg11) {
View v0
=
this.findViewById(
0x7F07008B
);
View v1
=
this.findViewById(
0x7F070023
);
if
(arg9
=
=
1
&& arg10
=
=
-
1
) {
try
{
MessageDigest
str
=
MessageDigest.getInstance(
"MD5"
);
str
.update(
"syclover"
.getBytes());
/
/
将syclover字符串进行md5加密
String temp_str
=
new BigInteger(
1
,
str
.digest()).toString(
16
);
/
/
将加密后的字符转化为字符串
8bfc8af07bca146c937f283b8ec768d4
}
catch(Exception v5) {
v5.printStackTrace();
}
if
(f.encode(arg11.getStringExtra(
"data_return"
), temp_str).equals(
"~8t808_8A8n848r808i8d8-8w808r8l8d8}8"
)) {
((TextView)v0).setVisibility(
0
);
((Button)v1).setVisibility(
4
);
/
/
去除所有的
8
之后剩下~t0_An4r0id
-
w0rld}
}
else
{
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!