好友
阅读权限20
听众
最后登录1970-1-1
|
这个Reverse比较古老,已经有20多年了,但难度确实不小。
先查壳
upx压缩壳,0.72,废弃版本,工具无法解压。
反正不用IDA进行调试,直接x32dbg中,dump内存,保存后拖入IDA。
这里说一下,网上常规的脱壳都是OEP->dump->IAT表修复等步骤,如果不需要在IDA中进行调试,直接dump内存,就可以IDA分析静态代码,伪代码都是正常的。
[C] 纯文本查看 复制代码 int __usercall sub_4020A0@<eax>(int a1@<ecx>, int a2@<ebx>, int a3@<edi>, int a4@<esi>)
{
int v4; // eax
char *i; // ecx
int v6; // ebx
char *v7; // edi
int v8; // eax
int v9; // esi
int v10; // eax
char *j; // ecx
int v12; // ebx
int v14; // eax
char v15[100]; // [esp+8Ch] [ebp-66Ch] BYREF
char v16[500]; // [esp+F0h] [ebp-608h] BYREF
char v17[500]; // [esp+2E4h] [ebp-414h] BYREF
char v18[500]; // [esp+4D8h] [ebp-220h] BYREF
int v19; // [esp+6CCh] [ebp-2Ch] BYREF
int v20; // [esp+6D0h] [ebp-28h] BYREF
int v21; // [esp+6D4h] [ebp-24h] BYREF
_DWORD v22[4]; // [esp+6D8h] [ebp-20h] BYREF
char v23[4]; // [esp+6E8h] [ebp-10h] BYREF
char v24[4]; // [esp+6ECh] [ebp-Ch] BYREF
char v25[4]; // [esp+6F0h] [ebp-8h] BYREF
int v26; // [esp+6F4h] [ebp-4h]
v26 = a1;
if ( (unsigned int)dword_417158(a2, a4, a3) >= 0x80000000 )
dword_4171D0(v15, aSoftwareMicros_0);
else
dword_4171D0(v15, aSoftwareMicros);
memset(v16, 0, sizeof(v16));
memset(v17, 0, sizeof(v17));
memset(v18, 0, sizeof(v18));
sub_412706((_DWORD *)(v26 + 0x5C), (int)v16, 100);// 获取name
sub_412706((_DWORD *)(v26 + 0xAC), (int)v17, 100);// code
strcpy(v18, v16);
sub_408D70(v18); // 逆序
strcat(v16, v18);
dword_41700C(0x80000002, v15, &v19);
v20 = 1;
v21 = 256;
dword_417008(v19, dword_422480, 0, &v20, v18, &v21);
strcat(v16, v18);
v20 = 1;
v21 = 256;
dword_417008(v19, dword_4224A0, 0, &v20, v18, &v21);
strcat(v16, v18);
v4 = 0;
for ( i = v16; *i; ++v4 )
++i;
v6 = v4;
sub_4014E0(v22);
v7 = &v16[v6];
*(_DWORD *)v7 = 0;
*((_DWORD *)v7 + 1) = 0;
*((_DWORD *)v7 + 2) = 0;
*((_DWORD *)v7 + 3) = 0;
*((_DWORD *)v7 + 4) = 0;
*((_DWORD *)v7 + 5) = 0;
*((_DWORD *)v7 + 6) = 0;
*((_DWORD *)v7 + 7) = 0;
*((_DWORD *)v7 + 8) = 0;
*((_DWORD *)v7 + 9) = 0;
*((_DWORD *)v7 + 10) = 0;
*((_DWORD *)v7 + 11) = 0;
*((_DWORD *)v7 + 12) = 0;
*((_DWORD *)v7 + 13) = 0;
*((_DWORD *)v7 + 14) = 0;
*((_DWORD *)v7 + 15) = 0;
*((_DWORD *)v7 + 16) = 0;
*((_DWORD *)v7 + 17) = 0;
*((_DWORD *)v7 + 18) = 0;
v7[76] = 0;
v7[77] = 0;
v7[78] = 0;
v16[v6] = 0x80;
v8 = 64 - (((_BYTE)v6 + 1) & 0x3F);
if ( v8 <= 7 )
v8 = 128 - (((_BYTE)v6 + 1) & 0x3F);
v9 = v8 + v6 + 1;
v10 = 0;
for ( j = v16; *j; ++v10 )
++j;
v12 = 0;
for ( *(_DWORD *)&v16[v9 - 8] = 8 * v10; v12 < v9; v12 += 64 )
sub_401500((int)&v16[v12], v22);
if ( sub_404797((int)v17, (unsigned __int8 *)"%lx%lx%lx", v23, v24, v25) != 3 )// sscanf
return sub_4115A6(aHmmmYouDonTEve, aFailed, 48);// MessageBox
sub_401AD0((int *)v23, dword_41C2C0, dword_41C3C0);
sub_401AD0((int *)v24, dword_41C4C0, dword_41C5C0);
v14 = -3;
while ( *(&v26 + v14) == v22[v14 + 3] )
{
if ( !++v14 )
return sub_4115A6(aManYouReGoodEn, aWelcome, 64);// MessageBox
}
return sub_4115A6(aBetterLuckNext, aFailed, 48);// MessageBox
}
代码不多,整体逻辑并不复杂,首先获取name和code,然后name反转(指定的name是KCTF,反转就是FTCK),反转后的name拼接3次,再拼接原始name,最后组成FTCKFTCKFTCKKCTF,再调用sub_401500进行魔改hash计算,hash值是不可逆的,算code也不需要完全复现hash算法,根据调试,KCTF为用户名,得到的hash值是0D15B8BA2C8F30576BD8A9AEC0C6DBFA。
sub_404797就是sscanf,要求输入3个16进制字符串,再调用sub_401AD0,经过2次不同的掩码数组加密,得到3组_DWORD值,并将hash值看作_DWORD数组,数组的前三个值,与加密后的3组值一一对应。
[C] 纯文本查看 复制代码 int *__cdecl sub_401AD0(int *a1, _DWORD *a2, _DWORD *a3)
{
int v5; // esi
int v6; // edi
int v7; // esi
int v8; // edi
int *result; // eax
int *v10; // [esp+18h] [ebp-24h]
unsigned __int8 v11; // [esp+1Ch] [ebp-20h]
int v12; // [esp+1Ch] [ebp-20h]
int v13; // [esp+20h] [ebp-1Ch]
unsigned __int8 v14; // [esp+20h] [ebp-1Ch]
int v15; // [esp+28h] [ebp-14h]
v5 = 0;
v6 = 0;
v10 = a1 + 1;
do
{
v13 = *a3 & *v10;
v11 = sub_401AB0(*a2 & *a1);
++v5;
++a3;
++a2;
v6 = ((unsigned __int8)sub_401AB0(v13) ^ v11) & 1 ^ (2 * v6);
}
while ( (unsigned __int8)v5 < 0x20u );
v7 = 0;
v15 = v6;
v8 = 0;
do
{
v12 = *a3 & *v10;
v14 = sub_401AB0(*a2 & *a1);
++v7;
++a3;
++a2;
v8 = ((unsigned __int8)sub_401AB0(v12) ^ v14) & 1 ^ (2 * v8);
}
while ( (unsigned __int8)v7 < 0x20u );
result = a1;
*a1 = v15;
*v10 = v8;
return result;
}
[C] 纯文本查看 复制代码 int __cdecl sub_401AB0(int a1)
{
int v1; // edx
int result; // eax
v1 = a1;
for ( result = 0; v1; v1 &= v1 - 1 )
++result;
return result;
}
sub_401AB0是计算参数的二进制表示中,1的个数,也就是汉明距离。sub_401AD0中,将2次汉明距离异或后,与1做按位与运算,也就是汉明距离的奇偶性,并以此作为2进制值计算出v6和v8.
说实话,这个逆运算困扰了我很久,按照反编译的代码,根本找不到逆运算的任何思路。看雪论坛在当年有一个帖子解释这个程序里有一句话,汉明距离奇偶性的判断有另外的等效计算方法,而那个方法是可逆的。于是寻找奇偶性计算的替代方法。
其实,奇偶性计算就是一个偶消奇不消的过程,值为1的位数是奇数还是偶数,那么,把每一bit进行异或即可,但没必要运行那么多次,折半异或性能更好。于是找到替代函数
[C] 纯文本查看 复制代码 static int haming_weight(_QWORD x)
{
x = (x >> 32) ^ (x & 0xffffffff);
x = (x >> 16) ^ (x & 0xffff);
x = (x >> 8) ^ (x & 0xff);
x = (x >> 4) ^ (x & 0xf);
x = (x >> 2) ^ (x & 0x3);
x = (x >> 1) ^ (x & 0x1);
return x;
}
同时,汉明距离异或值的奇偶性,等于汉明距离奇偶的异或值,这里就有一种豁然开朗的感觉,入参数组的data[0]和data[1]可以看作一个64位整数,两组key同时拼接为一组64为整数的数组,于是sub_401AD0可以等效替换为
[C] 纯文本查看 复制代码 static void __cdecl encrypt(_DWORD* Data, _DWORD* maskKey1, _DWORD* maskKey2)
{
_DWORD v8 = 0;
_DWORD v6 = 0;
for (int i = 0;i < 32;++i)
{
_QWORD data = ((_QWORD)Data[1] << 32) | Data[0];
_QWORD key = ((_QWORD)maskKey2[i] << 32) | maskKey1[i];
_QWORD key2 = ((_QWORD)maskKey2[i + 32] << 32) | maskKey1[i + 32];
//printf("Data:%llX, key:%llX, key2:%llX\n", data, key, key2);
v6 = haming_weight(data & key) ^ (v6 << 1);
v8 = haming_weight(data & key2) ^ (v8 << 1);
}
Data[0] = v6;
Data[1] = v8;
}
但到了这里,解密仍然是个老大难问题,干脆把代码交给deepseek试试,AI给出的结论是要用逆矩阵来解密,逆矩阵用高斯消元法。于是,经过AI多次给出错误答案并修修补补后,得到最终解密函数
[C] 纯文本查看 复制代码 // 预计算密钥的奇偶性矩阵
static void build_key_matrix(_DWORD* maskKey1, _DWORD* maskKey2, _DWORD matrix[64][64]) {
for (int i = 0; i < 32; ++i) {
_QWORD key1 = ((_QWORD)maskKey2[31 - i] << 32) | maskKey1[31 - i];
_QWORD key2 = ((_QWORD)maskKey2[63 - i] << 32) | maskKey1[63 - i];
// 填充矩阵的奇数行(key1)和偶数行(key2)
for (int j = 0; j < 64; ++j) {
matrix[i][j] = (key1 >> j) & 1;
matrix[i + 32][j] = (key2 >> j) & 1;
}
}
}
static bool matrix_inverse(_DWORD input[64][64], _DWORD output[64][64])
{
_DWORD aug[64][128]; // 增广矩阵 [I | A]
// 初始化增广矩阵
for (int i = 0; i < 64; ++i) {
for (int j = 0; j < 64; ++j) {
aug[i][j] = input[i][j];
aug[i][j + 64] = (i == j) ? 1 : 0; // 右侧初始化为单位矩阵
}
}
// 高斯-若尔当消元法
for (int col = 0; col < 64; ++col) {
// 寻找主元
int pivot = -1;
for (int row = col; row < 64; ++row) {
if (aug[row][col]) {
pivot = row;
break;
}
}
if (pivot == -1) return false; // 矩阵不可逆
// 交换行
if (pivot != col) {
for (int j = 0; j < 128; ++j) {
uint8_t temp = aug[col][j];
aug[col][j] = aug[pivot][j];
aug[pivot][j] = temp;
}
}
// 消元处理
for (int row = 0; row < 64; ++row) {
if (row != col && aug[row][col]) {
for (int j = 0; j < 128; ++j) {
aug[row][j] ^= aug[col][j];
}
}
}
}
// 提取逆矩阵
for (int i = 0; i < 64; ++i) {
for (int j = 0; j < 64; ++j) {
output[i][j] = aug[i][j + 64];
}
}
return true;
}
static void __cdecl decrypt(_DWORD* Data, _DWORD* maskKey1, _DWORD* maskKey2) {
_DWORD key_matrix[64][64];
_DWORD inv_matrix[64][64];
// 预计算阶段
build_key_matrix(maskKey1, maskKey2, key_matrix);
matrix_inverse(key_matrix, inv_matrix); // 需实现矩阵求逆
// 解密阶段
_DWORD cipher_bits[64];
for (int i = 0; i < 32; i++) {
cipher_bits[i] = (Data[0] >> (i)) & 1;
cipher_bits[i + 32] = (Data[1] >> (i)) & 1;
}
// 矩阵乘法恢复原始数据
_QWORD plain = 0;
for (int i = 0; i < 64; i++) {
_DWORD bit = 0;
for (int j = 0; j < 64; j++) {
bit ^= inv_matrix[i][j] & cipher_bits[j];
}
plain ^= (_QWORD)bit << i;
}
Data[0] = (DWORD)(plain & 0xFFFFFFFF);
Data[1] = (DWORD)(plain >> 32);
}
最后,解密过程为
[C] 纯文本查看 复制代码 _DWORD dword_41C5C0[] = {
0xDFCF8EDF, 0xFE7C76FE, 0xFCF8EDFC, 0xF9F1DBF9, 0xF3E3B7F2, 0xBF9F1DBF, 0x7F3E3B7F, 0xE7C76FE5,
0xCF8EDFCB, 0x7C76FE5F, 0xF8EDFCBE, 0x9F1DBF97, 0x3E3B7F2F, 0x8EDFCBE0, 0x1DBF97C1, 0xC76FE5F0,
0xF1DBF97C, 0xE3B7F2F8, 0x3B7F2F82, 0x76FE5F05, 0xBF97C156, 0x6FE5F055, 0xDBF97C15, 0xB7F2F82A,
0xDFCBE0AB, 0xEDFCBE0A, 0xF97C1561, 0xF2F82AC3, 0xE5F05586, 0x7F2F82AC, 0xFE5F0558, 0xFCBE0AB0,
0xCBE0AB0C, 0x97C15619, 0x2F82AC32, 0x5F055865, 0x7C156197, 0xF82AC32E, 0xF055865C, 0xE0AB0CB9,
0xC1561973, 0x82AC32E7, 0xBE0AB0CB, 0x055865CE, 0x0AB0CB9C, 0x2AC32E72, 0x15619739, 0x55865CE4,
0xAB0CB9C8, 0xAC32E723, 0x5865CE46, 0xB0CB9C8D, 0x6197391B, 0xC32E7236, 0x865CE46C, 0x0CB9C8D9,
0x56197391, 0x65CE46CB, 0xCB9C8D96, 0x197391B2, 0x32E72365, 0x97391B2C, 0x2E723659, 0x5CE46CB3
};
_DWORD dword_41C4C0[] = {
0xAE10E7F5, 0x7087FFB6, 0xE10FFF6D, 0x421FDEDF, 0x843FBDBF, 0xDC21EFEF, 0x3843FFDB, 0x887F5B7B,
0x90FE96F3, 0x07F45782, 0x0FE8AF04, 0xA1FD0DE3, 0xC3FA3BC3, 0xFE8AF04F, 0x7D15C09B, 0x7F457827,
0x1FD15E09, 0x3FA2BC13, 0xFA2B8136, 0x74572268, 0x15CA5A45, 0xC5728693, 0x515CA9A5, 0xA2B9534B,
0x0AE52D22, 0xE8AE44D0, 0xDCA5845F, 0x394B28BB, 0x72965177, 0x2B94B48B, 0x57296916, 0xAE52D22D,
0xE52CA2EF, 0x4A5965DB, 0x94B2CBB7, 0xA965B76A, 0x2596BDA5, 0x4B2D7B4A, 0x965AF695, 0xACB5CD2F,
0xD96BBA5B, 0x32D754B3, 0xD2CB4ED0, 0x65AEA967, 0xCB5D52CE, 0x2D750B30, 0x16BA8598, 0x5AEA1660,
0xB5D42CC0, 0x5750D30E, 0xAEA1A61D, 0xDD436C3E, 0x3A86F879, 0x750DF0F2, 0xEA1BE1E5, 0x5437E3CF,
0xEBA87985, 0x21BF7E74, 0x437EFCE8, 0xA86FC79E, 0xD0DFAF38, 0x86FDF9D1, 0x8DFBD3A7, 0x9BF7874A
};
_DWORD dword_41C3C0[] = {
0x9B4D1349, 0x369A2692, 0xCDA689A4, 0xDA689A48, 0xB4D13490, 0x69A26921, 0xD344D243, 0x6D344D24,
0xA689A486, 0x4D13490D, 0x9A26921A, 0x689A486B, 0x344D2435, 0xD13490D7, 0xA26921AE, 0x89A486BA,
0x44D2435D, 0x13490D75, 0x26921AEA, 0x4D2435D5, 0x9A486BAA, 0x3490D754, 0x6921AEA9, 0xD2435D52,
0xA486BAA5, 0x490D754B, 0x921AEA96, 0x2435D52C, 0x486BAA58, 0x90D754B1, 0x21AEA962, 0x435D52C4,
0x86BAA589, 0x1AEA9624, 0x0D754B12, 0x35D52C48, 0x6BAA5890, 0xD754B120, 0x5D52C481, 0xBAA58902,
0xAEA96240, 0xEA96240A, 0x754B1205, 0xAA58902A, 0x54B12055, 0xA96240AA, 0x52C48154, 0xD52C4815,
0xA58902A9, 0x4B120553, 0x96240AA7, 0x2C48154E, 0x58902A9D, 0xB120553A, 0x6240AA75, 0xC48154EA,
0x48154EAB, 0x902A9D56, 0x20553AAC, 0x8902A9D5, 0x120553AA, 0x240AA755, 0x8154EAB3, 0x40AA7559
};
_DWORD dword_41C2C0[] = {
0x0BCFEF35, 0x179FDE6B, 0xC5E7E798, 0x5E7F79AC, 0xBCFEF359, 0xF9FDC6B7, 0x73FBAD6A, 0x2F3FBCD6,
0xE7F75AD5, 0x4FEE95AF, 0x9FDD2B5E, 0xFF74CD76, 0xBFBA76B9, 0x7EE9BAE8, 0xFDD375D1, 0xF74D974E,
0x7BA6CBA7, 0x6E9B0E99, 0xDD361D32, 0x3A6C1A60, 0x74D834C0, 0xE9B06981, 0x5360F306, 0xA6C1E60C,
0xCD83EC1D, 0x1B07F83F, 0x360FF07E, 0x6C1FE0FD, 0xD83FC1FA, 0x307FA3F0, 0x60FF47E1, 0xC1FE8FC2,
0x03FD3F80, 0x0FF4FE02, 0x07FA7F01, 0x1FE9FC04, 0x3FD3F808, 0x7FA7F010, 0x7E9FE047, 0xFD3FC08E,
0xFF4FE021, 0xF4FF4232, 0x7A7FA119, 0xD3FD48C3, 0x27FAB183, 0x4FF56306, 0x9FEAC60D, 0x69FEA461,
0xBFD5AC1E, 0xFFAB7839, 0x7F56D076, 0xFEADA0ED, 0x7D5B61DE, 0xFAB6C3BC, 0x756DA77D, 0xEADB4EFA,
0x2DB58F80, 0x5B6B1F00, 0xB6D63E01, 0x55B6BDF1, 0xAB6D7BE3, 0xD6DAD7C2, 0x5B589808, 0xEDAC5C06
};
_DWORD Data[] = { 0xBAB8150D, 0x57308F2C, 0xAEA9D86B };
decrypt(Data + 1, dword_41C4C0, dword_41C5C0);
decrypt(Data, dword_41C2C0, dword_41C3C0);
printf("%X %X %X\n", Data[0], Data[1], Data[2]);
附上程序内执行成功的截图
|
免费评分
-
查看全部评分
|