900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Greedy Fly's Crackme

Greedy Fly's Crackme

时间:2022-08-21 01:45:31

相关推荐

Greedy Fly's Crackme

前些天在Crakmes.de拿到一个keygenme,作者说这是for newbies的,好吧,我比初学者还弱搞了一整天什么都没弄出来

今天思路有点点清晰了,先写点东西总结一下再说!

这是个Name/Serial型的KM(keygenme)怎么定位确定按钮我就不讲了,直接贴确定按钮的事件代码吧

这是个Name/Serial型的KM(keygenme)怎么定位确定按钮我就不讲了,直接贴确定按钮的事件代码吧

0040124D call 00401461; 释放一个名为Keygenme.dat的动态链接库文件 00401252 push 195 ; /ControlID = 195 (405.) 00401257 push dword ptr [ebp+8] ; |hWnd 0040125A call <jmp.&user32.GetDlgItem> ; \GetDlgItem:获取Name框句柄于eax 0040125F mov dword ptr [403190], eax 00401264 lea eax, dword ptr [4032A8] 0040126A push eax ; /lParam = 4032A8(Name存放地址) 0040126B push 20 ; |wParam = 20 0040126D push 0D ; |Message = WM_GETTEXT 0040126F push dword ptr [403190] ; |hWnd = NULL 00401275 call <jmp.&user32.SendMessageA> ; \SendMessageA 0040127A mov edx, eax; edx=Len(Name) 0040127C push edx 0040127D call 00401524 00401282 cmp esi, 15D50 00401288 jnb short 0040128F 0040128A jmp 004013B0; 长度不符合规格则退出程序 0040128F cmp eax, 0A 00401292 jbe short 00401299 ; 长度不能大于10,大于则退出程序 00401294 jmp 004013B0 00401299 push 0040302A; /FileName = "keygenme.dat" 0040129E call <jmp.&kernel32.LoadLibraryA>; \LoadLibraryA 加载刚才释放的动态链接库 004012A3 or eax, eax 004012A5 je 004013B0; 加载不成功则退出程序 004012AB mov dword ptr [40319C], eax 004012B0 call 00401B8C; 返回一片空区域00403300 004012B5 pop edx 004012B6 push edx ; /Arg2 004012B7 push 004032A8; |Arg1 = 004032A8 004012BC call 00401BA8; \Name的字符串处理函数,,下面的CALL也是(在403310创建用户拷贝) 004012C1 call 00401BF4 004012C6 mov edi, eax 004012C8 push 20 ; /Length = 20 (32.) 004012CA push 004032A8; |Destination = KeygenMe.004032A8 004012CF call <jmp.&kernel32.RtlZeroMemory>; \RtlZeroMemory 004012D4 push 00403037; /ProcNameOrOrdinal = "calc" 004012D9 push dword ptr [40319C] ; |hModule = NULL 004012DF call <jmp.&kernel32.GetProcAddress>; \GetProcAddress 004012E4 mov dword ptr [4031A0], eax 004012E9 push edi ; name处理中制作出来的一个表403300 004012EA call dword ptr [4031A0] ; DLL函数,在其中运算密码。 004012F0 push 004032A8 004012F5 push esi 004012F6 call 00401620; 也是计算函数要跟一下。 004012FB push dword ptr [40319C] ; /hLibModule = NULL 00401301 call <jmp.&kernel32.FreeLibrary> ; \FreeLibrary 00401306 push 28 ; /Count = 28 (40.) 00401308 push 004032C8; |Buffer = KeygenMe.004032C8 0040130D push 196 ; |ControlID = 196 (406.) 00401312 push dword ptr [ebp+8] ; |hWnd 00401315 call <jmp.&user32.GetDlgItemTextA>; \GetDlgItemTextA:得到密码。 0040131A push 004032C8 0040131F call 004010B4; 密码运算 00401324 cmp eax, 20 00401327 je short 0040132E ; 密码至少要有32位 00401329 jmp 004013B0 0040132E push 004032EA 00401333 push 004032C8 00401338 call 00401417 0040133D lea esi, dword ptr [4032A8] 00401343 lea edi, dword ptr [4032EA] 00401349 mov ecx, 10 0040134E cld 0040134F repe cmps byte ptr es:[edi], byte ptr [esi] ; 这里影响了标志位,但对JNZ没有影响 00401351 jnz short 004013B0 ; call 00401417导致了jnz的跳转,所以上面的代码基本上没啥用 00401353 push 0040302A; /FileName = "keygenme.dat" 00401358 call <jmp.&kernel32.DeleteFileA> ; \DeleteFileA 0040135D push 66 ; /ControlID = 66 (102.) 0040135F push dword ptr [ebp+8] ; |hWnd 00401362 call <jmp.&user32.GetDlgItem> ; \GetDlgItem 00401367 push 1; /ShowState = SW_SHOWNORMAL 00401369 push eax ; |hWnd 0040136A call <jmp.&user32.ShowWindow> ; \ShowWindow 0040136F push 192 ; /ControlID = 192 (402.) 00401374 push dword ptr [ebp+8] ; |hWnd 00401377 call <jmp.&user32.GetDlgItem> ; \GetDlgItem 0040137C push 0; /Enable = FALSE 0040137E push eax ; |hWnd 0040137F call <jmp.&user32.EnableWindow> ; \EnableWindow 00401384 push 195 ; /ControlID = 195 (405.) 00401389 push dword ptr [ebp+8] ; |hWnd 0040138C call <jmp.&user32.GetDlgItem> ; \GetDlgItem 00401391 push 0; /Enable = FALSE 00401393 push eax ; |hWnd 00401394 call <jmp.&user32.EnableWindow> ; \EnableWindow 00401399 push 196 ; /ControlID = 196 (406.) 0040139E push dword ptr [ebp+8] ; |hWnd 004013A1 call <jmp.&user32.GetDlgItem> ; \GetDlgItem 004013A6 push 0; /Enable = FALSE 004013A8 push eax ; |hWnd 004013A9 call <jmp.&user32.EnableWindow> ; \EnableWindow 004013AE jmp short 004013CC 004013B0 push 194 ; /ControlID = 194 (404.) 004013B5 push dword ptr [ebp+8] ; |hWnd 004013B8 call <jmp.&user32.GetDlgItem> ; \GetDlgItem 004013BD push 0; /lParam = 0 004013BF push 0; |wParam = 0 004013C1 push 0F5 ; |Message = BM_CLICK 004013C6 push eax ; |hWnd 004013C7 call <jmp.&user32.SendMessageA> ; \SendMessageA 004013CC jmp short 00401411 004013CE cmp dword ptr [ebp+10], 194 004013D5 jnz short 00401411 004013D7 push 0040302A; /FileName = "keygenme.dat" 004013DC call <jmp.&kernel32.DeleteFileA> ; \DeleteFileA 004013E1 push 0; /lParam = 0 004013E3 push 0; |wParam = 0 004013E5 push 10 ; |Message = WM_CLOSE 004013E7 push dword ptr [ebp+8] ; |hWnd 004013EA call <jmp.&user32.SendMessageA> ; \SendMessageA 004013EF jmp short 00401411 004013F1 cmp dword ptr [ebp+C], 10 004013F5 jnz short 00401411 004013F7 push dword ptr [403188] ; /hObject = 60050162 004013FD call <jmp.&gdi32.DeleteObject> ; \DeleteObject 00401402 call <jmp.&ole32.CoUninitialize> 00401407 push 0; /Result = 0 00401409 push dword ptr [ebp+8] ; |hWnd 0040140C call <jmp.&user32.EndDialog> ; \EndDialog 00401411 xor eax, eax 00401413 leave 00401414 retn 10 走了一遍程序,分析出来的大体过程:

