分析样例下载:链接: https://pan.baidu.com/s/1Df_8OhozSEE17MIuIigvhg 密码: 4tgi

练习algorithmbase_60.apk

老样子ida打开libnative-lib.so找到encodeFromJni_160。然后看到了有个byte_2F050字符串加密处理了。unidbg跑一下解开看看字符串是什么

image-20210119215422863

然后打开这个使用了加密字符串的函数。发现里面是ollvm混淆过了的

这里我先静态分析一下。把每个函数打开大致的浏览下里面的内容。然后发现了两个比较特殊的函数sub_121DC和sub_C29C。这两个函数里面直接就有疑似算法部分的代码。特别是sub_C29C函数中直接看到了有base64的特征。

image-20210119222948239

接下来静态分析一下sub_121DC的参数。发现第一个参数应该是一个大小为258字节的数据。可能是用来加密的表。

image-20210119224123191

然后frida来hook一下。观察参数的意义。

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
function hook_native(){
var base_addr=Module.getBaseAddress("libnative-lib.so");
var sub_121DC=base_addr.add(0x121DC+1);
Interceptor.attach(sub_121DC,{
onEnter:function(args){
console.log("onEnter sub_121DC");
this.args2=args[2]
console.log(hexdump(args[0],{length:258}));
console.log(args[1].readCString());
this.args3=args[3];
},onLeave:function(retval){
console.log("onLeave sub_121DC");
console.log(hexdump(this.args2,{length:this.args3.toInt32()}));
}
})
var sub_C29C=base_addr.add(0xC29C+1);
Interceptor.attach(sub_C29C,{
onEnter:function(args){
console.log("onEnter sub_C29C");
this.arg0=args[0];
console.log(hexdump(args[1],{length:args[2].toInt32()}));
console.log(args[2]);
},onLeave:function(retval){
console.log("onLeave sub_C29C");
console.log(retval);
console.log(hexdump(this.arg0,{length:0x30}));
}
})
}

根据下面的输出结果。可以确定

sub_121DC参数1是258字节的表。参数2是输入参数。参数3是输出参数。参数4是长度

sub_C29C参数1是输出,也就是最后加密的结果。参数2是sub_121DC最后的输出结果(就是sub_121DC执行完后的参数2)。参数3是参数2的长度

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
 onEnter sub_121DC
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
ff87d218 ee a4 3d 85 31 ce 6b 76 90 67 d2 7e be 8f 6a b2 ..=.1.kv.g.~..j.
ff87d228 28 16 c5 c4 2e a8 1d 9d 2d 21 27 b0 11 15 fb 0b (.......-!'.....
ff87d238 d6 26 36 79 1a 38 58 aa 47 44 49 c2 4d e1 05 18 .&6y.8X.GDI.M...
ff87d248 f4 2f 14 0c e9 cd 56 2c db 8d b3 d1 f9 60 eb df ./....V,.....`..
ff87d258 ab 9b b5 c7 0d d3 78 e3 45 99 63 01 5f c0 af 6c ......x.E.c._..l
ff87d268 3f 12 75 ac f0 e4 59 4e 84 37 9a 19 17 8a 7b 6e ?.u...YN.7....{n
ff87d278 3b 68 cf 87 d5 f7 ef 8c dc 07 7a 7c 5d bc 72 c1 ;h........z|].r.
ff87d288 0f d4 51 00 e8 2b 97 77 65 13 a1 04 94 1f a6 5b ..Q..+.we......[
ff87d298 82 98 b9 83 b6 b8 ca 91 4b 62 e0 f6 f2 c3 3e 2a ........Kb....>*
ff87d2a8 e5 ec 40 96 f1 46 bf 6f 73 95 29 bb e6 ba 70 d7 ..@..F.os.)...p.
ff87d2b8 5e 1b 7d 86 b7 c6 4c 55 5a 33 53 4a 25 cc d8 8b ^.}...LUZ3SJ%...
ff87d2c8 dd 66 ad b4 c8 61 10 35 ea 89 93 1c a9 20 92 cb .f...a.5..... ..
ff87d2d8 8e 42 e7 d9 1e 54 a5 a2 c9 da f8 71 34 f3 57 88 .B...T.....q4.W.
ff87d2e8 23 64 a3 a7 9f 48 6d fa 43 e2 de 30 ff ed 4f 03 #d...Hm.C..0..O.
ff87d2f8 39 22 b1 fc 81 50 80 08 ae 09 5c 02 0e a0 06 bd 9"...P....\.....
ff87d308 24 52 f5 69 9c 7f 41 fe 3c 74 0a fd 3a d0 32 9e $R.i..A.<t..:.2.
ff87d318 00 00 ..
dtYeKUzFKTZnVLYSNZxSbrE
onLeave sub_121DC
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
d6bd5e80 7d 1a b1 3b 1f 6b 7c 96 e9 e2 c1 a5 ea 3b e4 97 }..;.k|......;..
d6bd5e90 6d da 1e 01 75 92 e2 m...u..
onEnter sub_C29C
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
d6bd5e80 7d 1a b1 3b 1f 6b 7c 96 e9 e2 c1 a5 ea 3b e4 97 }..;.k|......;..
d6bd5e90 6d da 1e 01 75 92 e2 m...u..
0x17
onLeave sub_C29C
0x21
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
e53beac0 74 7c 75 3c 68 3c 78 61 74 65 42 74 4f 60 33 50 t|u<h<xateBtO`3P
e53bead0 51 20 4e 2f 47 79 6f 7a 49 42 32 4f 2f 75 5d 3d Q N/GyozIB2O/u]=
e53beae0 00 00 76 69 74 79 3b 00 6e 63 65 00 29 00 01 00 ..vity;.nce.)...

有了以上的一些记录。我们就可以锁定这两个就是关键加密函数了。接下来我们看看sub_121DC的结果是怎么计算出来的。这里由于是ollvm的。不方便静态分析。所以我直接用unidbg来模拟执行。并且trace这个函数的代码。看看是如何计算的

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

