环境
Windows xp sp3
工具
exeinfope
ollydbg
查壳
用exeinfoe查壳
测试
可以从左下角状态栏看出serial是无效的
直接OD载入字符串搜索
00401274 |. /75 17 jnz XChafe_1.0040128D
00401276 |. |6A 00 push 0x0 ; /Timerproc = NULL; Case 1 (WM_CREATE) of switch 0040123F
00401278 |. |6A 01 push 0x1 ; |Timeout = 1. ms
0040127A |. |68 CA0000>push 0xCA ; |TimerID = CA (202.)
0040127F |. |FF75 08 push [arg.1] ; |hWnd
00401282 |. |E8 A10200>call <jmp.&USER32.SetTimer> ; \SetTimer
00401287 |. |33C0 xor eax,eax
00401289 |. |C9 leave
0040128A |. |C2 1000 retn 0x10
0040128D |> \3D 130100>cmp eax,0x113
00401292 |. 75 50 jnz XChafe_1.004012E4
00401294 |. E8 BA0100>call Chafe_1.00401453 ; Case 113 (WM_TIMER) of switch 0040123F
00401299 |. 0FBE05 66>movsx eax,byte ptr ds:[0x403166]
004012A0 |. 3A05 6731>cmp al,byte ptr ds:[0x403167]
004012A6 |. 75 06 jnz XChafe_1.004012AE ; 这里不能相同
004012A8 |. 33C0 xor eax,eax
004012AA |. C9 leave
004012AB |. C2 1000 retn 0x10
004012AE |> A2 673140>mov byte ptr ds:[0x403167],al ; 可以看出[0x403167]的值要为0x10
004012B3 |. 83F8 10 cmp eax,0x10 ; 这里要相同
004012B6 |. 74 16 je XChafe_1.004012CE
004012B8 |. 68 653040>push Chafe_1.00403065 ; /Text = "Your serial is not valid."
004012BD |. FF35 7C31>push dword ptr ds:[0x40317C] ; |hWnd = NULL
004012C3 |. E8 660200>call <jmp.&USER32.SetWindowTextA> ; \SetWindowTextA
004012C8 |. 33C0 xor eax,eax
004012CA |. C9 leave
004012CB |. C2 1000 retn 0x10
004012CE |> 68 7F3040>push Chafe_1.0040307F ; /Text = "YES! You found your serial!!"
004012D3 |. FF35 7C31>push dword ptr ds:[0x40317C] ; |hWnd = NULL
004012D9 |. E8 500200>call <jmp.&USER32.SetWindowTextA> ; \SetWindowTextA
因为界面没有点击确认的按钮,加上那个SetTimer函数,可以猜测是用程序自己检测serial是否正确。
可以猜到[0x4012AE]里面的[0x403167]的值是根据serial是否正确才会为0x10
搜索程序,观察哪里使得[0x403167]地址的内容发生改变
参考位于 Chafe_1:.text 到常量 403166
地址 反汇编 注释
00401000 push 0x0 (初始 CPU 选择)
00401093 add byte ptr ds:[0x403166],0x4 ds:[00403166]=00
004010A3 mov byte ptr ds:[0x403166],ah
00401299 movsx eax,byte ptr ds:[0x403166] ds:[00403166]=00
00401398 add byte ptr ds:[0x403166],0x4 ds:[00403166]=00
00401465 movsx eax,byte ptr ds:[0x403166] ds:[00403166]=00
00401493 add byte ptr ds:[0x403166],0x4 且字符串长度在10以内
004014AA add byte ptr ds:[0x403166],0x4 ds:[00403166]=00
004014B3 mov byte ptr ds:[0x403166],0x0 ds:[00403166]=00
可以猜出,只要实现4个add byte ptr ds:[0x403166],0x4 ,就可以使得[0x403167]的值为0x10
跟随这4个add,查看相应的条件。
0040107B . B9 140000>mov ecx,0x14
00401080 . 2BC8 sub ecx,eax
00401082 . 8DB8 8C31>lea edi,dword ptr ds:[eax+0x40318C]
00401088 > C607 00 mov byte ptr ds:[edi],0x0
0040108B . 47 inc edi
0040108C . 49 dec ecx
0040108D .^ 75 F9 jnz XChafe_1.00401088
0040108F . 85C0 test eax,eax ; eax为输入的name的长度
00401091 . 74 10 je XChafe_1.004010A3
00401093 . 8005 6631>add byte ptr ds:[0x403166],0x4
0040109A . C605 6831>mov byte ptr ds:[0x403168],0x0
004010A1 . EB 06 jmp XChafe_1.004010A9
不难看出,ecx为保存name的字符数组的长度,将没有字符的部分用0填充。
所以这里的条件是只要输入了name就能使得[0x403166]加0x4了
00401361 . 8D3D 8C31>lea edi,dword ptr ds:[0x40318C]
00401367 . 0FBE05 68>movsx eax,byte ptr ds:[0x403168]
0040136E . 03F8 add edi,eax
00401370 . FE05 6831>inc byte ptr ds:[0x403168]
00401376 . A1 883140>mov eax,dword ptr ds:[0x403188]
0040137B . 8B25 A031>mov esp,dword ptr ds:[0x4031A0]
00401381 . 40 inc eax
00401382 . FF05 8831>inc dword ptr ds:[0x403188]
00401388 . 3307 xor eax,dword ptr ds:[edi]
0040138A . A3 883140>mov dword ptr ds:[0x403188],eax
0040138F . 803D 6831>cmp byte ptr ds:[0x403168],0x10 ; 这里要使得[401368]为0x10
00401396 . 75 07 jnz XChafe_1.0040139F
00401398 . 8005 6631>add byte ptr ds:[0x403166],0x4
0040139F > C9 leave
这里可以看作是这个程序的算法核心,因为这里有一个循环,它是用来计算[0x403188]的值,具体的计算方法为:
设x0为[0x403188]的初值,有:
x1 = (x0 + 1) xor k
x2 = (x1 + 1) xor k
x3 = (x3 + 1) xor k
…
xn = (xn-1 + 1) xor k
这个xn就是在循环结束的时候[0x403188]的值。k的话是输入的name,每次取name的4个字节,来进行异或,每次取完直接后,下一次取得4个字节会往后移动1个字节。
00401475 . 6A 00 push 0x0 ; /IsSigned = FALSE
00401477 . 8D45 FC lea eax,dword ptr ss:[ebp-0x4] ; |
0040147A . 50 push eax ; |pSuccess
0040147B . 6A 64 push 0x64 ; |ControlID = 64 (100.)
0040147D . FF35 7031>push dword ptr ds:[0x403170] ; |hWnd = 001402A8 ('TEXme v1.0',class='TEXcls')
00401483 . E8 640000>call <jmp.&USER32.GetDlgItemInt> ; \GetDlgItemInt
00401488 . A3 883140>mov dword ptr ds:[0x403188],eax
0040148D . 837D FC 0>cmp dword ptr ss:[ebp-0x4],0x0
00401491 . 74 07 je XChafe_1.0040149A ; 这里判断输入框是否有值,此时EAX为输入的值得16进制,字符不计算在内
00401493 . 8005 6631>add byte ptr ds:[0x403166],0x4 ; 且字符串长度在10以内
0040149A > C9 leave
0040149B . C3 retn
这段代码是将输入的serial转成数值,并且将这个值存储到[0x403188],显然这个就是刚刚的x0,也就是
[0x403188]的初值,这里的数值均为16进制。
0040149C . A1 883140>mov eax,dword ptr ds:[0x403188] ; 这里计算最后一步
004014A1 . 05 782411>add eax,0x9112478
004014A6 . 85C0 test eax,eax
004014A8 . 75 09 jnz XChafe_1.004014B3
004014AA . 8005 6631>add byte ptr ds:[0x403166],0x4
004014B1 . EB 07 jmp XChafe_1.004014BA
004014B3 > C605 6631>mov byte ptr ds:[0x403166],0x0
004014BA > 8B25 A031>mov esp,dword ptr ds:[0x4031A0]
004014C0 . C9 leave
004014C1 . C3 retn
这里是将循环过后计算得出的[0x403188]的值加上0x9112478,即[0x403188]+0x9112478
后面有个test,所以这里的计算结果为1 0000 0000,可以使得eax的值为0
所以程序的算法过程可以轻易的出了:
1.判断是否输入了name和serial,都有的话就加0x8
2.然后循环计算一个值,计算出来后加0x4
3.将计算出来的值再加上一个数,如果结果满足条件的话就加0x4
4.如果上面的条件都满足了,就会显示正确的提示。
注册机:
char c[20];unsigned int * p;unsigned int k = 0xF6EEDB88;memset(c,0,sizeof(c));scanf("%s",c);int len = strlen(c);k -= 0xf - len;for(int i=len-1;i>=0;i--){p = (unsigned int*)&c[i];k = (k-1)^*p;}printf("%u",k-1); //这里减1输出是因为最后异或算出来的值为x0+1的值