根据用户名长度计算出一个值,如果这个值不在给定区间则挂 有两个函数共同处理Name,生成一个表403310 然后把表的地址传入函数calc处理(calc函数藏在一个名为keygenme.dat的dll文件里) 获取到密码后根据密码生成一个小表 然后是一个小call计算出某个值放进eax和20h比较,不符合则挂 然后又是一个对密码的Call,里面有一个循环,估计是在做与用户名匹配的计算吧

今天我要先把calc函数之前的东西先分析出来!

004012BC call 00401BA8004012C1 call 00401BF4

先从这两个call开始分析:

00401BA8 push ebp 00401BA9 mov ebp, esp 00401BAB push esi 00401BAC push edi 00401BAD push ebx 00401BAE mov ebx, dword ptr [ebp+C] ; 长度 00401BB1 mov esi, dword ptr [ebp+8] ; name 00401BB4 jmp short 00401BE7 00401BB6 /mov eax, dword ptr [403344] 00401BBB |mov edx, 10 00401BC0 |sub edx, eax 00401BC2 |cmp ebx, edx 00401BC4 |jnb short 00401BC8 ; edx不可以大于ebx 00401BC6 |mov edx, ebx 00401BC8 |lea edi, dword ptr [eax+403310] 00401BCE |mov ecx, edx 00401BD0 |rep movs byte ptr es:[edi], byte ptr [esi] 00401BD2 |sub ebx, edx 00401BD4 |add eax, edx 00401BD6 |cmp eax, 10 00401BD9 |jnz short 00401BE2 00401BDB |call 00401A70 00401BE0 |xor eax, eax 00401BE2 |mov dword ptr [403344], eax 00401BE7 and ebx, ebx 00401BE9 \jnz short 00401BB6 00401BEB pop ebx 00401BEC pop edi 00401BED pop esi 00401BEE pop ebp 00401BEF retn 8