public class lianxi60 {
public static void main(String[] args) {
lianxi60 lianxi = new lianxi60();
lianxi.Call_EncodeFromJni_60();
}
private final AndroidEmulator emulator;
private final VM vm;
private DvmClass Clazz;
private lianxi60() {
emulator = new AndroidARMEmulator();
Memory memory = emulator.getMemory();
LibraryResolver resolver = new AndroidResolver(23);
memory.setLibraryResolver(resolver);
vm = emulator.createDalvikVM(null);
vm.setVerbose(false);
DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/lianxi60/libnative-lib.so"), false);
dm.callJNI_OnLoad(emulator);
Clazz = vm.resolveClass("com/kanxue/algorithmbase/MainActivity");
KingTrace trace=new KingTrace(emulator);
long start=0x400121DC;
long end=0x40012326;
trace.initialize(start,end,null);
emulator.getBackend().hook_add_new(trace,start,end,emulator);
}
public void Call_EncodeFromJni_60(){
String res=Clazz.callStaticJniMethodObject(emulator,"encodeFromJni_160(Ljava/lang/String;)Ljava/lang/String;","dtYeKUzFKTZnVLYSNZxSbrE").toString();
System.out.print(res);
}
}

执行成功后。得到了完整的汇编执行的记录。然后我们分别搜索0x7d 0x1a 0xb1。发现结果都是在0x122a0这行计算出来的。然后我们直接搜索0x122a0。然后就看到了这个函数计算的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x64 r3=0x19 //r0=0x7d"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x10 r3=0xa //r0=0x1a"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x11 r3=0xa0 //r0=0xb1"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x21 r3=0x1a //r0=0x3b"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0xb r3=0x14 //r0=0x1f"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x41 r3=0x2a //r0=0x6b"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x78 r3=0x4 //r0=0x7c"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x6 r3=0x90 //r0=0x96"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x49 r3=0xa0 //r0=0xe9"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x40 r3=0xa2 //r0=0xe2"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x40 r3=0x81 //r0=0xc1"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x24 r3=0x81 //r0=0xa5"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x42 r3=0xa8 //r0=0xea"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x8 r3=0x33 //r0=0x3b"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x40 r3=0xa4 //r0=0xe4"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x13 r3=0x84 //r0=0x97"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x4c r3=0x21 //r0=0x6d"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x5a r3=0x80 //r0=0xda"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x18 r3=0x6 //r0=0x1e"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x1 r3=0x0 //r0=0x1"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x60 r3=0x15 //r0=0x75"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x12 r3=0x80 //r0=0x92"
"[libnative-lib.so] [0x122a0] [ 18 43 ] 0x400122a0: orrs r0, r3-----r0=0x40 r3=0xa2 //r0=0xe2"

然后开始分析第一个7d的计算汇编。下面列出相关的日志

1
2
3
4
5
6
7
8
9
10
0x40012280: ldrb r1, [r1]-----r1=0xbffff771    r1=0xbffff771        //r1=0xa4                        ;debug 0xbffff771[0]=0xa4
0x4001227c: ldrb r0, [r1, r0]-----r0=0x1 r1=0xbffff670 r0=0x1 //r0=0xb7 ;debug 0xbffff670[0x1]=0xb7
0x40012284: ldrb r1, [r2, r1]-----r1=0xa4 r2=0xbffff670 r1=0xa4 //r1=0xa4 ;debug 0xbffff670[0xa4]=0xa4
0x4001228a: add r0, r1-----r0=0xb7 r1=0xa4 //r0=0x15b ;debug 0xb7+0xa4=0x15b
0x4001228e: uxtb r0, r0-----r0=0x15b r0=0x15b //r0=0x5b ;debug 0x15b&0xff=0x5b
0x4001228c: ldrb r2, [r3, r2]-----r2=0x0 r3=0x40212000 r2=0x0 //r2=0x64 ;debug 0x40212000[0x0]=0x64 input src
0x40012292: ldrb r0, [r3, r0]-----r0=0x5b r3=0xbffff670 r0=0x5b //r0=0x19 ;debug 0xbffff670[0x5b]=0x19
0x40012296: bic.w r3, r0, r2-----r3=0xbffff670 r0=0x19 r2=0x64 //r3=0x19 ;debug 0x19 &~ 0x64 = 0x19
0x4001229a: bic.w r0, r2, r0-----r0=0x19 r2=0x64 r0=0x19 //r0=0x64 ;debug 0x64 &~ 0x19 = 0x64
0x400122a0: orrs r0, r3-----r0=0x64 r3=0x19 //r0=0x7d ;debug 0x64|0x19=0x7d

这里的重点就是ldrb取到的数据到底是什么数据。首先我们断点在0x121dd的位置。也就是函数的入口位置。

image-20210123224352059

