[破解者 CrackJ]
[工具 Ollydbg and IDA]
[目标 crackme.exe]
[闲话 第一个靠自己破解的crackme,因为能力有限,欢迎给位指教]
[正文]
程序入口:
.text:0040100A public start
.text:0040100A start:
.text:0040100A jmp loc_401705
过去看看
.text:00401705 loc_401705: ; CODE XREF: .text:startj
.text:00401705 push 0
.text:00401707 call GetModuleHandleA
.text:0040170C mov dword_40413C, eax
.text:00401711 push 0
.text:00401713 push offset loc_401005
.text:00401718 push 0
.text:0040171A push 3E8h
.text:0040171F push dword_40413C
.text:00401725 call DialogBoxParamA
.text:0040172A push 0
.text:0040172C call ExitProcess
发现模态框 回调函数的地址为offset loc_401005 , 过去看看
.text:00401005 loc_401005: ; DATA XREF: .text:00401713o
.text:00401005 jmp loc_401502 ; 回调函数
依然是跳转
.text:00401502 loc_401502: ; CODE XREF: .text:loc_401005j
.text:00401502 push ebp ; 回调函数
.text:00401503 mov ebp, esp
.text:00401505 mov eax, [ebp+0Ch]
.text:00401508 cmp eax, 10h
.text:0040150B jnz short loc_40151C
.text:0040150D push 0
.text:0040150F push dword ptr [ebp+8]
.text:00401512 call EndDialog
.text:00401517 jmp loc_4016FC
.text:0040151C ; ---------------------------------------------------------------------------
.text:0040151C
.text:0040151C loc_40151C: ; CODE XREF: .text:0040150Bj
.text:0040151C cmp eax, 110h ; WM_INITDIALOG
.text:00401521 jnz short loc_401549
.text:00401523 mov ThreadCheck_40434C, 1
.text:0040152D push offset TreadArg_404348
.text:00401532 push 0
.text:00401534 push 0
.text:00401536 push offset loc_40100F ; 跳向新启动的线程
.text:0040153B push 0
.text:0040153D push 0
.text:0040153F call CreateThread ;初始化的时候启动了一个线程,用于检查标记,如果检查成功则开始真正的注册码比对
.text:00401544 jmp loc_4016FC
以下先看主线程
.text:0040156C loc_40156C: ; CODE XREF: .text:0040155Bj
.text:0040156C cmp ax, 3EDh
.text:00401570 jnz loc_4016FC
.text:00401576 push 104h
.text:0040157B push offset Name_404140 ;存放注册名字BUF的首地址
.text:00401580 push 3E9h
.text:00401585 push dword ptr [ebp+8]
.text:00401588 call GetDlgItemTextA ; 获取注册名字
.text:0040158D mov NameLen_403000, eax
.text:00401592 push eax
.text:00401593 cmp eax, 2 ; 名字长度要大于等于2否则推出
.text:00401596 jnb short loc_4015B2
.text:00401598 push 0
.text:0040159A push offset aCrackme ; "CrackMe"
.text:0040159F push offset aFailedToRegist ; "Failed to register!"
.text:004015A4 push 0FFFFFFFFh
.text:004015A6 call MessageBoxA
.text:004015AB push 0
.text:004015AD call ExitProcess
.text:004015B2 loc_4015B2: ; CODE XREF: .text:00401596j
.text:004015B2 push 104h
.text:004015B7 push offset Secret404240 ; 注册码首地址
.text:004015BC push 3EBh
.text:004015C1 push dword ptr [ebp+8]
.text:004015C4 call GetDlgItemTextA ; ;获取注册码
.text:004015C9 mov SerLen_404344, eax
.text:004015CE pop ebx
.text:004015CF push ebx
.text:004015D0 push eax
.text:004015D1 cmp eax, ebx ; 注册码长度大于名字长度
.text:004015D3 ja short loc_4015EF
.text:004015D5 push 0
.text:004015D7 push offset aCrackme ; "CrackMe"
.text:004015DC push offset aFailedToRegist ; "Failed to register!"
.text:004015E1 push 0FFFFFFFFh
.text:004015E3 call MessageBoxA
.text:004015E8 push 0
.text:004015EA call ExitProcess
.text:004015EF loc_4015EF: ; CODE XREF: .text:004015D3j
.text:004015EF cmp eax, 8
.text:004015F2 jbe short loc_4015FB ; 注册码长度不能大于8
.text:004015F4 push 0
.text:004015F6 call ExitProcess
.text:004015FB loc_4015FB: ; CODE XREF: .text:004015F2j
.text:004015FB xor ebx, ebx
.text:004015FD xor eax, eax
.text:004015FF lea esi, Name_404140
.text:00401605
.text:00401605 loc_401605: ; CODE XREF: .text:0040160Aj
.text:00401605 lodsb
.text:00401606 add ebx, eax ; 所有用户名的ascll码进行累加(以下简称用户名之和)
.text:00401608 cmp al, 0
.text:0040160A jnz short loc_401605
.text:0040160C push ebx
.text:0040160D xor ebx, ebx
.text:0040160F xor eax, eax
.text:00401611 lea esi, Secret404240
.text:00401617
.text:00401617 loc_401617: ; CODE XREF: .text:0040161Cj
.text:00401617 lodsb
.text:00401618 add ebx, eax ; 所有注册码ascll码之和的累加(以下简称注册码之和)
.text:0040161A cmp al, 0
.text:0040161C jnz short loc_401617
.text:0040161E push ebx
.text:0040161F pop eax ; 注册码之和
.text:00401620 pop ebx ; 用户名之和
.text:00401621 push ebx
.text:00401622 push eax
.text:00401623 and ebx, 0Fh ; 用户名之和的低4位(以下简称低位)
.text:00401626 and eax, 0Fh ; 注册码之和的低4位(以下简称低位)
.text:00401629 push ebx
.text:0040162A push eax
.text:0040162B mul ebx ; eax = 注册名与用户名低位的乘积
.text:0040162D push eax ; 注册码与用户名(低位)乘积
.text:0040162E pop esi ; 注册码与用户名(低位)乘积
.text:0040162F pop eax ; 注册码之和低位
.text:00401630 mov ebx, eax
.text:00401632 mul ebx ; eax = 注册码之和(低位)的平方
.text:00401634 push ebx
.text:00401635 push esi
.text:00401636 push eax ; 注册码之和(低位)的平方
.text:00401637 pop ecx ; 注册码之和(低位)的平方
.text:00401638 pop esi
.text:00401639 pop edi ; 注册码之和(低位)
.text:0040163A pop eax ; 用户名之和(低位)
.text:0040163B mov ebx, eax
.text:0040163D mul ebx ; eax = 用户名之和(低位)的平方
.text:0040163F push ebx
.text:00401640 push edi
.text:00401641 push esi
.text:00401642 push ecx
.text:00401643 push eax ; 用户名之和(低位)的平方
.text:00401644 pop eax
.text:00401645 pop ebx ; 注册码之和(低位)的平方
.text:00401646 push ebx
.text:00401647 push eax
.text:00401648 add eax, ebx ; eax = 用户名之和(低位)的平方+注册码之和(低位)的平方
.text:0040164A mov ebx, 4
.text:0040164F mul ebx
.text:00401651 xor ebx, ebx
.text:00401653 call $+5 ; eax =(用户名之和(低位)的平方+密码之和(低位)的平方)* 4
.text:00401658 cmp eax, 5
.text:0040165B jb short loc_401665 ; 小于5跳出
.text:0040165D inc ebx ; ebx = eax / 5
.text:0040165E sub eax, 5 ; 给eax循环减五
.text:00401661 pop ecx
.text:00401662 push ecx
.text:00401663 push ecx
.text:00401664 retn
以上代码为用户名与注册码的初步计算(并无实际意义)
.text:00401665 loc_401665: ; CODE XREF: .text:0040165Bj
.text:00401665 pop eax
.text:00401666 mov eax, [esp+8] ; 注册码与用户名(低位)的乘积
.text:0040166A cmp eax, ebx
.text:0040166C ja short loc_401690 ; 永远不成功的跳转 xy > (x*x+y*y)*4/5
.text:0040166E pop eax ; 用户名之和(低位)的平方
.text:0040166F pop eax ; 注册码之和(低位)的平方
.text:00401670 pop eax
.text:00401671 pop eax
.text:00401672 pop eax
.text:00401673 pop eax
.text:00401674 pop eax
.text:00401675 pop eax
.text:00401676 pop eax ; 用户名的长度
.text:00401677 mov ThreadCheck_40434C, 4 ;
.text:00401681 lea eax, TreadArg_404348
.text:00401687 mov ebx, [eax+4] ; ebx里放的就是threadcheck
.text:0040168A dec ebx ; threadcheck变成3
.text:0040168B mov [eax+4], ebx ; 放回threadcheck
.text:0040168E jmp short return_4016F1 ; 退出主线程
xy > (x*x+y*y)*4/5 x*x - 5xy + y*y < 0 ===> x*x/y*y - 5x/y + 1 < 0(当 y!=0) 代换一下,一个简单的二元一次方程组,
画个图一看与x轴无交点,无解。
主线程工作到此完毕
.text:00401024 loc_401024: ; CODE XREF: .text:loc_40100Fj
.text:00401024 push ebp
.text:00401025 mov ebp, esp
.text:00401027 push ebx
.text:00401028 push esi ; 0
.text:00401029 push edi ; 0
.text:0040102A
.text:0040102A loc_40102A: ; CODE XREF: .text:00401043j
.text:0040102A push 200h
.text:0040102F call Sleep
.text:00401034 mov eax, ThreadCheck_40434C
.text:00401039 and eax, 11h
.text:0040103C cmp ThreadCheck_40434C, 3 ;标记为3时,进行真正的注册码的计算
.text:00401043 jnz short loc_40102A
.text:00401045 sub esp, 8
.text:00401048 mov eax, SerLen_404344
.text:0040104D mov [esp], eax
.text:00401050 mov eax, NameLen_403000
.text:00401055 mov [esp+4], eax
.text:00401059 cmp eax, 8
.text:0040105C jbe short loc_401065 ; 名字的长度一定要小于等于8
.text:0040105E push 0
.text:00401060 call ExitProcess
来到检查线程
.text:00401065 loc_401065: ; CODE XREF: .text:0040105Cj
.text:00401065 xor ebx, ebx
.text:00401067 xor eax, eax
.text:00401069 lea esi, Name_404140
.text:0040106F
.text:0040106F loc_40106F: ; CODE XREF: .text:00401074j
.text:0040106F lodsb ; 从esi中取字节,放入AX
.text:00401070 add ebx, eax ; 用户名ascll码之和(以下简称用户名之和)
.text:00401072 cmp al, 0
.text:00401074 jnz short loc_40106F
.text:00401076 push ebx
.text:00401077 xor ebx, ebx
.text:00401079 xor eax, eax
.text:0040107B lea esi, Secret404240
.text:00401081
.text:00401081 loc_401081: ; CODE XREF: .text:00401086j
.text:00401081 lodsb
.text:00401082 add ebx, eax ; 注册码acsll之和(以下简称注册码之和)
.text:00401084 cmp al, 0
.text:00401086 jnz short loc_401081
.text:00401088 push ebx ; 注册码之和
.text:00401089 pop eax ; 注册码之和
.text:0040108A pop ebx ; 名字之和
.text:0040108B push ebx
.text:0040108C push eax
.text:0040108D and ebx, 0Fh ; 名字之和低位
.text:00401090 and eax, 0Fh ; 注册码之和低位
.text:00401093 push ebx
.text:00401094 push eax
.text:00401095 mov esi, [esp+10h] ; 注册码长度
.text:00401099 add esi, [esp+14h] ; 注册码长度+名字长度
.text:0040109D mov eax, esi ; 注册码长度+名字长度
.text:0040109F mul esi
.text:004010A1 mov esi, eax ; (注册码长度+名字长度)的平方
.text:004010A3 mov eax, [esp] ; 注册码之和的低位
.text:004010A6 mov ebx, [esp+4] ; 用户名之和的低位
.text:004010AA mul ebx
.text:004010AC mov ebx, 4
.text:004010B1 mul ebx ; eax=注册码之和的低位*用户名之和的低位*4
.text:004010B3 cmp esi, eax
.text:004010B5 jnz short loc_4010BE ; (注册码长度+名字长度)的平方 != 注册码之和的低位*用户名之和的低位*4
.text:004010B7 mov ecx, 1 ; 当相同时ecx = 1
.text:004010BC jmp short loc_4010D2 ;
此次跳转后主要有三个解密流程,分别给ecx赋值
.text:004010BE loc_4010BE: ; CODE XREF: .text:004010B5j
.text:004010BE cmp esi, eax
.text:004010C0 ja short loc_4010C9
.text:004010C2 mov ecx, 2 ; 小于时ecx = 2
.text:004010C7 jmp short loc_4010D2
.text:004010C9 ; ---------------------------------------------------------------------------
.text:004010C9
.text:004010C9 loc_4010C9: ; CODE XREF: .text:004010C0j
.text:004010C9 mov ecx, 3 ; 大于时ecx = 3
.text:004010CE sub esi, eax
.text:004010D0 mov ebx, esi ; (密码长度+名字长度)的平方 - 密码之和的低位*用户名之和的低位*4
.text:004010D2
.text:004010D2 loc_4010D2: ; CODE XREF: .text:004010BCj
.text:004010D2 ; .text:004010C7j
.text:004010D2 push offset Large_4013A2 ; 当ecx == 3时跳向这个地址
.text:004010D7 push offset Less_401245 ; 当ecx == 2时跳向这个地址
.text:004010DC push offset Same_4010E8 ; 当ecx == 1时跳向这个地址
.text:004010E1 push 0
.text:004010E3
.text:004010E3 loc_4010E3: ; CODE XREF: .text:004010E5j
.text:004010E3 pop eax
.text:004010E4 dec ecx
.text:004010E5 jnz short loc_4010E3
.text:004010E7 retn ; 实际上是跳转指令
.text:00401245 Less_401245: ; DATA XREF: .text:004010D7o
.text:00401245 mov ebx, [esp+4] ; 注册码之和低位,解密时的密钥
.text:00401249 mov ecx, 40h ; 解密次数
.text:0040124E lea esi, Msg2_40129B ; 被解密的代码
.text:00401254 lea edx, Exit2_40139B
.text:0040125A add esi, 100h ; 指向被解密代码的尾地址
.text:00401260 sub esi, 4 ; 指向被解密代码的最后一个双字
.text:00401263 push edx
.text:00401264 sub esp, 100h ; 抬高栈顶,一共是256字节
.text:0040126A mov edi, esp
.text:0040126C add edi, 100h ; 指向栈低
.text:00401272 sub edi, 4
.text:00401275 std ; 反向复制
.text:00401276 lodsd ; mov eax , [esi] ; sub esi , 4
.text:00401277 stosd ; mov [edi] , eax ; sub edi , 4
.text:00401278 dec ecx ; 3Fh
.text:00401279
.text:00401279 loc_401279: ; CODE XREF: .text:00401286j
.text:00401279 lodsd ; 从字典中取数据
.text:0040127A mov edx, [edi+4] ; 从栈上取数据
.text:0040127D xor eax, edx ; 解密
.text:0040127F xor eax, ebx ; 解密
.text:00401281 stosd ; mov [edi] , eax ; sub edi , 4 ; 把解密的数据放回栈上
.text:00401282 dec ecx
.text:00401283 cmp ecx, 0
.text:00401286 jnz short loc_401279
.text:00401288 cld
.text:00401289 mov eax, esp
.text:0040128B mov ebx, [eax]
.text:0040128D cmp ebx, 68006Ah ; 比较栈顶的解密数据
.text:00401293 jnz Exit2_40139B
.text:00401299 jmp eax ; 跳转到栈上的执行数据
.text:00401299 ; ---------------------------------------------------------------------------
.text:0040129B Msg2_40129B db 26h ; DATA XREF: .text:0040124Eo
.text:0040129C dd 44686840h, 6E684000h, 6607A840h, 0C886E800h, 0C8810100h
.text:0040129C dd 825FEC3h, 3325BF92h, 7AB0E392h, 35EBA2C2h, 381F690Dh
.text:0040129C dd 0B314690Dh, 0F3409114h, 87455314h, 7B942EDh, 0DFEC81ECh
.text:0040129C dd 8F63356h, 0D20A46DFh, 0F075A5FCh, 0A1FC59A1h, 3CA9FC5Dh
.text:0040129C dd 1B238B9Dh, 0F2BFD2B5h, 654546A0h, 65042A2Dh, 0ACF105A5h
.text:0040129C dd 0ACF105B1h, 0D138050h, 0F8574969h, 0F90589A0h, 1FEC68A0h
.text:0040129C dd 2B256901h, 38884CB8h, 1D857833h, 0BC3F348Bh, 0FFBA0112h
.text:0040129C dd 0A0F9459Bh, 9BFD2F5Fh, 65042AEEh, 0D13DC50h, 1E9AF469h
.text:0040129C dd 0E1DB6901h, 0AA436BD6h, 757AAAE5h, 0DFEABDBEh, 7FC5F026h
.text:0040129C dd 9EC5E5FBh, 72E86950h, 33690112h, 1A8CD6EDh, 0E065867Fh
.text:0040129C dd 96E850A9h, 7A3B46DFh, 93BE50Eh, 4F74A2F9h, 69887488h
.text:0040129C dd 0C083002h, 2ACC40B2h, 4F4C0438h, 0AE887488h, 0ACFD35A5h
.text:0040129C dd 0FD447B4Ah, 4CE2312h, 0F84B7023h
.text:00401398 db 57h, 68h, 0C0h
.text:0040139B ; ---------------------------------------------------------------------------
.text:0040139B
.text:0040139B Exit2_40139B: ; CODE XREF: .text:00401293j
.text:0040139B ; DATA XREF: .text:00401254o
.text:0040139B push 0
.text:0040139D call ExitProcess
当ecx == 2时跳向这个地址
本段代码的主要功能就是用注册码之和的低位当成解密密钥,首先取栈上的上一个已经解密的数据,
跟字典的数据异或,再跟密钥异或,最后放入栈中。当密钥为0xC时,即为完成解密操作,得出的
结果是注册失败,如果不成功则直接退出
.text:004010E8 Same_4010E8: ; DATA XREF: .text:004010DCo
.text:004010E8 mov ebx, [esp+0Ch] ; 用户名之和的低位
.text:004010EC mov ecx, 40h
.text:004010F1 lea esi, Msg1_40113E
.text:004010F7 lea edx, Exit1_40123E
当ecx == 1时 跟刚才的原理基本一样,但是密钥却变成用户名之和的低位,解密数据依赖于用户名,跟注册码无关
基本断定这也不是最终归的解密数据
.text:004013A2 Large_4013A2: ; DATA XREF: .text:loc_4010D2o
.text:004013A2 mov ecx, 40h
.text:004013A7 lea esi, Msg3_4013F4
.text:004013AD lea edx, Exit3_4014F4
.text:004013B3 add esi, 100h
.text:004013B9 sub esi, 4
.text:004013BC push edx
.text:004013BD sub esp, 100h
.text:004013C3 mov edi, esp
.text:004013C5 add edi, 100h
.text:004013CB sub edi, 4
.text:004013CE std
.text:004013CF lodsd
.text:004013D0 stosd
.text:004013D1 dec ecx
.text:004013D2
.text:004013D2 loc_4013D2: ; CODE XREF: .text:004013DFj
.text:004013D2 lodsd
.text:004013D3 mov edx, [edi+4]
.text:004013D6 xor eax, edx
.text:004013D8 xor eax, ebx ; (注册码长度+名字长度)的平方 - 注册码之和的低位*用户名之和的低位*4=6D注册成功
.text:004013DA stosd
.text:004013DB dec ecx
.text:004013DC cmp ecx, 0
.text:004013DF jnz short loc_4013D2
.text:004013E1 cld
.text:004013E2 mov eax, esp
.text:004013E4 mov ebx, [eax]
.text:004013E6 cmp ebx, 68006Ah
.text:004013EC jnz Exit3_4014F4
.text:004013F2 jmp eax
.text:004013F2 ; ---------------------------------------------------------------------------
.text:004013F4 Msg3_4013F4 dd 68684047h ; DATA XREF: .text:004013A7o
.text:004013F4 dd 68400031h
.text:004013F4 dd 7A8401Bh
.text:004013F4 dd 86E80007h
.text:004013F4 dd 810100A9h 其它数据略。。。。。。。。
原理同上,密钥为(注册码长度+名字长度)的平方 - 注册码之和的低位*用户名之和的低位*4 ,
简单写成表达式为(SerLen + NameLen)平方 - SerAllLow * NameAllLow * 4 要等于109 注册才为成功
已知 2<=x<=8 x<y<=8 => 2<=x<y<=8
已知 无论注册码与名字的长度如何,都不会影响SerAllLow , NameAllLow
当NameLen == 2 SerLen必为3 , 当 SerLen == 8 NameLen必为7
得出:25<=(SerLen + NameLen)平方<=225
已知:0<=SerAllLow<=15 , 0<=NameAllLow<=15
已知:0<=SerAllLow * NameAllLow * 4<=900
得出:密钥的大小就是0到255之间,写个程序遍历下就能得到6D这个密钥(爆破)
注意:当NameLen == 2 , NameLen == 8时 (SerLen + NameLen)平方 == 100 注册码永远不会出现
注意:即使满足上溯所有条件,但是(SerLen + NameLen)平方 - SerAllLow * NameAllLow * 4 也不一定能完全得到
109这个值,原因左右表达式不能完全覆盖其所有的取值范围的整数!!
以下为注册机代码
int main()
{
char szName[10] = {0};
char szSecret[10] = {0};
unsigned long ulNameAllLow = 0;
unsigned long ulSecretAllLow = 0;
unsigned long ulSecretLen = 0;
unsigned long ulNameLen = 0;
vector<unsigned long> vecSecretAllLow;
unsigned long tmp = 0;
long i = 0;
printf("please inter Name and secret \n");
gets(szName);
ulNameLen = strlen(szName);
if(ulNameLen <= 2 || ulNameLen >= 8 )
{
printf("check wrong!!!\n");
return 0;
}
for(i = 0; i < strlen(szName); i++)
{
ulNameAllLow += szName[i];
}
ulNameAllLow &= 0x0000000F;
for(ulSecretAllLow = 0; ulSecretAllLow <= 15; ulSecretAllLow++)
{
if( (109 + 4 * ulNameAllLow * ulSecretAllLow) <= 255)
{
vecSecretAllLow.push_back(ulSecretAllLow);// 查找注册码之和的最低位
}
}
int flag = 0;
int iSuccess = 0;
for(ulSecretLen = ulNameLen + 1; ulSecretLen <= 8 ; ulSecretLen++)
{
tmp = ulSecretLen + ulNameLen;
for(i = 0; i<vecSecretAllLow.size(); i++)
{
if( (tmp*tmp - 4*ulNameAllLow*vecSecretAllLow[i]) == 109 )
{
flag = 1;
ulSecretAllLow = vecSecretAllLow[i];
break;
}
}
if(flag == 1)
{
iSuccess = 1; //找到注册码
break;
}
}
if(iSuccess == 1)
{
//printf("low:%X\n", ulSecretAllLow);
//printf("len:%d\n", ulSecretLen);
for(i = 0; i<ulSecretLen ; i++)
{
if(i != (ulSecretLen-1))
{
szSecret[i] = 0x40;
}
else
{
szSecret[i] = 0x40+ulSecretAllLow;
}
}
printf("code:%s\n", szSecret);
}
else
{
printf("NAME wrong!!!\n");
}
return 0;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)