Nisy's CrackMe 算法分析+VB注册机源码(0.4版)
破解声明:我只是一只小菜鸟,这是我第一次弄分析算法并且写注册机,如有失误的地方还望各位纠正.破解过程:
有壳,尝试脱壳机脱,失败.OD载入运行,随便输入注册码发现是由标签提示,说来也巧,以前在看雪看过用钩子触发注册码校验的,于是想到试一下,重新载入然后在SetWindowsHookExA下断,运行发现 除开MFC自己的钩子,程序还会安装一个键盘钩子
; [GetCurrentThreadId
00401905|.50 push eax ; /ThreadID
00401906|.56 push esi ; |hModule
00401907|.68 50184000 push 00401850 ; |Hookproc = cm_nf_v0.00401850
0040190C|.6A 02 push 2 ; |HookType = WH_KEYBOARD
0040190E|.FF15 1C244200 call dword ptr ; \SetWindowsHookExA
然后直接看钩子函数:
004018BE .66:3D 5A00 cmp ax, 5A
004018C2 .77 05 ja short 004018C9
004018C4 .E8 A7FEFFFF call 00401770 ;校验注册码 键盘输入注册码的时候输入的是大写字母就会进入
这里是进行注册码校验:
004017B1 .50 push eax
004017B2 .B8 E8030000 mov eax, 3E8
004017B7 .83C0 02 add eax, 2
004017BA .50 push eax
004017BB .FF35 D4F04200 push dword ptr
004017C1 .B8 CC174000 mov eax, 004017CC
004017C6 .50 push eax
004017C7 .^ E9 84FDFFFF jmp 00401550 ;直接跳到GetDlgItemTextA+2 取注册码
004017CC .50 push eax
004017CD .8F45 FC pop dword ptr
004017D0 .C785 F8FEFFFF>mov dword ptr , 0
004017DA .EB 0F jmp short 004017EB
004017DC >8B85 F8FEFFFF mov eax, dword ptr
004017E2 .83C0 01 add eax, 1
004017E5 .8985 F8FEFFFF mov dword ptr , eax
004017EB >83BD F8FEFFFF>cmp dword ptr , 8
004017F2 .73 1E jnb short 00401812
004017F4 .8B8D F8FEFFFF mov ecx, dword ptr
004017FA .0FBE940D FCFE>movsx edx, byte ptr
00401802 .8B85 F4FEFFFF mov eax, dword ptr
00401808 .03C2 add eax, edx
0040180A .8985 F4FEFFFF mov dword ptr , eax
00401810 .^ EB CA jmp short 004017DC ;取注册码前八个字符的和
00401812 >81BD F4FEFFFF>cmp dword ptr , 272
0040181C .75 2A jnz short 00401848 ;如果总和=0x270
0040181E .0FBE8D FCFEFF>movsx ecx, byte ptr
00401825 .83F9 4E cmp ecx, 4E
00401828 .75 1E jnz short 00401848 ;第一个字符必须是 N
0040182A .8D95 FCFEFFFF lea edx, dword ptr
00401830 .52 push edx
00401831 .E8 5AFEFFFF call 00401690 ;用注册码做KEY 解密代码
00401836 .83C4 04 add esp, 4
00401839 .8D85 FCFEFFFF lea eax, dword ptr
0040183F .50 push eax
00401840 .E8 ABFDFFFF call 004015F0 ;执行解密后的代码
00401690部分代码:
004016DC|> /8B4C24 1C /mov ecx, dword ptr
004016E0|. |8A06 |mov al, byte ptr
004016E2|. |8A51 01 |mov dl, byte ptr ;取注册码的第二个字符为KEY
004016E5|. |32C2 |xor al, dl ;异或
004016E7|. |8D5424 14 |lea edx, dword ptr
004016EB|. |884424 13 |mov byte ptr , al
004016EF|. |52 |push edx
004016F0|. |8D4424 17 |lea eax, dword ptr
004016F4|. |6A 01 |push 1
004016F6|. |50 |push eax
004016F7|. |56 |push esi
004016F8|. |FFD5 |call ebp ;kernel32.GetCurrentProcess
004016FA|. |50 |push eax
004016FB|. |FFD3 |call ebx ;WriteProcessMemory 写回解密后的代码
004016FD|. |46 |inc esi
004016FE|. |8D0C37 |lea ecx, dword ptr
00401701|. |83F9 78 |cmp ecx, 78 ;解密0x78个字节
00401704|.^\7C D6 \jl short 004016DC
00401706|.6A 00 push 0 ; /pOldProtect = NULL
00401708|.6A 40 push 40 ; |NewProtect = PAGE_EXECUTE_READWRITE
0040170A|.6A 55 push 55 ; |Size = 55 (85.)
0040170C|.68 60154000 push 00401560 ; |Address = cm_nf_v0.00401560
00401711|.FF15 80224200 call dword ptr ; \VirtualProtect
00401717|.BE 60154000 mov esi, 00401560
0040171C|.BF F1FFFFFF mov edi, -0F
00401721|.83C6 0F add esi, 0F
00401724|.81EF 60154000 sub edi, 00401560
0040172A|>8B5424 1C /mov edx, dword ptr
0040172E|.8A0E |mov cl, byte ptr
00401730|.8A42 02 |mov al, byte ptr ;取注册码第三个字符作为KEY 解密第二部分代码
00401733|.8D5424 13 |lea edx, dword ptr
00401737|.32C1 |xor al, cl
00401739|.8D4C24 14 |lea ecx, dword ptr
0040173D|.51 |push ecx
0040173E|.6A 01 |push 1
00401740|.52 |push edx
00401741|.56 |push esi
00401742|.884424 23 |mov byte ptr , al
00401746|.FFD5 |call ebp ;kernel32.GetCurrentProcess
00401748|.50 |push eax
00401749|.FFD3 |call ebx ;WriteProcessMemory 写回解密后的代码
0040174B|.46 |inc esi
0040174C|.8D0437 |lea eax, dword ptr
0040174F|.83F8 55 |cmp eax, 55
00401752|.^ 7C D6 \jl short 0040172A
00401690这里会解密00401560和004015F0这两个函数里面的代码 注册码不对的话会解密成错误的代码,使程序出错.有意思的是在00401560和004015F0里面还进行了一次注册码的校验,如果注册码没完全正确的话那个对话框是不会出来的了
解密后004015F0里面的代码:
0040165C|. /75 26 jnz short 00401684 ;注册码第十个必须为X
0040165E|. |8078 0A 54 cmp byte ptr , 54
00401662|. |75 20 jnz short 00401684 ;注册码第十一个必须为T
00401664|. |8D5424 10 lea edx, dword ptr
00401668|. |C74424 10 000>mov dword ptr , 0
00401670|. |52 push edx ; /pThreadId
00401671|. |6A 00 push 0 ; |CreationFlags = 0
00401673|. |6A 00 push 0 ; |pThreadParm = NULL
00401675|. |68 60154000 push 00401560 ; |ThreadFunction = cm_nf_v0.00401560
0040167A|. |6A 00 push 0 ; |StackSize = 0
0040167C|. |6A 00 push 0 ; |pSecurity = NULL
0040167E|. |FF15 90224200 call dword ptr ; \CreateThread 弹出对话框这里里面还会比较注册码
解密后00401560里面的代码:
00401570 .68 FF000000 push 0FF ; /Count = FF (255.)
00401575 .50 push eax ; |Buffer
00401576 .68 EA030000 push 3EA ; |ControlID = 3EA (1002.)
0040157B .51 push ecx ; |hWnd => 001309B8 ('N's FirsT cRack Me V0.4',class='#32770')
0040157C .FF15 30244200 call dword ptr ; \GetDlgItemTextA 取注册码
00401582 .A1 D0F04200 mov eax, dword ptr
00401587 .85C0 test eax, eax
00401589 .74 58 je short 004015E3
0040158B .0FBE5424 07 movsx edx, byte ptr ;取第八个字符
00401590 .0FBE4424 03 movsx eax, byte ptr ;取第四个字符
00401595 .03D0 add edx, eax ;相加 是否等于0x84
00401597 .81FA 84000000 cmp edx, 84
0040159D .75 44 jnz short 004015E3
0040159F .57 push edi
004015A0 .8D7C24 04 lea edi, dword ptr
004015A4 .83C9 FF or ecx, FFFFFFFF
004015A7 .33C0 xor eax, eax
004015A9 .F2:AE repne scas byte ptr es:
004015AB .F7D1 not ecx
004015AD .49 dec ecx
004015AE .5F pop edi
004015AF .83F9 0C cmp ecx, 0C ;注册码长度是否为是12
004015B2 .75 2F jnz short 004015E3
004015B4 .807C24 0B 5Acmp byte ptr , 5A ;最后一个字母是否为Z
004015B9 .75 28 jnz short 004015E3
004015BB .807C24 08 41cmp byte ptr , 41 ;第九个字符是否小于A
004015C0 .7D 21 jge short 004015E3
004015C2 .6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
004015C4 .68 CCB04200 push 0042B0CC ; |Title = "Great!"
004015C9 .68 C0B04200 push 0042B0C0 ; |Text = "Good Job!"
004015CE .6A 00 push 0 ; |hOwner = NULL
004015D0 .FF15 34244200 call dword ptr ; \MessageBoxA
004015D6 .8B0D D0F04200 mov ecx, dword ptr
004015DC .51 push ecx ; /hHook => 01430B21
004015DD .FF15 38244200 call dword ptr ; \UnhookWindowsHookEx
根据如上算法 用VB写了如下拙劣的keygen:
Private Sub cmdKEY_Click()
Dim key As String
Do
key = getkey()
Loop While key = ""
txtText1.Text = key
End Sub
Function getkey() As String
Dim tmpstr As String
Dim L As Integer
Dim K As Integer
K = (78 + 76 + &H43 + Asc("P")) + &H41
I = 0
Do While K <= 626
If I = 5 Then
L = &H41
Else
L = Int(Rnd() * (90 - 65 + 1) + 65)
End If
K = K + L
tmpstr = tmpstr + Chr(L)
If 626 - K >= 65 And 626 - K <= 90 Then
L = 626 - K
tmpstr = tmpstr + Chr(L - 2)
getkey = "NLP" & Chr(&H43) & tmpstr & "A0XTZ"
Exit Do
End If
I = I + 1
Loop
End Function
Private Sub Form_Load()
Randomize (Now())
End Sub
小节:总的说来,这个crackme代码是非常清晰的,算法也非常简单的(复杂的话就没本菜鸟的机会了),在没有脱壳的时候,可以把算法部分直接拿到IDA里面F5就看到代码了,这点是本菜鸟在昨天晚上想到的办法(呵呵 有点投机取巧 要把汇编详细的还原成原始代码,本人还是非常非常困难的).整个程序有三个校验注册码的地方,而且都是相互关联,必须解决了第一个才能进行下一步,而且在解密代码的部分的时候是难倒人了的,没办法,我是只有穷举了.
/:013 强大 ~~
这个CM的猥琐的地方就在于 如何反推出KEY的第二位和第三位 这里老兄是咋搞定的哦
:loveliness:my first keygen 好好学习天天向上 回复 2# Nisy
呵呵 ~还有什么简单的方法么???只有穷举呗 我是一个字母一个字母的试出来的(呵呵~~好像听了有点无语...). 呵呵。IDA 里面找的吗? /:013學習了 我是这样找二三位的:
大胆猜测这个68与注册码第二位异或后也是24.
验证得出是L,0x4C
再加上接下来的解码也只用到了第三位.而且八位数的平均值要等于4E,那么4Eh*2-4Ch=50h. 太强大了 学习一下 ~
我搞的话 这样来测试应该是最简单的了 这次是模仿了之前的一个东东 也是这样的 用4个字节去做的抑或处理 {:3_171:} 就是大约碰撞 50*50*50*50 次 遂放弃之 ……
所以这次给大家留了一个可玩的点 O(∩_∩)O~
贴源码了 ~~
看来我搞错了函数 处理了一下GetDlgItemTextA 还应该再处理一下SetWindowHookEx等再有时间再写一个防止调试的CM出来
HHOOK g_HookKeyBoard = NULL;
HWND g_Hwnd = NULL;
DWORD g_GetDlgText = NULL;
void__declspec(naked) HookGetDlgItemText()
{
__asm
{
MOVEAX,g_GetDlgText
ADDEAX,2
jmpEAX
}
}
DWORD WINAPI MyCheckThread(LPVOID lpThreadParameter)
{
char szBuffer;
GetDlgItemText(g_Hwnd,IDC_EDIT1,szBuffer,MAXBYTE);
if (g_HookKeyBoard && (szBuffer + szBuffer== 132 )
&& (strlen(szBuffer) == 12 ) && szBuffer == 'Z'
&& szBuffer < 'A')
{
MessageBox(NULL,"Good Job!","Great!",MB_OK);
UnhookWindowsHookEx(g_HookKeyBoard);
}
return NULL;
}
BOOL bIsDec = FALSE;
void BeGreat(char * szBuffer)
{
BYTE szOk[] = {0x17,0x2b,0x22,0x37,0x64,0x30,0x63,0x0c,0x28,0x62,0};
for (int i = 0; i < 10 ; i++ )
{
szOk = szOk ^ 0x43;
}
::SetDlgItemText(g_Hwnd,IDC_OK,(LPCTSTR)szOk);
if ( szBuffer == 'X' && szBuffer == 'T')
{
DWORD dwThreadID = 0;
HANDLE hThread = CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)MyCheckThread,NULL,
NULL,&dwThreadID);
}
}
VOID DecryptCode(char * szBuffer)
{
if (bIsDec)
{
return;
}
PCHAR pAddr = (PCHAR)BeGreat;
BYTE ch;
DWORD dwWrite = 0;
VirtualProtect( (LPVOID)pAddr, 120, PAGE_EXECUTE_READWRITE, NULL);
for (int i = 0; i < 120 ; i ++)
{
ch = pAddr[ i + 20] ^ szBuffer; //0x50;
WriteProcessMemory(GetCurrentProcess(),(pAddr+i+20),&ch,1,&dwWrite);
}
// 第二段被和谐的代码
pAddr = (PCHAR)MyCheckThread;
VirtualProtect( (LPVOID)pAddr, 85,PAGE_EXECUTE_READWRITE, NULL);
for (i = 0; i < 85 ; i ++)
{
ch = pAddr[ i + 15] ^ szBuffer; //0x50这个是L;
WriteProcessMemory(GetCurrentProcess(),(pAddr+i+15),&ch,1,&dwWrite);
}
bIsDec = TRUE;
}
void ChechCode()
{
char szBuffer = {0};
DWORD dwLen = 1 ;
int nCount = 2;
__asm
{
push MAXBYTE
MOV EAX,EBP
sub EAX,256+4
push EAX
mov eax,1000
add eax,2
push eax
push g_Hwnd
mov EAX, offset OVER
PUSH EAX
jmp HookGetDlgItemText
}
OVER:
__asm push eax
__asm pop dwLen
for ( unsigned int i = 0 ; i < 8 ;i ++)
{
nCount += szBuffer;
}
if (nCount == 626 && szBuffer == 'N')
{
DecryptCode(szBuffer);
BeGreat(szBuffer);
}
return;
}
LRESULT CALLBACK KeyboardProc(int code, // hook code
WPARAM wParam,// virtual-key code
LPARAM lParam // keystroke-message information
)
{
BYTE szKeyState = {0};
if ( ((int)lParam) < 0 )
{
//16–23 Specifies the scan code
//得到键盘状态
GetKeyboardState(szKeyState);
WORD wChar = 0;
//根据键盘状态,虚拟键码,扫描码,得到输入的字符
int nRet = ToAscii(wParam,(lParam>>16) & 0xFF,szKeyState,&wChar,0);
if ( nRet == 1 && (wChar >= 'A' &&wChar <= 'Z') )
{
//AfxMessageBox((LPCTSTR)&wChar);
ChechCode();
}
}
return CallNextHookEx(g_HookKeyBoard,code,wParam,lParam);
}
void CSetHookDlg::OnSethook()
{
// TODO: Add your control notification handler code here
g_HookKeyBoard = ::SetWindowsHookEx( WH_KEYBOARD,
KeyboardProc,
AfxGetInstanceHandle(),
GetCurrentThreadId()
);
if (!g_HookKeyBoard)
{
AfxMessageBox("Hook Error!");
}
g_Hwnd = GetSafeHwnd();
BYTE szProc[] = {0x04,0x26,0x37,0x07,0x2F,0x24,0x0A,0x37,0x26,0x2e,
0x17,0x26,0x3b,0x37,0x02,0};
for (unsigned int i = 0; i < 15 ; i++ )
{
szProc = szProc ^ 0x43;
}
HMODULE hModule = GetModuleHandle("user32.dll");
g_GetDlgText = (DWORD)GetProcAddress(hModule,(LPCTSTR)szProc);
} 本帖最后由 专业路过 于 2010-5-29 18:04 编辑
回复 7# wuhanqi
非常学习了.呵呵 我是穷举出来的,既然所有注册码都是大写字母 那肯定这个KEY也是所有大写字母中的一个,我是在26个字母中一个一个试出来的 回复 9# 专业路过
人肉功底深厚啊!/:good
要是Nisy抽的字节多些,恐怕这种小聪明就不好使咯..哈哈
页:
[1]
2