然后看0xbffff670实际上就是函数的参数1。而0x40212000是函数的参数2。根据我们上面frida查到的。参数1就是258大小的一个字节数组。而参数2就是输入参数。因此我们可以根据trace的记录。来查一下和前面打印的字节数组是否对的上。

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
前面frida获取到的字节数组如下
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
ff87d218 ee a4 3d 85 31 ce 6b 76 90 67 d2 7e be 8f 6a b2 ..=.1.kv.g.~..j.
ff87d228 28 16 c5 c4 2e a8 1d 9d 2d 21 27 b0 11 15 fb 0b (.......-!'.....
ff87d238 d6 26 36 79 1a 38 58 aa 47 44 49 c2 4d e1 05 18 .&6y.8X.GDI.M...
ff87d248 f4 2f 14 0c e9 cd 56 2c db 8d b3 d1 f9 60 eb df ./....V,.....`..
ff87d258 ab 9b b5 c7 0d d3 78 e3 45 99 63 01 5f c0 af 6c ......x.E.c._..l
ff87d268 3f 12 75 ac f0 e4 59 4e 84 37 9a 19 17 8a 7b 6e ?.u...YN.7....{n
ff87d278 3b 68 cf 87 d5 f7 ef 8c dc 07 7a 7c 5d bc 72 c1 ;h........z|].r.
ff87d288 0f d4 51 00 e8 2b 97 77 65 13 a1 04 94 1f a6 5b ..Q..+.we......[
ff87d298 82 98 b9 83 b6 b8 ca 91 4b 62 e0 f6 f2 c3 3e 2a ........Kb....>*
ff87d2a8 e5 ec 40 96 f1 46 bf 6f 73 95 29 bb e6 ba 70 d7 ..@..F.os.)...p.
ff87d2b8 5e 1b 7d 86 b7 c6 4c 55 5a 33 53 4a 25 cc d8 8b ^.}...LUZ3SJ%...
ff87d2c8 dd 66 ad b4 c8 61 10 35 ea 89 93 1c a9 20 92 cb .f...a.5..... ..
ff87d2d8 8e 42 e7 d9 1e 54 a5 a2 c9 da f8 71 34 f3 57 88 .B...T.....q4.W.
ff87d2e8 23 64 a3 a7 9f 48 6d fa 43 e2 de 30 ff ed 4f 03 #d...Hm.C..0..O.
ff87d2f8 39 22 b1 fc 81 50 80 08 ae 09 5c 02 0e a0 06 bd 9"...P....\.....
ff87d308 24 52 f5 69 9c 7f 41 fe 3c 74 0a fd 3a d0 32 9e $R.i..A.<t..:.2.
ff87d318 00 00 ..

0x4001227c: ldrb r0, [r1, r0]-----r0=0x1 r1=0xbffff670 r0=0x1 //r0=0xb7 ;debug 0xbffff670[0x1]=0xb7
0x40012284: ldrb r1, [r2, r1]-----r1=0xa4 r2=0xbffff670 r1=0xa4 //r1=0xa4 ;debug 0xbffff670[0xa4]=0xa4
然后找上面数组对应的0x1和0xa4位置的数据
0xbffff670[0x1]=0xa4
0xbffff670[0xa4]=0xb7

结果发现两个都对不上。但是按上面的逻辑来说。读取的确实是这个内存的数据。那么真相只有一个。那就是这段内存数据在函数开始时确实是这样的。但是执行到达这里进行计算前。将这段内存的数据进行了修改。所以和我们开始看到的内存数据不同了。然后为了拿到完整的被修改过的这个字节数据。我在函数最后结束的地方打一个断点

1
2
3
Debugger dbg= emulator.attach();
dbg.addBreakPoint(0x40012326);
dbg.debug();

然后输入m0xbffff670 258 将这块内存打印出来。

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
[libnative-lib.so] [0x12322] [ bd e8 00 0f ] 0x40012322: pop.w {r8, sb, sl, fp}-----r8=0x3077af8a        //r8=0xfffe0a50
debugger break at: 0x40012326
>>> r0=0x713d41d5 r1=0x55db7bae r2=0xd822dfc5 r3=0xfe9855a1 r4=0xad19d0c2 r5=0x55db7bae r6=0x882e73ec r7=0xbffff640 r8=0xfffe0a50 sb=0x40149ec0 sl=0x0 fp=0x0 ip=0x20 SP=0xbffff634 LR=RX@0x40012277[libnative-lib.so]0x12277 PC=RX@0x40012326[libnative-lib.so]0x12326 cpsr: N=0, Z=1, C=1, V=0, T=1, mode=0b10000
=> *[libnative-lib.so]*[0x12327]*[* f0 bd ]*0x40012326:*pop {r4, r5, r6, r7, pc}
[libnative-lib.so] [0x12329] [ a1 55 ] 0x40012328: strb r1, [r4, r6]
[libnative-lib.so] [0x1232b] [ 98 fe ec 73 ] 0x4001232a: cdp2 p3, #9, c7, c8, c12, #7
[libnative-lib.so] [0x1232f] [ 2e 88 ] 0x4001232e: ldrh r6, [r5]
[libnative-lib.so] [0x12331] [ c2 d0 ] 0x40012330: beq #0x400122b8
[libnative-lib.so] [0x12333] [ 19 ad ] 0x40012332: add r5, sp, #0x64
[libnative-lib.so] [0x12335] [ c5 df ] 0x40012334: svc #0xc5
[libnative-lib.so] [0x12337] [ 22 d8 ] 0x40012336: bhi #0x4001237e
[libnative-lib.so] [0x12339] [ 8a af ] 0x40012338: add r7, sp, #0x228
[libnative-lib.so] [0x1233b] [ 77 30 ] 0x4001233a: adds r0, #0x77
[libnative-lib.so] [0x1233d] [ ba 25 ] 0x4001233c: movs r5, #0xba
[libnative-lib.so] [0x1233f] [ 2a 62 ] 0x4001233e: str r2, [r5, #0x20]
[libnative-lib.so] [0x12341] [ 5d 53 ] 0x40012340: strh r5, [r3, r5]
[libnative-lib.so] [0x12343] [ 37 65 ] 0x40012342: str r7, [r6, #0x50]
[libnative-lib.so] [0x12345] [ d5 41 ] 0x40012344: rors r5, r2
[libnative-lib.so] [0x12347] [ 3d 71 ] 0x40012346: strb r5, [r7, #4]

m0xbffff670 258

>-----------------------------------------------------------------------------<
[23:05:14 396]unidbg@0xbffff670, md5=44e110548f78d20dc6f2df3b306e45a3, hex=eeb722ef6ff723786d60b22efbde0d1deb1521edc5b4b80b6cc3d1023af00136a0269d791a3858aa474449c24de10518f42f140ce9cd562cdb8db327f96728dfab9bb5c76ad376e3459963be5fc0af2d3f1275ac16e4594e84379a19178a7b6e3b68cf87d5ce858cdc077a7c5dbc72c10fd45100e82b97776513a104941fa65b8298b983b6d2ca914b62e0f6f27e3e2ae5ec4096f146bf31739529bbe6ba70d75e1b7d86a4c64c555a33534a25ccd88bdd66ada8c8611035ea89931ca92092cb8e42e7d91e54a5a2c9daf87134f357886b64a3a79f4890fa43e28f30ffc44f03393db1fc81508008ae095cb00ed606bd2452f5699c7f41fe3c740afd11d0329e20ed
size: 258
0000: EE B7 22 EF 6F F7 23 78 6D 60 B2 2E FB DE 0D 1D ..".o.#xm`......
0010: EB 15 21 ED C5 B4 B8 0B 6C C3 D1 02 3A F0 01 36 ..!.....l...:..6
0020: A0 26 9D 79 1A 38 58 AA 47 44 49 C2 4D E1 05 18 .&.y.8X.GDI.M...
0030: F4 2F 14 0C E9 CD 56 2C DB 8D B3 27 F9 67 28 DF ./....V,...'.g(.
0040: AB 9B B5 C7 6A D3 76 E3 45 99 63 BE 5F C0 AF 2D ....j.v.E.c._..-
0050: 3F 12 75 AC 16 E4 59 4E 84 37 9A 19 17 8A 7B 6E ?.u...YN.7....{n
0060: 3B 68 CF 87 D5 CE 85 8C DC 07 7A 7C 5D BC 72 C1 ;h........z|].r.
0070: 0F D4 51 00 E8 2B 97 77 65 13 A1 04 94 1F A6 5B ..Q..+.we......[
0080: 82 98 B9 83 B6 D2 CA 91 4B 62 E0 F6 F2 7E 3E 2A ........Kb...~>*
0090: E5 EC 40 96 F1 46 BF 31 73 95 29 BB E6 BA 70 D7 ..@..F.1s.)...p.
00A0: 5E 1B 7D 86 A4 C6 4C 55 5A 33 53 4A 25 CC D8 8B ^.}...LUZ3SJ%...
00B0: DD 66 AD A8 C8 61 10 35 EA 89 93 1C A9 20 92 CB .f...a.5..... ..
00C0: 8E 42 E7 D9 1E 54 A5 A2 C9 DA F8 71 34 F3 57 88 .B...T.....q4.W.
00D0: 6B 64 A3 A7 9F 48 90 FA 43 E2 8F 30 FF C4 4F 03 kd...H..C..0..O.
00E0: 39 3D B1 FC 81 50 80 08 AE 09 5C B0 0E D6 06 BD 9=...P....\.....
00F0: 24 52 F5 69 9C 7F 41 FE 3C 74 0A FD 11 D0 32 9E $R.i..A.<t....2.
0100: 20 ED .
^-----------------------------------------------------------------------------^

