能力值:
( LV7,RANK:110 )
2 楼
由于是第一次发贴,图片不怎么如何上传,就不附图了,直接上代码。希望这次可以看得比较清楚
【软件名称】cycle.EXE
【应用平台】Win9x/NT/2000/XP
【软件大小】8K
【破解声明】破解只是感兴趣,无其它目的。失误之处敬请诸位大侠赐教!
【破解工具】PEiD,OD, UE
========================================================================================
【分析过程】
1. 查壳后,无壳。程序是用汇编写的
2. 用OD载入程序,停在这里
00401000 >/$ 6A 00 push 0x0 ; /pModule = NULL
00401002 |. E8 A4020000 call <jmp.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
00401007 |. A3 94214000 mov dword ptr ds:[0x402194],eax
0040100C |. 6A 00 push 0x0 ; /lParam = NULL
0040100E |. 68 29104000 push cycle.00401029 ; |DlgProc = cycle.00401029
00401013 |. 6A 00 push 0x0 ; |hOwner = NULL
00401015 |. 6A 68 push 0x68 ; |pTemplate = 0x68
00401017 |. FF35 94214000 push dword ptr ds:[0x402194] ; |hInst = NULL
0040101D |. E8 8F020000 call <jmp.&USER32.DialogBoxParamA> ; \DialogBoxParamA
00401022 |. 6A 00 push 0x0 ; /ExitCode = 0x0
00401024 \. E8 7C020000 call <jmp.&KERNEL32.ExitProcess> ; \ExitProcess
3. 右键 查找->当前模块名称,找到GetDlgItemTextA函数,从文本中读取字符串,就在这个函数下断,bp GetDlgItemTextA
4. F9先让程序跑起来,程序跑起来以后在Name和Serial分别输入obaby和1111111111111111,程序要求注册码为16位,稍后会分析到的。这里先输入16个1。
单击check,程序断下了。注意右下角:
0012FAE8 004010BA /CALL 到 GetDlgItemTextA 来自 cycle.004010B5
在这句单击右键,选择反汇编窗口跑随,这样就来到程序领空了。
0040109C /$ C705 82214000>mov dword ptr ds:[0x402182],0xFEDCBA98
004010A6 |. 6A 11 push 0x11 ; /Count = 11 (17.)
004010A8 |. 68 71214000 push cycle.00402171 ; |Buffer = cycle.00402171
004010AD |. 68 E9030000 push 0x3E9 ; |ControlID = 3E9 (1001.)
004010B2 |. FF75 08 push [arg.1] ; |hWnd = 001F06A8 ('CycleCrackme',class='#32770')
004010B5 |. E8 0F020000 call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
004010BA |. 0BC0 or eax,eax
004010BC |. 74 61 je short cycle.0040111F
004010BE |. 6A 11 push 0x11 ; /Count = 11 (17.)
004010C0 |. 68 60214000 push cycle.00402160 ; |Buffer = cycle.00402160
004010C5 |. 68 E8030000 push 0x3E8 ; |ControlID = 3E8 (1000.)
004010CA |. FF75 08 push [arg.1] ; |hWnd = 001F06A8 ('CycleCrackme',class='#32770')
004010CD |. E8 F7010000 call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
004010D2 |. 0BC0 or eax,eax
004010D4 |. 74 49 je short cycle.0040111F
004010D6 |. B9 10000000 mov ecx,0x10
004010DB |. 2BC8 sub ecx,eax
004010DD |. BE 60214000 mov esi,cycle.00402160
004010E2 |. 8BFE mov edi,esi ; cycle.00401029
向上翻,在0040109C /$ C705 82214000>mov dword ptr ds:[0x402182],0xFEDCBA98
这句下断,重新开始。F9运行程序,输入Name和Serial,程序断下了。上面这二个函数大家应该很熟悉,就是我们第一次下断的地方。从文本中读取数据。
5. F8一路往下走,一直到这里。
004010D2 |. 0BC0 or eax,eax
004010D4 |. 74 49 je short cycle.0040111F
004010D6 |. B9 10000000 mov ecx,0x10
004010DB |. 2BC8 sub ecx,eax
004010DD |. BE 60214000 mov esi,cycle.00402160
004010E2 |. 8BFE mov edi,esi ; cycle.00401029
004010E4 |. 03F8 add edi,eax
004010E6 |. FC cld
004010E7 |. F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[>
这里会先判断用户名是否为空,程序在这里会把用户名也变成16个字节的数据。例如我输入的用户名是obaby,经过上面的运算后用户名会变成obabyobabyobabyo,会用输入的用户名来填充其它为0的字节,一直到把16个字节填充完成。
6. F8往下走,到这里
004010E9 |. 33C9 xor ecx,ecx //首先把ecx清空
004010EB |. BE 71214000 mov esi,cycle.00402171 //这里指向注册码
004010F0 |> 41 /inc ecx //这里先加了,如果输入的注册码为16个字节,那么ecx会等于17(0x11)
004010F1 |. AC |lods byte ptr ds:[esi]
004010F2 |. 0AC0 |or al,al
004010F4 |. 74 0A |je short cycle.00401100
004010F6 |. 3C 7E |cmp al,0x7E
004010F8 |. 7F 06 |jg short cycle.00401100
004010FA |. 3C 30 |cmp al,0x30
004010FC |. 72 02 |jb short cycle.00401100
004010FE |.^ EB F0 \jmp short cycle.004010F0
00401100 |> 83F9 11 cmp ecx,0x11
00401103 |. 75 1A jnz short cycle.0040111F //这里不能跳,跳就死
这里实际上就是对输入的注册码进行计数,注册码必须为16个字节。输入的字节不能大于0x7E(~),也不能小于0x30(‘0’)。
7. 对用户名和注册码处理结束后,就是作者写的算法了。算法实际上分为二部分,我们一步步看。
00401105 |. E8 E7000000 call cycle.004011F1 //第一个call,F7进入
0040110A |. B9 01FF0000 mov ecx,0xFF01
0040110F |. 51 push ecx
00401110 |. E8 7B000000 call cycle.00401190 //第二个call,F7进入
00401115 |. 83F9 01 cmp ecx,0x1
00401118 |. 74 06 je short cycle.00401120 //必须跳,不跳就死
上面二个call运算完成后,ecx必须要为1。要不然就注册错误了。
004011F1 /$ A1 60214000 mov eax,dword ptr ds:[0x402160]
004011F6 |. 8B1D 64214000 mov ebx,dword ptr ds:[0x402164] //用户名前8个字节
004011FC |. 3305 71214000 xor eax,dword ptr ds:[0x402171]
00401202 |. 331D 75214000 xor ebx,dword ptr ds:[0x402175] //注册码的前8个字节异或
00401208 |. 25 0F1F3F7F and eax,0x7F3F1F0F
0040120D |. 81E3 00010307 and ebx,0x7030100
00401213 |. 33C9 xor ecx,ecx
00401215 |> 8BF0 /mov esi,eax
00401217 |. 8BFB |mov edi,ebx
00401219 |. D3E6 |shl esi,cl
0040121B |. D3E7 |shl edi,cl
0040121D |. 81E6 80808080 |and esi,0x80808080
00401223 |. 81E7 80808080 |and edi,0x80808080
00401229 |. 8BD6 |mov edx,esi ; cycle.00401029
0040122B |. C0EE 07 |shr dh,0x7
0040122E |. 66:C1E2 07 |shl dx,0x7
00401232 |. C1EA 08 |shr edx,0x8
00401235 |. C0EE 07 |shr dh,0x7
00401238 |. 66:C1E2 07 |shl dx,0x7
0040123C |. C1EA 08 |shr edx,0x8
0040123F |. C0EE 07 |shr dh,0x7
00401242 |. 66:D1EA |shr dx,1
00401245 |. 8BF2 |mov esi,edx
00401247 |. 8BD7 |mov edx,edi
00401249 |. C0EE 07 |shr dh,0x7
0040124C |. 66:C1E2 07 |shl dx,0x7
00401250 |. C1EA 08 |shr edx,0x8
00401253 |. C0EE 07 |shr dh,0x7
00401256 |. 66:C1E2 07 |shl dx,0x7
0040125A |. C1EA 08 |shr edx,0x8
0040125D |. C0EE 07 |shr dh,0x7
00401260 |. 66:C1EA 05 |shr dx,0x5
00401264 |. 8BFA |mov edi,edx
00401266 |. 33FE |xor edi,esi ; cycle.00401029
00401268 |. 8BD7 |mov edx,edi
0040126A |. 81E2 FF000000 |and edx,0xFF
00401270 |. 51 |push ecx
00401271 |. 52 |push edx
00401272 |. BA 08000000 |mov edx,0x8
00401277 |. 91 |xchg eax,ecx
00401278 |. 83F8 03 |cmp eax,0x3
0040127B |. 7F 0F |jg short cycle.0040128C
0040127D |. F6E2 |mul dl
0040127F |. 5A |pop edx ; cycle.004010BA
00401280 |. 83C0 08 |add eax,0x8
00401283 |. 91 |xchg eax,ecx
00401284 |. D3C0 |rol eax,cl
00401286 |. 33C2 |xor eax,edx
00401288 |. D3C8 |ror eax,cl
0040128A |. EB 0D |jmp short cycle.00401299
0040128C |> 83E8 03 |sub eax,0x3
0040128F |. F6E2 |mul dl
00401291 |. 5A |pop edx ; cycle.004010BA
00401292 |. 91 |xchg eax,ecx
00401293 |. D3C3 |rol ebx,cl
00401295 |. 33DA |xor ebx,edx
00401297 |. D3CB |ror ebx,cl
00401299 |> 59 |pop ecx ; cycle.004010BA
0040129A |. 41 |inc ecx
0040129B |. 83F9 08 |cmp ecx,0x8
0040129E |.^ 0F85 71FFFFFF \jnz cycle.00401215
004012A4 \. C3 retn
第二个call
00401190 /$ 5F pop edi ; cycle.004010BA
00401191 |. 59 pop ecx ; cycle.004010BA
00401192 |. 57 push edi
00401193 |. 81F9 80000000 cmp ecx,0x80
00401199 |. 7E 55 jle short cycle.004011F0
0040119B |. 51 push ecx
0040119C |. 8BF1 mov esi,ecx
0040119E |. 81E1 FF000000 and ecx,0xFF
004011A4 |. 8BF8 mov edi,eax
004011A6 |. 83F9 08 cmp ecx,0x8
004011A9 |. 7E 05 jle short cycle.004011B0
004011AB |. 8BFB mov edi,ebx
004011AD |. C1E9 04 shr ecx,0x4
004011B0 |> C1C7 08 /rol edi,0x8
004011B3 |. D1E9 |shr ecx,1
004011B5 |.^ 75 F9 \jnz short cycle.004011B0
004011B7 |. C1EE 08 shr esi,0x8
004011BA |. 23FE and edi,esi ; cycle.00401029
004011BC |. 81E7 FF000000 and edi,0xFF
004011C2 |. 59 pop ecx ; cycle.004010BA
004011C3 |> BE 80000000 mov esi,0x80
004011C8 |> 85FE /test esi,edi
004011CA |. 74 20 |je short cycle.004011EC
004011CC |. 33FE |xor edi,esi ; cycle.00401029
004011CE |. 57 |push edi
004011CF |. 81E1 00FF0000 |and ecx,0xFF00
004011D5 |. 87CE |xchg esi,ecx
004011D7 |. 32E9 |xor ch,cl
004011D9 |. 33F1 |xor esi,ecx
004011DB |. 87F1 |xchg ecx,esi ; cycle.00401029
004011DD |. 51 |push ecx
004011DE |. FF05 82214000 |inc dword ptr ds:[0x402182] //这里很重要
004011E4 |. E8 A7FFFFFF |call cycle.00401190
004011E9 |. 5F |pop edi ; cycle.004010BA
004011EA |.^ EB D7 |jmp short cycle.004011C3
004011EC |> D1EE |shr esi,1
004011EE |.^ 75 D8 \jnz short cycle.004011C8
004011F0 \> C3 retn
这里用到的数据实际上是第一个call里面对用户名和注册码进行运算后的数据。
inc dword ptr ds:[0x402182]这里自加的数据,实际上是
0040109C /$ C705 82214000>mov dword ptr ds:[0x402182],0xFEDCBA98
第二次下断的第一句。这个数据开始作者就定义了,只是会根据实际输入的用户名和注册码运算后,再次改变这个数据。
上面这二个call对输入的用户名和注册码进行运算后,ecx必须要为1。要不然就跳向结束了。当ecx=1后,跳到后半部分的算法运算了,到这里程序还只用到了前8位数据,后8后数据还没有用到。
00401120 |> \A1 68214000 mov eax,dword ptr ds:[0x402168]
00401125 |. 8B1D 6C214000 mov ebx,dword ptr ds:[0x40216C] //用户名的后8位数据
0040112B |. 33C3 xor eax,ebx
0040112D |. 3305 82214000 xor eax,dword ptr ds:[0x402182] //就是刚刚提到的那个比较重要的数据
00401133 |. 0D 40404040 or eax,0x40404040
00401138 |. 25 77777777 and eax,0x77777777
0040113D |. 3305 79214000 xor eax,dword ptr ds:[0x402179]
00401143 |. 3305 7D214000 xor eax,dword ptr ds:[0x40217D]//对注册码的后8位进行异或
00401149 |.^ 75 CF jnz short cycle.0040111A //ZF必须要为1,不死就死了
0040114B |. E8 2B000000 call cycle.0040117B
程序的算法一共分为二处,第一处运算完成为ecx=1,第二处运算完成后ZF=1。知道这个条件后,就可以自已动手写一个注册机了。下面是用masm32写的一个注册机。
.386
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
include user32.inc
include masm32.inc
includelib kernel32.lib
includelib user32.lib
includelib masm32.lib
crackcallA proto
crackcallB proto
crackcallC proto
crackcallD proto
.data
value dword 0fedcba98h
printname db 'input name: ',0
printpawd db 'input password: ',0
szFmt db 'done!!!!',0
username db 17 dup (0)
password db 17 dup (0) .code
start:
invoke StdOut,addr printname
invoke StdIn,addr username,lengthof username
invoke StdOut,addr printpawd
invoke StdIn,addr password,lengthof password
xor ecx,ecx
lea esi,username
lea edi,username
usernameloop:
mov al,byte ptr [esi]
or al,al
jz usernamelab
inc ecx
inc esi
jmp usernameloop
usernamelab:
mov eax,10h
mov edx,ecx
sub eax,ecx
xchg eax,ecx
mov esi,edi
add edi,edx
cld
rep movs byte ptr es:[edi],byte ptr [esi]
xor ecx,ecx
lea esi,password
passwordloop:
inc ecx
mov al,byte ptr [esi]
or al,al
je passwordcount
inc esi
jmp passwordloop
passwordcount:
cmp ecx,011h
jnz over call crackcallA
mov ecx,0ff01h
push ecx
call crackcallB cmp ecx,1
je tab1
call crackcallC
tab1:
mov eax,dword ptr [username+8]
mov ebx,dword ptr [username+12]
xor eax,ebx
xor eax,dword ptr [value]
or eax,040404040h
and eax,077777777h
xor eax,dword ptr [password+8]
xor eax,dword ptr [password+12]
jz done
call crackcallD
done:
invoke StdOut,addr szFmt
over:
invoke ExitProcess,0 crackcallA proc
mov eax,dword ptr [username]
mov ebx,dword ptr [username+4]
xor eax,dword ptr [password]
xor ebx,dword ptr [password+4]
and eax,07f3f1f0fh
and ebx,07030100h
xor ecx,ecx
begin:
mov esi, eax
mov edi, ebx
shl esi, cl
shl edi, cl
and esi, 080808080h
and edi, 080808080h
mov edx, esi
shr dh, 7
shl dx, 7
shr edx, 8
shr dh, 7
shl dx, 7
shr edx, 8
shr dh, 7
shr dx, 1
mov esi, edx
mov edx, edi
shr dh, 7
shl dx, 7
shr edx, 8
shr dh, 7
shl dx, 7
shr edx, 8
shr dh, 7
shr dx, 5
mov edi, edx
xor edi, esi
mov edx, edi
and edx, 0FFh
push ecx
push edx
mov edx, 8
xchg eax, ecx
cmp eax, 3
jg lab1
mul dl
pop edx
add eax, 8
xchg eax, ecx
rol eax, cl
xor eax, edx
ror eax, cl
jmp lab2
lab1:
sub eax, 3
mul dl
pop edx
xchg eax, ecx
rol ebx, cl
xor ebx, edx
ror ebx, cl
lab2:
pop ecx
inc ecx
cmp ecx, 8
jnz begin
ret
crackcallA endp
crackcallB proc
pop edi
pop ecx
push edi
cmp ecx, 080h
jle done1
push ecx
mov esi, ecx
and ecx, 0FFh
mov edi, eax
cmp ecx, 8
jle lab1
mov edi, ebx
shr ecx, 4
lab1:
rol edi, 8
shr ecx, 1
jnz lab1
shr esi, 8
and edi, esi
and edi, 0FFh
pop ecx
lab3:
mov esi, 080h
lab4:
test esi, edi
je lab2
xor edi, esi
push edi
and ecx, 0FF00h
xchg esi, ecx
xor ch, cl
xor esi, ecx
xchg ecx, esi
push ecx
inc dword ptr [value]
call crackcallB
pop edi
jmp lab3
lab2:
shr esi, 1
jnz lab4
done1:
ret
crackcallB endp
crackcallC proc
xor ecx,ecx
tap0:
lea edi,password
mov al,byte ptr [edi]
inc al
mov byte ptr [edi],al
call crackcallA
mov ecx,0ff01h
push ecx
call crackcallB
;add esp,4
cmp ecx,1
je retncall
lea edi,password
mov al,byte ptr [edi]
;inc al
;mov byte ptr [edi],al
;mov al,byte ptr [edi]
cmp al,07eh
jle tap0
lea edi,password
mov byte ptr [edi],'0'
lea edi,password
mov al, byte ptr [edi+1]
inc al
mov byte ptr [edi+1],al
mov al,byte ptr [edi+1]
cmp al,07eh
jle tap0
lea edi,password
mov byte ptr [edi+1],'0'
lea edi,password
mov al, byte ptr [edi+2]
inc al
mov byte ptr [edi+2],al
mov al,byte ptr [edi+2]
cmp al,07eh
jle tap0
lea edi,password
mov byte ptr [edi+2],'0'
lea edi,password
mov al,byte ptr [edi+3]
inc al
mov byte ptr [edi+3],al
mov al,byte ptr [edi+3]
cmp al,07eh
jle tap0
lea edi,password
mov byte ptr [edi+3],'0'
lea edi,password
mov al,byte ptr [edi+4]
inc al
mov byte ptr [edi+4],al
mov al,byte ptr [edi+4]
cmp al,07eh
jle tap0
lea edi,password
mov byte ptr [edi+4],'0'
lea edi,password
mov al,byte ptr [edi+5]
inc al
mov byte ptr [edi+5],al
mov al,byte ptr [edi+5]
cmp al,07eh
jle tap0
lea edi,password
mov byte ptr [edi+5],'0'
lea edi,password
mov al,byte ptr [edi+6]
inc al
mov byte ptr [edi+6],al
mov al,byte ptr [edi+6]
cmp al,07eh
jle tap0
lea edi,password
mov byte ptr [edi+6],'0'
lea edi,password
mov al,byte ptr [edi+7]
inc al
mov byte ptr [edi+7],al
mov al,byte ptr [edi+7]
cmp al,07eh
jle tap0
retncall:
ret
crackcallC endp
crackcallD proc
tap0:
lea edi,password
mov al,byte ptr [edi+8]
inc al
mov byte ptr [edi+8],al
mov eax,dword ptr [username+8]
mov ebx,dword ptr [username+12]
xor eax,ebx
xor eax,dword ptr [value]
or eax,040404040h
and eax,077777777h
xor eax,dword ptr [password+8]
xor eax,dword ptr [password+12]
jz retncall
lea edi,password
mov al,byte ptr [edi+8]
cmp al,07eh
jle tap0
mov byte ptr [edi+8],'0'
lea edi,password
mov al,byte ptr [edi+9]
inc al
mov byte ptr [edi+9],al
cmp al,07eh
jle tap0
mov byte ptr [edi+9],'0'
lea edi,password
mov al,byte ptr [edi+0ah]
inc al
mov byte ptr [edi+0ah],al
cmp al,07eh
jle tap0
mov byte ptr [edi+0ah],'0'
lea edi,password
mov al,byte ptr [edi+0bh]
inc al
mov byte ptr [edi+0bh],al
cmp al,07eh
jle tap0
mov byte ptr [edi+0bh],'0'
lea edi,password
mov al,byte ptr [edi+0ch]
inc al
mov byte ptr [edi+0ch],al
cmp al,07eh
jle tap0
mov byte ptr [edi+0ch],'0'
lea edi,password
mov al,byte ptr [edi+0dh]
inc al
mov byte ptr [edi+0dh],al
cmp al,07eh
jle tap0
mov byte ptr [edi+0dh],'0'
lea edi,password
mov al,byte ptr [edi+0eh]
inc al
mov byte ptr [edi+0eh],al
cmp al,07eh
jle tap0
mov byte ptr [edi+0eh],'0'
lea edi,password
mov al,byte ptr [edi+0fh]
inc al
mov byte ptr [edi+0fh],al
cmp al,07eh
jle tap0
retncall:
ret
crackcallD endp
end start
测试成功,附上二种计算正确的注册码:
1. obaby
19400000CQrC0000
2.obaby
;7422222VSpA3222
【分析总结】
其实编写注册机,注册机里面的算法可以照抄程序本身的,只是加了一些测试环节。我们只需要满足二个条件就可以了。Ecx=1和zf=1。如果满足这二个条件,那么注册码就是正解的。每个用户名可以生成多个注册码。所以用户名和注册码并不是唯一的。