代码很长(因为里面还有一个很长的call),不过不用怕!先得出特殊结论,在总结一般性质!!

为了方便,先假设一下

403344 =table1

403310 =table2

edx先减去table1的值再跟ebx比较 如果ebx<edx的话就使得edx=ebx 然后table2+eax=edi,后面应该会有数据存到edi指向内存 ecx=edx作为循环记数 然后将用户名拷贝一份到edi指向内存 刚才edx是大于ebx的,那么当ebx大于edx的时候这里会得出 ebx比edx大多少(否则为0) 然后比较edx+eax是否等于16,如果不等于把当前eax(eax+edx)放入table1 如果等于则执行函数401A70,清空eax。这两种情况之后都要退出本函数,因为 ebx早就为0了

在这里似乎又是一次长度检测,如果不符合某条件的用户名会被进行不同的处理。 我们输入了8个字符的用户名,不需要进行CALL,那么就先忽略这个Call吧 所以呢这个函数目前的作用呢只有两个,就是 1 把32A8处的用户名数据传送到3310 2 把403344的值置为8(本例的用户长度)00401BF2 mov edi, edi 00401BF4 push esi 00401BF5 push edi 00401BF6 mov eax, dword ptr [403344] 00401BFB mov edx, 10 00401C00 sub edx, eax 00401C02 lea edi, dword ptr [eax+403310] ; 用长度+name的副本 00401C08 and edx, 0FF 00401C0E mov eax, edx; 填充内容 00401C10 mov ecx, edx; 次数 00401C12 rep stos byte ptr es:[edi] 00401C14 call 00401A70; 这个干什么的?? 00401C19 mov esi, 00403330 00401C1E mov edi, 00403310 00401C23 mov ecx, 10 00401C28 rep movs byte ptr es:[edi], byte ptr [esi] 00401C2A call 00401A70; 这个干什么的?? 00401C2F mov eax, 00403300 ; 返回这个表 00401C34 pop edi 00401C35 pop esi 00401C36 retn

在这个函数里面,他将我们的用户名和用户名长度等数据与他自身提供的数据进行异或等运算,最后将结果放在一个表里面,先写到这里具体深入的分析现在再做!

过了N久…………

好吧,已经完成了对这两个函数的分析了,如下:

这个函数的作用其实就是对某一处内存拷贝来拷贝去,异或来异或去而已。

将第二排数据每四个与第一排数据的每四个Xor,结果存放到第三排

将第一排和第三排的当前字符传给reg1和reg2