这里我们再拿trace的记录对比。就发现能对的上了。现在这块内存就是算法计算时所使用的数据了。但是我们得搞清楚这个table是怎么计算变化的。先查看另外一个ldr的数据来源。然后看看他们之间是否有点什么关联

另一个内存0xbffff771。我们搜索一下使用到的位置是0x40012280。搜索一下这行的代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xa4"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xe1"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x66"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x97"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x65"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xd0"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x46"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xd6"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x3d"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xf"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x8d"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x4b"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xda"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x44"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x16"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x3e"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x54"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x19"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xdd"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xb"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xb3"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x85"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x22"

然后发现这个数据每次都是从同一个位置读取的。这里我注意到了这个数据的长度似乎和input的长度一致。不知道input变动这里是否会改变。我们尝试输入两个32位的input来对比下

1
2
3
4
public void Call_EncodeFromJni_60(){
String res=Clazz.callStaticJniMethodObject(emulator,"encodeFromJni_160(Ljava/lang/String;)Ljava/lang/String;","1234567890abcdefghgklmnoqrstuvwx").toString();
System.out.print(res);
}

对应的0xbffff771数据如下

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

"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xa4"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xe1"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x66"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x97"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x65"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xd0"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x46"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xd6"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x3d"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xf"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x8d"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x4b"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xda"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x44"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x16"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x3e"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x54"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x19"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xdd"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xb"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xb3"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x85"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x22"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x4f"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x14"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x3b"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xeb"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xfc"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x11"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xc"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x17"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xed"

然后再修改一下输入参数

1
2
3
4
public void Call_EncodeFromJni_60(){
String res=Clazz.callStaticJniMethodObject(emulator,"encodeFromJni_160(Ljava/lang/String;)Ljava/lang/String;","9876543210gqrstuklmnovwxabfghcde").toString();
System.out.print(res);
}

对应内存的输出结果如下

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

"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xa4"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xe1"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x66"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x97"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x65"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xd0"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x46"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xd6"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x3d"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xf"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x8d"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x4b"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xda"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x44"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x16"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x3e"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x54"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x19"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xdd"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xb"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xb3"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x85"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x22"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x4f"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x14"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x3b"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xeb"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xfc"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x11"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xc"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0x17"
"[libnative-lib.so] [0x12280] [ 09 78 ] 0x40012280: ldrb r1, [r1]-----r1=0xbffff771 r1=0xbffff771 //r1=0xed"

