app的版本是 10.37.0
定位 抓包啥的没啥好说的,参数定位直接jadx搜索,很快定位到,直接上图最终调用so加密的函数
该app有frida检测,怎么绕过看上一篇绕过libmsaoaidsec.so的Frida检测
从函数看加密跟aes相关,但其实魔改了多很,其中laesEncryptByteArr
的三个参数,bArr是明文,str可以理解为扩展后的key,bArr2是iv,str和bArr2都是固定的
unidng分析 直接搭起框架,函数调用
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 package com.zhihu;import com.github.unidbg.AndroidEmulator;import com.github.unidbg.Module;import com.github.unidbg.linux.android.AndroidEmulatorBuilder;import com.github.unidbg.linux.android.AndroidResolver;import com.github.unidbg.linux.android.dvm.AbstractJni;import com.github.unidbg.linux.android.dvm.DalvikModule;import com.github.unidbg.linux.android.dvm.StringObject;import com.github.unidbg.linux.android.dvm.VM;import com.github.unidbg.linux.android.dvm.array.ByteArray;import com.github.unidbg.memory.Memory;import com.github.unidbg.virtualmodule.android.AndroidModule;import com.github.unidbg.virtualmodule.android.JniGraphics;import com.github.unidbg.virtualmodule.android.MediaNdkModule;import com.github.unidbg.virtualmodule.android.SystemProperties;import java.io.File;import java.util.ArrayList;import java.util.List;public class encrypt extends AbstractJni { private final AndroidEmulator emulator; private final VM vm; private final Module module ; public encrypt () { emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.zhihu.android" ).build(); final Memory memory = emulator.getMemory(); memory.setLibraryResolver(new AndroidResolver (23 )); vm = emulator.createDalvikVM(new File ("unidbg-android/src/test/resources/zhihu/zhihu10.37.0.apk" )); vm.setJni(this ); vm.setVerbose(true ); new AndroidModule (emulator, vm).register(memory); new MediaNdkModule (emulator, vm).register(memory); new JniGraphics (emulator, vm).register(memory); new SystemProperties (emulator, null ).register(memory); DalvikModule dm = vm.loadLibrary("bangcle_crypto_tool" , true ); module = dm.getModule(); dm.callJNI_OnLoad(emulator); } public static String bytesToHex (byte [] bytes) { StringBuilder hexString = new StringBuilder (); for (byte b : bytes) { String hex = Integer.toHexString(b & 0xFF ); if (hex.length() == 1 ) { hexString.append('0' ); } hexString.append(hex); } return hexString.toString(); } public void callAesEncryptByteArr () { List<Object> list = new ArrayList <>(10 ); list.add(vm.getJNIEnv()); list.add(0 ); byte [] input = {-117 , -115 , -128 , -119 , 33 , 40 , 34 , -127 , -115 , 35 , 41 , -115 , 35 , 41 , -127 , -128 , 33 , -126 , 41 , -120 , 34 , -128 , 35 , -120 , -125 , -128 , -118 , 42 , 42 , 35 , -128 , -126 }; byte [] iv = {-103 , 48 , 58 , 58 , 50 , 52 , 58 , 57 , -110 , -110 , 58 , 59 , 58 , -103 , -110 , -110 }; String key = "541a3a5896fbefd351917c8251328a236a7efbf27d0fad8283ef59ef07aa386dbb2b1fcbba167135d575877ba0205a02f0aac2d31957bc7f028ed5888d4bbe69ed6768efc15ab703dc0f406b301845a0a64cf3c427c82870053bd7ba6721649c3a9aca8c3c31710a6be5ce71e4686842732d9314d6898cc3fdca075db46d1ccf3a7f9b20615f4a303c5235bd02c5cdc791eb123b9d9f7e72e954de3bcbf7d314064a1eced78d13679d040dd4080640d18c37bbde" ; ByteArray arr1 = new ByteArray (vm, input); ByteArray arr2 = new ByteArray (vm, iv); list.add(vm.addLocalObject(arr1)); list.add(vm.addLocalObject(new StringObject (vm, key))); list.add(vm.addLocalObject(arr2)); Number number = module .callFunction(emulator, 0x9e98 , list.toArray()); ByteArray resultArr = vm.getObject(number.intValue()); System.out.println("result: " + bytesToHex(resultArr.getValue())); } public static void main (String[] args) { encrypt e = new encrypt (); e.callAesEncryptByteArr(); } }
运行报错了,该问题内存未分配,调用free释放内存导致的问题
这里可以直接在调用free的地方patch掉,不执行free就可以了
把traceCode开启,看报错的位置
1 emulator.traceCode(module .base, module .base + module .size);
1 2 3 4 5 6 7 8 9 10 [00 :19 :30 167 ][libbangcle_crypto_tool.so 0x048c8 ] [a01740f9] 0x120048c8 : "ldr x0, [x29, #0x28]" x29=0xe4fff5b0 => x0=0x12052000 [00 :19 :30 168 ][libbangcle_crypto_tool.so 0x048cc ] [1f001feb] 0x120048cc : "cmp x0, xzr" => nzcv: N=0 , Z=0 , C=1 , V=0 x0=0x12052000 [00 :19 :30 168 ][libbangcle_crypto_tool.so 0x048d0 ] [20010054 ] 0x120048d0 : "b.eq #0x120048f4" nzcv: N=0 , Z=0 , C=1 , V=0 [00 :19 :30 169 ][libbangcle_crypto_tool.so 0x048d4 ] [a01740f9] 0x120048d4 : "ldr x0, [x29, #0x28]" x29=0xe4fff5b0 => x0=0x12052000 [00 :19 :30 169 ][libbangcle_crypto_tool.so 0x048d8 ] [52f6ff97] 0x120048d8 : "bl #0x12002220" [00 :19 :30 177 ][libbangcle_crypto_tool.so 0x02220 ] [f00000b0] 0x12002220 : "adrp x16, #0x1201f000" => x16=0x1201f000 [00 :19 :30 179 ][libbangcle_crypto_tool.so 0x02224 ] [11ba47f9] 0x12002224 : "ldr x17, [x16, #0xf70]" x16=0x1201f000 => x17=0x1218bac0 [00 :19 :30 182 ][libbangcle_crypto_tool.so 0x02228 ] [10c23d91] 0x12002228 : "add x16, x16, #0xf70" x16=0x1201f000 => x16=0x1201ff70 [00 :19 :30 184 ][libbangcle_crypto_tool.so 0x0222c ] [20021fd6] 0x1200222c : "br x17" x17=0x1218bac0Invalid address 0x12052000 passed to free: value not allocated [crash]A/libc: Invalid address 0x12052000 passed to free: value not allocated
程序是在0x0222c这个位置奔溃的,ida调到对应位置
1 2 3 4 void free (void *p) { free (p); }
free函数,可以往上一点调到调用它的位置,0x048d8,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void __fastcall check_package_name (__int64 a1) { char *pkgname; if ( aComZhihuAndroi[0 ] ) { pkgname = get_pkgname(a1); if ( !strcmp (aComZhihuAndroi, pkgname) ) dword_20024 = 1 ; if ( pkgname ) free (pkgname); } else { dword_20024 = 1 ; } }
直接在 0x48d8 调转到free那里把指令改为npo,就不会执行了。
1 2 3 4 5 6 7 public void patch () { try (Keystone keystone = new Keystone (KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian);) { KeystoneEncoded encoded = keystone.assemble("nop" ); byte [] patchCode = encoded.getMachineCode(); emulator.getMemory().pointer(module .base + 0x48d8 ).write(patchCode); } }
很顺利不用补环境直接跑出来了结果
1 result: 0f02c75267dd7becc6b32f941837721f6f89610d4c49c3b5a8ddd649767e953de0a02e505b8e684fb5654137c266f12e
开始ida分析函数
ida分析
整个函数看下来,只有 sub_8B2C 这个函数最可疑跟进查看
好多aes加密相关的函数,怎么判断程序调用了哪个
记录函数对内存的访问以及发起访问的地址(PC指针),绘制成折线图,可以看函数的调用情况
使用Unidbg的ReadHook,监控这个函数的地址范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void traceAESRead () { ReadHook readHook = new ReadHook () { @Override public void hook (Backend backend, long address, int size, Object user) { int now = emulator.getBackend().reg_read(Arm64Const.UC_ARM64_REG_PC).intValue(); if ((now > module .base) & (now < (module .base + module .size))){ System.out.println(now - module .base); } } @Override public void onAttach (UnHook unHook) {} @Override public void detach () {} }; emulator.getBackend().hook_add_new(readHook, module .base+0x8b2c , module .base+module .size+0x9080 , null ); }
trade函数放在调用函数前执行,将打印出来的地址放到一个文件里,用python执行
1 2 3 4 5 6 7 8 9 10 import matplotlib.pyplot as pltimport numpyinput = numpy.loadtxt("trace.txt" , int )plt.plot(range (len (input )), input ) plt.ylim(23500 ,29000 ) plt.plot(range (len (input )), input ) plt.show()
三次分组,每次分组执行都一样10轮调用,跳到24000即 0x5dc0 的位置
调用的函数是 Bangcle_WB_LAES_encrypt ,这里就是aes的加密逻辑了,查找引用看参数来源
把 Bangcle_WB_LAES_encrypt; 作为参数传给了 Bangcle_CRYPTO_cbc128_encrypt,
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 __int64 __fastcall Bangcle_CRYPTO_cbc128_encrypt ( __int64 input_data, __int64 buff, int input_data_len, __int64 iv, __int64 key, void (__fastcall *Bangcle_WB_LAES_encrypt)(__int64, __int64, __int64, unsigned int *)) { unsigned int v12; __int64 iv_1; int i; v12 = 0 ; iv_1 = iv; while ( input_data_len > 15 ) { for ( i = 0 ; i <= 15 ; ++i ) *(_BYTE *)(buff + i) = *(_BYTE *)(input_data + i) ^ *(_BYTE *)(iv_1 + i); Bangcle_WB_LAES_encrypt(buff, buff, key, &v12); iv_1 = buff; input_data_len -= 16 ; input_data += 16LL ; buff += 16LL ; } return v12; }
unidbg调试函数 使用unidbg对 Bangcle_CRYPTO_cbc128_encrypt 进行hook查看参数
参数一跟我们一开始前面的明文hex对比 8b8d8089212822818d23298d23298180218229882280238883808a2a2a238082
后面填充了16字节的 9b,这里填充是固定的,有兴趣的可以自己探索
1 2 3 4 5 6 7 8 9 10 11 12 13 mx0 >-----------------------------------------------------------------------------< [01 :24 :35 310 ]x0=RW@0x12353060 , md5=630 d2fa2c2528055ad2374d1d4f9419b, hex=8b 8d8089212822818d23298d23298180218229882280238883808a2a2a2380829b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 size: 112 0000 : 8B 8 D 80 89 21 28 22 81 8 D 23 29 8 D 23 29 81 80 ....!("..#).#).. 0010: 21 82 29 88 22 80 23 88 83 80 8A 2A 2A 23 80 82 !.)." .#....**#..0020 : 9B 9B 9B 9B 9B 9B 9B 9B 9B 9B 9B 9B 9B 9B 9B 9B ................0030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................0040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................0050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................0060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................^-----------------------------------------------------------------------------^
参数二是空的,用来存放结果的
参数三是 0x30,明文填充后的长度
参数四是 iv
1 2 3 4 5 6 7 8 9 10 11 12 13 mx3 >-----------------------------------------------------------------------------< [01 :34 :12 007 ]x3=RW@0x12054000 [libc++.so]0x4000 , md5=d2b01352c3f52a790b79eb18ddc8938c, hex=99303a3a32343a3992923a3b3a999292000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 size: 112 0000 : 99 30 3A 3A 32 34 3A 39 92 92 3A 3B 3A 99 92 92 .0 ::24 :9. .:;:...0010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................0020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................0030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................0040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................0050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................0060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................^-----------------------------------------------------------------------------^
参数五看不出是啥,也没跟key对应,需要在往上看来源,这里注意参数五出来的是指针,需要打印指针的内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 m0x123580c0 176 >-----------------------------------------------------------------------------< [01 :56 :59 956 ]RW@0x123580c0 , md5=070a52023ae19a58ba6af416776beb5f, hex=8cc1bbc96bc566b80528b0777044afe8475bb7b8d7f563bb1d906c77817f05f1ee0c4b61cf4fd3619a744038a4b0f887036de86538dacfb2d951843df75d3cf5fb0ead3988157a3f2a2211ba9c18e9fe73d212241f0183a05d757ea66e80f0d8260b251051b1d44bb07252166917c70eecdd96f9a9d03d09ae5748d5002b811a35457064266861a73891d7fdc5f1286f87a52a68d300c4019fede9401c704ad4edd9095dc91e3780123c14cbb663a1e4 size: 176 0000 : 8C C1 BB C9 6B C5 66 B8 05 28 B0 77 70 44 AF E8 ....k.f..(.wpD..0010 : 47 5B B7 B8 D7 F5 63 BB 1D 90 6C 77 81 7F 05 F1 G[....c...lw....0020 : EE 0C 4B 61 CF 4F D3 61 9A 74 40 38 A4 B0 F8 87 ..Ka.O.a.t@8. ...0030 : 03 6D E8 65 38 DA CF B2 D9 51 84 3D F7 5D 3C F5 .m.e8....Q.=.]<.0040 : FB 0E AD 39 88 15 7A 3F 2A 22 11 BA 9C 18 E9 FE ...9 ..z?*"...... 0050: 73 D2 12 24 1F 01 83 A0 5D 75 7E A6 6E 80 F0 D8 s..$....]u~.n... 0060: 26 0B 25 10 51 B1 D4 4B B0 72 52 16 69 17 C7 0E &.%.Q..K.rR.i... 0070: EC DD 96 F9 A9 D0 3D 09 AE 57 48 D5 00 2B 81 1A ......=..WH..+.. 0080: 35 45 70 64 26 68 61 A7 38 91 D7 FD C5 F1 28 6F 5Epd&ha.8.....(o 0090: 87 A5 2A 68 D3 00 C4 01 9F ED E9 40 1C 70 4A D4 ..*h.......@.pJ. 00A0: ED D9 09 5D C9 1E 37 80 12 3C 14 CB B6 63 A1 E4 ...]..7..<...c.. ^-----------------------------------------------------------------------------^
就sub_3BA8和sub_47BC调用了v19,既上面的参数五
其实是在 sub_3BA8 对key做了一个异或
1 2 3 for ( i = 4 ; i < str_data_len; ++i ) *(_BYTE *)(*(_QWORD *)buff + i - 4LL ) = str_data[i] ^ str_data[i % 3 ]; *(_DWORD *)(buff + 8 ) = str_data_len - 4 ;
key的长度是180字节,这里出来的结果是176个字节,所以这里就是密钥编排了,可以对这个函数hook,在返回前打印,因为key是固定的,所以可以直接拿编排后的使用。这里出来的结果就是上面的参数五了
好了,参数来源有了,回到上面继续看 Bangcle_CRYPTO_cbc128_encrypt 的逻辑,明文长度填充后是48,刚好3个分组,每次明文加密后会作为iv进行下一分组的加密
1 2 3 4 5 6 7 8 9 10 11 v12 = 0 ; iv_1 = iv; while ( input_data_len > 15 ){ for ( i = 0 ; i <= 15 ; ++i ) *(_BYTE *)(buff + i) = *(_BYTE *)(input_data + i) ^ *(_BYTE *)(iv_1 + i); Bangcle_WB_LAES_encrypt(buff, buff, key, &v12); iv_1 = buff; input_data_len -= 16 ; input_data += 16LL ; buff += 16LL ; }
跟进Bangcle_WB_LAES_encrypt
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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 __int64 __fastcall Bangcle_WB_LAES_encrypt (__int64 input_data, __int64 buff, __int64 *key_state) { key_state_1 = *key_state; round_count = *((_DWORD *)key_state + 4 ) / 32 + 6 ; for ( i = 0 ; i <= 15 ; ++i ) *(&v36 + i) = (16 * (byte_B248[(unsigned __int8)(16 * (*(_BYTE *)(input_data + i) >> 4 )) ^ (*(unsigned __int8 *)(key_state_1 + i) >> 4 ) & 0xF ] >> 4 )) ^ (byte_B248[(unsigned __int8)(16 * *(_BYTE *)(input_data + i)) ^ *(_BYTE *)(key_state_1 + i) & 0xF ] >> 4 ); for ( j = 1 ; j < round_count; ++j ) { v20 = HIBYTE(dword_B548[v36]); v21 = BYTE2(dword_B548[v36]); v22 = BYTE1(dword_B548[v36]); v23 = dword_B548[v36]; v24 = HIBYTE(dword_B548[v40]); v25 = BYTE2(dword_B548[v40]); v26 = BYTE1(dword_B548[v40]); v27 = dword_B548[v40]; v28 = HIBYTE(dword_B548[v44]); v29 = BYTE2(dword_B548[v44]); v30 = BYTE1(dword_B548[v44]); v31 = dword_B548[v44]; v32 = HIBYTE(dword_B548[v48]); v33 = BYTE2(dword_B548[v48]); v34 = BYTE1(dword_B548[v48]); v35 = dword_B548[v48]; v4 = HIBYTE(dword_B948[v41]); v5 = BYTE2(dword_B948[v41]); v6 = BYTE1(dword_B948[v41]); v7 = dword_B948[v41]; v8 = HIBYTE(dword_B948[v45]); v9 = BYTE2(dword_B948[v45]); v10 = BYTE1(dword_B948[v45]); v11 = dword_B948[v45]; v12 = HIBYTE(dword_B948[v49]); v13 = BYTE2(dword_B948[v49]); v14 = BYTE1(dword_B948[v49]); v15 = dword_B948[v49]; v16 = HIBYTE(dword_B948[v37]); v17 = BYTE2(dword_B948[v37]); v18 = BYTE1(dword_B948[v37]); v19 = dword_B948[v37]; for ( i = 0 ; i <= 15 ; ++i ) *(&v20 + i) = (16 * (byte_B348[(unsigned __int8)(16 * ((unsigned __int8)*(&v20 + i) >> 4 )) ^ ((unsigned __int8)*(&v4 + i) >> 4 ) & 0xF ] >> 4 )) ^ (byte_B348[(unsigned __int8)(16 * *(&v20 + i)) ^ *(&v4 + i) & 0xF ] >> 4 ); v4 = HIBYTE(dword_BD48[v46]); v5 = BYTE2(dword_BD48[v46]); v6 = BYTE1(dword_BD48[v46]); v7 = dword_BD48[v46]; v8 = HIBYTE(dword_BD48[v50]); v9 = BYTE2(dword_BD48[v50]); v10 = BYTE1(dword_BD48[v50]); v11 = dword_BD48[v50]; v12 = HIBYTE(dword_BD48[v38]); v13 = BYTE2(dword_BD48[v38]); v14 = BYTE1(dword_BD48[v38]); v15 = dword_BD48[v38]; v16 = HIBYTE(dword_BD48[v42]); v17 = BYTE2(dword_BD48[v42]); v18 = BYTE1(dword_BD48[v42]); v19 = dword_BD48[v42]; for ( i = 0 ; i <= 15 ; ++i ) *(&v20 + i) = (16 * (byte_B348[(unsigned __int8)(16 * ((unsigned __int8)*(&v20 + i) >> 4 )) ^ ((unsigned __int8)*(&v4 + i) >> 4 ) & 0xF ] >> 4 )) ^ (byte_B348[(unsigned __int8)(16 * *(&v20 + i)) ^ *(&v4 + i) & 0xF ] >> 4 ); v4 = HIBYTE(dword_C148[v51]); v5 = BYTE2(dword_C148[v51]); v6 = BYTE1(dword_C148[v51]); v7 = dword_C148[v51]; v8 = HIBYTE(dword_C148[v39]); v9 = BYTE2(dword_C148[v39]); v10 = BYTE1(dword_C148[v39]); v11 = dword_C148[v39]; v12 = HIBYTE(dword_C148[v43]); v13 = BYTE2(dword_C148[v43]); v14 = BYTE1(dword_C148[v43]); v15 = dword_C148[v43]; v16 = HIBYTE(dword_C148[v47]); v17 = BYTE2(dword_C148[v47]); v18 = BYTE1(dword_C148[v47]); v19 = dword_C148[v47]; for ( i = 0 ; i <= 15 ; ++i ) *(&v20 + i) = (16 * (byte_B348[(unsigned __int8)(16 * ((unsigned __int8)*(&v20 + i) >> 4 )) ^ ((unsigned __int8)*(&v4 + i) >> 4 ) & 0xF ] >> 4 )) ^ (byte_B348[(unsigned __int8)(16 * *(&v20 + i)) ^ *(&v4 + i) & 0xF ] >> 4 ); for ( i = 0 ; i <= 15 ; ++i ) *(&v36 + i) = (16 * (byte_B348[(unsigned __int8)(16 * ((unsigned __int8)*(&v20 + i) >> 4 )) ^ (*(unsigned __int8 *)(key_state_1 + 16 * j + i) >> 4 ) & 0xF ] >> 4 )) ^ (byte_B348[(unsigned __int8)(16 * *(&v20 + i)) ^ *(_BYTE *)(key_state_1 + 16 * j + i) & 0xF ] >> 4 ); } v20 = byte_C548[v36]; v21 = byte_C548[v41]; v22 = byte_C548[v46]; v23 = byte_C548[v51]; v24 = byte_C548[v40]; v25 = byte_C548[v45]; v26 = byte_C548[v50]; v27 = byte_C548[v39]; v28 = byte_C548[v44]; v29 = byte_C548[v49]; v30 = byte_C548[v38]; v31 = byte_C548[v43]; v32 = byte_C548[v48]; v33 = byte_C548[v37]; v34 = byte_C548[v42]; v35 = byte_C548[v47]; for ( i = 0 ; ; ++i ) { result = (unsigned int )i; if ( i > 15 ) break ; *(_BYTE *)(buff + i) = (16 * (byte_B448[(unsigned __int8)(16 * ((unsigned __int8)*(&v20 + i) >> 4 )) ^ (*(unsigned __int8 *)(key_state_1 + 16 * j + i) >> 4 ) & 0xF ] >> 4 )) ^ (byte_B448[(unsigned __int8)(16 * *(&v20 + i)) ^ *(_BYTE *)(key_state_1 + 16 * j + i) & 0xF ] >> 4 ); } return result; }
看到里面的dword_B548,dword_B948等等都是一些大表
在还原算法时候可以直接拿来用。
既然 Bangcle_WB_LAES_encrypt 是加密的地方了,那应该或多或少有aes影子
可以先看以下round_count是不是真的是10
round_count = *((_DWORD *)data_ptr + 4) / 32 + 6;
用unidbg查看,这里是_DWORD类型,每4个字节一组,查看第4*4的字节位置截取4字节
1 2 3 4 5 6 7 8 9 10 11 12 13 mx2 >-----------------------------------------------------------------------------< [02 :20 :49 310 ]x2=unidbg@0xe4fff530 , md5=70 dda4f32687556b499d15b8cca17ed6, hex=c080351200000000b0000000040000008000000001000000000000000000000000000000000000007c5d00120000000000000000000000001000000030000000603035120000000000000000ffffffffe0f5ffe400000000000000000000000010f6ffe4000000004c8f001200000000 size: 112 0000 : C0 80 35 12 00 00 00 00 B0 00 00 00 04 00 00 00 ..5 .............0010 : 80 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................0020 : 00 00 00 00 00 00 00 00 7 C 5 D 00 12 00 00 00 00 ........|]......0030 : 00 00 00 00 00 00 00 00 10 00 00 00 30 00 00 00 ............0 ...0040 : 60 30 35 12 00 00 00 00 00 00 00 00 FF FF FF FF `05. ............0050 : E0 F5 FF E4 00 00 00 00 00 00 00 00 00 00 00 00 ................0060 : 10 F6 FF E4 00 00 00 00 4 C 8F 00 12 00 00 00 00 ........L.......^-----------------------------------------------------------------------------^
80 00 00 00 就是我们要的,记得转为小端序 00 00 00 80,对应的十进制是128,计算结果是 128/32+6 = 10
for ( j = 1; j < round_count; ++j ) 刚好9轮
整个看下来多多少少能看出来,一开始就使用input_data和key_state进行异或,可以标记为初始轮密钥加
1 2 for ( i = 0 ; i <= 15 ; ++i ) *(&v36 + i) = (16 * (byte_B248[(unsigned __int8)(16 * (*(_BYTE *)(input_data + i) >> 4 )) ^ (*(unsigned __int8 *)(key_state_1 + i) >> 4 ) & 0xF ] >> 4 )) ^ (byte_B248[(unsigned __int8)(16 * *(_BYTE *)(input_data + i)) ^ *(_BYTE *)(key_state_1 + i) & 0xF ] >> 4 );
每个for循环结束前的异或也是轮密钥加,中间整块就是字节替换,循环左移,列混淆
for循环结束后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 v20 = byte_C548[v36]; v21 = byte_C548[v41]; v22 = byte_C548[v46]; v23 = byte_C548[v51]; v24 = byte_C548[v40]; v25 = byte_C548[v45]; v26 = byte_C548[v50]; v27 = byte_C548[v39]; v28 = byte_C548[v44]; v29 = byte_C548[v49]; v30 = byte_C548[v38]; v31 = byte_C548[v43]; v32 = byte_C548[v48]; v33 = byte_C548[v37]; v34 = byte_C548[v42]; v35 = byte_C548[v47];
编排后就是 字节替换和循环左移了
1 2 3 4 v36 v40 v44 v48 v36 v40 v44 v48 v37 v41 v45 v49 ==> v41 v45 v49 v37 v38 v42 v46 v50 v46 v50 v38 v42 v39 v43 v47 v51 v51 v39 v43 v47
最后结束前就是轮密钥加
这个加密看下来很顺滑,没有混淆过,扔给gpd就能让它给出对应的python代码了。
其中几个点,比如
1 2 3 4 v20 = HIBYTE(dword_B548[v36]); v21 = BYTE2(dword_B548[v36]); v22 = BYTE1(dword_B548[v36]); v23 = dword_B548[v36];
假设一个 <font style="color:rgb(64, 64, 64);">DWORD</font>
的值为 <font style="color:rgb(64, 64, 64);">0xAABBCCDD</font>
,它的字节结构如下:
字节位置
字节值
字节 3
AA
最高有效字节
字节 2
BB
字节 1
CC
字节 0
DD
最低有效字节
还有v20,v21,v22等都是连续存储的,通过v20的指针按偏移取后面的值
上结果 0f02c75267dd7becc6b32f941837721f6f89610d4c49c3b5a8ddd649767e953de0a02e505b8e684fb5654137c266f12e
跟unidbg跑出来的一样