[破解实例][OllyDbg] CrackMe005-ajj.2

来源:互联网 发布:淘宝客服周总结报告 编辑:程序博客网 时间:2024/06/04 20:12

[破解实例][OllyDbg] CrackMe005-ajj.2


本例可执行文件(含脱壳版和破解版)下载:CrackMe005-ajj.2


这个程序和上一个类似,但是界面中只有一个输入框,下面的“注册”按钮貌似没有什么效果。查找字符串可以找到破解成功和失败的提示,但是破解是为了锻炼思路和能力,聪明的作者是不会留下有用的字符串来帮助别人破解自己的软件的。所以,我们尽量不再使用查找字符串的方法。

从界面可以发现的貌似有用的信息有:
1. 鼠标会提示:如果你能不用暴力注册成功,你就知道“层层设防”的意思了!
2. 图片框之中的图片会滑动,肯定是通过定时器来控制;
3. 点击任何一个空间都没有明显现象发生。
4. 另外,说明文件提示,如果破解成功的话,程序界面之后发生很小的变化。

那么可以猜测,程序判断错误的时候不再弹出提示框,判断成功的时候界面会出现微小变化。而且,要最终破解程序,满足一个条件是不够的,可能要同时满足好几个条件。同上一个实例一样,我们现在并不知道程序是如何触发对注册的判断。


一、查壳

用PEiD查壳结果为UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo,程序被UPX加壳了,可以找个专门的工具脱壳,我用的是UPXEasyGUI工具。
脱掉后继续查壳结果为Borland Delphi 4.0 - 5.0,无壳。


二、破解

使用Delphi反编译工具DedeDark打开脱壳后的CKme002.exe,可以直接看到事件的入口地址:

事件

FormCreate,猜测是创建界面时触发的事件;
Timer1Timer,猜测是定时触发的某个事件;
Button1MouseDown,猜测是按下”注册”按钮触发的事件;
Panel1DblClick,猜测是双击图片框中没有图片显示的地方时触发的事件;
Edit2DblClick,猜测是双击输入框时触发的事件;
FormMouseMove,猜测是鼠标在界面中移动时触发的事件;
Image1MouseDown,猜测是单击图片1时触发的事件;
Image2MouseDown,猜测是单击图片2时触发的事件;
Image3MouseDown,猜测是单击图片3时触发的事件;
Image4MouseDown,猜测是单击图片4时触发的事件;
Timer2Timer,猜测是定时触发的某个事件;
Button1Click,猜测是单击”注册”按钮触发的事件。

使用OllyDbg工具打开脱壳后的CKme002.exe,依据事件的入口地址,找到每个事件对应的反汇编代码,单步跟踪,我们可以很容易在Timer2Timer事件中找到突破口。

贴出看看Timer2Timer事件的代码分析:

004473E4 .. PUSH EBX004473E5 .. MOV EBX,EAX                     ; EBX = EAX004473E7 .. CMP DWORD PTR DS:[EBX+304],0C34 ; c1 => Mem[EBX+304] != 0xC34004473F1 .. JE CKme002_.0044747F004473F7 .. CMP DWORD PTR DS:[EBX+308],230D ; c2 => Mem[EBX+308] != 0x230D00447401 .. JE SHORT CKme002_.0044747F00447403 .. CMP DWORD PTR DS:[EBX+310],0F94 ; c3 => Mem[EBX+310] == 0xF940044740D .. JNZ SHORT CKme002_.0044747F0044740F .. MOV EAX,DWORD PTR DS:[EBX+318]00447415 .. CMP EAX,DWORD PTR DS:[EBX+314]  ; c4 => Mem[EBX+314] == Mem[EBX+318]0044741B .. JNZ SHORT CKme002_.0044747F0044741D .. CMP DWORD PTR DS:[EBX+31C],3E7  ; c5 => Mem[EBX+31C] != 0x3E700447427 .. JE SHORT CKme002_.0044747F      ; if c1 && c2 && c3 && c4 && c5 then continue, else end00447429 .. XOR EDX,EDX0044742B .. MOV EAX,DWORD PTR DS:[EBX+2D8]  ; EAX = Mem[EBX+2D8]00447431 .. MOV ECX,DWORD PTR DS:[EAX]      ; ECX = Mem[EAX]00447433 .. CALL DWORD PTR DS:[ECX+5C]      ; call CKme002_.00423FE800447436 .. XOR EDX,EDX00447438 .. MOV EAX,DWORD PTR DS:[EBX+2DC]  ; EAX = Mem[EBX+2DC]0044743E .. MOV ECX,DWORD PTR DS:[EAX]      ; ECX = Mem[EAX]00447440 .. CALL DWORD PTR DS:[ECX+5C]      ; call CKme002_.00423FE800447443 .. XOR EDX,EDX00447445 .. MOV EAX,DWORD PTR DS:[EBX+2E0]  ; EAX = Mem[EBX+2E0]0044744B .. MOV ECX,DWORD PTR DS:[EAX]      ; ECX = Mem[EAX]0044744D .. CALL DWORD PTR DS:[ECX+5C]      ; call CKme002_.00423FE800447450 .. XOR EDX,EDX00447452 .. MOV EAX,DWORD PTR DS:[EBX+2E4]  ; EAX = Mem[EBX+2E4]00447458 .. MOV ECX,DWORD PTR DS:[EAX]      ; ECX = Mem[EAX]0044745A .. CALL DWORD PTR DS:[ECX+5C]      ; call CKme002_.00423FE80044745D .. MOV EAX,DWORD PTR DS:[4498A8]00447462 .. ADD EAX,70                      ; EAX = Mem[4498A8]+0x70, store addr where store "如果你能不用暴力注册..."00447465 .. MOV EDX,CKme002_.0044748C       ; EDX = addr where store "厉害厉害真厉害!佩服佩服真佩服!"0044746A .. CALL CKme002_.0040395C          ; change the tips of mouse pointer0044746F .. MOV EDX,CKme002_.004474B8       ; EDX = addr where store "注册了"00447474 .. MOV EAX,DWORD PTR DS:[EBX+2EC]  ; EAX = Mem[EBX+2EC]0044747A .. CALL CKme002_.004240BC          ; change the text of button10044747F .. POP EBX00447480 .. RETN

该事件是程序定时触发的。
每隔一段时间,程序会主动触发该事件,依次判断5个条件(记为c1~c5,内容见上述代码),如果都成立,则将注册成功的信息返回到界面。可见,爆破这个程序非常容易,只需要把这5个条件的判断跳转全部NOP掉即可。

这5个条件是判断Mem[ebx+0x304]、Mem[ebx+0x308]、Mem[ebx+0x310]、Mem[ebx+0x314]、Mem[ebx+0x318]、Mem[ebx+0x31C]这6个内存的取值情况。但是在定时器触发的Timer2Timer事件中,并没有对这些内存值进行修改。因此在其他事件里,必然包含对这些内存的赋值指令。我们接下去要做的,就是找到这些指令。

步骤1、搜索内存地址ebx+0x304被赋值的地方:

00446C1C .. PUSH EBP...00446C2B .. MOV EBX,EAX                      ; EBX = EAX     ...00446C3B .. XOR EDX,EDX00446C3D .. MOV EAX,DWORD PTR DS:[EBX+2D8]00446C43 .. CALL CKme002_.00423FA4           ; Image1.Visible = false00446C48 .. XOR EDX,EDX00446C4A .. MOV EAX,DWORD PTR DS:[EBX+2DC]00446C50 .. CALL CKme002_.00423FA4           ; Image2.Visible = false00446C55 .. XOR EDX,EDX00446C57 .. MOV EAX,DWORD PTR DS:[EBX+2E0]00446C5D .. CALL CKme002_.00423FA4           ; Image3.Visible = false00446C62 .. XOR EDX,EDX00446C64 .. MOV EAX,DWORD PTR DS:[EBX+2E4]00446C6A .. CALL CKme002_.00423FA4           ; Image4.Visible = false00446C6F .. XOR EDX,EDX00446C71 .. MOV EAX,DWORD PTR DS:[EBX+2D8]00446C77 .. CALL CKme002_.00423FA4           ; Image1.Visible = false00446C7C .. XOR EDX,EDX00446C7E .. MOV EAX,DWORD PTR DS:[EBX+2FC]00446C84 .. CALL CKme002_.00423FA4           ; Label3.Visible = false00446C89 .. XOR EDX,EDX00446C8B .. MOV EAX,DWORD PTR DS:[EBX+2F0]00446C91 .. MOV ECX,DWORD PTR DS:[EAX]00446C93 .. CALL DWORD PTR DS:[ECX+5C]       ; Edit2.disable = true00446C96 .. XOR EDX,EDX00446C98 .. MOV EAX,DWORD PTR DS:[EBX+2F0]00446C9E .. CALL CKme002_.00423FA4           ; Edit2.Visible = false...00446D23 .. MOV DWORD PTR DS:[EBX+308],28E  ; Mem[EBX+308] = 0x28E00446D2D .. MOV DWORD PTR DS:[EBX+30C],9    ; Mem[EBX+30C] = 0x900446D37 .. MOV DWORD PTR DS:[EBX+314],0B   ; Mem[EBX+314] = 0xB00446D41 .. XOR EAX,EAX                     ; EAX = 000446D43 .. MOV DWORD PTR DS:[EBX+318],EAX  ; Mem[EBX+318] = EAX00446D49 .. MOV EDX,CKme002_.00446DEC       ; EDX = addr where store "X:\ajj.126.c0m\j\o\j\o\ok.txt"00446D4E .. LEA EAX,DWORD PTR SS:[EBP-1D0]  ; EAX = EBP-0x1D000446D54 .. CALL CKme002_.00405546          ; Mem[EAX+48] = str at EDX00446D59 .. LEA EAX,DWORD PTR SS:[EBP-1D0]00446D5F .. CALL CKme002_.0040576B00446D64 .. CALL CKme002_.004027F400446D69 .. TEST EAX,EAX00446D6B .. JNZ SHORT CKme002_.00446DB8     ; if file not found then goto 0x00446DB800446D6D .. LEA EDX,DWORD PTR SS:[EBP-4]00446D70 .. LEA EAX,DWORD PTR SS:[EBP-1D0]00446D76 .. CALL CKme002_.00403ED800446D7B .. CALL CKme002_.004027C400446D80 .. MOV EAX,DWORD PTR SS:[EBP-4]    ; EAX = addr where store value in the file00446D83 .. MOV EDX,CKme002_.00446E14       ; EDX = addr where store "ajj写的CKme真烂!"00446D88 .. CALL CKme002_.00403C98          ; cmp strings at EAX and EDX00446D8D .. JE SHORT CKme002_.00446D9900446D8F .. MOV DWORD PTR DS:[EBX+304],0C34 ; if not equal then Mem[EBX+304] = 0xC3400446D99 .. LEA EAX,DWORD PTR SS:[EBP-1D0]00446D9F .. CALL CKme002_.004055E800446DA4 .. CALL CKme002_.004027C400446DA9 .. MOV DL,100446DAB .. MOV EAX,DWORD PTR DS:[EBX+2F0]00446DB1 .. CALL CKme002_.00423FA4           ; Edit2.Visible = true00446DB6 .. JMP SHORT CKme002_.00446DC200446DB8 .. MOV DWORD PTR DS:[EBX+304],0C34 ; Mem[EBX+304] = 0xC34...

Mem[EBX+304]有且只有2次赋值,均发生在上述的FormCreate事件中,该事件是窗台创建时触发的。

下断点,重启程序,单步跟踪,可以发现,若文件X:\ajj.126.c0m\j\o\j\o\ok.txt不存在或者文件内容不为“ ajj写的CKme真烂!“(最前面有一个空格,感叹号是英文字符,最后两个符号的ASCII码均为FF),则Mem[EBX+304]会被赋值0xC34。

为了满足条件c1,该文件必须存在且内容必须一致。新建文件X:\ajj.126.c0m\j\o\j\o\ok.txt,并保存内容为“ ajj写的CKme真烂!”,重启程序,条件c1对应的判断跳转不会成功,说明我们破解了作者的第一层防御。

我们还获得了一颗彩蛋——程序界面中多了一个输入框,但是被禁用了。其实这已经从代码分析出来了:在FormCreate事件中,先将Edit2设置不可见和禁用,若文件存在且内容一致,再将其设置为可见。其中Edit2控件的ID为0x2F0,这在FormCreate事件上方可以找到,0x446A3A-0x446ADC定义了所有控件的ID和Name。

因此,在程序的某个地方肯定有代码将Edit2设置为启用,可以搜索该控件的ID,即0x2F0。在FormCreate事件中搜索到的结果可以先排除掉,然后还可以排除在Edit2DblClick事件中搜索到的结果,因为目前Edit2为禁用状态,该事件不会被触发。这样,只在其他一个地方找到该ID:

