PETools查看概况
32位程序,入口点 Section: [UPX1], EP: 0x00012760
有upx壳,脱壳:.\upx.exe -d <your path to>\mfykm1.exe
根据参考链接2,也可以用ollydbg手动脱壳,以后再学吧。
被逆向exe可在参考链接2下载。
作者:hans774882968以及hans774882968
x64dbg动态分析
搜索字符串,点击Good Job
那个串,即可定位到以下代码段。
0040150B | mov eax,dword ptr ss:[ebp-4] |
0040150E | cmp eax,dword ptr ds:[444410] |
00401514 | jne mfykm1.401524 |
00401516 | mov dword ptr ss:[esp],mfykm1.4400B0 | 4400B0:"\n\nGood Job! Make a keygen and tutorial and send it in to crackmes.de\n"
0040151D | call <JMP.&printf> |
00401522 | jmp mfykm1.401530 |
00401524 | mov dword ptr ss:[esp],mfykm1.4400F6 | 4400F6:"\n\nTry again, sorry!\n"
0040152B | call <JMP.&printf> |
00401530 | mov dword ptr ss:[esp],mfykm1.44010B | 44010B:"PAUSE"
复制代码
关键跳就是00401514 | jne mfykm1.401524
,而比较的两个主体是eax
和ds:[444410]
。
本来我想试试内存断点功能的,但是后来发现不同于ollydbg,x64dbg的内存断点是内存页的,没啥用。
求[444410]
的代码就在若干printf语句的上面,很容易找到:
0040147E | push ebp |
0040147F | mov ebp,esp |
00401481 | sub esp,18 |
00401484 | and esp,FFFFFFF0 |
00401487 | mov eax,0 |
0040148C | add eax,F |
0040148F | add eax,F |
00401492 | shr eax,4 |
00401495 | shl eax,4 |
00401498 | mov dword ptr ss:[ebp-8],eax |
0040149B | mov eax,dword ptr ss:[ebp-8] |
0040149E | call mfykm1.40D1E0 |
004014A3 | call mfykm1.40CE20 |
004014A8 | call mfykm1.401390 |
004014AD | mov eax,dword ptr ds:[444558] |
004014B2 | imul eax,dword ptr ds:[444554] |
004014B9 | add eax,dword ptr ds:[44455C] |
004014BF | mov edx,eax |
004014C1 | sub edx,dword ptr ds:[444558] |
004014C7 | mov eax,dword ptr ds:[44455C] |
004014CC | imul eax,eax,CDD |
004014D2 | lea eax,dword ptr ds:[edx+eax] |
004014D5 | add eax,dword ptr ds:[44455C] |
004014DB | mov dword ptr ds:[444410],eax |
复制代码
代码很短,而且可以看到只有3个函数调用。其中004014A8 | call mfykm1.401390
是最后一个函数调用,因此我们在4014a8
处打断点,然后点“重新运行”。3个函数调用都执行后,发现几个比较重要的内存[444554], [444558], [44455c]
的值由全0变成了6, 2, 23f0
。运行完后[444410]
的值是parseInt("01CE8E1A",16) // 30314010
,把这个30314010输进去,果然是正确的。
进一步分析
根据以上代码,[444410]
的值只依赖于这3个内存,因为eax, edx
都被这3个内存的值覆盖了。那么后面几条指令就根据这3个内存的值来模拟一遍即可。
不过我们不能满足于此,我们可以看看上面3个函数哪些修改了以上3个内存。
小技巧:在CPU这个tab的汇编语句(左上角)或内存(左下角)的区域,鼠标右键,选择“转到”,然后选择“表达式”,即可输入地址进行跳转。
我们在第1个函数调用0040149E | call mfykm1.40D1E0
处打断点,并“重新运行”。可以发现第1个函数调用之前,到第2个函数之后,以上3个内存都是全0。因此只有第3个函数是有用的。
“步进”第3个函数,发现004013FE call <JMP.&GetVersionExA>
之后3个关键内存一起发生了变化,变成了最终值6, 2, 23f0
;而在此之前它们都是0。
结论:只有GetVersionExA
有用。于是我们可以开始写代码了。但根据参考链接1,GetVersionExA在
Windows10下已经废弃,其行为为:
Applications not manifested for Windows 8.1 or Windows 10 will return the Windows 8 OS version value (6.2)
所以python应该是不存在这玩意的替代品的,有也懒得查了。所以这个注册机就用cpp写。
运行下面的cpp代码的demo函数,即可得知osvi.dwMajorVersion = 6, osvi.dwMinorVersion = 2, osvi.dwBuildNumber = 0x23f0
。
#include <windows.h>
#include <stdio.h>
void demo() {
OSVERSIONINFO osvi;
BOOL bIsWindowsXPorLater;
ZeroMemory (&osvi, sizeof (OSVERSIONINFO) );
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
GetVersionEx (&osvi);
printf ("%d %d %d\n", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
bIsWindowsXPorLater =
( (osvi.dwMajorVersion > 5) ||
( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion >= 1) ) );
if (bIsWindowsXPorLater)
printf ("The system meets the requirements.\n");
else printf ("The system does not meet the requirements.\n");
}
int main() {
demo();
OSVERSIONINFO osvi;
BOOL bIsWindowsXPorLater;
ZeroMemory (&osvi, sizeof (OSVERSIONINFO) );
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
GetVersionEx (&osvi);
const int x = osvi.dwMajorVersion, y = osvi.dwMinorVersion, z = osvi.dwBuildNumber;
int eax = y * x + z;
int edx = eax - y;
eax = z * 0xcdd + edx + z;
printf("%d\n",eax);
/*
004014AD | mov eax,dword ptr ds:[444558] |
004014B2 | imul eax,dword ptr ds:[444554] |
004014B9 | add eax,dword ptr ds:[44455C] |
004014BF | mov edx,eax |
004014C1 | sub edx,dword ptr ds:[444558] |
004014C7 | mov eax,dword ptr ds:[44455C] |
004014CC | imul eax,eax,CDD |
004014D2 | lea eax,dword ptr ds:[edx+eax] |
004014D5 | add eax,dword ptr ds:[44455C] |
004014DB | mov dword ptr ds:[444410],eax |
*/
return 0;
}
复制代码
参考链接
GetVersionExA
文档:docs.microsoft.com/en-us/windo…- www.bilibili.com/video/BV1Nf…