本帖最后由 whklhh 于 2017-10-3 12:53 编辑
Reversing.kr是韩国的一个逆向题目网站
有分国度的排行榜,还是挺有意思的
本题即来自于第十二关AutoHotKey
http://reversing.kr/challenge.php可以下载到文件
解压出来一个ReadMe一个exe
这次的ReadMe终于看懂了,要求找到两个md5,解密后以空格连接作为flag
那么这两个md5会以什么形式出现呢……
查壳显示UPX,乖乖用工具脱掉后再运行提示“Exe corrupted”
未脱壳的程序可以正常运行,为包括文本框和两个按钮的GUI程序
那么很明显,这个提示意味着自校验
用OD逐步跟踪弹窗函数,同时以IDA辅助观察整体结构
最终找到了sub_4508C7函数中
IDA显示在上层调用中直接以它的布尔返回值来决定是否弹错误提示
分析自校验函数:
[C++] 纯文本查看 复制代码 int __thiscall sub_4508C7(void *this, int a2, char *a3)
{
void *file; // esi@1
FILE *v4; // eax@1
signed int v6; // eax@3
signed int v7; // eax@5
signed int i; // edx@5
__int32 v9; // eax@7
FILE *v10; // ST18_4@7
int v11; // eax@7
signed int v12; // eax@12
signed int v13; // edi@19
int v14; // edi@20
int v15; // ecx@20
signed int v16; // [sp-4h] [bp-550h]@16
char v17; // [sp+Ch] [bp-540h]@1
CHAR Filename; // [sp+410h] [bp-13Ch]@1
char v19[16]; // [sp+518h] [bp-34h]@11
char v20[8]; // [sp+528h] [bp-24h]@4
char v21[8]; // [sp+530h] [bp-1Ch]@4
int v22; // [sp+538h] [bp-14h]@9
int v23; // [sp+53Ch] [bp-10h]@7
int v24; // [sp+540h] [bp-Ch]@20
__int32 v25; // [sp+544h] [bp-8h]@7
char v26; // [sp+54Bh] [bp-1h]@17
char *v27; // [sp+558h] [bp+Ch]@7
file = this;
sub_450F56(&v17);
GetModuleFileNameA(0, &Filename, 0x104u);
v4 = fopen(&Filename, aRb);
*(_DWORD *)file = v4;
if ( !v4 )
return 1;
v6 = 0;
do
{
v20[v6] = byte_466514[v6];
v21[v6] = byte_46650C[v6];
++v6;
}
while ( v6 < 8 ); // 复制十六个字节
v7 = strlen(a3);
*((_DWORD *)file + 68) = 0;
for ( i = 0; i < v7; ++i )
*((_DWORD *)file + 68) += a3;
fseek(*(FILE **)file, -8, 2); // 将文件指针设置到倒数前8个字节处(2表示文件尾END,-8表示偏移offset)
fread((char *)file + 4, 4u, 1u, *(FILE **)file);// 读取前4个字节存储在file+4处
v9 = ftell(*(FILE **)file);
v10 = *(FILE **)file;
v25 = v9; // 文件指针当前位置,即倒数4个字节处
fread(&v23, 4u, 1u, v10); // 读取后4个字节放入v23中
fseek(*(FILE **)file, 0, 0);
v27 = 0;
v11 = 0;
if ( v25 > 0 )
{
do
{
if ( *(_BYTE *)(*(_DWORD *)file + 12) & 0x10 )
break;
fread(&v22, 1u, 1u, *(FILE **)file);
v11 = sub_450F95((int)&v17, v22); // k=*(a1 + 1024)
// result = k<<8 ^ a1 + 4*(a2^(k>>24))
// k=result
// return result
++v27;
}
while ( (signed int)v27 < v25 );
}
if ( v23 != (v11 ^ 0xAAAAAAAA) ) // v11 = k,(v22从第1字节循环到倒数第4字节)
// v23=后4字节
goto LABEL_25;
fseek(*(FILE **)file, *((_DWORD *)file + 1), 0);// 将文件指针设置到*(file+4)即倒数第5-8字节的值处
if ( !fread(v19, 0x10u, 1u, *(FILE **)file) ) // 读取16个字节入v19
goto LABEL_25;
v12 = 0;
do
{
if ( v19[v12] != v20[v12] ) // 与固定内容比较,v20\v21来自于之前的赋值
break;
++v12;
}
while ( v12 < 16 );
if ( v12 != 16 )
{ // 失败
LABEL_25:
v16 = 3;
LABEL_19:
v13 = v16;
fclose(*(FILE **)file);
return v13;
}
fread(&v26, 1u, 1u, *(FILE **)file); // 读取一个字节,要求为3
if ( v26 != 3 )
{
v16 = 4;
goto LABEL_19;
}
fread(&v24, 4u, 1u, *(FILE **)file); // 读4个字节放入v24
v14 = v24 ^ 0xFAC1;
fread((char *)file + 12, 1u, v24 ^ 0xFAC1, *(FILE **)file);// 再往后读取第(读取内容^0xfac1)个字节,放入file+12
sub_450ABA((int)file + 12, v14, v14 + 50130); // 复杂计算,改变file+12处的值
// 跟踪知解密出来是一串md5
*((_BYTE *)file + v14 + 12) = 0;
v15 = 0;
for ( *((_DWORD *)file + 68) = 0; v15 < v14; ++v15 )
*((_DWORD *)file + 68) += *((_BYTE *)file + v15 + 12);// 将md5的值累加至file+68
*((_DWORD *)file + 2) = ftell(*(FILE **)file);
return 0;
}
还是有点复杂的,需要慢慢分析
以二进制形式open自己以后,通过文件指针来灵活变换和读取
关键要注意到fread是从文件指针读取内容的API,并且会让文件指针响应后移;fseek是设置文件指针的API
大体流程:
文件最后4字节要等于一个经过复杂循环计算的值异或0xAAAAAAAA
前4字节要为一段文件中确定序列的位置,该序列为:
前16字节等于程序最初从byte_466514和byte_46650c处复制的16个字节
下1字节等于03
下4字节与0xFAC1异或,得数作为下一次读取的长度
读取刚才算出数的长度的数后,与长度+50130进行复杂运算
结果累加至一个值
要跳过弹窗的话,只需要将最后8字节改成符合要求即可
不过直接用可以通过自校验的原程序进行动态调试的过程中就能发现:
最后复杂运算的结果就是32位长度的可见字符串,估计它就是第一串md5了,用http://pmd5.com/解密可知:
下一步就是找到第二串md5了
虽然当点击OK后程序自动结束,没有任何提示
但是我们还可以断API、查看调用堆栈向上跟踪IDA中搜索GetWindowTextA,查看交叉引用发现有很多函数,按往常的套路我猜在DialogFunc中,不过为了以防万一,还是到OD中下断吧
调用树仍然查不到信息,于是在GetDlgItemTextA和GetWindowTextA中各下断点,随便输入后点击OK,断到:
IDA中查看00425FB7,果然是DigLogFunc~继续往下跟踪,在查看内存地址的时候不小心又看到了一处有意思的字符串:
虽然不明白是啥意思,但是这个32位可见字符串很吸引人呢,解密试试:
更稳妥一点的话,对buffer地址下访问断点,F9后可以断到某处strcmp中:
仍然是它,那么flag就是这俩组合了,提交完成
以往的题目名还能看出来一些端倪,比方说twist暗示了整个流程的恶心;WindowsKernel暗示内核驱动,这次的AutoHotKey不明白跟内容有什么关系……
|