00446FDC .. CMP DWORD PTR DS:[EAX+308],29D ; c => Mem[EAX+308] == 0x29D00446FE6 .. JNZ SHORT CKme002_.00446FF5    ; if c then continue else end00446FE8 .. MOV DL,1                       ; DL = 100446FEA .. MOV EAX,DWORD PTR DS:[EAX+2F0]00446FF0 .. MOV ECX,DWORD PTR DS:[EAX]00446FF2 .. CALL DWORD PTR DS:[ECX+5C]     ; Edit2.enable = true00446FF5 .. RETN

这个地方发生在上述的Panel1DblClick事件中,该事件是由鼠标双击图片框中没有图片显示的地方触发的。

但是,要让启用Edit2的代码被执行,必须保证条件c成立,即0x00446FE6处不会发生跳转,也就是说,问题转化到使Mem[EAX+308]的值等于0x29D。

步骤2、搜索内存地址ebx+0x308被赋值的地方:

00446FA4 .. PUSH EBP...00446FA7 .. MOV EDX,DWORD PTR DS:[EAX+308]  ; EDX = Mem[EAX+308]00446FAD .. CMP EDX,230D00446FB3 .. JE SHORT CKme002_.00446FD5      ; if EDX == 0x230D then end00446FB5 .. CMP CL,100446FB8 .. JNZ SHORT CKme002_.00446FC300446FBA .. ADD DWORD PTR DS:[EAX+308],3    ; else if CL == 1 then Mem[EAX+308] += 300446FC1 .. JMP SHORT CKme002_.00446FD500446FC3 .. CMP EDX,29400446FC9 .. JGE SHORT CKme002_.00446FD500446FCB .. MOV DWORD PTR DS:[EAX+308],230D ; else if EDX < 0x294 then Mem[EAX+308] = 0x230D00446FD5 .. POP EBP...

Mem[EBX+308]有且只有3次赋值,1次是在之前分析的FormCreate事件中被赋初值0x28E,另外两次均发生在上述的Button1MouseDown事件中,被该事件是鼠标单击“注册”按钮时触发的。

单步跟踪发现,鼠标左键单击按钮时CL被置为0,鼠标右键单击按钮时CL被置为1。

因此,
(1) 为了满足条件c2,即Mem[EBX+308] != 0x230D,在该值还小于0x294的时候,一定不能用鼠标左键点击注册”按钮。
(2) 为了满足条件c,即Mem[EBX+308] == 0x29D,要让Button1MouseDown事件中0x00446FBA处的指令被执行5次,而且只能被执行5次,也就是说,我们要在“注册”按钮上用鼠标右键点击5次,而且只能是5次。

满足以上两条要求之后,Mem[EBX+308]的值就等于0x29D,于是Edit2控件会被启用,而且条件c2对应的判断跳转不会成功,说明我们破解了作者的第二层防御。

步骤3、搜索内存地址ebx+0x310被赋值的地方:

