-
-
[求助]写注册机时遇到的困难
-
发表于: 2005-3-12 19:35 4764
-
我在研究CRACKME中的序列号:Serializer(CoDe_InSiDe)时遇到的问题:
------------------------------------------------------------------------
用OLLYDBG载入之后会看到停在这里:
00401200 >/$ BF 00104000 MOV EDI,SERIALIZ.00401000
00401205 |. 57 PUSH EDI
00401206 |. 33C0 XOR EAX,EAX
00401208 |. 33C9 XOR ECX,ECX
0040120A |> 8A07 /MOV AL,BYTE PTR DS:[EDI]
0040120C |. 3C C7 |CMP AL,0C7
0040120E |. 74 05 |JE SHORT SERIALIZ.00401215
00401210 |. FE07 |INC BYTE PTR DS:[EDI]
00401212 |. 47 |INC EDI
00401213 |.^EB F5 \JMP SHORT SERIALIZ.0040120A
00401215 |> 47 /INC EDI
00401216 |. 8A07 |MOV AL,BYTE PTR DS:[EDI]
00401218 |. 3C C7 |CMP AL,0C7
0040121A |. 74 06 |JE SHORT SERIALIZ.00401222
0040121C |. 8007 04 |ADD BYTE PTR DS:[EDI],4
0040121F |. 90 |NOP
00401220 |.^EB F3 \JMP SHORT SERIALIZ.00401215
00401222 |> 33C0 XOR EAX,EAX
00401224 |. 33C9 XOR ECX,ECX
00401226 |. 33D2 XOR EDX,EDX
00401228 |. 33F6 XOR ESI,ESI
0040122A |. 58 POP EAX
0040122B |. FFD0 CALL EAX
0040122D \. C3 RETN
从40120A到00401220是解密代码:也就是说真正有用的代码被加密了
在这里应该注意到:想爆破这个程序的话要注意把指令加密后写入程序相应位置
加密算法很简单我就不赘述了,在40122B处下个断点,运行程序就自己将代码解密了,这时候按CTRL+A就能看到.00401000处开始的本来是一串不知所云的DB数据变成代码了^_^
认真观察能找到这一段代码:
0040107D |. 85C0 TEST EAX,EAX ; EAX 中是用户名的长度
0040107F |. 75 04 JNZ SHORT SERIALIZ.00401085 ; 若长度为0则直接返回,否则处理用户名
00401081 |> 33C0 XOR EAX,EAX
00401083 |. 5D POP EBP
00401084 |. C3 RETN
00401085 |> 33C0 XOR EAX,EAX ; 准备工作
00401087 |. 33C9 XOR ECX,ECX
00401089 |. 33D2 XOR EDX,EDX
0040108B |. BF 00134000 MOV EDI,SERIALIZ.00401300 ; EDI = NAME
00401090 |> 8A07 /MOV AL,BYTE PTR DS:[EDI] ; EAC = 当前字符ASCII码
00401092 |. 3C 00 |CMP AL,0 ; 若当前字符为空
00401094 |. 74 21 |JE SHORT SERIALIZ.004010B7
00401096 |. C1C0 08 |ROL EAX,8 ; EAX 不带进位左移1个字符
00401099 |. 8A07 |MOV AL,BYTE PTR DS:[EDI] ; EAX += 当前字符ASCII码
0040109B |. 3C 00 |CMP AL,0 ; 若字符串结束则结束处理
0040109D |. 74 18 |JE SHORT SERIALIZ.004010B7
0040109F |. C1C0 08 |ROL EAX,8 ; EAX 不带进位左移1个字符
004010A2 |. 47 |INC EDI ; 指向下一个字符
004010A3 |. 8A07 |MOV AL,BYTE PTR DS:[EDI] ; EAX += 当前字符ASCII码
004010A5 |. 3C 00 |CMP AL,0 ; 若字符串结束则结束处理
004010A7 |. 74 0E |JE SHORT SERIALIZ.004010B7
004010A9 |. C1C0 08 |ROL EAX,8 ; EAX 不带进位左移1个字符
004010AC |. 8A07 |MOV AL,BYTE PTR DS:[EDI] ; EAX += 当前字符ASCII码
004010AE |. 3C 00 |CMP AL,0 ; 若字符串结束则结束处理
004010B0 |. 74 05 |JE SHORT SERIALIZ.004010B7
004010B2 |. 01C1 |ADD ECX,EAX ; ECX += EAX
004010B4 |. 47 |INC EDI ; EDI ++ 指向下一个字符
004010B5 |.^ EB D9 \JMP SHORT SERIALIZ.00401090 ; 开始处理下一个字符
004010B7 |> 3D 00000000 CMP EAX,0 ; 若 EAX == 0 则结束
004010BC |.^ 74 C3 JE SHORT SERIALIZ.00401081
004010BE |. 8BC1 MOV EAX,ECX ; 存处理结果X,ECX
004010C0 |. A3 20134000 MOV DWORD PTR DS:[401320],EAX ; 将结果存储在 .401320
注意到了没:如果用户名的长度是奇数,则最后一个字符会被忽略。
看来由用户名处理得来得结果并不是最终的注册码,只好接着往下看了:
004010DE |. 85C0 TEST EAX,EAX ; EAX 中存储的是用户输入的注册码的长度
004010E0 |. 75 04 JNZ SHORT SERIALIZ.004010E6 ; 若没输入注册码则结束
004010E2 |. 33C0 XOR EAX,EAX
004010E4 |. 5D POP EBP
004010E5 |. C3 RETN
004010E6 |> 33C0 XOR EAX,EAX ; 准备工作
004010E8 |. 33C9 XOR ECX,ECX
004010EA |. 33D2 XOR EDX,EDX
004010EC |. 33F6 XOR ESI,ESI
004010EE |. BF 00144000 MOV EDI,SERIALIZ.00401400 ; EDI指向用户输入的注册码
004010F3 |> 8A07 /MOV AL,BYTE PTR DS:[EDI] ; EAX = 当前字符ASCII码
004010F5 |. 3C 00 |CMP AL,0 ; 若字符串结束则结束处理
004010F7 |. 74 16 |JE SHORT SERIALIZ.0040110F
004010F9 |. 8A4F 01 |MOV CL,BYTE PTR DS:[EDI+1] ; ECX = 下一个字符ASCII码
004010FC |. 2BC1 |SUB EAX,ECX ; EAX -= ECX
004010FE |. 01C1 |ADD ECX,EAX ; ECX += EAX
00401100 |. 01CA |ADD EDX,ECX ; EDX += ECX
00401102 |. 47 |INC EDI ; EDI ++ 指向下一个字符
00401103 |. 42 |INC EDX ; EDX ++
00401104 |. C1C0 08 |ROL EAX,8 ; EAX 不带进位左移1个字符
00401107 |. C1C9 08 |ROR ECX,8 ; ECX 不带进位右移1个字符
0040110A |. C1C2 04 |ROL EDX,4 ; EDX 不带进位左移1个字节
0040110D |.^ EB E4 \JMP SHORT
0040110F |> 8BC2 MOV EAX,EDX ; 将处理结果暂存在 EAX
00401111 |> A3 31144000 MOV DWORD PTR DS:[401430],EAX ; 将处理结果保存
00401116 |. 33D2 XOR EDX,EDX ; EDX 清零
00401118 |. 83F9 01 CMP ECX,1 ; 若 ECX == 1 ?
0040111B |. 75 02 JNZ SHORT SERIALIZ.0040111F ; 否,则继续处理
0040111D |. EB 21 JMP SHORT SERIALIZ.00401140 ; 是,则开始对数据进行处理并比较
0040111F |> C1C8 08 ROR EAX,8 ; AH == 0 ?
00401122 |. 3C 00 CMP AL,0
00401124 |. 75 0C JNZ SHORT SERIALIZ.00401132
00401126 |. C605 12114000>MOV BYTE PTR DS:[401112],31 ; 是,则.401112 = 31H
0040112D |> 33C9 XOR ECX,ECX ; ECX = 1
0040112F |. 41 INC ECX
00401130 |.^ EB DF JMP SHORT SERIALIZ.00401111
00401132 |> B0 00 MOV AL,0 ; 否,则:
00401134 |. C1C8 08 ROR EAX,8 ; EAX 不带进位右移1个字符
00401137 |. C605 12114000>MOV BYTE PTR DS:[401112],31 ; .401112 = 31H
0040113E |.^\EB ED JMP SHORT SERIALIZ.0040112D
后面还有将用户名的处理结果与用户输入的注册码分别与“BYTE”做异或处理的操作,怎么才能得到真实的注册码呢?
因为与两个结果进行异或的数据是同一个字符串,所以只要用户输入的注册码的处理结果与用户名的处理结果一致就行了(就算不一样我也知道怎么做 :P)
所以想得到真正的注册码只要对用户名的处理结果进行逆操作就行了,值得一提的是004010FC与004010FE处的两条指令其实只完成了ECX = EAX -_- 而且CL中的数据就是EDI所指向的字符与其后面一个字符的差
这个算法中涉及了大量位操作,但是用TC2.0中的内联汇编好象不支持32位的寄存器 -_-!!
只好用C写函数来模拟移位操作了 -_-!!
---------------------------------------------------------------------------------
虽然算法都找出来了,但是在写注册机的时候我遇到了个问题:注册机中的EDX 的初始值可以根据由用户名的处理结果来得到,但是EAX 和 ECX 中的数据到哪弄?
还有个问题:怎么才算脱壳完全?
------------------------------------------------------------------------
用OLLYDBG载入之后会看到停在这里:
00401200 >/$ BF 00104000 MOV EDI,SERIALIZ.00401000
00401205 |. 57 PUSH EDI
00401206 |. 33C0 XOR EAX,EAX
00401208 |. 33C9 XOR ECX,ECX
0040120A |> 8A07 /MOV AL,BYTE PTR DS:[EDI]
0040120C |. 3C C7 |CMP AL,0C7
0040120E |. 74 05 |JE SHORT SERIALIZ.00401215
00401210 |. FE07 |INC BYTE PTR DS:[EDI]
00401212 |. 47 |INC EDI
00401213 |.^EB F5 \JMP SHORT SERIALIZ.0040120A
00401215 |> 47 /INC EDI
00401216 |. 8A07 |MOV AL,BYTE PTR DS:[EDI]
00401218 |. 3C C7 |CMP AL,0C7
0040121A |. 74 06 |JE SHORT SERIALIZ.00401222
0040121C |. 8007 04 |ADD BYTE PTR DS:[EDI],4
0040121F |. 90 |NOP
00401220 |.^EB F3 \JMP SHORT SERIALIZ.00401215
00401222 |> 33C0 XOR EAX,EAX
00401224 |. 33C9 XOR ECX,ECX
00401226 |. 33D2 XOR EDX,EDX
00401228 |. 33F6 XOR ESI,ESI
0040122A |. 58 POP EAX
0040122B |. FFD0 CALL EAX
0040122D \. C3 RETN
从40120A到00401220是解密代码:也就是说真正有用的代码被加密了
在这里应该注意到:想爆破这个程序的话要注意把指令加密后写入程序相应位置
加密算法很简单我就不赘述了,在40122B处下个断点,运行程序就自己将代码解密了,这时候按CTRL+A就能看到.00401000处开始的本来是一串不知所云的DB数据变成代码了^_^
认真观察能找到这一段代码:
0040107D |. 85C0 TEST EAX,EAX ; EAX 中是用户名的长度
0040107F |. 75 04 JNZ SHORT SERIALIZ.00401085 ; 若长度为0则直接返回,否则处理用户名
00401081 |> 33C0 XOR EAX,EAX
00401083 |. 5D POP EBP
00401084 |. C3 RETN
00401085 |> 33C0 XOR EAX,EAX ; 准备工作
00401087 |. 33C9 XOR ECX,ECX
00401089 |. 33D2 XOR EDX,EDX
0040108B |. BF 00134000 MOV EDI,SERIALIZ.00401300 ; EDI = NAME
00401090 |> 8A07 /MOV AL,BYTE PTR DS:[EDI] ; EAC = 当前字符ASCII码
00401092 |. 3C 00 |CMP AL,0 ; 若当前字符为空
00401094 |. 74 21 |JE SHORT SERIALIZ.004010B7
00401096 |. C1C0 08 |ROL EAX,8 ; EAX 不带进位左移1个字符
00401099 |. 8A07 |MOV AL,BYTE PTR DS:[EDI] ; EAX += 当前字符ASCII码
0040109B |. 3C 00 |CMP AL,0 ; 若字符串结束则结束处理
0040109D |. 74 18 |JE SHORT SERIALIZ.004010B7
0040109F |. C1C0 08 |ROL EAX,8 ; EAX 不带进位左移1个字符
004010A2 |. 47 |INC EDI ; 指向下一个字符
004010A3 |. 8A07 |MOV AL,BYTE PTR DS:[EDI] ; EAX += 当前字符ASCII码
004010A5 |. 3C 00 |CMP AL,0 ; 若字符串结束则结束处理
004010A7 |. 74 0E |JE SHORT SERIALIZ.004010B7
004010A9 |. C1C0 08 |ROL EAX,8 ; EAX 不带进位左移1个字符
004010AC |. 8A07 |MOV AL,BYTE PTR DS:[EDI] ; EAX += 当前字符ASCII码
004010AE |. 3C 00 |CMP AL,0 ; 若字符串结束则结束处理
004010B0 |. 74 05 |JE SHORT SERIALIZ.004010B7
004010B2 |. 01C1 |ADD ECX,EAX ; ECX += EAX
004010B4 |. 47 |INC EDI ; EDI ++ 指向下一个字符
004010B5 |.^ EB D9 \JMP SHORT SERIALIZ.00401090 ; 开始处理下一个字符
004010B7 |> 3D 00000000 CMP EAX,0 ; 若 EAX == 0 则结束
004010BC |.^ 74 C3 JE SHORT SERIALIZ.00401081
004010BE |. 8BC1 MOV EAX,ECX ; 存处理结果X,ECX
004010C0 |. A3 20134000 MOV DWORD PTR DS:[401320],EAX ; 将结果存储在 .401320
注意到了没:如果用户名的长度是奇数,则最后一个字符会被忽略。
看来由用户名处理得来得结果并不是最终的注册码,只好接着往下看了:
004010DE |. 85C0 TEST EAX,EAX ; EAX 中存储的是用户输入的注册码的长度
004010E0 |. 75 04 JNZ SHORT SERIALIZ.004010E6 ; 若没输入注册码则结束
004010E2 |. 33C0 XOR EAX,EAX
004010E4 |. 5D POP EBP
004010E5 |. C3 RETN
004010E6 |> 33C0 XOR EAX,EAX ; 准备工作
004010E8 |. 33C9 XOR ECX,ECX
004010EA |. 33D2 XOR EDX,EDX
004010EC |. 33F6 XOR ESI,ESI
004010EE |. BF 00144000 MOV EDI,SERIALIZ.00401400 ; EDI指向用户输入的注册码
004010F3 |> 8A07 /MOV AL,BYTE PTR DS:[EDI] ; EAX = 当前字符ASCII码
004010F5 |. 3C 00 |CMP AL,0 ; 若字符串结束则结束处理
004010F7 |. 74 16 |JE SHORT SERIALIZ.0040110F
004010F9 |. 8A4F 01 |MOV CL,BYTE PTR DS:[EDI+1] ; ECX = 下一个字符ASCII码
004010FC |. 2BC1 |SUB EAX,ECX ; EAX -= ECX
004010FE |. 01C1 |ADD ECX,EAX ; ECX += EAX
00401100 |. 01CA |ADD EDX,ECX ; EDX += ECX
00401102 |. 47 |INC EDI ; EDI ++ 指向下一个字符
00401103 |. 42 |INC EDX ; EDX ++
00401104 |. C1C0 08 |ROL EAX,8 ; EAX 不带进位左移1个字符
00401107 |. C1C9 08 |ROR ECX,8 ; ECX 不带进位右移1个字符
0040110A |. C1C2 04 |ROL EDX,4 ; EDX 不带进位左移1个字节
0040110D |.^ EB E4 \JMP SHORT
0040110F |> 8BC2 MOV EAX,EDX ; 将处理结果暂存在 EAX
00401111 |> A3 31144000 MOV DWORD PTR DS:[401430],EAX ; 将处理结果保存
00401116 |. 33D2 XOR EDX,EDX ; EDX 清零
00401118 |. 83F9 01 CMP ECX,1 ; 若 ECX == 1 ?
0040111B |. 75 02 JNZ SHORT SERIALIZ.0040111F ; 否,则继续处理
0040111D |. EB 21 JMP SHORT SERIALIZ.00401140 ; 是,则开始对数据进行处理并比较
0040111F |> C1C8 08 ROR EAX,8 ; AH == 0 ?
00401122 |. 3C 00 CMP AL,0
00401124 |. 75 0C JNZ SHORT SERIALIZ.00401132
00401126 |. C605 12114000>MOV BYTE PTR DS:[401112],31 ; 是,则.401112 = 31H
0040112D |> 33C9 XOR ECX,ECX ; ECX = 1
0040112F |. 41 INC ECX
00401130 |.^ EB DF JMP SHORT SERIALIZ.00401111
00401132 |> B0 00 MOV AL,0 ; 否,则:
00401134 |. C1C8 08 ROR EAX,8 ; EAX 不带进位右移1个字符
00401137 |. C605 12114000>MOV BYTE PTR DS:[401112],31 ; .401112 = 31H
0040113E |.^\EB ED JMP SHORT SERIALIZ.0040112D
后面还有将用户名的处理结果与用户输入的注册码分别与“BYTE”做异或处理的操作,怎么才能得到真实的注册码呢?
因为与两个结果进行异或的数据是同一个字符串,所以只要用户输入的注册码的处理结果与用户名的处理结果一致就行了(就算不一样我也知道怎么做 :P)
所以想得到真正的注册码只要对用户名的处理结果进行逆操作就行了,值得一提的是004010FC与004010FE处的两条指令其实只完成了ECX = EAX -_- 而且CL中的数据就是EDI所指向的字符与其后面一个字符的差
这个算法中涉及了大量位操作,但是用TC2.0中的内联汇编好象不支持32位的寄存器 -_-!!
只好用C写函数来模拟移位操作了 -_-!!
---------------------------------------------------------------------------------
虽然算法都找出来了,但是在写注册机的时候我遇到了个问题:注册机中的EDX 的初始值可以根据由用户名的处理结果来得到,但是EAX 和 ECX 中的数据到哪弄?
还有个问题:怎么才算脱壳完全?
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
- [求助]关于最新版的OD的跳转路径的显示问题 4693
- [原创]Bad Sector CrackMe的算法分析与注册机 4582
- [求助]写注册机时遇到的困难 4765
赞赏
雪币:
留言: