【吾爱2013CM大赛解答】 -- 驱动crackme -- 网际座山雕 KeyGen分析
本帖最后由 JoyChou 于 2013-12-19 14:20 编辑程序拿到手后是MFC程序,还是用MFC按钮入口查看器查看,在上一篇破文中,我已经发了附件了。
找到按钮入口地址为00401A50,下断,F9,输入序列号LoveKido,断下来,先看看程序整理流程。
豁然的看到DeviceIoControl这个API,由此可以看出,此程序是通过DeviceIoControl产生IRP_MJ_DEVICE_CONTROL例程来和sys通信的。
不过这个函数,可以看到发送到sys的数据已经返回到exe的数据,通过DeviceIoControl函数的参数
发送到sys的InBuffer:
即机器码+自己输入的序列号
返回到exe的OutBuffer:
可以看出只有9位,中间被00截断了,由此推断序列号只有9位,刚好和机器码相同
继续往下看就可以看到程序中唯一的一个关键跳
00401BC3 .803D 22684400>cmp byte ptr ds:,0x21 ;关键判断 446822-446780 = A2
00401BCA .75 21 jnz Xmycrack.00401BED
00401BCC .B8 80674400 mov eax,mycrack.00446780
00401BD1 .C605 D8674400>mov byte ptr ds:,0xDB
00401BD8 .FFD0 call eax ;正确提示
00401BDA .8B4C24 14 mov ecx,dword ptr ss:
00401BDE .64:890D 00000>mov dword ptr fs:,ecx
00401BE5 .59 pop ecx
00401BE6 .5F pop edi
00401BE7 .5E pop esi
00401BE8 .5B pop ebx
00401BE9 .83C4 10 add esp,0x10
00401BEC .C3 retn
00401BED >6A 00 push 0x0
00401BEF .6A 00 push 0x0
00401BF1 .68 30984300 push mycrack.00439830 ;胜败乃兵家常事,大侠请重新来过。
00401BF6 .8BCF mov ecx,edi
00401BF8 .E8 47690000 call mycrack.00408544 ;错误提示
分析到这,可能有的Cracker都想这么简单?直接就爆破就完事了?
不过发现00401BD8 这个call里面的内容是加密后的,必须靠正确的序列号才能解码。
现在就可以开始算法分析了……
先看驱动层
IDA载入,在DriverEntry中找到IRP_MJ_DEVICE_CONTROL例程函数,它的宏是十进制的14,对应sub_10748这个函数,F5一目了然
最后的算法:buffer = buffer - buffer + 1
至于为什么Irp堆栈指针偏移0x3就是控制码,用Windbg dt下IO_STACK_LOCATION结构,就可以很清楚的看到。
0:000> dt _IO_STACK_LOCATION
ntdll!_IO_STACK_LOCATION
+0x000 MajorFunction : UChar
+0x001 MinorFunction : UChar
+0x002 Flags : UChar
+0x003 Control : UChar
接着就可以继续分析exe的算法
先从00401BCA关键跳入手,的值必须为0x21,很明显可以发现的值是由0x446780地址引起的。
446822 - 446780 = 0xA2,当ecx为0xA2的时候,edx = ecx % 9(取9的余) = 0,
00401B70 > /8A99 F8384400 mov bl,byte ptr ds: ;d 004438f8+0a2 = 0x22
00401B76 . |80FB E0 cmp bl,0xE0
00401B79 . |73 16 jnb Xmycrack.00401B91
00401B7B . |B8 398EE338 mov eax,0x38E38E39 ;edx = ecx mod 9
00401B80 . |F7E1 mul ecx
00401B82 . |D1EA shr edx,1
00401B84 . |8D04D2 lea eax,dword ptr ds:
00401B87 . |8BD1 mov edx,ecx
00401B89 . |2BD0 sub edx,eax ;edx = 0,1,2,3,4到8
00401B8B . |2A9A 70654400 sub bl,byte ptr ds: ;446570 驱动返回出来的buffer
00401B91 > |8899 80674400 mov byte ptr ds:,bl ;ecx=A2时,bl必须=0x21
先要这段的作用是edx = ecx % 9,
原理可以看看《C++反汇编与逆向分析技术揭密》,里面有相关的证明 或 百度398EE338这个Magic Number
00401B7B .B8 398EE338 mov eax,0x38E38E39 ;edx = ecx mod 9
00401B80 .F7E1 mul ecx
00401B82 .D1EA shr edx,1
00401B84 .8D04D2 lea eax,dword ptr ds:
00401B87 .8BD1 mov edx,ecx
00401B89 .2BD0 sub edx,eax ;edx = 0,1,2,3,4到8
现在看00401B8B这句,当ecx = 0xA2,此时的edx=0,bl = 0x22,
因为执行完00401B8B这句后,bl必须=0x21,所以等于1(数字的1,而不是ASCII的'1')
也就是说,驱动返回的OutBuffer第一个必须是数字1,那其它的呢?可耻的猜驱动返回的OutBuffer为{1,2,3,4,5,6,7,8,9}
那么根据buffer = buffer - buffer + 1即等价于{1,2,3,4,5,6,7,8,9} = buffer - buffer + 1
buffer是输入的,buffer即机器码
做一个等号两边移位的等价操作,可以推算出: buffer = buffer + {0,1,2,3,4,5,6,7,8}
简单的写了个C语言的注册机,测试下,成功。
像类似这样的代码,推荐直接在OD里面汇编操作,或者python
#include "stdafx.h"
#include <string.h>
#include <windows.h>
int main(int argc, char *argv[])
{
char szMachineCode = {0};
char szResult = {0};
printf("输入机器码:");
scanf("%s", szMachineCode);
for (int i = 0; i < 9; i++)
{
szResult = szMachineCode + i;
}
szResult = '\0';
puts(szResult);
return 0;
}
最后分析下为什么这个shellcode在win7上不能运行
004467C6 33C0 xor eax,eax ; mycrack.00446780
004467C8 64:3340 30 xor eax,dword ptr fs: ; ppeb
004467CC 8B40 0C mov eax,dword ptr ds: ; pldr
004467CF 8B70 1C mov esi,dword ptr ds: ; InInitializationOrderModuleList->Flink(即ntdll.dll)
004467D2 AD lods dword ptr ds: ; kernel32.dll
004467D3 8B48 08 mov ecx,dword ptr ds: ; ldr_data_talbe_entry结果中距离InInitializationOrderModuleList偏移0x8,即dllbase
在win7上,InInitializationOrderModuleList模块的顺序是ntdll.dll->kernelBase.dll->Kernel32.dll
最后给一个win7和xp可以用的shellcode
int main()
{
_asm{
nop
nop
nop
nop
nop
CLD ; clear flag DF
;store hash
push 0x1e380a6a ;hash of MessageBoxA
push 0x4fd18963 ;hash of ExitProcess
push 0x0c917432 ;hash of LoadLibraryA
mov esi,esp ; esi = addr of first function hash
lea edi, ; edi = addr to start writing function
; make some stack space
xor ebx,ebx
mov bh, 0x04
sub esp, ebx
; push a pointer to "user32" onto stack
mov bx, 0x3233 ; rest of ebx is null
push ebx
push 0x72657375
push esp
xor edx,edx
; find base addr of kernel32.dll
mov ebx,fs: //得到peb结构体的地址
mov ebx, //得到Ldr结构体的地址
mov ebx, //得到ldr.InLoadOrderModuleList.Flink 第一个模块,当前进程
mov ebx, //得到第二个模块地址 ntdll.dll
mov ebx, //得到第三个模块地址 kernel32.dll
mov ebx,//得到第三个模块地址(kernel32模块的dllbase)
mov ebp,ebx
find_lib_functions:
lodsd ; load next hash into al and increment esi
cmp eax, 0x1e380a6a ; hash of MessageBoxA - trigger
; LoadLibrary("user32")
jne find_functions
xchg eax, ebp ; save current hash
call ; LoadLibraryA
xchg eax, ebp ; restore current hash, and update ebp
; with base address of user32.dll
find_functions:
pushad ; preserve registers
mov eax, ; eax = start of PE header
mov ecx, ; ecx = relative offset of export table
add ecx, ebp ; ecx = absolute addr of export table
mov ebx, ; ebx = relative offset of names table
add ebx, ebp ; ebx = absolute addr of names table
xor edi, edi ; edi will count through the functions
next_function_loop:
inc edi ; increment function counter
mov esi, ; esi = relative offset of current function name
add esi, ebp ; esi = absolute addr of current function name
cdq ; dl will hold hash (we know eax is small)
hash_loop:
movsx eax, byte ptr
cmp al,ah
jz compare_hash
ror edx,7
add edx,eax
inc esi
jmp hash_loop
compare_hash:
cmp edx, ; compare to the requested hash (saved on stack from pushad)
jnz next_function_loop
mov ebx, ; ebx = relative offset of ordinals table
add ebx, ebp ; ebx = absolute addr of ordinals table
mov di, ; di = ordinal number of matched function
mov ebx, ; ebx = relative offset of address table
add ebx, ebp ; ebx = absolute addr of address table
add ebp, ; add to ebp (base addr of module) the
; relative offset of matched function
xchg eax, ebp ; move func addr into eax
pop edi ; edi is last onto stack in pushad
stosd ; write function addr to and increment edi
push edi
popad ; restore registers
; loop until we reach end of last hash
cmp eax,0x1e380a6a
jne find_lib_functions
function_call:
xor ebx,ebx
push ebx // cut string
push 0x20756F68 //push" uoh"
push 0x43796F4A //push"CyoJ"
mov eax,esp //load address of JoyChou
push ebx
push eax
push eax
push ebx
call ; //call MessageboxA
push ebx
call ; // call ExitProcess
nop
nop
nop
nop
}
return 0;
}
沙发还是自己坐。 学习了,感谢。 膜拜大大,让小菜学习。 {:1_931:}学习了 感谢大大 为毛那个最终的KeyBuffer = {0,1,2,3,4,5,6,7,8}都是猜出来的
楼主做的好,非常详细的分析。
页:
[1]