004470EC .. PUSH EBP...004470F4 .. MOV EBX,EAX                      ; EBX = EAX004470F6 .. MOV EDX,DWORD PTR SS:[EBP+8]004470F9 .. MOV EAX,DWORD PTR SS:[EBP+C]...0044710A .. MOV ECX,DWORD PTR DS:[EBX+2E0]  ; Image300447110 .. CMP BYTE PTR DS:[ECX+47],1      ; a1 => Byte[ECX+47] == 100447114 .. JNZ SHORT CKme002_.0044712F00447116 .. CMP EAX,0E2                     ; a2 => EAX > 0xE20044711B .. JLE SHORT CKme002_.0044712F0044711D .. CMP EDX,12C                     ; a3 => EDX > 0x12C00447123 .. JLE SHORT CKme002_.0044712F00447125 .. MOV DWORD PTR DS:[EBX+310],10   ; if a1 and a2 and a3 then Mem[EBX+310] = 0x100044712F .. MOV ECX,DWORD PTR DS:[EBX+2DC]  ; Image200447135 .. CMP BYTE PTR DS:[ECX+47],1      ; b1 => Byte[ECX+47] == 100447139 .. JNZ SHORT CKme002_.004471A70044713B .. CMP EAX,17                      ; b2 => EAX < 0x170044713E .. JGE SHORT CKme002_.004471A700447140 .. CMP EDX,12C                     ; b3 => EDX > 0x12C00447146 .. JLE SHORT CKme002_.004471A700447148 .. CMP DWORD PTR DS:[EBX+310],10   ; b4 => Mem[EBX+310] == 0x100044714F .. JNZ SHORT CKme002_.004471A700447151 .. CMP DWORD PTR DS:[EBX+30C],9    ; b5 => Mem[EBX+30C] != 900447158 .. JE SHORT CKme002_.004471A70044715A .. MOV DWORD PTR DS:[EBX+310],0F94 ; if b1 && b2 && b3 && b4 && b5 then Mem[EBX+310] = 0xF9400447164 .. MOV EAX,DWORD PTR DS:[EBX+30C]  ; EAX = Mem[EBX+30C]...00447179 .. MOV DWORD PTR DS:[EBX+314],41   ; switch(EAX) case 0 => Mem[EBX+314] = 0x4100447183 .. JMP SHORT CKme002_.004471A700447185 .. MOV DWORD PTR DS:[EBX+314],3D   ; switch(EAX) case 1 => Mem[EBX+314] = 0x3D0044718F .. JMP SHORT CKme002_.004471A700447191 .. MOV DWORD PTR DS:[EBX+314],34   ; switch(EAX) case 2 => Mem[EBX+314] = 0x340044719B .. JMP SHORT CKme002_.004471A70044719D .. MOV DWORD PTR DS:[EBX+314],0DF  ; switch(EAX) case 3 => Mem[EBX+314] = 0xDF004471A7 .. CMP DWORD PTR DS:[EBX+310],0F94 ; if Mem[EBX+310] != 0xF94 then end004471B1 .. JNZ SHORT CKme002_.004471F9004471B3 .. LEA EDX,DWORD PTR SS:[EBP-4]    ; EDX = EBP-4004471B6 .. MOV EAX,DWORD PTR DS:[EBX+2E8]  ; Edit1004471BC .. CALL CKme002_.0042408C          ; Mem[addr] = Name, Mem[EDX] = addr004471C1 .. MOV EAX,DWORD PTR SS:[EBP-4]    ; EAX = addr where store Name004471C4 .. MOV EDX,CKme002_.00447230       ; EDX = addr where store "ajj"004471C9 .. CALL CKme002_.00403C98          ; cmp strings at EAX and EDX004471CE .. JNZ SHORT CKme002_.004471F9     ; if not equal then end004471D0 .. MOV DL,1                        ; DL = 1004471D2 .. MOV EAX,DWORD PTR DS:[EBX+2FC]  ; Label3004471D8 .. CALL CKme002_.00423FA4          ; Label3.Visible = true004471DD .. LEA EDX,DWORD PTR SS:[EBP-8]    ; EDX = EBP004471E0 .. MOV EAX,DWORD PTR DS:[EBX+30C]  ; EAX = Mem[EBX+30C]004471E6 .. CALL CKme002_.00407C98          ; Mem[addr] = EAX, Mem[EDX] = addr004471EB .. MOV EDX,DWORD PTR SS:[EBP-8]    ; EDX = addr004471EE .. MOV EAX,DWORD PTR DS:[EBX+2FC]  ; Label3004471F4 .. CALL CKme002_.004240BC          ; show Mem[EDX](who equals Mem[EBX+30C]) on Label3004471F9 .. XOR EAX,EAX...

Mem[EBX+310]有且只有2次被赋值,均发生在上述的FormMouseMove事件中,被该事件是鼠标在界面中移动时触发的。

单步跟踪发现:
(1) 图片框中有Image3,条件a1才会成立,鼠标在界面右下角(坐标<0xE2, 0x12C>位于界面右下角)移动时,条件a2 && a3才会成立;
(2) 同理,图片框中有Image2,条件b1才会成立,鼠标在界面左下角(坐标<0x17, 0x12C>位于界面左下角)移动时,条件b2 && b3才会成立。

为了满足条件c3,即Mem[EBX+310] == 0xF94,必须先让鼠标经过界面右下角,再让鼠标经过界面左下角,还要令Mem[EBX+30C]的取值不为0x9,也就是说,问题转化到使Mem[EAX+30C]的值不等于0x9。

步骤4、搜索内存地址ebx+0x30C被赋值的地方:

