练习第一题的algorithmbase_10.apk连接如下
链接: https://pan.baidu.com/s/1hUyNDzd0VDzqD16X_IhiPg 密码: ocg5
首先用jadx打开目标应用。看到下面的代码
看到我们需要还原算法的函数是encodeFromJni_10。参数是随机16个ascii字符。接下来用ida打开libnative-jni.so。找到我们的目标函数。然后修改第一个参数的类型为JNIEnv*,然后把第三个参数的名称修改成input。最后把F5的代码贴到下面
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 void __fastcall Java_com_kanxue_algorithmbase_MainActivity_encodeFromJni_110 (JNIEnv *a1, __int64 a2, __int64 input) { unsigned __int64 v3; void *input_1; JNIEnv *v5; const char *input_2; int v7; int v8; _BOOL4 v9; _BYTE *v10; char *v11; signed int v12; char *output; __int64 v14; __int64 v15; void *v16; unsigned __int64 v17; int v18; void *v19; __int64 v20; v3 = _ReadStatusReg(ARM64_SYSREG(3 , 3 , 13 , 0 , 2 )); input_1 = (void *)input; v20 = *(_QWORD *)(v3 + 40 ); v5 = a1; input_2 = (*a1)->GetStringUTFChars(a1, (jstring)input, 0L L); sub_E968(&v17, (__int64)input_2); (*v5)->ReleaseStringUTFChars(v5, input_1, input_2); if ( v17 & 1 ) v7 = v18; else v7 = (unsigned __int64)(unsigned __int8)v17 >> 1 ; v8 = sub_EE10(v7); v15 = 0L L; v16 = 0L L; v14 = 0L L; if ( v8 ) { sub_EA94(&v14, v8, 0 ); v9 = (v14 & 1 ) == 0 ; } else { v9 = 1 ; LOWORD(v14) = 0 ; } if ( v9 ) v10 = (char *)&v14 + 1 ; else v10 = v16; if ( v17 & 1 ) v11 = (char *)v19; else v11 = (char *)&v17 + 1 ; if ( v17 & 1 ) v12 = v18; else v12 = (unsigned __int64)(unsigned __int8)v17 >> 1 ; sub_EE38(v10, (__int64)v11, v12); if ( v14 & 1 ) output = (char *)v16; else output = (char *)&v14 + 1 ; ((void (__fastcall *)(JNIEnv *, char *))(*v5)->NewStringUTF)(v5, output); if ( v14 & 1 ) operator delete (v16) ; if ( v17 & 1 ) operator delete (v19) ; *(_QWORD *)(v3 + 40 ); }
这里可以从参数的一步步传递、返回值的向上回溯这两个方式来进行分析。
我先从返回值开始向上查。打开最后和返回值output相关的函数sub_EE38。发现了一些熟悉的东西
点进aAbcdefghijklmn数组里面看看是什么
说明这里可能是一个base64.我们用frida来hook一下encodeFromJni_10这个函数的参数和返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function hook_java ( ) { Java.perform(function ( ) { var native_lib=Java.use("com.kanxue.algorithmbase.MainActivity" ); native_lib.encodeFromJni_10.implementation=function (input ) { var res=this .encodeFromJni_10(input); console .log("input:" ,input,"output:" ,res); return res; } }); } function main ( ) { hook_java(); } setImmediate(main)
然后看看输出结果
1 input: 2vOHn\n(boFhN|_T output: MnZPSG5cbihib0ZoTnxfVA==
猜测是对参数进行了base64的处理。然后直接拿input值进行base64处理后对比下结果
对比输出结果是一致的。那么第一题的结果就是直接base64加密。
=====================================================================================================================
练习第一题的algorithmbase_11.apk连接如下
链接: https://pan.baidu.com/s/1_XqSzUqHfRxSBnU0Ddzlog 密码: u37v
第一步同样是jadx打开分析一下。然后看到的代码基本一致。不过这个是用的encodeFromJni_11函数。然后继续ida打开分析。然后用frida再hook看到参数和返回值如下
1 input: i17gs-B[4hbzg@-l output: Kjbw1wBqg+ptKG0Q1tAqLA==
然后我们再用工具对input进行base64加密后。发现和返回值不一样了
1 input: i17gs-B[4hbzg@-l base64: aTE3Z3MtQls0aGJ6Z0AtbA==
接下来。我们从返回值开始分析。先将sub_EE38这个函数进行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 30 function hook_native ( ) { var base_addr=Module.getBaseAddress("libnative-lib.so" ); var sub_EE38=base_addr.add(0xEE38 ); Interceptor.attach(sub_EE38,{ onEnter:function (args ) { this .arg0=args[0 ]; console .log("========onenter========" ) console .log(args[1 ].readCString()); console .log(args[2 ]); },onLeave :function (retval ) { console .log("========onleave========" ) console .log(this .arg0.readCString()); } }) } function hook_java ( ) { Java.perform(function ( ) { var native_lib=Java.use("com.kanxue.algorithmbase.MainActivity" ); native_lib.encodeFromJni_11.implementation=function (input ) { var res=this .encodeFromJni_11(input); console .log("input:" ,input,"output:" ,res); return res; } }); } function main ( ) { hook_java(); hook_native(); } setImmediate(main)
然后点击check后得到下面的日志
1 2 3 4 5 6 ========onenter======== Wyi":?fq>Dd!xA6z 0x10 ========onleave======== lw+Xz8W41VbyhGg6TbbvT5== input: Wyi":?fq>Dd!xA6z output: lw+Xz8W41VbyhGg6TbbvT5==
说明这个函数就是关键的函数了。然后我们分析里面是如何计算出来。首先根据我们上面得到的数据。知道了三个参数是什么,然后进行重命名。我把新的代码贴下面
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 __int64 __fastcall sub_EE38 (_BYTE *output, __int64 input, signed int data_10) { signed __int64 v3; _BYTE *v4; unsigned __int8 *v5; unsigned __int64 v6; _BYTE *v7; signed __int64 v8; char v9; __int64 v10; __int64 result; if ( data_10 - 2 < 1 ) { LODWORD(v3) = 0 ; v7 = output; if ( data_10 <= 0 ) goto LABEL_11; } else { v3 = 0L L; v4 = output; do { v5 = (input + v3); v6 = *(input + v3); v3 += 3L L; *v4 = aAyzabfghz0cmbd[v6 >> 2 ]; v4[1 ] = aAyzabfghz0cmbd[16 * *v5 & 0x30 LL | (v5[1 ] >> 4 )]; v4[2 ] = aAyzabfghz0cmbd[4 * v5[1 ] & 0x3C LL | (v5[2 ] >> 6 )]; LOBYTE(v6) = aAyzabfghz0cmbd[v5[2 ] & 0x3F ]; v7 = v4 + 4 ; v4[3 ] = v6; v4 += 4 ; } while ( v3 < data_10 - 2 ); if ( v3 >= data_10 ) goto LABEL_11; } *v7 = aAyzabfghz0cmbd[*(input + v3) >> 2 ]; v8 = 16 * *(input + v3) & 0x30 LL; if ( v3 == data_10 - 1 ) { v7[1 ] = aAyzabfghz0cmbd[v8]; v9 = 61 ; } else { v10 = input + v3; v7[1 ] = aAyzabfghz0cmbd[v8 | (*(v10 + 1 ) >> 4 )]; v9 = aAyzabfghz0cmbd[4 * *(v10 + 1 ) & 0x3C LL]; } v7[2 ] = v9; v7[3 ] = 61 ; v7 += 4 ; LABEL_11: result = (v7 - output + 1 ); *v7 = 0 ; return result; }
前面那段可以看到之前分析的是base64.然后由于第三个参数我们hook知道了值。所以分析一下就可以知道必然是走以下的代码来进行加密的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 index = 0L L; output_1 = output; do { v5 = (input + index); v6 = *(input + index); index += 3L L; *output_1 = aAyzabfghz0cmbd[v6 >> 2 ]; output_1[1 ] = aAyzabfghz0cmbd[16 * *v5 & 0x30 LL | (v5[1 ] >> 4 )]; output_1[2 ] = aAyzabfghz0cmbd[4 * v5[1 ] & 0x3C LL | (v5[2 ] >> 6 )]; LOBYTE(v6) = aAyzabfghz0cmbd[v5[2 ] & 0x3F ]; v7 = output_1 + 4 ; output_1[3 ] = v6; output_1 += 4 ; } while ( index < data_10 - 2 );if ( index >= data_10 ) goto LABEL_11;
然后对照逻辑写个py版本的加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 idx=0 data=10 input="""Wyi":?fq>Dd!xA6z""" input_bytes=bytes(input,"utf-8" ) bkey="AYZabFGHz0cmBdefghijklCDE1KLMNTU56789+/VWXnopq23IJrstuvwOPQRSxy4" output="" while True : v5 = input_bytes[idx] v5_1 = input_bytes[idx+1 ] v5_2 = input_bytes[idx + 2 ] print(input_bytes[idx]) v6 = input_bytes[idx] idx += 3 output += bkey[v6 >> 2 ] output += bkey[16 * v5 & 0x30 | (v5_1 >> 4 )] output += bkey[4 * v5_1 & 0x3C | (v5_2 >> 6 )] v6 = bkey[v5_2 & 0x3f ] output += v6 if idx > data-2 : break print(output) print(idx)
写到这里之后计算一下看看结果
对比ida上的逻辑发现并不符合结束的判断。这里的结果是9.所以还得继续往后执行。并且前面部分的计算已经和我们的输出结果一致了。
继续把后面的代码贴一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 *v7 = aAyzabfghz0cmbd[*(input + v3) >> 2 ]; v8 = 16 * *(input + v3) & 0x30 LL; if ( v3 == data_10 - 1 ) { v7[1 ] = aAyzabfghz0cmbd[v8]; v9 = 61 ; } else { v10 = input + v3; v7[1 ] = aAyzabfghz0cmbd[v8 | (*(v10 + 1 ) >> 4 )]; v9 = aAyzabfghz0cmbd[4 * *(v10 + 1 ) & 0x3C LL]; } v7[2 ] = v9; v7[3 ] = 61 ; v7 += 4 ;
然后继续再完善下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 idx=0 data=10 input="""Wyi":?fq>Dd!xA6z""" input_bytes=bytes(input,"utf-8" ) bkey="AYZabFGHz0cmBdefghijklCDE1KLMNTU56789+/VWXnopq23IJrstuvwOPQRSxy4" output="" v7=0 while True : v5 = input_bytes[idx] v5_1 = input_bytes[idx+1 ] v5_2 = input_bytes[idx + 2 ] v6 = input_bytes[idx] idx += 3 v7 += 4 output += bkey[v6 >> 2 ] output += bkey[16 * v5 & 0x30 | (v5_1 >> 4 )] output += bkey[4 * v5_1 & 0x3C | (v5_2 >> 6 )] v6 = bkey[v5_2 & 0x3f ] output += v6 if idx > data+2 : break output+=bkey[input_bytes[idx]>>2 ] v8 = 16 * input_bytes[idx] & 0x30 if idx > data - 1 : output += bkey[v8] v9 = '=' else : output += bkey[v8 | (input_bytes[idx+1 ]>> 4 )] v9 = bkey[4 * input_bytes[idx+1 ] & 0x3C ] output += v9 output += '=' print(output)
最后输出结果
1 lw+Xz8W41VbyhGg6TbbvT5==
到这里第二题就搞定了。
=====================================================================================================================
第三题algorithmbase_12.apk
链接: https://pan.baidu.com/s/1imjPHj7eSnBUJmWWeB3NKQ 密码: 9bk2
同样用frida来hook看下结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function hook_java ( ) { Java.perform(function ( ) { var native_lib=Java.use("com.kanxue.algorithmbase.MainActivity" ); native_lib.encodeFromJni_12.implementation=function (input ) { var res=this .encodeFromJni_12(input); console .log("input:" ,input,"output:" ,res); return res; } }); } function main ( ) { hook_java(); } setImmediate(main)
1 input: 5Ki*$:&xh.!u4{|[mAR*a^x"yK5 output: |_zxO-qJia.+]-)C|RzH*uCB_-x.PaN-h_lC
ida分析同样是从返回值向上回溯。找到第一个相关函数sub_8B04进行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 30 31 32 function hook_native ( ) { var base_addr=Module.getBaseAddress("libnative-lib.so" ); var sub_8B04=base_addr.add(0x8B04 +1 ); console .log("sub_8b04:" ,sub_8B04) Interceptor.attach(sub_8B04,{ onEnter:function (args ) { console .log("========onenter========" ) this .arg0=args[0 ]; console .log(args[1 ].readCString()); console .log(args[2 ]); },onLeave :function (retval ) { console .log("========onleave========" ) console .log(this .arg0.readCString()); } }) } function hook_java ( ) { Java.perform(function ( ) { var native_lib=Java.use("com.kanxue.algorithmbase.MainActivity" ); native_lib.encodeFromJni_12.implementation=function (input ) { var res=this .encodeFromJni_12(input); console .log("input:" ,input,"output:" ,res); return res; } }); } function main ( ) { hook_java(); hook_native(); } setImmediate(main)
1 2 3 4 5 6 ========onenter======== 3ez|q|z\8QzIU]}`bM ZMlJ] 0x18 ========onleave======== ~v]IlQ*Kk_!W\S{j]]@`T+jR*{Zi{} input: 3ez|q|z\8QzIU]}`bM ZMlJ] output: ~v]IlQ*Kk_!W\S{j]]@`T+jR*{Zi{}
对比输出结果。看出加密位置确实也还是这里。接下来贴上里面的加密逻辑
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 data_0x16 = data_0x18 - 2 ; data_0x18_1 = data_0x18; output_1 = output; v6 = 0 ; v7 = 0 ; while ( 1 ) { v8 = (_BYTE *)(output_1 + v6); if ( v7 >= data_0x16 ) break ; v9 = input + v7; v6 += 4 ; *v8 = aAyzpq23ijrtffg[(unsigned int )*(unsigned __int8 *)(input + v7) >> 2 ]; v10 = *(unsigned __int8 *)(input + v7 + 1 ); v11 = *(_BYTE *)(input + v7); v7 += 3 ; v8[1 ] = aAyzpq23ijrtffg[(v10 >> 4 ) & 0xFFFFFFCF | 16 * (v11 & 3 )]; v8[2 ] = aAyzpq23ijrtffg[((unsigned int )*(unsigned __int8 *)(v9 + 2 ) >> 6 ) & 0xFFFFFFC3 | 4 * (*(_BYTE *)(v9 + 1 ) & 0xF )]; v8[3 ] = aAyzpq23ijrtffg[*(_BYTE *)(v9 + 2 ) & 0x3F ]; } if ( v7 < data_0x18_1 ) { *v8 = aAyzpq23ijrtffg[(unsigned int )*(unsigned __int8 *)(input + v7) >> 2 ]; v12 = 16 * *(unsigned __int8 *)(input + v7) & 0x30 ; if ( data_0x18_1 - 1 == v7 ) { v13 = 61 ; v8[1 ] = aAyzpq23ijrtffg[v12]; } else { v8[1 ] = aAyzpq23ijrtffg[v12 | ((unsigned int )*(unsigned __int8 *)(input + v7 + 1 ) >> 4 )]; v13 = aAyzpq23ijrtffg[4 * (*(_BYTE *)(input + v7 + 1 ) & 0xF )]; } v8[3 ] = 61 ; v8[2 ] = v13; v8 += 4 ; } *v8 = 0 ; return (int )&v8[-output_1 + 1 ];
aAyzpq23ijrtffg这个数组静态分析的时候是没有特殊字符的。实际上是有动态的对其进行赋值的。修改下frida脚本。将其打印一下
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 function hook_native(){ var base_addr=Module.getBaseAddress("libnative-lib.so" ); var sub_8B04=base_addr.add(0x8B04 +1 ); Interceptor.attach(sub_8B04,{ onEnter:function(args){ console.log("========onenter========" ) this.arg0=args[0 ]; console.log("input:" ,args[1 ].readCString()); console.log(args[2 ]); //0001 B000 var dump_addr=base_addr.add(0x0001B000 ); console.log("aAyzpq23ijrtffg sub_8B04:" ,dump_addr.readCString()) },onLeave:function(retval){ console.log("========onleave========" ) console.log("output:" ,this.arg0.readCString()); } }) var sub_8ABC=base_addr.add(0x8ABC +1 ); Interceptor.attach(sub_8ABC,{ onEnter:function(args){ var dump_addr=base_addr.add(0x0001B000 ); console.log("aAyzpq23ijrtffg sub_8ABC enter:" ,dump_addr.readCString()) }, onLeave:function(retval){ var dump_addr=base_addr.add(0x0001B000 ); console.log("aAyzpq23ijrtffg sub_8ABC leave:" ,dump_addr.readCString()) } }) var encode= Module.getExportByName("libnative-lib.so" ,"Java_com_kanxue_algorithmbase_MainActivity_encodeFromJni_112" ); Interceptor.attach(encode,{ onEnter:function(args){ var dump_addr=base_addr.add(0x0001B000 ); console.log("aAyzpq23ijrtffg encode enter:" ,dump_addr.readCString()) },onLeave:function(retval){ } }) } function hook_java(){ Java.perform(function(){ var native_lib=Java.use("com.kanxue.algorithmbase.MainActivity" ); native_lib.encodeFromJni_12.implementation=function(input){ input="3ez|q|z\\8QzIU]}`bM ZMlJ]" var res=this.encodeFromJni_12(input); console.log("input:" ,input,"output:" ,res); return res; } }); } function main(){ hook_java(); hook_native(); } setImmediate(main)
下面是输出结果
1 2 3 4 5 6 7 8 9 10 aAyzpq23ijrtffg encode enter: YABhi*+QRjL^~pqrst[\])STUuZ|}klM-./ _Pb({mnoyzV!37NO@vwWHIJK`a, aAyzpq23ijrtffg sub_8ABC enter: YABhi*+QRjL^~pqrst[\])STUuZ|}klM-./ _Pb({mnoyzV!37NO@vwWHIJK`a, aAyzpq23ijrtffg sub_8ABC leave: YABhi*+QRjL^~pqrst[\])STUuZ|}klM-./ _Pb({mnoyzV!37NO@vwWHIJK`a, ========onenter======== input: 3ez|q|z\8QzIU]}`bM ZMlJ] 0x18 aAyzpq23ijrtffg sub_8B04: YABhi*+QRjL^~pqrst[\])STUuZ|}klM-./ _Pb({mnoyzV!37NO@vwWHIJK`a, ========onleave======== output: ~v]IlQ*Kk_!W\S{j]]@`T+jR*{Zi{} input: 3ez|q|z\8QzIU]}`bM ZMlJ] output: ~v]IlQ*Kk_!W\S{j]]@`T+jR*{Zi{}
这里可以看出aAyzpq23ijrtffg修改的时机似乎比较早。在调用加密前的时候这个数据就已经修改了。得到正确的key之后。再根据上面的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 bkey="""YABhi*+QRjL^~pqrst[\])STUuZ|}klM-./ _Pb({mnoyzV!37NO@vwWHIJK`a,""" input="3ez|q|z\8QzIU]}`bM ZMlJ]" input_bytes=bytes(input,"utf-8" ) output="" cnt=len(input) precnt=cnt-2 cnt1=cnt v6=0 v7=0 while True : if v7 >= precnt: break v9 = v7 v6 += 4 output += bkey[input_bytes[v7] >> 2 ] v10 = input_bytes[v7+1 ] v11 = input_bytes[v7] v7 += 3 output += bkey[(v10 >> 4 ) & 0xFFFFFFCF | 16 * (v11 & 3 )] output += bkey[((input_bytes[v9+2 ]>>0x6 ) & 0xFFFFFFC3 ) | (4 * (input_bytes[v9+1 ] & 0xF ))] output += bkey[input_bytes[v9+2 ] & 0x3F ] if v7 < cnt1: output += bkey[input_bytes[v7] >> 2 ] v12 = 16 * input_bytes[v7] & 0x30 if cnt1 - 1 == v7: v13 = '=' output += bkey[v12] else : output += bkey[v12 | (input_bytes[v7+1 ] >> 4 )] v13 = bkey[4 * (input_bytes[v7+1 ] & 0xF )] output += v13 output += '=' print(output)
最后输出结果
1 ~v]IlQ*Kk_!W\S{j]]@`T+jR*{Zi{}
和那边的结果一致了。但是还需要继续排查那个key是如何转变过来的。因为每个不同的input。key都会发生变化。所以key应该是根据input来进行计算的。上面的hook脚本处理的sub_8ABC这个函数是看到非常疑似修改了这个bkey的函数。但是hook后发现好像不是。为了精确。我调整了一下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 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 function hook_native ( ) { var base_addr=Module.getBaseAddress("libnative-lib.so" ); var sub_8B04=base_addr.add(0x8B04 +1 ); console .log("sub_8b04:" ,sub_8B04) Interceptor.attach(sub_8B04,{ onEnter:function (args ) { console .log("========onenter sub_8b04========" ) this .arg0=args[0 ]; console .log(args[1 ].readCString()); console .log(args[2 ]); },onLeave :function (retval ) { console .log("========onleave sub_8b04========" ) console .log(this .arg0.readCString()); } }) var sub_8ABC=base_addr.add(0x8ABC +1 ); console .log("sub_8ABC:" ,sub_8ABC) Interceptor.attach(sub_8ABC,{ onEnter:function (args ) { console .log("========onenter sub_8ABC========" ) console .log(args[0 ]); var dump_addr=base_addr.add(0x1B000 ); console .log("dump_addr:" ,dump_addr.readCString()); },onLeave :function (retval ) { console .log("========onleave sub_8ABC========" ); var dump_addr=base_addr.add(0x1B000 ); console .log("dump_addr:" ,dump_addr.readCString()); } }) } function hook_init_so ( ) { var dlopen_ext=Module.getExportByName(null ,"android_dlopen_ext" ); Interceptor.attach(dlopen_ext,{ onEnter:function (args ) { var soname=ptr(args[0 ]).readCString(); if (soname.indexOf("libnative-lib.so" )!=-1 ){ this .hook_start=true ; } },onLeave :function (retval ) { if (this .hook_start){ console .log("start hook" ); hook_native(); this .hook_start=false ; } } }) } function hook_java ( ) { Java.perform(function ( ) { var native_lib=Java.use("com.kanxue.algorithmbase.MainActivity" ); native_lib.encodeFromJni_12.implementation=function (input ) { input="TjFJ,U.@IyVufb'X{" var res=this .encodeFromJni_12(input); console .log("input:" ,input,"output:" ,res); return res; } }); } function main ( ) { hook_java(); hook_init_so(); } setImmediate(main)
将时机调整为打开时就hook。
1 frida -U --no-pause -f com.kanxue.algorithmbase -l lianxi2.js
然后下面是触发时的输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 sub_8b04: 0xd199bb05 sub_8ABC: 0xd199babd ========onenter sub_8ABC======== 0x11 dump_addr: AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9+/VWXnoOPQRSxy4 ========onleave sub_8ABC======== dump_addr: PHKa`#"X[cEWwvyx{z}RUT Z]\|SutbeD$'&)VYk!rdgfps_(:>GFI~^A@CBih% ========onenter sub_8b04======== TjFJ,U.@IyVufb'X{ 0x11 ========onleave sub_8b04======== T"r"}':TW)HcbT\I\Y[k Xf= input: TjFJ,U.@IyVufb'X{ output: T"r"}':TW)HcbT\I\Y[k Xf=
看到这里就证明确实是这个在这个函数里面发生的变动。接着看这个函数的处理。并且发现sub_8ABC的入参和input的长度一致。最后我们参考下面的c++代码。修改成py的实现
1 2 3 4 5 6 7 8 9 10 int __fastcall sub_8ABC (int count) { int v1; size_t i; v1 = count; for ( i = 0 ; i < strlen (aAyzpq23ijrtffg); ++i ) aAyzpq23ijrtffg[i] = aAyzpq23ijrtffg_0[i] ^ v1; return 4 * sub_133E8(v1 + 2 , 3 ) + 1 ; }
下面放上完整代码
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 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) return res.decode("utf-8" ) input="""TjFJ,U.@IyVufb'X{""" bkey=getKey(len(input)) cnt=len(input) cntpre=cnt-2 cnt1=cnt output="" print("bkey:" +bkey) input_bytes=bytes(input,"utf-8" ) v6=0 v7=0 idx=0 print(input_bytes[0 ]) print(input_bytes[0 ]&0xffffffff ) while True : v8 = idx+v6 if v7 >= cntpre: break v9 = v7 v6 += 4 output+=bkey[input_bytes[v7] >> 2 ] v10=input_bytes[v7+1 ] v11=input_bytes[v7] v7+=3 output +=bkey[(v10 >> 4 ) & 0xFFFFFFCF | 16 * (v11 & 3 )] output +=bkey[((input_bytes[v9 + 2 ] >> 6 ) & 0xFFFFFFC3 ) | (4 * (input_bytes[v9 + 1 ] & 0xF ))] output +=bkey[(input_bytes[v9 + 2 ])&0x3f ] if v7 < cnt1 : output +=bkey[input_bytes[v7] >> 2 ] v12 = 16 * input_bytes[v7] & 0x30 if cnt1 - 1 == v7: v13 = '=' output+=bkey[v12] else : output+=bkey[v12 | (input_bytes[v7+1 ] >> 4 )] v13=bkey[4 * (input_bytes[v7+1 ] & 0xF )] output+='=' output+=v13 print(output)
输出结果如下。和日志中的一致。第三题搞定。这几个算法看下来。算法部分基本是差不多的。主要变化的就是那个bkey部分。应该都是使用的base64的算法。只是key进行了调整。
1 PHKa`#"X[cEWwvyx{z}RUT Z]\|SutbeD$'&)VYk!rdgfps_(:>GFI~^A@CBih%
====================================================================================================================
第四题algorithmbase_13.apk
和上面的例子差不多的步骤。依然是ida打开。然后直接分析encodeFromJni_13函数。然后根据返回值找到最后的处理函数sub_8B04。然后写个hook脚本。这次我们直接将前面注意到每次都变化的base64的key给打印出来。脚本直接用上面的hook脚本。只修改为encodeFromJni_13即可。输出如下
1 2 3 4 5 6 7 8 9 10 11 12 13 sub_8b04: 0xd67a1b05 sub_8ABC: 0xd67a1abd ========onenter sub_8ABC======== 0x18 dump_addr: AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9+/VWXnoOPQRSxy4 ========onleave sub_8ABC======== dump_addr: YABhi*+QRjL^~pqrst[\])STUuZ|}klM-./ _Pb({mnoyzV!37NO@vwWHIJK`a, ========onenter sub_8b04======== #e9mUmm7YI!eEn}`b6jXg;Qg 0x18 ========onleave sub_8b04======== JvDHB]EyB[eUlt2_k)P`L+Jvm_5bh@2b input: #e9mUmm7YI!eEn}`b6jXg;Qg output: JvDHB]EyB[eUlt2_k)P`L+Jvm_5bh@2b
老样子。我还是用之前的py模拟来进行处理。先放上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 prelen = len - 2 ; cnt = len; output_1 = output; v6 = 0 ; v7 = 0 ; while ( 1 ) { v8 = (_BYTE *)(output_1 + v6); if ( v7 >= prelen ) break ; v9 = input + v7; v6 += 4 ; *v8 = aAyzpq23ijrtffg[(unsigned int )*(unsigned __int8 *)(input + v7) >> 2 ] ^ cnt; v10 = *(unsigned __int8 *)(input + v7 + 1 ); v11 = *(_BYTE *)(input + v7); v7 += 3 ; v8[1 ] = aAyzpq23ijrtffg[(v10 >> 4 ) & 0xFFFFFFCF | 16 * (v11 & 3 )]; v8[2 ] = aAyzpq23ijrtffg[((unsigned int )*(unsigned __int8 *)(v9 + 2 ) >> 6 ) & 0xFFFFFFC3 | 4 * (*(_BYTE *)(v9 + 1 ) & 0xF )] ^ cnt; v8[3 ] = aAyzpq23ijrtffg[*(_BYTE *)(v9 + 2 ) & 0x3F ]; } if ( v7 < cnt ) { *v8 = aAyzpq23ijrtffg[(unsigned int )*(unsigned __int8 *)(input + v7) >> 2 ]; v12 = 16 * *(unsigned __int8 *)(input + v7) & 0x30 ; if ( cnt - 1 == v7 ) { v13 = 61 ; v8[1 ] = aAyzpq23ijrtffg[v12]; } else { v8[1 ] = aAyzpq23ijrtffg[v12 | ((unsigned int )*(unsigned __int8 *)(input + v7 + 1 ) >> 4 )]; v13 = aAyzpq23ijrtffg[4 * (*(_BYTE *)(input + v7 + 1 ) & 0xF )]; } v8[3 ] = 61 ; v8[2 ] = v13; v8 += 4 ;a }
下面贴上完整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 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) return res input="""#e9mUmm7YI!eEn}`b6jXg;Qg""" input_bytes=bytes(input,"utf-8" ) bkey=getKey(len(input)) cnt = len(input) prelen = cnt - 2 output = "" v6 = 0 v7 = 0 while True : if v7 >= prelen: break v9 = v7 v6 += 4 output += chr(bkey[input_bytes[v7] >> 2 ] ^ cnt) v10 = input_bytes[v7+1 ] v11 = input_bytes[v7] v7 += 3 output += chr(bkey[(v10 >> 4 ) & 0xFFFFFFCF | 16 * (v11 & 3 )]) output += chr(bkey[(input_bytes[v9+2 ] >> 6 ) & 0xFFFFFFC3 | 4 * (input_bytes[v9+1 ] & 0xF )] ^ cnt) output += chr(bkey[input_bytes[v9+2 ] & 0x3F ]) if v7 < cnt: output += chr(bkey[input_bytes[v7] >> 2 ]) v12 = 16 * input_bytes[v7] & 0x30 if cnt - 1 == v7: v13 = '=' output += bkey[v12] else : output += bkey[v12 | (input_bytes[v7+1 ] >> 4 )] v13 = bkey[4 * (input_bytes[v7+1 ] & 0xF )] output += v13 output += '=' print(output)
输出结果对比正确。第四题完成
1 JvDHB]EyB[eUlt2_k)P`L+Jvm_5bh@2b