菱菱邦sd字段白盒AES逆向
decheckcode逆向
so初步分析
ida打开libencrypt.so
,函数窗口搜索Java
说明是静态注册的,打开checkcode
发现有近2000行代码,那还是先看decheckcode
吧。
在77
上按r
将其转为字符
这就很明显了,由于sd
字符串都是以M
开头的,所以调用的是aes_decrypt1_ptr
。
看起来是个AES-128-CBC
解密,看看CWAESCipher::WBACRAES128_DecryptCBC
结构很清晰了,继续看看
image-20220106211733714 image-20220106211821169this
的值是off_1ADC0
unidbg分析
so初步看完了,接下来就是结合unidbg进行分析
先hook看看base64_decode
。
由于base64_decode
多次被调用到,所以选择先在sub_138AC
下断点
public LingLingBang() {
//...
emulator.attach().addBreakPoint(module.base + 0x138AC+1);
}
然后输入blr
在函数返回处下断点,c
继续运行到函数返回处,这时候输入b0xe535
在base64_decode
函数下断点,c
继续运行到base64_decode
入口
看看r0
的数据
blr
在函数返回处下断点,c
继续运行到函数返回处,看看原r1
的值。
和cyberchef对比一下
image-20220107093512475是标准base64实现
接下来就是看看aes的实现,尝试网上搜索WBACRAES
和WBACR AES
从搜索结果看,是白盒AES,这就有点麻烦了。这样一来,我们无法直接拿到AES的key
和iv
,然后验证。虽然块解密的过程和标准AES实现不太一样,但是整体流程还是一样的。
接下来hook一下CWAESCipher::WBACRAES_DecryptOneBlock
,看看块解密的输入输出
emulator.attach().addBreakPoint(module.base + 0x5910+1);
image-20220107100254521
和之前base64的结果对比可以看出,这就是最后一块的数据。
blr
在函数返回处下断点,c
继续运行到函数返回处,看看原r1
的值。
这就是最后一块解密后的数据,我们根据AES-CBC
的流程,把它和前一块的数据进行异或试试。
s0 = bytes.fromhex('75 9E 38 5F F2 04 21 EC E9 11 8F EF C7 D9 BF 6D')
s1 = bytes.fromhex('4D AC 0C 0F B6 31 56 AD A8 50 CE D2 C3 DD BB 69')
s2 = bytes(map(lambda x:x[0]^x[1], zip(s0, s1)))
image-20220107100853991
数据正确,这样一来,我们只要解决块解密,就能逆向出算法了。
C++还原块解密
奈何想从白盒AES里找到key还是很困难的,所以我选择把块解密的方法抠出来,用C++
实现,导出为dll
文件,然后python再调用dll
文件。实在是无能为力了。。
#include <stdio.h>
#include <string.h>
typedef char int8;
typedef signed char sint8;
typedef unsigned char uint8;
typedef short int16;
typedef signed short sint16;
typedef unsigned short uint16;
typedef int int32;
typedef signed int sint32;
typedef unsigned int uint32;
#define _BYTE uint8
#define _WORD uint16
#define _DWORD uint32
#define _QWORD uint64
#define LOBYTE(x) (*((_BYTE*)&(x))) // low byte
#define LOWORD(x) (*((_WORD*)&(x))) // low word
#define LODWORD(x) (*((_DWORD*)&(x))) // low dword
#define HIBYTE(x) (*((_BYTE*)&(x)+1))
#define HIWORD(x) (*((_WORD*)&(x)+1))
#define HIDWORD(x) (*((_DWORD*)&(x)+1))
#define BYTEn(x, n) (*((_BYTE*)&(x)+n))
#define WORDn(x, n) (*((_WORD*)&(x)+n))
#define BYTE1(x) BYTEn(x, 1) // byte 1 (counting from 0)
#define BYTE2(x) BYTEn(x, 2)
#define BYTE3(x) BYTEn(x, 3)
#define BYTE4(x) BYTEn(x, 4)
#define BYTE5(x) BYTEn(x, 5)
#define BYTE6(x) BYTEn(x, 6)
#define BYTE7(x) BYTEn(x, 7)
#define BYTE8(x) BYTEn(x, 8)
#define BYTE9(x) BYTEn(x, 9)
#define BYTE10(x) BYTEn(x, 10)
#define BYTE11(x) BYTEn(x, 11)
#define BYTE12(x) BYTEn(x, 12)
#define BYTE13(x) BYTEn(x, 13)
#define BYTE14(x) BYTEn(x, 14)
#define BYTE15(x) BYTEn(x, 15)
#define WORD1(x) WORDn(x, 1)
#define WORD2(x) WORDn(x, 2) // third word of the object, unsigned
#define WORD3(x) WORDn(x, 3)
#define WORD4(x) WORDn(x, 4)
#define WORD5(x) WORDn(x, 5)
#define WORD6(x) WORDn(x, 6)
#define WORD7(x) WORDn(x, 7)
_BYTE unk_18D8E[] = {0,0,1,3,2,2,3,1};
_BYTE invFirstRoundTable_auth1[] = {0x8d,0xba,0xd5,/* 省略 */0xd9,0x62,0x74,0xb8}; // invFirstRoundTable_auth1
_BYTE byte_invRoundTables_auth1[] = {0,0,0,0,/* 省略 */93,241,194,24,79};
_DWORD* invRoundTables_auth1 = (_DWORD*)byte_invRoundTables_auth1;
_BYTE invXorTables_auth1[] = {0, 0, 0, /* 省略 */6, 7, 13, 3, 11};
// extern "C" _declspec(dllexport) int WBACRAES_DecryptOneBlock(unsigned __int8 *a2, unsigned __int8 *a3, int a4);
int WBACRAES_DecryptOneBlock(unsigned __int8 *a2, unsigned __int8 *a3, int a4)
{
unsigned __int8 (*v5)[8]; // r3
int v6; // r11
char *v7; // r1
int v8; // r3
_DWORD *v9; // r6
int j; // r2
int v11; // r10
int v12; // r0
int v13; // r0
void **v14; // r8
int v15; // r0
int k; // r2
char *v17; // r3
char v18; // r5
int v19; // r0
char v20; // r1
int v21; // r6
unsigned int v22; // r5
int v23; // r1
char v24; // r8
_BYTE *v25; // r12
int v26; // r12
int v27; // r7
_BYTE *v28; // r8
int v29; // r8
int v30; // r7
int v31; // r2
char *v32; // r1
int i; // r3
_BYTE *v34; // r5
int v35; // r8
char *v36; // r6
int v37; // r8
int l; // r3
int m; // r2
int v41; // [sp+0h] [bp-E0h]
_BYTE *v42; // [sp+4h] [bp-DCh]
int v43; // [sp+8h] [bp-D8h]
int v44; // [sp+Ch] [bp-D4h]
int v47; // [sp+1Ch] [bp-C4h]
char v49[4]; // [sp+30h] [bp-B0h] BYREF
int v50; // [sp+34h] [bp-ACh]
_DWORD s[42]; // [sp+38h] [bp-A8h] BYREF
memset(s, 0, 0x20u);
// v44 = CSecFunctProvider::PrepareAESMatrix((CSecFunctProvider *)a2, (unsigned __int8 *)&word_10, (int)s, v5);
for (int i=0; i!=4; i++) {
for(int j=0; j!=4; j++) {
*((_BYTE *)s + i + 8 * j) = *((_BYTE *)a2 + 4 * i + j);
}
}
// for (int i=0; i<2; i++) {
// for (int j=0; j<16; j++) {
// printf("%02x ", *((_BYTE *)s + i*16 + j));
// }
// printf("\n");
// }
// printf("\n");
v44 = 0;
if ( !v44 )
{
v6 = 10;
while ( v6 >= a4 )
{
if ( !--v6 )
{
if ( a4 == 1 )
{
s[8] = s[0];
s[9] = s[1];
s[10] = s[2];
s[11] = s[3];
s[12] = s[4];
s[13] = s[5];
s[14] = s[6];
s[15] = s[7];
v30 = 1;
do
{
v31 = 0;
v32 = (char *)&unk_18D8E;
for ( i = 0; i != 4; ++i )
{
//if ( v30 == 1 || v30 != 2 )
//{
v34 = invFirstRoundTable_auth1;
v35 = (v32[1] + (_BYTE)v6) & 3;
//}
// else
// {
// v34 = &invFirstRoundTable_auth2_ptr;
// v35 = (v32[1] + (_BYTE)v6) & 3;
// }
v32 += 2;
v36 = (char *)&s[2 * i + 32] + v35;
v37 = i + 4 * v35;
*((_BYTE *)s + v6 + v31) = *((_BYTE *)v34 + 256 * v37 + (unsigned __int8)*(v36 - 96));
v31 += 8;
}
++v6;
}
while ( v6 != 4 );
}
break;
}
v7 = (char *)&unk_18D8E;
v8 = 0;
v47 = 4 * v6;
do
{
v9 = &s[2 * v8 + 32];
for ( j = 0; j != 4; ++j )
{
v11 = 1;
// if ( v11 == 1 )
// {
v15 = (v7[1] + (_BYTE)j) & 3;
v50 = invRoundTables_auth1[256 * (v8 + 4 * (v15 + v47)) + *((unsigned __int8 *)v9 + v15 - 128)];
// }
// else
// {
// v12 = (v7[1] + (_BYTE)j) & 3;
// v13 = *((unsigned __int8 *)v9 + v12 - 128) + ((v8 + 4 * (v12 + v47)) << 8);
// if ( v11 == 2 )
// v14 = &invRoundTables_auth2_ptr;
// else
// v14 = &invRoundTables_auth1_ptr;
// v50 = *((_DWORD *)*v14 + v13);
// }
s[4 * v8 + 16 + j] = v50;
}
++v8;
v7 += 2;
}
while ( v8 != 4 );
for ( k = 0; k != 4; ++k )
{
v17 = (char *)&s[16] + k;
v41 = 0;
do
{
v43 = 0;
v49[1] = v17[16];
v18 = *v17;
v19 = 96 * v6 + 24 * v41;
v49[2] = v17[32];
v20 = v17[48];
v21 = v18 & 0xF;
v49[0] = v18;
v22 = v18 & 0xF0;
v49[3] = v20;
v23 = 6 * k;
do
{
v24 = v49[++v43];
// if ( v11 == 1 || v11 != 2 )
v25 = invXorTables_auth1;
// else
// v25 = &invXorTables_auth2_ptr;
v42 = v25;
v26 = v23 + 1;
v27 = v24 & 0xF0 | (v22 >> 4);
LOBYTE(v21) = v42[256 * (v19 + v23) + (v21 | (16 * (v24 & 0xF)))] & 0xF;
// if ( v11 == 1 || v11 != 2 )
v28 = invXorTables_auth1;
// else
// v28 = &invXorTables_auth2_ptr;
v23 += 2;
v22 = (unsigned __int8)(16 * *((_BYTE *)v28 + 256 * (v26 + v19) + v27));
}
while ( v43 != 3 );
v29 = v41;
v17 += 4;
*((_BYTE *)&s[2 * k] + v41++) = v22 | v21;
}
while ( v29 != 3 );
}
}
for ( l = 0; l != 4; ++l )
{
for ( m = 0; m != 4; ++m )
a3[4 * l + m] = *((_BYTE *)&s[2 * m] + l);
}
}
return v44;
}
int main() {
_BYTE a2[16] = {0x8D, 0x63, 0xD7, 0x56, 0xDB, 0x55, 0xCD, 0x06, 0x56, 0x70, 0xB9, 0x74, 0xE6, 0x24, 0xB5, 0x86};
_BYTE a3[16];
int ret;
for(int i=0; i<16; i++){
printf("%02X ", a2[i]);
}
printf("\n");
ret = WBACRAES_DecryptOneBlock(a2, a3, 1);
// 75 9E 38 5F F2 04 21 EC E9 11 8F EF C7 D9 BF 6D
for(int i=0; i<16; i++){
printf("%02X ", a3[i]);
}
printf("\n");
return 0;
}
其中有些分支由于入参的原因,是不会进入的,所以直接去掉
image-20220107104958267然后就是编译,运行,看看代码有没有问题
image-20220107105509651和unidbg上是一样的,接下来就是把它编译成dll
文件,供python调用了。
编译生成dll文件
visual studio选择新建项目->动态连接库
image-20220107102627044右键文件,选择属性,修改为不使用预编译头。
image-20220107102934260接下来就是把之前的C++
代码复制到文件里,需要添加一行代码
extern "C" _declspec(dllexport) int WBACRAES_DecryptOneBlock(unsigned __int8 *a2, unsigned __int8 *a3, int a4);
由于我的python和windows是64位版本的,所以还要设置一下生成的版本
image-20220107104016304最后就是选择生成->生成解决方案就行了。
python调用dll文件
接下来就是用python调用dll文件
import base64
import gzip
from ctypes import *
dll_path = r".\Dll1.dll"
cryptor = cdll.LoadLibrary(dll_path)
decrypt_block = cryptor.WBACRAES_DecryptOneBlock
decrypt_block.restype = c_int
def aes_decrypt(data):
previous = bytes([0]*16)
bucket = []
for idx in range(0, len(data), 16):
block = data[idx: idx+16]
din = c_char_p(block)
dout = c_char_p(bytes([0]*16))
decrypt_block(din, dout, 1)
out = bytes(map(lambda x: x[0] ^ x[1], zip(dout._objects, previous)))
print(out)
bucket.append(out)
previous = block
output = b''.join(bucket)
output = output[:-output[-1]]
return output
def decrypt(data):
data1 = base64.b64decode(data[1:])
data3 = aes_decrypt(data1)
print(data3)
data4 = base64.b64decode(data3)
print(data4)
data5 = gzip.decompress(data4)
print(data5)
def test():
data = 'MLj4JOPMKPdGeytkeBPy/TvgGCgIOWegKndYgDVbmrctTnrov1z7Uei09BKfKhlW/xnLMOg1HaIN7llJqgPFjmayzV1OI2Rqd7heXxQw1VAmVCe6yXyiiyQ5OHaZ7hhRm5BRpmlFqgY1ebjsv8WPnnw+OZXydnPcySv4Lt5bwmHrOQfygHI7KhDFN40NU8/Csuumues12zRmtaQH9ycfD8fnu27yxX0UQeZBL/VPS+UVNrAwPtjFWrahQztLD3btpjWPXVttVzQZWcLl05iS1hg=='
decrypt(data)
if __name__ == '__main__':
test()
image-20220107103545173
至此,AES-CBC-decrypt
的逆向也就完成了,代码仅供把玩。
网友评论