00446FF8 .. PUSH EBP...00447003 .. MOV EBX,EAX                    ; EBX = EAX...00447013 .. LEA EDX,DWORD PTR SS:[EBP-4]   ; EDX = EBP-400447016 .. MOV EAX,DWORD PTR DS:[EBX+2F0] ; Edit20044701C .. CALL CKme002_.0042408C         ; Mem[addr] = Serial, Mem[EDX] = addr00447021 .. MOV EAX,DWORD PTR SS:[EBP-4]   ; EAX = addr where store Serial00447024 .. CALL CKme002_.00403B88         ; EAX = strlen(Serial)00447029 .. CMP EAX,8                      ; if EAX != 8 then end0044702C .. JNZ CKme002_.004470C400447032 .. LEA EDX,DWORD PTR SS:[EBP-8]   ; EDX = EBP-800447035 .. MOV EAX,DWORD PTR DS:[EBX+2F0] ; Edit20044703B .. CALL CKme002_.0042408C         ; Mem[addr] = Serial, Mem[EDX] = addr00447040 .. MOV EAX,DWORD PTR SS:[EBP-8]   ; EAX = addr where store Serial00447043 .. CMP BYTE PTR DS:[EAX+1],5F     ; if Serial[1] != '_' then end00447047 .. JNZ SHORT CKme002_.004470C400447049 .. LEA EDX,DWORD PTR SS:[EBP-C]   ; EDX = EBP-C0044704C .. MOV EAX,DWORD PTR DS:[EBX+2F0] ; Edit200447052 .. CALL CKme002_.0042408C         ; Mem[addr] = Serial, Mem[EDX] = addr00447057 .. MOV EAX,DWORD PTR SS:[EBP-C]   ; EAX = addr where store Serial0044705A .. CMP BYTE PTR DS:[EAX+5],2C     ; if Serial[5] != ',' then end0044705E .. JNZ SHORT CKme002_.004470C400447060 .. LEA EDX,DWORD PTR SS:[EBP-10]  ; EDX = EBP-1000447063 .. MOV EAX,DWORD PTR DS:[EBX+2E8] ; Edit100447069 .. CALL CKme002_.0042408C         ; Mem[addr] = Name, Mem[EDX] = addr0044706E .. MOV EAX,DWORD PTR SS:[EBP-10]  ; EAX = addr where store Name00447071 .. CALL CKme002_.00403B88         ; EAX = strlen(Name)00447076 .. ADD EAX,3                      ; if EAX%3 != 0 then end00447079 .. MOV ECX,30044707E .. CDQ0044707F .. IDIV ECX00447081 .. TEST EDX,EDX00447083 .. JNZ SHORT CKme002_.004470C400447085 .. PUSH 0                         ; push 000447087 .. PUSH 4                         ; push 400447089 .. LEA EDX,DWORD PTR SS:[EBP-14]  ; EDX = EBP-140044708C .. MOV EAX,DWORD PTR DS:[EBX+2E8] ; Edit100447092 .. CALL CKme002_.0042408C         ; Mem[addr] = Name, Mem[EDX] = addr00447097 .. MOV EAX,DWORD PTR SS:[EBP-14]  ; EAX = addr where store Name0044709A .. CALL CKme002_.00403B88         ; EAX = strlen(Name)0044709F .. CDQ                            ; EDX = the highest bit of EAX004470A0 .. PUSH EDX                       ; push EDX004470A1 .. PUSH EAX                       ; push EAX004470A2 .. XOR EAX,EAX004470A4 .. CALL CKme002_.00407F90004470A9 .. ADD EAX,DWORD PTR SS:[ESP]004470AC .. ADC EDX,DWORD PTR SS:[ESP+4]004470B0 .. ADD ESP,8004470B3 .. ADD EAX,2004470B6 .. ADC EDX,0004470B9 .. CALL CKme002_.00405D05004470BE .. MOV DWORD PTR DS:[EBX+30C],EAX ; Mem[EBX+30C] = EAX004470C4 .. XOR EAX,EAX...

Mem[EBX+30C]有且只有2次被赋值,一次是在之前的FormCreate事件中被赋初值为0x9,另一次发生在上述的Edit2DblClick事件中,被该事件是鼠标双击Edit2时触发的。

单步跟踪发现:
注册码必须满足长度为8、第2个字符为’_’、第6个字符为’,’,而且注册名必须满足长度为3点倍数,0x004470BE处对Mem[EBX+30C]的赋值才会被执行,具体的赋值算法很复杂,我们暂不关心。