edx=edx xor reg1

用edx寻址数组403080把寻址数据放到reg3

reg2=reg2 xor reg3

reg2被放到第三行的第一个字符

edx=reg2

这个循环有n个以上的操作,一共循环18h/4次

--------------------------------------------------------------------

现在重头设过字符串指针

从403300开始取字节与以eax寻址的数组403080中取的字节Xor

然后再放回当前位置。

这个操作也有很多次

循环30h/4次

其实这里只是一个小循环,在小循环外面还有这样的操作

eax=eax+edx

eax=eax & ffh

edx++

ebp复原为-30以使得小循环能正确寻址到数据

然后jnz到1B25处。

做一下总结吧,现在看来呢,如果我们想写注册机的话就要把他里面的数组403080中的数据弄下来

然后写一个跟他差不多的算法来运算出最后得出的一个表。

对了有点东西忘记了,现在先从这个有很多Xor操作的函数里面出去吧。

现在发现程序又很奇怪地把3330那排数据拷贝到了3310处然后再对这个表进行了很多Xor操作(还是用的同一个CALL)

然后就把最后得出的一个表4033300返回到eax里面。

再退出到外面的一层函数我们发现403300这个表被传到了一个从GetProcAdress得来的函数处

这个函数叫做calc,是个动态链接库的导出函数,只不过作者把dll文件做成资源放在exe里面,运行的时候

才释放出来,加载然后获取其中函数calc。

今天的任务结束了,终于迈出了解决难题的第一步!

--------------------------------------------------------------------

刚刚分析出后面的总体过程了,先写点什么吧

首先把之前算出的403300这个表传入函数calc

在calc里面又有几个函数计算出另一个表,表地址放在esi

calc函数之后将esi处的数据拷贝至004032A8处。

接下来获取密码,然后有一个函数对密码长度作检测

长度检测过关之后就传入了一两个表,一个全部为零,另一个是我们填写的密码

在这个函数里面因为影响了标志位Z,所以影响了后来的jnz跳转至错误或正确提示信息

现在开始深入分析每个函数:

进入到calc函数中:

10005AC0 push ebp 10005AC1 mov ebp, esp 10005AC3 push 10005C24 ; ASCII "47B84ABE85D739C51EEB4357DE61" 10005AC8 push 10 10005ACA push dword ptr [ebp+8] ; 403300 10005ACD call 10005B40 ; calc the arg 10005AD2 mov esi, 10005C3C; get ret 10005AD7 mov byte ptr [10005C2C], 0 10005ADE mov edi, 10005C24; ASCII "47B84ABE85D739C51EEB4357DE61" 10005AE3 push 10 10005AE5 push 10001000 10005AEA call 10002250 10005AEF push 10005C64 10005AF4 push esi 10005AF5 call 100022BC 10005AFA push 10 10005AFC push 10001000 10005B01 call 10002250 10005B06 push 10005C84 10005B0B push edi 10005B0C call 100022BC 10005B11 push 10005C84 10005B16 push 10005C64 10005B1B call 10005BA0 10005B20 sub esi, esi 10005B22 lea esi, dword ptr [10005C64] 10005B28 push 40 10005B2A push 10005C24 ; ASCII "47B84ABE85D739C51EEB4357DE61" 10005B2F call 10005B38 ; jmp 到 ntdll.RtlZeroMemory 10005B34 leave 10005B35 retn 4 调用了很多call,不过也只好慢慢地来分析了:

10005AC0 push ebp 10005AC1 mov ebp, esp 10005AC3 push 10005C24 10005AC8 push 10 10005ACA push dword ptr [ebp+8] ; 403300 10005ACD call 10005B40 ; calc the arg 第一个call传入了三个参数,第一个是一块空表10005c24(顺着push的顺序来说,免得混淆)

第二个是常数10,进到里面之后我们会知道这是一个循环变量

第三个是我们熟悉的403300,就是之前分析出来的两个函数对Name的运算结果

现在进入这个CALL看看:

