http://www.cppblog.com/Files/sleepwom/cycle.zip
Name: ffff
Serial: 11223344556677889
在所有 GetDlgItemTextA API设置断点
004010A6 |. 6A 11 push 11 ; /Count = 11 (17.)
004010A8 |. 68 71214000 push 00402171 ; |Buffer = cycle.00402171
; 右击 -> 数据窗口中跟随 -> 立即数
004010AD |. 68 E9030000 push 3E9 ; |ControlID = 3E9 (1001.)
004010B2 |. FF75 08 push dword ptr [ebp+8] ; |hWnd
004010B5 |. E8 0F020000 call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
; 00402171 31 31 32 32 33 33 34 34 35 35 36 36 37 37 38 38 1122334455667788
; 00402181 00 98 BA DC FE 43 79 63 6C 65 43 72 61 63 6B 6D .樅荥CycleCrackm
004010BA |. 0BC0 or eax, eax ; 判断 eax 是否为 0, 如果是 jump 0040111F
004010BC |. 74 61 je short 0040111F
004010BE |. 6A 11 push 11 ; /Count = 11 (17.)
004010C0 |. 68 60214000 push 00402160 ; |Buffer = cycle.00402160
; 右击 -> 数据窗口中跟随 -> 立即数
004010C5 |. 68 E8030000 push 3E8 ; |ControlID = 3E8 (1000.)
004010CA |. FF75 08 push dword ptr [ebp+8] ; |hWnd
004010CD |. E8 F7010000 call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
; 00402160 66 66 66 66 00 00 00 00 00 00 00 00 00 00 00 00 ffff............
; 00402170 00 31 31 32 32 33 33 34 34 35 35 36 36 37 37 38 .112233445566778
004010D2 |. 0BC0 or eax, eax ; 判断 eax 是否为 0, 如果是 jump 0040111F
004010D4 |. 74 49 je short 0040111F
004010D6 |. B9 10000000 mov ecx, 10 ; ecx = 0x10
004010DB |. 2BC8 sub ecx, eax ; ecx = ecx - eax (eax 此时存放用户名的长度)
004010DD |. BE 60214000 mov esi, 00402160 ; esi 指向用户名
004010E2 |. 8BFE mov edi, esi ; edi 指向用户名
004010E4 |. 03F8 add edi, eax ; edi 指向用户名后面的位置
004010E6 |. FC cld
; 清除方向标志,在字符串的比较,赋值,读取等一系列和rep连用的操作中,
; di或si是可以自动增减的而不需要人来加减它的值,
; cld即告诉程序si,di向前移动,std指令为设置方向,告诉程序si,di向后移动
004010E7 |. F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
; rep 指令
;格式:REP MOVS(或REP STOS,或REP LODS);
;执行的操作:使REP后面的串操作指令重复执行,执行的次数预先存放在CX寄存器中,每执行一次串操作指令,
;CX寄存器的内容自动减1,一直重复执行到CX=0,指令才结束。具体的操作步骤是:
;第1步 如果CX=0,则退出REP,否则执行REP后面的串操作指令;
;第2步 CX ← CX-1;
;第3步 执行串操作指令
;第4步 重复第1步~第3步。
;rep movs byte ptr es:[edi], byte ptr [esi]
;的意思是,重复执行 movs byte ptr es:[edi], byte ptr [esi] 这个语句,次数是 ecx 寄存器中的次数
;1. esi 指向输入的用户名
;2. edi 指向输入用户名后面的内存
;3. ecx 是通过 0x10- 输入用户名的长度得来的
;也就是说,如果输入的用户名,不足 0x10 这个长度, 就是用‘输入的用户名’重复去填充,直至 0x10 这个长度
;如果输入的用户名已经是 0x10 这个长度,那么就不做任何操作了
004010E9 |. 33C9 xor ecx, ecx ; ecx 清 0
004010EB |. BE 71214000 mov esi, 00402171 ; ASCII "11223344556677889" ; esi 指向输入的序列号, 为 lods 指令作准备
004010F0 |> 41 /inc ecx ; ecx 自增 1
004010F1 |. AC |lods byte ptr [esi]
; 字符串操作中,从串取指令,
;从DS:SI所指向的空间中取出一个字节/字/双字放入寄存器中AL/AX/EAX,
;同时把SI+(-)1/2/4.(LODS相当于MOV AL,[SI] INC SI.)
;从串取指令就是从DS:SI所指向字符串中取出一个字符.
004010F2 |. 0AC0 |or al, al ; 判断字符是否为 0? 字符串结束标志
004010F4 |. 74 0A |je short 00401100 ;如果al,即取出来的字符等于0,就是跳转到00401100 这里的 0 是结束字符
004010F6 |. 3C 7E |cmp al, 7E ; 将 al 跟 0x7e 进行比较, 7e 的十进制是 126, 7E 对应的字符是 '~'
004010F8 |. 7F 06 |jg short 00401100 ; j 表示 jump, g 表示 greater , 也就是说,取出来的字符如果大于 '~' 这个字符,就是跳转到 00401100
004010FA |. 3C 30 |cmp al, 30 ; 将 al跟 0x30 进行比较,30的十进制是 48, 0x30 对应的字符是 '-'
004010FC |. 72 02 |jb short 00401100 ; j 表示 jump, b 表示 below, jb 判断 cf 标志位,低于时跳转, jl 用于带符号的运算,jb用于 无符号的运算
004010FE |.^ EB F0 \jmp short 004010F0 ; 循环,直至字符串结束
00401100 |> 83F9 11 cmp ecx, 11 ; 判断字符串长度是否为 0x11
00401103 |. 75 1A jnz short 0040111F ; cmp 算术减法运算结果为零,就把ZF(零标志)置1. jnz 判断当 ZF(零标志位)为 0 时就跳转
00401105 |. E8 E7000000 call 004011F1
0040110A |. B9 01FF0000 mov ecx, 0FF01
0040110F |. 51 push ecx
00401110 |. E8 7B000000 call 00401190
00401115 |. 83F9 01 cmp ecx, 1
00401118 |. 74 06 je short 00401120 <------------- 破解关键点 1, 使 ZF 标志位 ==1
0040111A |> E8 47000000 call 00401166
0040111F |> C3 retn
00401120 |> A1 68214000 mov eax, dword ptr [402168]
00401125 |. 8B1D 6C214000 mov ebx, dword ptr [40216C]
0040112B |. 33C3 xor eax, ebx
0040112D |. 3305 82214000 xor eax, dword ptr [402182]
00401133 |. 0D 40404040 or eax, 40404040
00401138 |. 25 77777777 and eax, 77777777
0040113D |. 3305 79214000 xor eax, dword ptr [402179]
00401143 |. 3305 7D214000 xor eax, dword ptr [40217D]
00401149 |.^ 75 CF jnz short 0040111A <------------- 破解关键点 2, 使 ZF 标志位 ==1
0040114B |. E8 2B000000 call 0040117B ; 指向成功显示"Congratulations!"
00401150 \. C3 retn
不难得出,破解有两个关键点
00401105 |. E8 E7000000 call 004011F1 <------------- 作用未知
0040110A |. B9 01FF0000 mov ecx, 0FF01 <------------- ecx = 0X0FF01
0040110F |. 51 push ecx <-------------- ecx 进栈
00401110 |. E8 7B000000 call 00401190 <-------------- ***** 在此设置断点进去分析 *****
00401115 |. 83F9 01 cmp ecx, 1 <------------- 只要使 ecx == 1
00401118 |. 74 06 je short 00401120 <------------- 破解关键点 1, 使 ZF 标志位 ==1
===============================================================================================
call 00401190 断点处
00401190 /$ 5F pop edi ; cycle.00401115
; Call 会先将返回地址入栈,再进行跳转,这里跳转后的第一条指令就是POP
; 明显就是 先取出返回地址,
; 这里这么做,是为了取 ecx 的值,因为在调用call之前, 会先把 ecx 入栈
00401191 |. 59 pop ecx
00401192 |. 57 push edi
00401193 |. 81F9 80000000 cmp ecx, 80 ; 这里比较ecx 是否小于或等于 0x80, 如果小于或等于 0x80 ,就直接 return
00401199 |. 7E 55 jle short 004011F0
; JLE∶ 指令助记符——(有符号数比较)小于或等于转移(等价于JNG)。当SF和OF异号或ZF=1时转移(段内直接短转移)。
0040119B |. 51 push ecx
0040119C |. 8BF1 mov esi, ecx
0040119E |. 81E1 FF000000 and ecx, 0FF
004011A4 |. 8BF8 mov edi, eax
004011A6 |. 83F9 08 cmp ecx, 8
004011A9 |. 7E 05 jle short 004011B0
004011AB |. 8BFB mov edi, ebx
004011AD |. C1E9 04 shr ecx, 4 ;
; SHR 指令助记符——无符号数的逻辑右移。经常用来除以2。 当移位次数大于 1时,则移位次数应预先置于CL寄存器中,写成“SHR …,CL”。
004011B0 |> C1C7 08 /rol edi, 8
; ROL∶ 指令助记符——循环左移。
; 格式为:ROL 目的操作数,1
; 或ROL 目的操作数,CL (其中CL中存放的是移动次数)
004011B3 |. D1E9 |shr ecx, 1
004011B5 |.^ 75 F9 \jnz short 004011B0
004011B7 |. C1EE 08 shr esi, 8
004011BA |. 23FE and edi, esi
004011BC |. 81E7 FF000000 and edi, 0FF
004011C2 |. 59 pop ecx
004011C3 |> BE 80000000 mov esi, 80
004011C8 |> 85FE /test esi, edi
;test属于逻辑运算指令 功能: 执行BIT与BIT之间的逻辑运算
; 两操作数作与运算,仅修改标志位,不回送结果.
004011CA |. 74 20 |je short 004011EC
004011CC |. 33FE |xor edi, esi
004011CE |. 57 |push edi
004011CF |. 81E1 00FF0000 |and ecx, 0FF00
004011D5 |. 87CE |xchg esi, ecx
004011D7 |. 32E9 |xor ch, cl
004011D9 |. 33F1 |xor esi, ecx
004011DB |. 87F1 |xchg ecx, esi
004011DD |. 51 |push ecx
004011DE |. FF05 82214000 |inc dword ptr [402182]
004011E4 |. E8 A7FFFFFF |call 00401190 ;递归调用
004011E9 |. 5F |pop edi
004011EA |.^ EB D7 |jmp short 004011C3
004011EC |> D1EE |shr esi, 1
004011EE |.^ 75 D8 \jnz short 004011C8
004011F0 \> C3 retn
将上面的汇编转换成C,大概是...
#include <iostream>
#include <stdlib.h>
#include <vector>
using namespace std;
// 模拟ASM的 PUSH, POP 操作
template<class T>
class Stack
{
public:
void push(T data)
{
vstack.push_back(data);
}
void pop(T& data)
{
if (vstack.empty())
{
data = 0;
}
else
{
data = vstack.back();
vstack.pop_back();
}
}
private:
vector<T> vstack;
};
typedef unsigned long DWORD;
Stack<DWORD> thisStack;
DWORD n402182 = 0xFEDCBA98;
std::vector<DWORD> g_stack;
void XChange(DWORD& a, DWORD& b)
{
DWORD c = 0;
c = a;
a = b;
b = c;
}
int SHR(DWORD& a)
{
int nBit = a & 0x01;
a >>= 0x01;
return nBit;
}
void XOR_HIGH_LOW(DWORD& a)
{
char value[2] = {0};
memcpy(value, &a, sizeof(short));
value[1] |= value[0];
memcpy(&a, value, sizeof(short));
}
DWORD fun(DWORD nECX)
{
DWORD nRtn = nECX;
if( nECX <= 0x80 ) // cmp ecx, 80
return nRtn; // jle short 004011F0
thisStack.push(nECX); // push ecx
DWORD nESI = nECX; // mov esi, ecx
nECX &= 0xFF; // and ecx, 0FF
DWORD nEDI = 0x549417E7; // nEAX 值 mov edi, eax ////////////////////// EAX 值
if( nECX <= 0x08 ) // cmp ecx, 8
goto LABEL_004011B0; // jle short 004011B0
nEDI = 0x02F23D32; // nEBX 值 mov edi, ebx ///////////////////// EBX 值
nECX >>= 0x04; // shr ecx, 4
LABEL_004011B0:
_asm rol nEDI, 8 // rol edi, 8
int nBit = SHR(nECX); // SHR ecx, 1
if( !nBit )
goto LABEL_004011B0; // jnz short 004011B0
nESI >>= 0x08;
nEDI &= nESI;
nEDI &= 0xFF;
thisStack.pop(nECX);
LABEL_004011C3:
nESI = 0x80; // mov esi, 80
LABEL_004011C8:
int nResult = (nESI & nEDI);
if( nResult == 0x00 ) // test esi, edi
goto LABEL_004011EC; // je short 004011EC
nEDI ^= nESI;
thisStack.push(nEDI); // push edi
nECX &= 0xFF00; // and ecx, 0FF00
XChange(nESI, nECX); // xchg esi, ecx
XOR_HIGH_LOW(nECX); // xor ch, cl
nESI ^= nECX; // xor esi, ecx
XChange(nECX, nESI); // xchg ecx, esi
n402182++; // push ecx; inc dword ptr [402182] // n402182 地址
nECX = fun(nECX); // call 00401190
thisStack.pop(nEDI); // pop edi
goto LABEL_004011C3;
LABEL_004011EC:
if( SHR(nESI) != 1)
goto LABEL_004011C8;
return nECX;
}
void main()
{
printf("0x%x\n", fun(0x0FF01));
}