题目来源至 https://www.malwaretech.com/beginner-malware-reversing-challenges
所有挑战都是在不使用调试器的情况下完成的,你的目标应该是能够在不运行exe的情况下完成每个挑战。
string3难度等级为2星
strings3.exe包含存储在可执行文件中的未加密flag。运行时,程序将输出flag的MD5哈希值,但不输出原始值。你能得到flag吗?
本地解压后如下:
拖进IDA里,查看下,发现与string1与string2完全不一样。
还是原先的步骤,先找到hash生成函数。
这里发现生成hash的函数使用了类的结构,因为call ??0MD5@@QAE@XZ ; MD5::MD5(void) 执行了类对象的构造方法。
重点来到 call ?digestString@MD5@@QAEPADPAD@Z ; MD5::digestString(char *)
这里是生成hash的主方法,IDA标识了eax为char* 类型,与这个方法的参数类型一致,所以判断push eax为参数入栈。
lea eax, [ebp+var_4A0]
eax的值为var_4A0变量的地址,var_4A0变量在上面的call ds:LoadStringA这个API函数中有涉及。
LoadStringA函数
从与指定模块关联的可执行文件中加载字符串资源,并将字符串复制到具有终止空字符的缓冲区中,或者返回指向字符串资源本身的只读指针。
int LoadStringA( HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int cchBufferMax );
参数
hInstance
类型:HINSTANCE
可执行文件包含字符串资源的模块实例的句柄。要获取应用程序本身的句柄,请使用NULL调用GetModuleHandle函数。
uID
输入:UINT
要加载的字符串的标识符。
lpBuffer
类型:LPTSTR
接收字符串的缓冲区(如果cchBufferMax非零)或者是字符串资源本身的只读指针(如果cchBufferMax为零)。长度必须足以容纳指针(8个字节)。
cchBufferMax
输入:int
缓冲区的大小,以字符为单位。如果字符串长于指定的字符数,则该字符串将被截断并以null结尾。如果此参数为0,则lpBuffer将接收指向字符串资源本身的只读指针。
来源: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-loadstringa
win32 API函数的调用约定为stdcall,参数从右至左入栈。
所以上面的4个push操作就是参数入栈。
mov [ebp+var_8], eax
mov eax, 1
shl eax, 8
xor edx, edx
inc edx
shl edx, 4
or eax, edx
mov [ebp+var_4], eax
push 3FFh
lea ecx, [ebp+var_4A0]
push ecx
mov edx, [ebp+var_4]
push edx
push 0
call ds:LoadStringA
cchBufferMax为3FFh,十进制为1023
lpBuffer为ecx
uID为edx
hInstance为0,也就是为NULL
mov [ebp+var_4A0], 0
lea ecx, [ebp+var_4A0]
lpBuffer为ecx,ecx为var_4A0变量所在的地址,而往上查,发现var_4A0已经被赋值为0,所以ecx为0。
uID为edx,edx=[ebp+var_4],而[ebp+var_4]=eax。
eax的计算是下面这几条汇编指令:
mov eax, 1
shl eax, 8
xor edx, edx
inc edx
shl edx, 4
or eax, edx
最后算得eax=272。
LoadStringA(0, 272, &var_4A0, 1023);
这里还有个快速的方法,F5反编译。直接使用IDAF5插件反编译功能翻译成伪c代码。
LoadStringA函数是加载字符串资源,所以目前需要使用另一个工具,ResourceHacker。
uID为要加载的字符串的标识符,使用ResourceHacker打开string3,来到字符串资源处。
查找下272标识符里的内容
正确flag