10005B40 push ebp 10005B41 mov ebp, esp 10005B43 push edi 10005B44 push esi 10005B45 push ebx 10005B46 mov ebx, dword ptr [ebp+C]; 10 10005B49 mov edi, dword ptr [ebp+10]; 1005c24 10005B4C test ebx, ebx 10005B4E mov esi, dword ptr [ebp+8]; 403300 10005B51 je short 10005B89; ebx=0就跑路 10005B53 movzx eax, byte ptr [esi] 10005B56 mov ecx, eax 10005B58 add edi, 2 10005B5B shr ecx, 4 10005B5E and eax, 0F 10005B61 and ecx, 0F 10005B64 cmp eax, 0A 10005B67 sbb edx, edx 10005B69 adc eax, 0 10005B6C lea eax, dword ptr [eax+edx*8+37] 10005B70 cmp ecx, 0A 10005B73 sbb edx, edx 10005B75 adc ecx, 0 10005B78 shl eax, 8 10005B7B lea ecx, dword ptr [ecx+edx*8+37] 10005B7F or eax, ecx 10005B81 inc esi 10005B82 mov word ptr [edi-2], ax 10005B86 dec ebx 10005B87 jnz short 10005B53 10005B89 mov eax, edi 10005B8B mov byte ptr [edi], 0 10005B8E sub eax, dword ptr [ebp+10]; 生成表的长度放在eax 10005B91 pop ebx 10005B92 pop esi 10005B93 pop edi 10005B94 pop ebp 10005B95 retn 0C 我分析出来的东西:

每次从esi所指内存挪一个字节到eax和ecx,然后edi+=2 先将ecx右移四位 然后eax和ecx都 & fh 后面的操作分为两部分 第一部分 cmp eax,0a 如果上面换算出来的eax>a则 CF=0,导致下面的edx=0和adc操作将+1 否则 CF=1, 导致下面的edx=-1和adc操作将无影响 edx=0/-1 eax=eax+1/0 eax=edx*8+eax+37

cmp ecx,0a 如果上面换算出来的ecx>a则 CF=0,导致下面的edx=0和adc操作将+1 否则 CF=1, 导致下面的edx=-1和adc操作将无影响 edx=0/-1 ecx=ecx+1/0 ecx=edx*8+eax+37 --------------------- 然后将eax左移8位之后or eax,ecx esi指向下个字符 ax放到新表内 ebx--(循环变量) 最后把生成表的长度放在eax

------------------------------------------------------------------------------------------------

我看了看这个keygenme的calc函数的后面的代码,吓了我一大跳!要逆向的东西真是超多!所以我决定还是先把calc函数放一放,写注册机的时候直接把dll载入然后用他的calc函数就好了

------------------------------------------------------------------------------------------------

接下来继续!

之前看错了以为40131F那里也是什么有意义的函数,原来是个获取密码长度的函数(密码长度应为32位)

再看401338处的函数,这个函数将我们输入的密码做一下换算弄出一个表来,过程如下:

每次从password中拿一个字节到edx,如果小于40h则ebx=-1 大于则ebx=0 edx=edx-37h ebx=ebx & 7 指针指向下个字符 ebx+edx大于0则结束函数 小于0则: eax=ebx eax=eax<<4 [empty]=al 然后又跟40142D那里一样………如果最终ebx为正数则退出 为负数则: ebx=ebx & fh eax=eax+ebx [empty]=al 一直这样循环下去直到填满empty这个表

-------------------------------------------------

接下来的代码:

0040133D lea esi, dword ptr [4032A8] 00401343 lea edi, dword ptr [4032EA] 00401349 mov ecx, 10 0040134E cld 0040134F repe cmps byte ptr es:[edi], byte ptr [esi] ; 两个表比较,下面的jnz根据标志位跳转到成功提示 00401351 jnz short 004013B0 在这段代码中将calc中计算出来的表和刚才从我们的密码中换算出来的表做比较(repe cmps意思是相等则继续比较,不等则置ZF为0)

如果ZF条件满足jnz就跳到成功提示信息了(或者直接退出程序)时间问题就不写注册机了哈哈

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。