好友
阅读权限 40
听众
最后登录 1970-1-1
想去睡觉了,临睡觉前再发个以前的东东,这个比较简单,用IDA 分析的,没有调式过,估计用OD更简单吧,大牛飘过,跟我一样菜的朋友能进来指点一下!
正文如下:
不知道CM的分析应该发到哪个版块,本来以为是CM和RM的那个版块,可是上面写的是活动版块,不敢乱发,就发到这个我比较熟悉的版块吧,如果发错位置了,就麻烦斑斑大哥代劳转移一下!谢谢!
首先感谢PPT同学给我了这个CM,很有意思,它的具体要求如下:
Hi! There!..It's my first keygenme (crackme). Here are some tasks you should do:
-------------------------------------------------------------------------
1. To start searching a key for your name, you need to patch something to enter
more than 1 symbol, (Patch it to 80 symbols) (no jne, je, & etc jump patching.)
2. Find key generator solution, and write a keygen
3. Patch it, to stop closing after the registration procedure
(not allowed to patch jne, je, & etc "jump patching". (e.g: "jne" -> "je")
-------------------------------------------------------------------------
Exe file is not protected, and it is very easy to find key & to write a
keygen. But no built-in keygens (injections). As u can see I made it for newbies...
__________________
P.s: Don't forget what i said: "no jump patching". :)
Have fun!!! ;) THanx to crackmes.de
好了,不多说了,进入正题,应该说这个CM确实很容易,完全的明码,根本没有隐藏!很适合象我这样的新人!而且它也确实很有意思!
刚拿到这个CM的时候,直接用OD打开,这个小程序基本上就一目了然了!顺手在GetDlgItemTextA上F2,F9,可是他居然断不下来!
基本上到了DialogBoxParamA这个函数就飞了……
想必没有人会不知道DialogBoxParamA这个函数是干什么的吧!
用资源编辑器找到两个文本框的ID:1001,1002,转换成HEX就是3E9,3EA.text:0040102E ; =============== S U B R O U T I N E =======================================
.text:0040102E
.text:0040102E ; Attributes: bp-based frame
.text:0040102E
.text:0040102E ; INT_PTR __stdcall DialogFunc(HWND, UINT, WPARAM, LPARAM)
.text:0040102E DialogFunc proc near ; DATA XREF: start+13o
.text:0040102E
.text:0040102E hDlg = dword ptr 8
.text:0040102E arg_4 = dword ptr 0Ch
.text:0040102E arg_8 = dword ptr 10h
.text:0040102E
.text:0040102E push ebp
.text:0040102F mov ebp, esp
.text:00401031 mov eax, [ebp+arg_4]
.text:00401034 cmp eax, 110h
.text:00401039 jnz short loc_401089
.text:0040103B mov eax, [ebp+hDlg]
.text:0040103E mov hDlg, eax
.text:00401043 push 3E9h ; nIDDlgItem
.text:00401048 push [ebp+hDlg] ; hDlg
.text:0040104B call GetDlgItem
.text:00401050 mov hWnd, eax
.text:00401055 xor eax, eax
.text:00401057 push 3EAh ; nIDDlgItem
.text:0040105C push [ebp+hDlg] ; hDlg
.text:0040105F call GetDlgItem
.text:00401064 mov dword_403210, eax
.text:00401069 xor eax, eax
.text:0040106B call sub_40130D
.text:00401070 push 13h ; uFlags
.text:00401072 push 0 ; cy
.text:00401074 push 0 ; cx
.text:00401076 push 0 ; Y
.text:00401078 push 0 ; X
.text:0040107A push 0FFFFFFFFh ; hWndInsertAfter
.text:0040107C push [ebp+hDlg] ; hWnd
.text:0040107F call SetWindowPos
.text:00401084 jmp loc_401271
GetDlgItem到两个文本框以后,都会跟进.text:0040106B call sub_40130D 这个函数里面去
我们也进去看看吧~.text:0040130D ; =============== S U B R O U T I N E =======================================
.text:0040130D
.text:0040130D
.text:0040130D sub_40130D proc near ; CODE XREF: DialogFunc+3Dp
.text:0040130D push eax
.text:0040130E xor eax, eax
.text:00401310 mov eax, hDlg
.text:00401315 push 0 ; lParam
.text:00401317 push 1 ; wParam
.text:00401319 push 0C5h ; Msg
.text:0040131E push 3E9h ; nIDDlgItem
.text:00401323 push eax ; hDlg
.text:00401324 call SendDlgItemMessageA
.text:00401329 xor eax, eax
.text:0040132B mov eax, hDlg
.text:00401330 push 0 ; lParam
.text:00401332 push 1 ; wParam
.text:00401334 push 0C5h ; Msg
.text:00401339 push 3EAh ; nIDDlgItem
.text:0040133E push eax ; hDlg
.text:0040133F call SendDlgItemMessageA
.text:00401344 pop eax
.text:00401345 retn
.text:00401345 sub_40130D endp
看到了吧,这里向两个文本框发送消息了做文本个数的限制了,要取消它,就把上面的两个PUSH 1改成PUSH 50就OK了!
这样输入字符字数的限制就取消了!
再看取消信息提示后失败的方法,找到MessageBoxA我们来看看(它有两个MessageBoxA,我想大家不会找错的!).text:0040111B ; ---------------------------------------------------------------------------
.text:0040111B
.text:0040111B loc_40111B: ; CODE XREF: DialogFunc+A8j
.text:0040111B ; DialogFunc+ADj ...
.text:0040111B xor eax, eax
.text:0040111D cmp ecx, 1
.text:00401120 jnz short loc_401130
.text:00401122 xor ecx, ecx
.text:00401124 mov ecx, offset aCongratulation ; "Congratulations!!!"
.text:00401129 mov eax, offset aWellDonePlease ; "Well, done, please, submit your solutio"...
.text:0040112E jmp short loc_40113C
.text:00401130 ; ---------------------------------------------------------------------------
.text:00401130
.text:00401130 loc_401130: ; CODE XREF: DialogFunc+F2j
.text:00401130 xor ecx, ecx
.text:00401132 mov eax, offset aSomethingWrong ; "Something wrong with serial, or name."
.text:00401137 mov ecx, offset aError__ ; "Error.."
.text:0040113C
.text:0040113C loc_40113C: ; CODE XREF: DialogFunc+100j
.text:0040113C push 0 ; uType
.text:0040113E push ecx ; lpCaption
.text:0040113F push eax ; lpText
.text:00401140 push [ebp+hDlg] ; hWnd
.text:00401143 call MessageBoxA
.text:00401148 mov eax, 0
.text:0040114D cmp eax, 0
.text:00401150 jnz loc_401255
.text:00401156 jmp loc_exit
我想大家在看到这个函数后面的代码的时候,谁都不会没有感觉的:.text:00401148 mov eax, 0
.text:0040114D cmp eax, 0
.text:00401150 jnz loc_401255
这里的JNZ基本上就是被NOP掉了,怎么改才能让它正常也不用我说了吧,把赋值的0改成1就OK了~
好了,下面我们来分析一下他的这个程序的算法流程!.text:004010F1 mov eax, offset aISaidItIsVeryE ; "=== I said it is very easy, for newbies"...
.text:004010F6 xor eax, eax
.text:004010F8 xor ecx, ecx
.text:004010FA xor ebx, ebx
.text:004010FC xor edx, edx
.text:004010FE
.text:004010FE loc_4010FE: ; CODE XREF: DialogFunc+150j
.text:004010FE inc ebx ;这里把EBX+1
.text:004010FF push ebx
.text:00401100 push hWnd ; hWnd
.text:00401106 call GetWindowTextLengthA
.text:0040110B xor ecx, ecx
.text:0040110D mov ebx, eax
.text:0040110F mov eax, offset Buffer
.text:00401114 call sub_Name ;这里检测我们输入的用户名
.text:00401119 jmp short loc_40115B
我们跟进这个sub_Name函数看看吧!{
.text:004012D1 ; =============== S U B R O U T I N E =======================================
.text:004012D1
.text:004012D1
.text:004012D1 sub_Name proc near ; CODE XREF: DialogFunc+E6p
.text:004012D1 ; sub_Name +17j
.text:004012D1 mov dl, [eax]
.text:004012D3 imul ecx, 48h
.text:004012D6 sub ecx, edx
.text:004012D8 sub ecx, 6Fh
.text:004012DB mov edx, ecx
.text:004012DD xor ecx, 0BACAFh
.text:004012E3 inc eax
.text:004012E4 dec ebx
.text:004012E5 cmp ebx, 0
.text:004012E8 jnz short sub_4012D1
.text:004012EA retn
.text:004012EA sub_Name endp
}
继续,我们来到loc_40115B来看一下.text:0040115B ; ---------------------------------------------------------------------------
.text:0040115B
.text:0040115B loc_40115B: ; CODE XREF: DialogFunc+EBj
.text:0040115B pop ebx
.text:0040115C cmp ebx, 2 ;看到没?如果处理用户名时这里是1,处理完了程序就再跳回loc_4010FE把EBX+1再就处理注册码了!
.text:0040115F jz short loc_401183
.text:00401161 xor edx, edx
.text:00401163 mov dword_403218, ecx
.text:00401169 xor eax, eax
.text:0040116B push offset pcbBuffer ; pcbBuffer
.text:00401170 push offset Buffer ; lpBuffer
.text:00401175 call GetUserNameA
.text:0040117A xor edx, edx
.text:0040117C xor ecx, ecx
.text:0040117E jmp loc_4010FE
:
好我们到loc_401183看看:; ---------------------------------------------------------------------------
.text:00401183
.text:00401183 loc_401183: ; CODE XREF: DialogFunc+131j
.text:00401183 xor edx, edx
.text:00401185 mov dword_40321C, ecx
.text:0040118B push dword_40321C
.text:00401191 push dword_403218
.text:00401197 call sub_40127A
来到sub_40127A,我不多注释了,大家可以边用OD跟这个CM边看我帖出来的代码,这里程序条理代码如下{
.text:0040127A
.text:0040127A ; =============== S U B R O U T I N E =======================================
.text:0040127A
.text:0040127A ; Attributes: bp-based frame
.text:0040127A
.text:0040127A sub_40127A proc near ; CODE XREF: DialogFunc+169p
.text:0040127A
.text:0040127A arg_0 = dword ptr 8
.text:0040127A arg_4 = dword ptr 0Ch
.text:0040127A
.text:0040127A push ebp
.text:0040127B mov ebp, esp
.text:0040127D xor eax, eax
.text:0040127F xor ebx, ebx
.text:00401281 mov eax, [ebp+arg_0]
.text:00401284 mov ebx, [ebp+arg_4]
.text:00401287 mov edx, eax
.text:00401289 xor edx, ebx
.text:0040128B xor edx, 0FFACh
.text:00401291 xor ebx, 553h
.text:00401297 add eax, ebx
.text:00401299 add ebx, edx
.text:0040129B dec ebx
.text:0040129C add eax, ebx
.text:0040129E leave
.text:0040129F retn 8
.text:0040129F sub_40127A endp
.text:0040129F
}
.text:0040119C mov dword_403220, eax
.text:004011A1 call sub_401346
{
.text:00401346
.text:00401346 ; =============== S U B R O U T I N E =======================================
.text:00401346
.text:00401346
.text:00401346 sub_401346 proc near ; CODE XREF: DialogFunc+173p
.text:00401346 mov eax, dword_40321C
.text:0040134B mov ebx, dword_403218
.text:00401351 cmp eax, ebx
.text:00401353 jnz short locret_401363
.text:00401355 xor ebx, 8Eh
.text:0040135B add ebx, eax
.text:0040135D mov dword_403218, ebx
.text:00401363
.text:00401363 locret_401363: ; CODE XREF: sub_401346+Dj
.text:00401363 retn
.text:00401363 sub_401346 endp
.text:00401363
}
.text:004011A6 push dword_40321C
.text:004011AC push offset unk_403224
.text:004011B1 call sub_4012A2
{
.text:004012A2
.text:004012A2 ; =============== S U B R O U T I N E =======================================
.text:004012A2
.text:004012A2 ; Attributes: bp-based frame
.text:004012A2
.text:004012A2 sub_4012A2 proc near ; CODE XREF: DialogFunc+183p
.text:004012A2 ; DialogFunc+199p ...
.text:004012A2
.text:004012A2 arg_0 = dword ptr 8
.text:004012A2 arg_4 = dword ptr 0Ch
.text:004012A2
.text:004012A2 push ebp
.text:004012A3 mov ebp, esp
.text:004012A5 mov esi, [ebp+arg_0]
.text:004012A8 add esi, 8
.text:004012AB mov eax, [ebp+arg_4]
.text:004012AE mov ecx, 10h
.text:004012B3 xor ebx, ebx
.text:004012B5
.text:004012B5 loc_4012B5: ; CODE XREF: sub_4012A2+29j
.text:004012B5 xor edx, edx
.text:004012B7 div ecx
.text:004012B9 dec esi
.text:004012BA add dl, 30h
.text:004012BD cmp dl, 3Ah
.text:004012C0 jb short loc_4012C5
.text:004012C2 add dl, 7
.text:004012C5
.text:004012C5 loc_4012C5: ; CODE XREF: sub_4012A2+1Ej
.text:004012C5 mov [esi], dl
.text:004012C7 inc ebx
.text:004012C8 cmp ebx, 8
.text:004012CB jnz short loc_4012B5
.text:004012CD leave
.text:004012CE retn 8
.text:004012CE sub_4012A2 endp
.text:004012CE
}
.text:004011A6 push dword_40321C
.text:004011AC push offset unk_403224
.text:004011B1 call sub_4012A2
.text:004011B6 mov eax, offset unk_403224
.text:004011BB add eax, 8
.text:004011BE push dword_403220
.text:004011C4 lea eax, [eax]
.text:004011C6 push eax
.text:004011C7 call sub_4012A2
.text:004011CC mov eax, offset unk_403224
.text:004011D1 add eax, 10h
.text:004011D4 push dword_403218
.text:004011DA lea eax, [eax]
.text:004011DC push eax
.text:004011DD call sub_4012A2
.text:004011E2 mov eax, dword_403210
在这里反复的调用sub_4012A2函数,只要稍微用OD跟一下,我想即使不懂汇编也能明白,它这是在把我们在上面三个函数中得到的值连接起来!
下面的代码就很明显了,获取我们输入的注册码!.text:004011E7 push eax ; hWnd
.text:004011E8 call GetWindowTextLengthA ;获取我们输入的注册码的长度,
.text:004011ED mov ecx, 0
.text:004011F2 cmp eax, 18h
.text:004011F5 jnz loc_40111B ;如果小于24位,就直接提示失败了!
.text:004011FB inc eax
.text:004011FC push eax ; cchMax
.text:004011FD push offset String ; lpString
.text:00401202 push 3EAh ; nIDDlgItem
.text:00401207 push [ebp+hDlg] ; hDlg
.text:0040120A call GetDlgItemTextA ;获取我们输入的假码
.text:0040120F mov eax, offset String
.text:00401214 call sub_4012EB ;这里是转换得到真码了!我们跟进这个CALL看看去,双击sub_4012EB
真正出现注册码的CALL在这里sub_4012EB:
{
.text:004012EB
.text:004012EB ; =============== S U B R O U T I N E =======================================
.text:004012EB
.text:004012EB
.text:004012EB sub_4012EB proc near ; CODE XREF: DialogFunc+1E6p
.text:004012EB mov edx, offset unk_SN
.text:004012F0 add edx, 4
.text:004012F3 mov byte ptr [edx], 2Dh
.text:004012F6 add edx, 0Ah
.text:004012F9 mov byte ptr [edx], 2Dh
.text:004012FC mov edx, offset unk_SN
.text:00401301 mov byte ptr [edx], 4Bh
.text:00401304 inc edx
.text:00401305 mov byte ptr [edx], 4Fh
.text:00401308 inc edx
.text:00401309 mov byte ptr [edx], 53h
.text:0040130C retn
.text:0040130C sub_4012EB endp
}
好了,知道了这个,下面就剩下写注册机了~
怎么给程序打补丁我就不说了,如果大家有不知道的可以看我的源程序!
这里我只列出我的KEYGEN的算法部分!(程序本来就是用MASM32写的,所以我也就不翻译成C了,直接用内联汇编来写了,代码直接用IDA考出来时候,省时省力,嘿嘿~!)DWORD CKeygenDlg::CLName(char *Name)
{
DWORD result;
int len = strlen(Name);
_asm{
mov eax,Name
mov ebx,len
xor edx,edx
xor ecx,ecx
L000:
mov dl, byte ptr [eax]
imul ecx, ecx, 0x48
sub ecx, edx
sub ecx, 0x6F
mov edx, ecx
xor ecx, 0x0BACAF
inc eax
dec ebx
cmp ebx, 0
jnz L000
mov result,ecx
}
return result;
}
DWORD CKeygenDlg::CkSn1(DWORD code)
{
DWORD result;
_asm{
xor eax, eax
xor ebx, ebx
mov eax, code
mov ebx, code
mov edx, eax
xor edx, ebx
xor edx, 0x0FFAC
xor ebx, 0x553
add eax, ebx
add ebx, edx
dec ebx
add eax, ebx
mov result,eax
}
return result;
}
DWORD CKeygenDlg::CkSn2(DWORD code)
{
DWORD result;
_asm{
mov eax, code
mov ebx, code
xor ebx, 0x8E
add ebx, eax
mov result, ebx
}
return result;
}
void CKeygenDlg::CkSn3(char *_addr,DWORD _code)
{
itoa(_code,_addr,16); //这个用汇编反而麻烦了,直接用C就OK了~
}
char * CKeygenDlg::GetSerial(DWORD _addr)
{
char *Serial = NULL;
_asm{
mov edx, _addr
add edx, 4
mov byte ptr [edx], 0x2D
add edx, 0x0A
mov byte ptr [edx], 0x2D
mov edx, _addr
mov byte ptr [edx], 0x4B
inc edx
mov byte ptr [edx], 0x4F
inc edx
mov byte ptr [edx], 0x53
}
Serial = (char *)_addr;
return Serial;
}
void CKeygenDlg::OnKey()
{
//查找CM的窗口,确定程序是否已经启动!
……(省略,详见附件)
//先PATCH掉它对输入字符的个数限制
……(省略,详见附件)
//再PATCH掉它提示后就退出
……(省略,详见附件)
//再写一下它的算法了
const int nBufSize = 128;
TCHAR chBuf[nBufSize];
ZeroMemory(chBuf,nBufSize);
DWORD t_rst;
DWORD dwRet = nBufSize;
if (::GetUserName(chBuf,&dwRet))
{
DWORD tmpsn = 0;
char *tmpserial = new char[24];
m_Name = chBuf;
tmpsn = CLName(chBuf); //这里得到0x30A2E5B8
CkSn3(tmpserial,tmpsn);
tmpserial += 8;
t_rst = CkSn1(tmpsn); //得到0x91E9A739
CkSn3(tmpserial,t_rst);
tmpserial += 8;
t_rst = CkSn2(tmpsn); //得到0x6145CAEE
CkSn3(tmpserial,t_rst);
tmpserial -= 16;
m_Serial = GetSerial((DWORD )tmpserial);
}
m_Serial.MakeUpper();
UpdateData(FALSE);
}
哈哈,第一次用IDA分析程序,真的很别扭,但是,不得不承认IDA功能的强大!
这个程序我从分析到写KEYGEN,一共用了将近4个小时,希望这个能对大家有所帮助!
[ 本帖最后由 bester 于 2009-2-13 02:18 编辑 ]