填好规范的注册名和注册码,双击Edit2,再重复步骤3点操作,就能令Mem[EBX+310] 等于 0xF94,从而条件c3对应的判断跳转不会成功,说明我们破解了作者的第三层防御。

如果注册名为”ajj”,此处就能得到另一颗彩蛋——图片框下方多了一个粗体数字。其实这已经代码分析出来了:在FormMouseMove事件中,一旦Mem[EBX+310] 被赋值为 0xF94,且注册名为”ajj”,0x004471B3-0x004471F4的代码就会被执行,这段代码的作用是将Label3设置可见,并将Mem[EBX+30C]的值显示在上面 。

步骤5、搜索内存地址ebx+0x314被赋值的地方:

Mem[EBX+314]有且只有5次被赋值,一次是在之前的FormCreate事件中被赋初值为0xB,其他四次均发生在之前的FormMouseMove事件中。

在FormMouseMove事件中,一旦Mem[EBX+310] 被赋值为 0xF94,0x00447179-0x0044719D的代码就会被执行,这段代码的作用是将根据Mem[EBX+30C]的值来给Mem[EBX+310]赋值,而Mem[EBX+30C]取值为0、1、2、3分别对应Mem[EBX+310]赋值为0x41、0x3D、0x34、0xDF。

步骤6、搜索内存地址ebx+0x318被赋值的地方:

(1)Image1MouseDown事件:

00447234 .. PUSH EBP...00447311 .. MOV EBX,ECX                   ; EBX = ECX0044723B .. MOV ESI,EAX                   ; ESI = EAX0044723D .. PUSH 0                     0044723F .. MOV CX,WORD PTR DS:[447270]   ; CX = 400447246 .. MOV DL,2                      ; DL = 200447248 .. MOV EAX,CKme002_.0044727C     ; EAX = addr where store "注册尚未成功..."0044724D .. CALL CKme002_.00445694        ; show the dialog with failure infomation00447252 .. TEST BL,BL                    ; if BL == 000447254 .. JNZ SHORT CKme002_.0044725D00447256 .. ADD DWORD PTR DS:[ESI+318],2  ; Mem[ESI+318] += 20044725D .. CMP BL,1                      ; if BL == 100447260 .. JNZ SHORT CKme002_.0044726900447262 .. ADD DWORD PTR DS:[ESI+318],11 ; Mem[ESI+318] += 0x1100447269 .. POP ESI...

(2)Image2MouseDown事件:

004472A0 .. PUSH EBP...00447311 .. MOV EBX,ECX                   ; EBX = ECX004472A7 .. MOV ESI,EAX                   ; ESI = EAX004472A9 .. PUSH 0004472AB .. MOV CX,WORD PTR DS:[4472DC]   ; CX = 4004472B2 .. MOV DL,2                      ; DL = 2004472B4 .. MOV EAX,CKme002_.004472E8     ; EAX = addr where store "注册尚未成功..."004472B9 .. CALL CKme002_.00445694        ; show the dialog with failure infomation004472BE .. TEST BL,BL                    ; if BL == 0004472C0 .. JNZ SHORT CKme002_.004472C9   ;004472C2 .. ADD DWORD PTR DS:[ESI+318],3  ; Mem[ESI+318] += 3004472C9 .. CMP BL,1                      ; if BL == 1004472CC .. JNZ SHORT CKme002_.004472D5   ;004472CE .. ADD DWORD PTR DS:[ESI+318],13 ; Mem[ESI+318] += 0x13004472D5 .. POP ESI...

(3)Image3MouseDown事件:

0044730C .. PUSH EBP...00447311 .. MOV EBX,ECX                   ; EBX = ECX00447313 .. MOV ESI,EAX                   ; ESI = EAX00447315 .. PUSH 000447317 .. MOV CX,WORD PTR DS:[447348]   ; CX = 40044731E .. MOV DL,2                      ; DL = 200447320 .. MOV EAX,CKme002_.00447354     ; EAX = addr where store "注册尚未成功..."00447325 .. CALL CKme002_.00445694        ; show the dialog with failure infomation0044732A .. TEST BL,BL                    ; if BL == 00044732C .. JNZ SHORT CKme002_.004473350044732E .. ADD DWORD PTR DS:[ESI+318],5  ; Mem[ESI+318] += 500447335 .. CMP BL,1                      ; if BL == 100447338 .. JNZ SHORT CKme002_.004473410044733A .. ADD DWORD PTR DS:[ESI+318],17 ; Mem[ESI+318] += 0x1700447341 .. POP ESI...