那么这里可以看出来这串数据也是固定的。那么这个数据是否和第一个参数有什么关联呢。我们下面贴上原始的258字节的参数1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0000: EE A4 3D 85 31 CE 6B 76 90 67 D2 7E BE 8F 6A B2    ..=.1.kv.g.~..j.
0010: 28 16 C5 C4 2E A8 1D 9D 2D 21 27 B0 11 15 FB 0B (.......-!'.....
0020: D6 26 36 79 1A 38 58 AA 47 44 49 C2 4D E1 05 18 .&6y.8X.GDI.M...
0030: F4 2F 14 0C E9 CD 56 2C DB 8D B3 D1 F9 60 EB DF ./....V,.....`..
0040: AB 9B B5 C7 0D D3 78 E3 45 99 63 01 5F C0 AF 6C ......x.E.c._..l
0050: 3F 12 75 AC F0 E4 59 4E 84 37 9A 19 17 8A 7B 6E ?.u...YN.7....{n
0060: 3B 68 CF 87 D5 F7 EF 8C DC 07 7A 7C 5D BC 72 C1 ;h........z|].r.
0070: 0F D4 51 00 E8 2B 97 77 65 13 A1 04 94 1F A6 5B ..Q..+.we......[
0080: 82 98 B9 83 B6 B8 CA 91 4B 62 E0 F6 F2 C3 3E 2A ........Kb....>*
0090: E5 EC 40 96 F1 46 BF 6F 73 95 29 BB E6 BA 70 D7 ..@..F.os.)...p.
00A0: 5E 1B 7D 86 B7 C6 4C 55 5A 33 53 4A 25 CC D8 8B ^.}...LUZ3SJ%...
00B0: DD 66 AD B4 C8 61 10 35 EA 89 93 1C A9 20 92 CB .f...a.5..... ..
00C0: 8E 42 E7 D9 1E 54 A5 A2 C9 DA F8 71 34 F3 57 88 .B...T.....q4.W.
00D0: 23 64 A3 A7 9F 48 6D FA 43 E2 DE 30 FF ED 4F 03 #d...Hm.C..0..O.
00E0: 39 22 B1 FC 81 50 80 08 AE 09 5C 02 0E A0 06 BD 9"...P....\.....
00F0: 24 52 F5 69 9C 7F 41 FE 3C 74 0A FD 3A D0 32 9E $R.i..A.<t..:.2.

然后继续列出第二个固定的计算数据

1
a4 e1 66 97 65 d0 46 d6 3d 0f 8d 4b da 44 16 3e 54 19 dd 0b b3 85 22 4f 14 3b eb fc 11 0c 17 ed

接着我们列出trace里面每次从内存里面取到的两个数据

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

"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xb7 r1=0xa4 //r0=0x15b"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x22 r1=0x3d //r0=0x5f"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xef r1=0x85 //r0=0x174"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x6f r1=0x31 //r0=0xa0"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xf7 r1=0xce //r0=0x1c5"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x23 r1=0x6b //r0=0x8e"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x78 r1=0x76 //r0=0xee"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x6d r1=0x90 //r0=0xfd"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x60 r1=0x67 //r0=0xc7"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xb2 r1=0xd2 //r0=0x184"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xc3 r1=0x7e //r0=0x141"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x1 r1=0xbe //r0=0xbf"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xde r1=0x8f //r0=0x16d"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xd r1=0x6a //r0=0x77"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x1d r1=0xd2 //r0=0xef"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xeb r1=0x28 //r0=0x113"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xf0 r1=0x16 //r0=0x106"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x21 r1=0xc5 //r0=0xe6"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xed r1=0xc4 //r0=0x1b1"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xc3 r1=0x2e //r0=0xf1"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xb4 r1=0xa8 //r0=0x15c"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0xb8 r1=0xd2 //r0=0x18a"
"[libnative-lib.so] [0x1228a] [ 08 44 ] 0x4001228a: add r0, r1-----r0=0x36 r1=0x9d //r0=0xd3"

似乎看起来没有什么关联。然后我们看下每次相加的两个值是否都在那个table里面。并且他们的索引是多少。我简单整理几个看一下有没什么关联

1
2
3
4
data:0xb7 idx:0xa4        data:0xa4 idx:0x1
data:0x22 idx:0xe1 data:0x3d idx:0x2
data:0xef idx:0x66 data:0x85 idx:0x3
data:0x6f idx:0x97 data:0x31 idx:0x4

那这就相当巧了。因为我们在两个idx中都看到了熟悉的东西。第一个idx是我们那个固定字节数组。第二个idx直接就是索引。如此我们就知道了这个table是怎么变化的了。每次迭代进行计算的时候。先把固定key的数据和当前计算的下一个索引的值进行交换。然后以此类推。下面贴上py的还原代码

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
import re,struct

tbkeyStr="""ee a4 3d 85 31 ce 6b 76 90 67 d2 7e be 8f 6a b2 28 16 c5 c4 2e a8 1d 9d 2d 21 27 b0 11 15 fb 0b d6 26 36 79 1a 38 58 aa 47 44 49 c2 4d e1 05 18 f4 2f 14 0c e9 cd 56 2c db 8d b3 d1 f9 60 eb df ab 9b b5 c7 0d d3 78 e3 45 99 63 01 5f c0 af 6c 3f 12 75 ac f0 e4 59 4e 84 37 9a 19 17 8a 7b 6e 3b 68 cf 87 d5 f7 ef 8c dc 07 7a 7c 5d bc 72 c1 0f d4 51 00 e8 2b 97 77 65 13 a1 04 94 1f a6 5b 82 98 b9 83 b6 b8 ca 91 4b 62 e0 f6 f2 c3 3e 2a e5 ec 40 96 f1 46 bf 6f 73 95 29 bb e6 ba 70 d7 5e 1b 7d 86 b7 c6 4c 55 5a 33 53 4a 25 cc d8 8b dd 66 ad b4 c8 61 10 35 ea 89 93 1c a9 20 92 cb 8e 42 e7 d9 1e 54 a5 a2 c9 da f8 71 34 f3 57 88 23 64 a3 a7 9f 48 6d fa 43 e2 de 30 ff ed 4f 03 39 22 b1 fc 81 50 80 08 ae 09 5c 02 0e a0 06 bd 24 52 f5 69 9c 7f 41 fe 3c 74 0a fd 3a d0 32 9e"""
tb2keyStr="""a4 e1 66 97 65 d0 46 d6 3d 0f 8d 4b da 44 16 3e 54 19 dd 0b b3 85 22 4f 14 3b eb fc 11 0c 17 ed"""

def b2hex(bins):
return ' '.join(["%02X" % x for x in bins]).strip()

def StrToHexSplit(str):
buf = bytes(0)
lines = re.split (r'[\r\n ]',str)
for code in lines:
if len (code) <= 0:
continue
num = int (code,16)
bnum = struct.pack ('B',num)
buf += bnum
return buf

def calc_data(input):
tbkey = bytearray(StrToHexSplit(tbkeyStr))
tb2key = bytearray(StrToHexSplit(tb2keyStr))
res=bytes(0)
for i in range(len(input)):
tb2value=tb2key[i]
tmp=tbkey[i+1]
tbkey[i+1]=tbkey[tb2key[i]]
tbkey[tb2key[i]]=tmp
tbvalue1 = tbkey[i + 1]
tbvalue2=tbkey[tb2value]
tbvalue=tbvalue1+tbvalue2
tbvalue=tbvalue&0xff
chdata=ord(input[i])
tbres=tbkey[tbvalue]
cdata=(tbres&~chdata)|(chdata&~tbres)
res+=struct.pack("B",cdata)
return res

if __name__ == '__main__':
res=calc_data("dtYeKUzFKTZnVLYSNZxSbrE")
print(b2hex(res))

结算的结果如下。和我们之前frida抓到的第一个加密函数的结果对的上了。接下来就开始处理第二个加密函数。有点像base64的函数

1
7D 1A B1 3B 1F 6B 7C 96 E9 E2 C1 A5 EA 3B E4 97 6D DA 1E 01 75 92 E2

我们再回头看看加密的明文和结果

1
input: TjHWtazVgwyqzLAuIoPtjuYvsyjCi output: t|u<h<xateBtO`3PQ N/GyozIB2O/u]=

