Reverse bang

考点:梆梆加固脱壳

加固特征分析

apktools 加载工程后,部分文件丢失(灰色)。smail 代码只有 SecShell 类;lib 中发现 libSecShell.so ,结合最新各大apk加固特征库和吾爱帖子推测应该是梆梆加固免费版

梆梆加固会隐藏源码,然后在 com 中加载 libSecShell.so ,来运行程序。

1
System.loadLibrary("SecShell");

程序脱壳

脱壳过程一直踩坑,各种环境问题而出错。

准备使用DexExtractor来脱壳的,但是 AS ADV arm架构机器一直报错运行不起来。失败记录独立成篇,跪求大佬指导。

选择使用 xposed + apkshelling 模块脱壳,脱壳原理移步作者 github 。

编译apkshelling

apkshelling 会匹配是否目标包名及是否加壳,是的话就脱壳。但稳妥起见将目标 apk 的包名填入到 apkshelling 源码中的 XposedEntry 。

编译生成 apk ,安装到虚拟器中,在 xpoesed 中激活启用,重启虚拟机。

apk 源码中有 activity 需要编译成无桌面图标的。如果要编译无桌面图标 apk 看这里:

[collapse title=”展开查看详情” status=”false”]

launch 选择 Nothing。

或者需注释掉xml 里面的 APP 入口Activity中的下面这句即可:

1
<category android:name="android.intent.category.LAUNCHER" />

[/collapse]

dump dex

重启后安装&运行题目的 apk。查看 xpoesed 日志看看有没有 dump 到dex :

1
adb logcat -s Xposed

成功 dump 会有类似 log :

1
2
3
4
5
6
I/Xposed  ( 2450): Load package: com.example.how_debug
I/Xposed ( 2450): Found com.SecShell.SecShell.ApplicationWrapper
I/Xposed ( 2450): Thread: 81, File: /data/data/com.example.how_debug/00081-01.dex
I/Xposed ( 2450): Thread: 81, File: /data/data/com.example.how_debug/00081-02.dex
I/Xposed ( 2450): Thread: 81, File: /data/data/com.example.how_debug/00081-03.dex
I/Xposed ( 2450): Thread: 81, File: /data/data/com.example.how_debug/00081-04.dex

dex 会被 dump 到/data/data/包名

逆向找flag

把 dex 拖到本机:

1
adb pull /data/data/com.example.how_debug/00081-02.dex ./

dump 出来的 dex 文件,使用 jeb 打开,也可以选择 jadx 。

dump 出来的 4 个 dex 只有一个是程序源码,挨个找一下(com.example.how_debug)就行。

打开后就能找到 flag :

碎碎念

搜罗一下发现 FART 好像也可以脱这个壳,但相比于 apkshelling 能力比较弱,具体对比&安装&使用,看这里:ApkShelling脱壳和FART脱壳

实在太菜 DexExtractor 环境部署不来。

Reverse signal

源码分析

程序要求输入一串字符,前面有一个校验长度是否为 15 。检验通过后会进入下面这个伪虚拟机的加密函数里面。操作的指令存放在 a1 ,加密的 flag 也在 a1 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
int __cdecl vm_operad(int *a1, int a2)
{
int result; // eax
char v3[100]; // [esp+13h] [ebp-E5h]
char v4[100]; // [esp+77h] [ebp-81h]
char v5; // [esp+DBh] [ebp-1Dh]
int v6; // [esp+DCh] [ebp-1Ch]
int v7; // [esp+E0h] [ebp-18h]
int v8; // [esp+E4h] [ebp-14h]
int v9; // [esp+E8h] [ebp-10h]
int v10; // [esp+ECh] [ebp-Ch]

v10 = 0;
v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
while ( 1 )
{
result = v10;
if ( v10 >= a2 )
return result;
switch ( a1[v10] )
{
case 1:
v4[v7] = v5;
++v10;
++v7;
++v9;
break;
case 2:
v5 = a1[v10 + 1] + v3[v9];
v10 += 2;
break;
case 3:
v5 = v3[v9] - LOBYTE(a1[v10 + 1]);
v10 += 2;
break;
case 4:
v5 = a1[v10 + 1] ^ v3[v9];
v10 += 2;
break;
case 5:
v5 = a1[v10 + 1] * v3[v9];
v10 += 2;
break;
case 6:
++v10;
break;
case 7:
if ( v4[v8] != a1[v10 + 1] )
{
printf("what a shame...");
exit(0);
}
++v8;
v10 += 2;
break;
case 8:
v3[v6] = v5;
++v10;
++v6;
break;
case 10:
read(v3);
++v10;
break;
case 11:
v5 = v3[v9] - 1;
++v10;
break;
case 12:
v5 = v3[v9] + 1;
++v10;
break;
default:
continue;
}
}
}

执行的指令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 读入明文
10
# 加密开始
4, 8, 3, 1,
4, 8, 5, 1,
3, 8, 11, 1,
12, 8, 4, 1,
5, 8, 3, 1,
11, 8, 11, 1,
4, 8, 3, 1,
2, 8, 4, 1,
12, 8, 11, 1,
5, 8, 2, 1,
2, 8, 4, 1,
2, 8, 5, 1,
5, 8, 2, 1,
4, 8, 3, 1,
2, 8, 12, 1,
# 校验 15 位密文
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7

1 是将明文加密后的字符赋值到 v4 ,用于最后校验密文。

思路

每个字符加密操作,没有用到迭代的中间变量,所以 a1 中的密文提取出来,然后按照各个字符逐个解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
m1=(c[0]+5)^0x10
m2=(c[1]/3)^0x20
m3=c[2]+3
m4=(c[3]^4)-1
m5=(c[4]+0x21)/3
m6=(c[5]+2)
m7=(c[6]+0x20)^9
m8=((c[7]^0x24)-0x51)&0xff
m9=c[8]
m10=((c[9]&0xff)-0x25)/2
m11=((c[10]^0x41)-0x36)
m12=(c[11]-0x20)
m13=(c[12]-0x25)/3
m14=(c[13]+0x20)^9
m15=c[14]-0x42

最后结果:

1
757515121f3d478

事后发觉如果每个字符都是单独加密的,没有迭代的。可以试试把每一个密码的所有明文对应的密文爆破出来,然后就只需要挑出来对应的明文即可。ascii 可见字符应该是 95 个,每一轮的输入 15 个相同 明文,然后提取加密的字符串,可以实现的。

听群里老师傅能用 angr 秒。