(4)Image4MouseDown事件:

00447378 .. PUSH EBP...00447311 .. MOV EBX,ECX                   ; EBX = ECX0044737F .. MOV ESI,EAX                   ; ESI = EAX00447381 .. PUSH 000447383 .. MOV CX,WORD PTR DS:[4473B4]   ; CX = 40044738A .. MOV DL,2                      ; DL = 20044738C .. MOV EAX,CKme002_.004473C0     ; EAX = addr where store "注册尚未成功..."00447391 .. CALL CKme002_.00445694        ; show the dialog with failure infomation00447396 .. TEST BL,BL                    ; if BL == 000447398 .. JNZ SHORT CKme002_.004473A10044739A .. ADD DWORD PTR DS:[ESI+318],7  ; Mem[ESI+318] += 7004473A1 .. CMP BL,1                      ; if BL == 1004473A4 .. JNZ SHORT CKme002_.004473AD004473A6 .. ADD DWORD PTR DS:[ESI+318],1B ; Mem[ESI+318] += 0x1B004473AD .. POP ESI...

Mem[EBX+318](EBX+318和ESI+318指向同一片内存)有多次被赋值,一次是在之前的FormCreate事件中被赋初值为0,其他四次均发生在上述事件中,
这些事件分别是鼠标单击图片1、图片2、图片3、图片4时触发的。

单步跟踪发现:
鼠标左键单击按钮时CL被置为0,鼠标右键单击按钮时CL被置为1,而在事件中CL的值被赋给了BL,它决定了Mem[ESI+318]的修改。

因此,通过点击4个图片,使得加和出来的Mem[ESI+318]等于步骤5得到的Mem[ESI+314],从而条件c4对应的判断跳转不会成功,说明我们破解了作者的第四层防御。

步骤7、搜索内存地址ebx+0x31C被赋值的地方:

004474C0 .. MOV DWORD PTR DS:[EAX+31C],3E7 ; Mem[EAX+31C] = 0x3E7004474CA .. RETN

Mem[EBX+31C](EBX+31C和EAX+31C指向同一片内存)有且只有1次被赋值,发生在上述的Button1Click事件中,该事件是鼠标左键双击“注册”按钮时触发的。而且,鼠标左键双击“注册”按钮,会先触发Button1MouseDown事件,再触发Button1Click事件,然后再依次触发一遍。

因此,只需用鼠标左键双击“注册”按钮,Mem[EBX+31C]就会被赋值0x3E7,从而条件c5对应的判断跳转不会成功,说明我们破解了作者的第五层防御。

至此,我们完全破解了这个程序。


三、总结

最后整理给出程序的注册流程:
(1) 新建文件X:\ajj.126.c0m\j\o\j\o\ok.txt,文件内容为“ ajj写的CKme真烂!“(最前面有一个空格,感叹号是英文字符,最后两个符号的ASCII码均为FF)。本操作的作用是显示输入框Edit2,但它是被禁用的。
(2) 打开程序,在“注册”按钮上用鼠标右键点击5次。本操作之前不能用鼠标左键点击按钮,本操作之后不能用鼠标右键点击按钮。
(3) 在图片框中双击没有图片显示的地方。操作(2)和本操作的作用是启禁用输入框Edit2。
(4) 输入注册名和注册码,然后双击输入框Edit2。注册名必须为”ajj”,注册码长度为8且第2个字符为’_’且第6个字符为’,’。
(5) 当图片3“性相近”出现时,鼠标在界面右下角移动;然后当图片2“性本善”出现时,鼠标在界面左下角移动。本操作的作用是在图片框下方显示一个粗体数字。
(6) 根据上一操作显示的粗体数字点击图片框中的图片(可以有其他点击方式):
数字为0时,鼠标左键点击3次图片3,鼠标右键点击图片3和图片4各1次;
数字为1时,鼠标左键点击1次图片1,鼠标右键点击图片1、图片2和图片3各1次;
数字为2时,鼠标左键点击1次图片1,鼠标右键点击图片3和图片4各1次;
数字为3时,鼠标左键点击1次图片4,鼠标右键点击8次图片4;
(7) 用鼠标左键双击“注册”按钮。


0 0