首先我们能够直接确定肯定不是标准的base64的结果。因为标准的是不会有那些特殊字符的。那么base64的table必然经过修改了。用ida打开看一下

image-20210124192319165

ida中显示的这个table也和结果不符。因为结果中有|和]这两个特殊字符。所以这个table必然是经过动态修改了的。那么我们用ida翻一下。看看是否有什么地方直接对这个数据进行修改的。

image-20210124192746077

找到修改的位置之后。然后我们需要查一下这里赋值的到底是什么。那么先看下汇编的表示

image-20210124192907313

我们需要hook到0xc1b4行。打印一下r0和r1的值是多少。

1
2
3
4
5
6
7
var sub_C1B4=base_addr.add(0xC1B4+1);
Interceptor.attach(sub_C1B4,{
onEnter:function(args){
console.log(this.context.r0,this.context.r1)
},onLeave:function(retval){
}
})

然后看到打印结果如下。r1固定是0x17。而这个0x17刚刚好是我们输入参数的长度。

r0转换成ascii码就是我们之前看到的AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9+/VWXnoOPQRSxy4

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
0x41 0x17
0x59 0x17
0x5a 0x17
0x70 0x17
0x71 0x17
0x32 0x17
0x33 0x17
0x49 0x17
0x4a 0x17
0x72 0x17
0x54 0x17
0x46 0x17
0x66 0x17
0x67 0x17
0x68 0x17
0x69 0x17
0x6a 0x17
0x6b 0x17
0x6c 0x17
0x43 0x17
0x44 0x17
0x45 0x17
0x31 0x17
0x4b 0x17
0x4c 0x17
0x4d 0x17
0x6d 0x17
0x42 0x17
0x64 0x17
0x65 0x17
0x73 0x17
0x74 0x17
0x55 0x17
0x35 0x17
0x36 0x17
0x37 0x17
0x38 0x17
0x47 0x17
0x48 0x17
0x7a 0x17
0x30 0x17
0x63 0x17
0x75 0x17
0x76 0x17
0x77 0x17
0x61 0x17
0x62 0x17
0x4e 0x17
0x39 0x17
0x2b 0x17
0x2f 0x17
0x56 0x17
0x57 0x17
0x58 0x17
0x6e 0x17
0x6f 0x17
0x4f 0x17
0x50 0x17
0x51 0x17
0x52 0x17
0x53 0x17
0x78 0x17
0x79 0x17
0x34 0x17

那么知道了这个table怎么变化之后。我们开始实现一下py的版本

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
import re,struct

def getKey(cnt):
bkey = "AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9+/VWXnoOPQRSxy4"
bkey_bytes=bytes(bkey,"utf-8")
res=bytearray()
for i in range(len(bkey)):
res.append((bkey_bytes[i] &~ cnt)|(cnt &~ bkey_bytes[i]))
return res

def base64Encode(bkey,input):
cnt=len(input)
cntpre=cnt-2
cnt1=cnt
output=bytes(0)
input_bytes=input
v6=0
v7=0
while True:
if v7 >= cntpre:
break
v9 = v7
v6 += 4
output+=struct.pack("B",bkey[input_bytes[v7] >> 2])
v28_0 = (cnt & ~v6) | (v6 & ~ cnt)
output += struct.pack("B", v28_0)

v10=input_bytes[v7+1]
v11=input_bytes[v7]
v7+=3
output +=struct.pack("B",bkey[(v10 >> 4) & 0xFFFFFFCF | 16 * (v11 & 3)])
output +=struct.pack("B",bkey[((input_bytes[v9 + 2] >> 6) & 0xFFFFFFC3) | (4 * (input_bytes[v9 + 1] & 0xF))])
output +=struct.pack("B",bkey[(input_bytes[v9 + 2])&0x3f])

if v7 < cnt1 :
output +=struct.pack("B",bkey[input_bytes[v7] >> 2])
v12 = 16 * input_bytes[v7] & 0x30
if cnt1 - 1 == v7:
v13 = '='
output+=bkey[v12]
else:
output+=struct.pack("B",bkey[v12 | (input_bytes[v7+1] >> 4)])
v13=struct.pack("B",bkey[4 * (input_bytes[v7+1] & 0xF)])
output+=bytes([0x3d])
output+=v13
return output

def StrToHexSplit(str):
buf = bytes(0)
lines = re.split (r'[\r\n ]',str)
for code in lines:
if len (code) <= 0:
continue
num = int (code,16)
bnum = struct.pack ('B',num)
buf += bnum
return buf
def b2hex(bins):
return ' '.join(["%02X" % x for x in bins]).strip()

input="""7D 1A B1 3B 1F 6B 7C 96 E9 E2 C1 A5 EA 3B E4 97 6D DA 1E 01 75 92 E2"""
input_bytes=StrToHexSplit(input)
bkey=getKey(len(input_bytes))
print("bkey:",b2hex(bkey))
res=base64Encode(bkey,input_bytes)
print("res:",b2hex(res))

然后py的输出结果是

1
2
bkey: 56 4E 4D 67 66 25 24 5E 5D 65 43 51 71 70 7F 7E 7D 7C 7B 54 53 52 26 5C 5B 5A 7A 55 73 72 64 63 42 22 21 20 2F 50 5F 6D 27 74 62 61 60 76 75 59 2E 3C 38 41 40 4F 79 78 58 47 46 45 44 6F 6E 23
res: 63 13 7C 62 3C 7F 1F 3C 6F 61 63 1B 65 55 74 58 07 60 24 50 46 03 20 59 2F 50 0F 79 78 7A 5E 0B 42 25 4F 2F 75 3D 5D

先验证一下key生成的是否正确。用frida取一下

1
2
var addr_key=base_addr.add(0x2F0B4);
console.log(hexdump(addr_key,{length:64}))

结果是

