吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4719|回复: 7
收起左侧

[CrackMe] 【吾爱2013CM大赛解答】 -- 驱动crackme -- 网际座山雕 KeyGen分析

[复制链接]
JoyChou 发表于 2013-12-15 18:00
CM是什么?Crackme是什么?这是什么东西?楼主发的什么?
他们都是一些公开给别人尝试破解的小程序,制作 Crackme 的人可能是程序员,想测试一下自己的软件保护技术,也可能是一位 Cracker,想挑战一下其它 Cracker 的破解实力,也可能是一些正在学习破解的人,自己编一些小程序给自己破解,KeyGenMe是要求别人做出它的 keygen (序号产生器), ReverseMe 要求别人把它的算法做出逆向分析, UnpackMe 是要求别人把它成功脱壳,本版块禁止回复非技术无关水贴。

本帖最后由 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位,刚好和机器码相同

继续往下看就可以看到程序中唯一的一个关键跳
[AppleScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
00401BC3   803D 22684400>cmp byte ptr ds:[0x446822],0x21          ;  关键判断 446822-446780 = A2
00401BCA   .  75 21         jnz Xmycrack.00401BED
00401BCC   .  B8 80674400   mov eax,mycrack.00446780
00401BD1   .  C605 D8674400>mov byte ptr ds:[0x4467D8],0xDB
00401BD8   .  FFD0          call eax                                 ;  正确提示
00401BDA   .  8B4C24 14     mov ecx,dword ptr ss:[esp+0x14]
00401BDE   .  64:890D 00000>mov dword ptr fs:[0],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[i+9] - buffer + 1  

至于为什么Irp堆栈指针偏移0x3就是控制码,用Windbg dt下IO_STACK_LOCATION结构,就可以很清楚的看到。
[AppleScript] 纯文本查看 复制代码
1
2
3
4
5
6
0:000> dt _IO_STACK_LOCATION
ntdll!_IO_STACK_LOCATION
   +0x000 MajorFunction    : UChar
   +0x001 MinorFunction    : UChar
   +0x002 Flags            : UChar
   +0x003 Control          : UChar


接着就可以继续分析exe的算法
先从00401BCA关键跳入手,[0x446822]的值必须为0x21,很明显可以发现[446822]的值是由0x446780地址引起的。
446822 - 446780 = 0xA2,当ecx为0xA2的时候,edx = ecx % 9(取9的余) = 0,
[AppleScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
00401B70   > /8A99 F8384400 mov bl,byte ptr ds:[ecx+0x4438F8]        ;  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:[edx+edx*8]
00401B87   . |8BD1          mov edx,ecx
00401B89   . |2BD0          sub edx,eax                              ;  edx = 0,1,2,3,48
00401B8B   . |2A9A 70654400 sub bl,byte ptr ds:[edx+0x446570]        ;  446570 驱动返回出来的buffer
00401B91   > |8899 80674400 mov byte ptr ds:[ecx+0x446780],bl        ;  ecx=A2时,bl必须=0x21


先要这段的作用是edx = ecx % 9,
原理可以看看《C++反汇编与逆向分析技术揭密》,里面有相关的证明 或 百度398EE338这个Magic Number
[AppleScript] 纯文本查看 复制代码
1
2
3
4
5
6
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:[edx+edx*8]
00401B87   8BD1          mov edx,ecx
00401B89   2BD0          sub edx,eax                              ;  edx = 0,1,2,3,48


现在看00401B8B这句,当ecx = 0xA2,此时的edx=0,bl = 0x22,
因为执行完00401B8B这句后,bl必须=0x21,所以[0x446570]等于1(数字的1,而不是ASCII的'1')
也就是说,驱动返回的OutBuffer第一个必须是数字1,那其它的呢?可耻的猜驱动返回的OutBuffer为{1,2,3,4,5,6,7,8,9}
那么根据buffer = buffer[i+9] - buffer + 1  即等价于{1,2,3,4,5,6,7,8,9} = buffer[i+9] - buffer + 1
buffer[i+9]是输入的,buffer即机器码
做一个等号两边移位的等价操作,可以推算出: buffer[i+9] = buffer + {0,1,2,3,4,5,6,7,8}

简单的写了个C语言的注册机,测试下,成功。
像类似这样的代码,推荐直接在OD里面汇编操作,或者python


[AppleScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
#include "stdafx.h"
#include <string.h>
#include <windows.h>
 
int main(int argc, char *argv[])
{
        char szMachineCode[MAX_PATH] = {0};
 
        char szResult[9] = {0};
        printf("输入机器码:");
        scanf("%s", szMachineCode);
 
        for (int i = 0; i < 9; i++)
        {
                szResult = szMachineCode + i;
        }
        szResult = '\0';
 
        puts(szResult);
        return 0;
}


最后分析下为什么这个shellcode在win7上不能运行
[AppleScript] 纯文本查看 复制代码
1
2
3
4
5
6
004467C6    33C0            xor eax,eax                              ; mycrack.00446780
004467C8    64:3340 30      xor eax,dword ptr fs:[eax+0x30]          ; ppeb
004467CC    8B40 0C         mov eax,dword ptr ds:[eax+0xC]           ; pldr
004467CF    8B70 1C         mov esi,dword ptr ds:[eax+0x1C]          ; InInitializationOrderModuleList->Flink(即ntdll.dll)
004467D2    AD              lods dword ptr ds:[esi]                  ; kernel32.dll
004467D3    8B48 08         mov ecx,dword ptr ds:[eax+0x8]           ; ldr_data_talbe_entry结果中距离InInitializationOrderModuleList偏移0x8,即dllbase


在win7上,InInitializationOrderModuleList模块的顺序是ntdll.dll->kernelBase.dll->Kernel32.dll

最后给一个win7和xp可以用的shellcode
[AppleScript] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
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,[esi-0xc]   ; 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:[0x30]   //得到peb结构体的地址
            mov ebx,[ebx + 0xc] //得到Ldr结构体的地址
            mov ebx,[ebx + 0xc] //得到ldr.InLoadOrderModuleList.Flink 第一个模块,当前进程
            mov ebx,[ebx]   //得到第二个模块地址 ntdll.dll
            mov ebx,[ebx]   //得到第三个模块地址 kernel32.dll
            mov ebx,[ebx+0x18//得到第三个模块地址(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 [edi - 0x8]        ; LoadLibraryA
            xchg eax, ebp           ; restore current hash, and update ebp
                                    ; with base address of user32.dll
        find_functions:
            pushad                      ; preserve registers
            mov eax, [ebp + 0x3c]       ; eax = start of PE header
            mov ecx, [ebp + eax + 0x78] ; ecx = relative offset of export table
            add ecx, ebp                ; ecx = absolute addr of export table
            mov ebx, [ecx + 0x20]       ; 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, [ebx + edi * 4]    ; 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[esi]
            cmp al,ah
            jz compare_hash
            ror edx,7
            add edx,eax
            inc esi
            jmp hash_loop
        compare_hash:  
            cmp edx, [esp + 0x1c]       ; compare to the requested hash (saved on stack from pushad)
            jnz next_function_loop
            mov ebx, [ecx + 0x24]       ; ebx = relative offset of ordinals table
            add ebx, ebp                ; ebx = absolute addr of ordinals table
            mov di, [ebx + 2 * edi]     ; di = ordinal number of matched function
            mov ebx, [ecx + 0x1c]       ; ebx = relative offset of address table
            add ebx, ebp                ; ebx = absolute addr of address table
            add ebp, [ebx + 4 * edi]    ; 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 [edi] 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 [edi - 0x04] ; //call MessageboxA
            push ebx
            call [edi - 0x08] ; // call ExitProcess
            nop
            nop
            nop
            nop
    }
    return 0;
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x

免费评分

参与人数 1热心值 +1 收起 理由
Chief + 1 吾爱破解2013CM大赛,有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| JoyChou 发表于 2013-12-15 18:04
沙发还是自己坐。
wanghongmin1 发表于 2013-12-15 18:25
xjun 发表于 2013-12-15 18:25
头像被屏蔽
bambooqj 发表于 2013-12-15 19:02
提示: 作者被禁止或删除 内容自动屏蔽
a070458 发表于 2013-12-15 19:39
{:1_931:}学习了 感谢大大
Sandman 发表于 2013-12-15 21:22
为毛那个最终的KeyBuffer = {0,1,2,3,4,5,6,7,8}都是猜出来的

点评

没有更多的Tips  发表于 2013-12-15 21:52
网际座山雕 发表于 2013-12-17 09:38

楼主做的好,非常详细的分析。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-6-18 17:44

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表