1
2
3
4
5
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d6c7a0b4 56 4e 4d 67 66 25 24 5e 5d 65 43 51 71 70 7f 7e VNMgf%$^]eCQqp.~
d6c7a0c4 7d 7c 7b 54 53 52 26 5c 5b 5a 7a 55 73 72 64 63 }|{TSR&\[ZzUsrdc
d6c7a0d4 42 22 21 20 2f 50 5f 6d 27 74 62 61 60 76 75 59 B"! /P_m'tba`vuY
d6c7a0e4 2e 3c 38 41 40 4f 79 78 58 47 46 45 44 6f 6e 23 .<8A@OyxXGFEDon#

这里就知道key是计算正确的了。然后看看加密结果是否和之前对的上。

1
2
3
4
5
#py计算的
63 13 7C 62 3C 7F 1F 3C 6F 61 63 1B 65 55 74 58 07 60 24 50 46 03 20 59 2F 50 0F 79 78 7A 5E 0B 42 25 4F 2F 75 3D 5D
#frida的结果转成16进制的
t|u<h<xateBtO`3PQ N/GyozIB2O/u]=
74 7C 75 3C 68 3C 78 61 74 65 42 74 4F 60 33 50 51 20 4E 2F 47 79 6F 7A 49 42 32 4F 2F 75 5D 3D

明显和我们看到的结果不一致。说明可能不单单是table修改了。可能连base64算法都修改了。先找到之前看到的base64算法处。把关键的几行代码贴在下面

1
2
3
4
5
6
7
v6 = aAyzpq23ijrtffg[((v14 >> 2) ^ 0x3FFFFFC0) & (v14 >> 2)];
*v28 = a3 & ~v6 | v6 & ~(_BYTE)a3;
v15 = v28 + 2;
v28[1] = aAyzpq23ijrtffg[(unsigned __int8)((16 * *v13 ^ 0xC0) & 16 * *v13 | (*(_BYTE *)(a2 + v29 + 1) >> 4))];
v16 = (_BYTE *)(v29 + 2 + a2);
v17 = (((~(4 * *(unsigned __int8 *)(a2 + v29 + 1)) | 0xFFFFFFC3) & 0xFFFFFFF6)
+ (~(~(4 * *(unsigned __int8 *)(a2 + v29 + 1)) | 0xFFFFFFC3) & 8)) ^ ((((unsigned int)(unsigned __int8)*v16 >> 6) & 1) + (~((unsigned int)(unsigned __int8)*v16 >> 6) & 0xFFFFFFF6)

然后我们找个base64的标准的算法看一下是什么样子的

1
2
3
4
*v4 = aAbcdefghijklmn[v6 >> 2];
v4[1] = aAbcdefghijklmn[16 * (unsigned int)*v5 & 0x30LL | ((unsigned __int64)v5[1] >> 4)];
v4[2] = aAbcdefghijklmn[4 * (unsigned int)v5[1] & 0x3CLL | ((unsigned __int64)v5[2] >> 6)];
LOBYTE(v6) = aAbcdefghijklmn[v5[2] & 0x3F];

然后再看看我们的py代码的关键计算处

1
2
3
4
5
6
7
output+=struct.pack("B",bkey[input_bytes[v7] >> 2])
v10=input_bytes[v7+1]
v11=input_bytes[v7]
v7+=3
output +=struct.pack("B",bkey[(v10 >> 4) & 0xFFFFFFCF | 16 * (v11 & 3)])
output +=struct.pack("B",bkey[((input_bytes[v9 + 2] >> 6) & 0xFFFFFFC3) | (4 * (input_bytes[v9 + 1] & 0xF))])
output +=struct.pack("B",bkey[(input_bytes[v9 + 2])&0x3f])

那就看出来了。总共是有4个字节要赋值。那么根据这个特征。我们能看到那个v28就是主要用来存放4个字节计算的。那么我们先整理一下v28的赋值

1
2
3
4
5
6
7
8
9
v6 = aAyzpq23ijrtffg[((v14 >> 2) ^ 0x3FFFFFC0) & (v14 >> 2)];
*v28 = a3 & ~v6 | v6 & ~(_BYTE)a3;
v28[1] = aAyzpq23ijrtffg[(unsigned __int8)((16 * *v13 ^ 0xC0) & 16 * *v13 | (*(_BYTE *)(a2 + v29 + 1) >> 4))];
v15 = v28 + 2;
v18 = v15 + 1;
v17 = (((~(4 * *(unsigned __int8 *)(a2 + v29 + 1)) | 0xFFFFFFC3) & 0xFFFFFFF6)
+ (~(~(4 * *(unsigned __int8 *)(a2 + v29 + 1)) | 0xFFFFFFC3) & 8)) ^ ((((unsigned int)(unsigned __int8)*v16 >> 6) & 1) + (~((unsigned int)(unsigned __int8)*v16 >> 6) & 0xFFFFFFF6));
*v15 = a3 & ~aAyzpq23ijrtffg[v17] | aAyzpq23ijrtffg[v17] & ~(_BYTE)a3;
*v18 = aAyzpq23ijrtffg[v19];

然后第一个字节我们对应的修改成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# output+=struct.pack("B",bkey[input_bytes[v7] >> 2])       第一个字节生成规则修改了                     
v6 = bkey[((input_bytes[v7] >> 2) ^ 0x3FFFFFC0) & (input_bytes[v7] >> 2)]
v28_0 = (cnt & ~v6) | (v6 & ~ cnt)
output += struct.pack("B", v28_0)

v10=input_bytes[v7+1]
v11=input_bytes[v7]
v7+=3
# output +=struct.pack("B",bkey[(v10 >> 4) & 0xFFFFFFCF | 16 * (v11 & 3)]) 第二个字节生成规则修改了
output +=struct.pack("B",bkey[ ((16 * v11 ^ 0xC0) & 16 * v11 | (v10 >> 4))&0xff])
# output +=struct.pack("B",bkey[((input_bytes[v9 + 2] >> 6) & 0xFFFFFFC3) | (4 * (input_bytes[v9 + 1] & 0xF))]) 第三个规则也修改
v17= ((input_bytes[v9 + 2] >> 6) & 0xFFFFFFC3) | (4 * (input_bytes[v9 + 1] & 0xF))
output +=struct.pack("B",(cnt &~ bkey[v17])|(bkey[v17] &~ cnt))
output +=struct.pack("B",bkey[(input_bytes[v9 + 2])&0x3f])

依照c++的代码把每个计算部分都修改完成后。下面贴上完整的py代码

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import re,struct

tbkeyStr="""ee a4 3d 85 31 ce 6b 76 90 67 d2 7e be 8f 6a b2 28 16 c5 c4 2e a8 1d 9d 2d 21 27 b0 11 15 fb 0b d6 26 36 79 1a 38 58 aa 47 44 49 c2 4d e1 05 18 f4 2f 14 0c e9 cd 56 2c db 8d b3 d1 f9 60 eb df ab 9b b5 c7 0d d3 78 e3 45 99 63 01 5f c0 af 6c 3f 12 75 ac f0 e4 59 4e 84 37 9a 19 17 8a 7b 6e 3b 68 cf 87 d5 f7 ef 8c dc 07 7a 7c 5d bc 72 c1 0f d4 51 00 e8 2b 97 77 65 13 a1 04 94 1f a6 5b 82 98 b9 83 b6 b8 ca 91 4b 62 e0 f6 f2 c3 3e 2a e5 ec 40 96 f1 46 bf 6f 73 95 29 bb e6 ba 70 d7 5e 1b 7d 86 b7 c6 4c 55 5a 33 53 4a 25 cc d8 8b dd 66 ad b4 c8 61 10 35 ea 89 93 1c a9 20 92 cb 8e 42 e7 d9 1e 54 a5 a2 c9 da f8 71 34 f3 57 88 23 64 a3 a7 9f 48 6d fa 43 e2 de 30 ff ed 4f 03 39 22 b1 fc 81 50 80 08 ae 09 5c 02 0e a0 06 bd 24 52 f5 69 9c 7f 41 fe 3c 74 0a fd 3a d0 32 9e"""
tb2keyStr="""a4 e1 66 97 65 d0 46 d6 3d 0f 8d 4b da 44 16 3e 54 19 dd 0b b3 85 22 4f 14 3b eb fc 11 0c 17 ed"""

def b2hex(bins):
return ' '.join(["%02X" % x for x in bins]).strip()

def StrToHexSplit(str):
buf = bytes(0)
lines = re.split (r'[\r\n ]',str)
for code in lines:
if len (code) <= 0:
continue
num = int (code,16)
bnum = struct.pack ('B',num)
buf += bnum
return buf

#修改key
def getKey(cnt):
bkey = "AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9+/VWXnoOPQRSxy4"
bkey_bytes=bytes(bkey,"utf-8")
res=bytearray()
for i in range(len(bkey)):
res.append((bkey_bytes[i] &~ cnt)|(cnt &~ bkey_bytes[i]))
return res

#修改的base64算法
def base64Encode(bkey,input):
cnt=len(input)
cntpre=cnt-2
cnt1=cnt
output=bytes(0)
input_bytes=input
v6=0
v7=0
while True:
if v7 >= cntpre:
break
v9 = v7
v6 += 4
# output+=struct.pack("B",bkey[input_bytes[v7] >> 2])
v6 = bkey[((input_bytes[v7] >> 2) ^ 0x3FFFFFC0) & (input_bytes[v7] >> 2)]
v28_0 = (cnt & ~v6) | (v6 & ~ cnt)
output += struct.pack("B", v28_0)

v10=input_bytes[v7+1]
v11=input_bytes[v7]
v7+=3
# output +=struct.pack("B",bkey[(v10 >> 4) & 0xFFFFFFCF | 16 * (v11 & 3)])
output +=struct.pack("B",bkey[ ((16 * v11 ^ 0xC0) & 16 * v11 | (v10 >> 4))&0xff])
# output +=struct.pack("B",bkey[((input_bytes[v9 + 2] >> 6) & 0xFFFFFFC3) | (4 * (input_bytes[v9 + 1] & 0xF))])
v17= ((input_bytes[v9 + 2] >> 6) & 0xFFFFFFC3) | (4 * (input_bytes[v9 + 1] & 0xF))
output +=struct.pack("B",(cnt &~ bkey[v17])|(bkey[v17] &~ cnt))
output +=struct.pack("B",bkey[(input_bytes[v9 + 2])&0x3f])
if v7 < cnt1 :
output +=struct.pack("B",bkey[input_bytes[v7] >> 2])
v12 = 16 * input_bytes[v7] & 0x30
if cnt1 - 1 == v7:
v13 = '='
output+=bkey[v12]
else:
output+=struct.pack("B",bkey[v12 | (input_bytes[v7+1] >> 4)])
v13=struct.pack("B",bkey[4 * (input_bytes[v7+1] & 0xF)])
output+=bytes([0x3d])
output+=v13
return output

#加密算法1.把明文先加密一遍
def calc_data(input):
tbkey = bytearray(StrToHexSplit(tbkeyStr))
tb2key = bytearray(StrToHexSplit(tb2keyStr))
res=bytes(0)
for i in range(len(input)):
tb2value=tb2key[i]
tmp=tbkey[i+1]
tbkey[i+1]=tbkey[tb2key[i]]
tbkey[tb2key[i]]=tmp
tbvalue1 = tbkey[i + 1]
tbvalue2=tbkey[tb2value]
tbvalue=tbvalue1+tbvalue2
tbvalue=tbvalue&0xff
chdata=ord(input[i])
tbres=tbkey[tbvalue]
cdata=(tbres&~chdata)|(chdata&~tbres)
res+=struct.pack("B",cdata)
return res

if __name__ == '__main__':
res=calc_data("dtYeKUzFKTZnVLYSNZxSbrE")
print("data:",b2hex(res))
bkey = getKey(len(res))
print("bkey:", b2hex(bkey))
res = base64Encode(bkey, res)
print("res:", b2hex(res))

输出结果。对比上面frida抓到的完全一致。到这里做完了

1
2
3
data: 7D 1A B1 3B 1F 6B 7C 96 E9 E2 C1 A5 EA 3B E4 97 6D DA 1E 01 75 92 E2
bkey: 56 4E 4D 67 66 25 24 5E 5D 65 43 51 71 70 7F 7E 7D 7C 7B 54 53 52 26 5C 5B 5A 7A 55 73 72 64 63 42 22 21 20 2F 50 5F 6D 27 74 62 61 60 76 75 59 2E 3C 38 41 40 4F 79 78 58 47 46 45 44 6F 6E 23
res: 74 7C 75 3C 68 3C 78 61 74 65 42 74 4F 60 33 50 51 20 4E 2F 47 79 6F 7A 49 42 32 4F 2F 75 3D 5D