第五章 演示版保护技术

5.1序列号保护方式

5.1.1 序列号保护机制

(1) 将 用户名等信息作为自变量,通过F 函数变换后得到注册码

序列号 = F(用户名)

第二章中的TraceMe 即为这种方式产生序列号

(2)通过注册码验证用户名的正确性

作者在生成序列号时,依旧使用同样的算法

序列号 = F(用户名)

但在程序中采用F的逆运算 进行验证

用户名 = G(序列号) , G为F的逆函数

面对这种方法,除了爆破可以考虑:

  • 通过找到G函数反推F函数.
  • 利用给定序列号 求得用户名,并使用该用户名

(3)通过对等函数 检查注册码 (并不是很懂啥叫对等函数)

F(用户名) = G(序列号)

(4)同时将用户名和注册码作为自变量

特定值 = F(用户名, 序列号)

由于失去了 用户名 与序列号的一一对应的关系,可能作者也无法写出注册机… 必须维护一个数据库保存对应数据.

5.1.2如何攻击序列号保护机制

方法1:跟踪输入注册码之后的判断

用户输入注册码之后,软件会调用API复制字符串到缓冲区中,跟随这部分就有可能找到判断注册码是否正确的代码.

常用API:GetWindowTextA(W) GetDlgItemTextA(W) GetDlgItemInt

MessageBoxA(W) MessageBoxExA(W), ShowWindow, MessageBoxIndirectA(W) CreateDialogParamA(W) CreateDialogIndirectParam(W) DialogBoxParamA(W) DialogBoxIndirectParamA(W)

方法2:跟踪程序启动时对注册码的判断过程

常用API:

RegQueryValueExA(W) (若注册码存于注册表中)

GetPrivateProfileStringA(W) GetPrivateProfileIntA(W) GetProfileStringA(W) 等函数 (若注册码存于ini 文件中)

CreateFileA(W) _lopen() (并没见过这个函数) (若注册码存于一般文件中)

1.数据约束的秘诀

大概就是编程者会把用户输入的序列号和算出来的序列号保存到 临近的栈区域内…然后被抓到.

以第二章的TraceMe为例,可以在输入的序列号旁边找到正确的序列号

2.利用消息断点

例如TraceMe中 的Check 按钮, 按下时会有WM_LBUTTONDOWN WM_LBUTTONUP 的消息.

3.利用提示消息

利用人机对话 的文本,寻找相关位置

可以通过寻找 “恭喜你成功” 对应的代码位置, 找到判断比较的位置,并进一步寻找到算法位置

5.1.3 字符串比较形式

这里没有示例程序我也懒得手打代码了…直接截书上的图了

(1) 寄存器直接比较

(2)函数比较

(3)函数比较2

感觉这两种就是用不同方式传参…

(4)串串香比较

5.1.4 制作注册机

1.对明码比较软件的攻击

以第二章的TraceMe为例子

此处跳转至 算法部分,为注册机第一次中断地址 , 比较时 密码明文存在ebp 中,为第二次中断地址

004011E3 . 52 push edx
004011E4 . 50 push eax
004011E5 . E8 56010000 call TraceMe.00401340


----------------------------------------------------
0040138D  |.  55            push ebp                                 ; /String2 = "喳"
0040138E  |.  50            push eax                                 ; |String1 = "123123"
0040138F  |.  FF15 04404000 call dword ptr ds:[<&KERNEL32.lstrcmpA>] ; \lstrcmpA

2.非明码比较

Serial.exe 分析

查看程序调用的API

可以看到熟悉的API,直接在这里下断,并运行程序

BP GetDlgItemTextA

输入密码后点击OK, 程序在断点处停了下来,Alt F9 两三次以后返回程序领空,

发现了刚才输入的Name 和Serial, 说明找对位置了.

处理Name 部分的代码

0040137E  /$  8B7424 04     mov esi,dword ptr ss:[esp+0x4]           ;  serial.0040218E    // 将Name 的地址存入 esi
00401382  |.  56            push esi                                 ;  serial.WndProc
00401383  |>  8A06          /mov al,byte ptr ds:[esi] // 取Name的第一个字节
00401385  |.  84C0          |test al,al  //检查Name是否为空
00401387  |.  74 13         |je short serial.0040139C 如果为空则跳转
00401389  |.  3C 41         |cmp al,0x41  //判断是否大于 'A' 41h
0040138B  |.  72 1F         |jb short serial.004013AC //若小于则为非字母,报错
0040138D  |.  3C 5A         |cmp al,0x5A 判断是否大于'Z' 5Ah
0040138F  |.  73 03         |jnb short serial.00401394 若大于 可能为小写字母,跳转至 00401394 call 函数, 将小写字母转换为大写字母
00401391  |.  46            |inc esi          esi ++ 看下一个字母                       ;  serial.WndProc
00401392  |.^ EB EF         |jmp short serial.00401383
00401394  |>  E8 39000000   |call serial.004013D2  // 转到函数 (将小写字母转为大写)
00401399  |.  46            |inc esi                                 ;  serial.WndProc
0040139A  |.^ EB E7         \jmp short serial.00401383
0040139C  |>  5E            pop esi      //执行到这里我们可以看到ESI指向地址的已经全步为大写字母, esi 出栈, 重新指向Name首位
0040139D  |.  E8 20000000   call serial.004013C2 //进入另一个函数,对大写后的Name进行处理
004013A2  |.  81F7 78560000 xor edi,0x5678 得出的结果于 5678h 异或
004013A8  |.  8BC7          mov eax,edi 结果置于 eax 中
004013AA  |.  EB 15         jmp short serial.004013C1 // 返回
004013AC  |>  5E            pop esi     // 报错                              ;  serial.00401232
004013AD  |.  6A 30         push 0x30                                ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004013AF  |.  68 60214000   push serial.00402160                     ; |Error!
004013B4  |.  68 69214000   push serial.00402169                     ; |Incorrect!,Try Again
004013B9  |.  FF75 08       push [arg.1]                             ; |hOwner = 001408AC ('CrackMe v1.0',class='No need to disasm the code!')
004013BC  |.  E8 79000000   call <jmp.&USER32.MessageBoxA>           ; \MessageBoxA
004013C1  \>  C3            retn //返回,结束函数
004013C2  /$  33FF          xor edi,edi  // edi 清零
004013C4  |.  33DB          xor ebx,ebx  // ebx 清零
004013C6  |>  8A1E          /mov bl,byte ptr ds:[esi] esi 首位 复制到 ebx 中
004013C8  |.  84DB          |test bl,bl 判断是否为空
004013CA  |.  74 05         |je short serial.004013D1 为空则跳出循环
004013CC  |.  03FB          |add edi,ebx edi = edi + ebx 把Name的每一个字符相加
004013CE  |.  46            |inc esi       指向下一位                          ;  serial.WndProc
004013CF  |.^ EB F5         \jmp short serial.004013C6 循环
004013D1  \>  C3            retn
004013D2  /$  2C 20         sub al,0x20  //字母值减20,转为大写
004013D4  |.  8806          mov byte ptr ds:[esi],al
004013D6  \.  C3            retn

处理Serial 部分的代码

004013D8  /$  33C0          xor eax,eax //清零 eax
004013DA  |.  33FF          xor edi,edi // 清零 edi
004013DC  |.  33DB          xor ebx,ebx // 清零 ebx
004013DE  |.  8B7424 04     mov esi,dword ptr ss:[esp+0x4]    Serial 的地址 置于 esi 中        ;  serial.0040217E
004013E2  |>  B0 0A         /mov al,0xA 将 0Ah置于 eax 中
004013E4  |.  8A1E          |mov bl,byte ptr ds:[esi] serial 首位 放于 ebx中
004013E6  |.  84DB          |test bl,bl 若该字节不为空
004013E8  |.  74 0B         |je short serial.004013F5
004013EA  |.  80EB 30       |sub bl,0x30  ebx = bl-30h ,即为 serial[i]-30h
004013ED  |.  0FAFF8        |imul edi,eax  edi = edi * 10 左移 1位
004013F0  |.  03FB          |add edi,ebx    edi = edi *10 + serial[i]-30h
004013F2  |.  46            |inc esi                    指向下一位               ;  serial.00402193
004013F3  |.^ EB ED         \jmp short serial.004013E2 循环
004013F5  |>  81F7 34120000 xor edi,0x1234 处理后的结果 于 1234h 异或
004013FB  |.  8BDF          mov ebx,edi 将结果置于 ebx中
004013FD  \.  C3            retn 返回

Serial的部分可以看作将 十进制转为16进制,再和 0x1234进行异或,所以我们求出Name的运算结果,再与0x1234进行异或,就是 Serial 部分运算之前的结果了.所以注册机代码可以为

int i,k1=0,k2=0;
char ch;
for(i=0; cName[i]!=0&&i<=9;i++)


{
    ch=(BYTE)cName[i];
    if(ch<'A')  return FALSE; 

    k1+=(ch>'Z')?(ch-32):ch;    
}                         
k2=k1^0x5678^0x1234;  

5.2 警告窗口

去除 警告窗口 :修改程序资源,静态分析,动态分析

修改资源可以将警告窗口改为透明或者不可见

若要完全去除,找到创建窗口的代码并跳过. 显示窗口的常用函数

MessageBoxA(W), MessageBoxExA(W) DialogBoxParamA(W) ShowWindow CreateWindowExA(W),

若这些断点对警告窗口无效,可以利用消息设置断点拦截.

利用ResourceHacker找到 要去除的窗口

用W32DASM打开… 不知道为什么对话框参考我这里是灰色的…

不管了,先跳过。

直接用OD 定位 调用窗口的位置

从DialogBoxParam的函数说明我们可以看出, 函数是用 对话框ID指定要启动的对话框的,以及指定函数地址处理对话框。

对lpDialogFunc 函数下断,此处为 bp 004010C4

运行中断在这里以后,可以看出 这个函数 会启动 一个 ID为 0x65 的窗口,从Resourcehacker中我们可以知道这个窗口即为 主窗口。我们也可以得知处理主窗口的函数 00401109

所以这里有两种方法更改,

跳过 警告窗口

将00401051 的push 000000 改为 jmp 4010E5 即为 直接跳至调用主窗口的部分。

将两个DialogBoxParam函数的参数对调,将主窗口的参数 放置在调用警告窗口的函数中。

5.3时间限制

5.3.1 计时器

1.SetTimer() 函数

向系统申请一个计时器,超时了系统会向窗口发送WM_TIMER消息,或调用程序提供的回调函数

2.高精度的多媒体计时器

timeSetEvent 函数

3.GetTickCount()函数

返回值为自程序启动后所过时间,调用两次相减即可知道程序运行多长时间

4.timeGetTime()函数,功能同上

5.3.2 时间限制

限制30天,基本原理为第一次运行时取当前时间并存储到一个或者多个位置,

下次运行时进行比对,复杂一点的可以是 多次记录时间,如果本次的时间比上次大,就更新时间,如果小于,可以判断出用户更改了时间,拒绝运行。

获取时间的API:

GetSystemTime GetLocalTime GetFileTime

5.3.3 拆除时间保护

查看程序模块可以看到 SetTimer

在SetTimer处下断

004010C2 . 8B7424 08 mov esi,dword ptr ss:[esp+0x8] ; Case 110 (WM_INITDIALOG) of switch 004010A5
004010C6 . 6A 00 push 0x0 ; /Timerproc = NULL  可以直接 用jmp 跳过此处代码
004010C8 . 68 E8030000 push 0x3E8 ; |Timeout = 1000. ms
004010CD . 6A 01 push 0x1 ; |TimerID = 0x1
004010CF . 56 push esi ; |hWnd = 002206E8 ('Timer',class='#32770')
004010D0 . FF15 30204000 call dword ptr ds:[<&USER32.SetTimer>] ; \SetTimer
004010D6 . A1 04304000 mov eax,dword ptr ds:[0x403004]

5.4 菜单功能限制

5.4.1 相关函数

BOOL EnableMenuItem(
  HMENU hMenu,
  UINT  uIDEnableItem,
  UINT  uEnable
);
BOOL EnableWindow(
  HWND hWnd,
  BOOL bEnable
);

5.4.2 拆解菜单限制保护

004011E3 6A 00 push 0x0  // 找到 EnableMenuitem并将 参数1 改为0 
004011E5 . 68 459C0000 push 0x9C45 ; |ItemID = 9C45 (40005.)
004011EA . 50 push eax ; |hMenu = NULL
004011EB . FF15 9C404000 call dword ptr ds:[<&USER32.EnableMenuIt>; \EnableMenuItem
004011F1 . 5E pop esi ; EnableMe.00401120

5.5 KeyFile 保护

软件读取KeyFile来判断是否为 激活版

5.5.1 相关API

FindFirstFileA 确定注册文件是否存在

CreateFileA _lopen , 确定文件是否存在,打开文件以获得句柄

GetFileSize, GetFileSizeEx 获得注册文件的大小

GetFileAttributesA, GetFileAttributesExA 获得文件的属性

SetFilePointer, SetFilePointerEx, 移动文件指针

ReadFile 读取文件内容

5.5.3拆解KeyFile保护

1.拆解KeyFile的一般思路

1.利用Process Monitor等工具,监视文件操作,找到KeyFile的文件名

2.伪造一个KeyFile文件

3.调试器中对CreateFileA下断,查看打开的文件指针,并记下返回的句柄。

4.对ReadFile下断,找到 与3 相同的文件,找到缓冲区,对缓冲区下内存断点。

2.监视文件的操作

可以利用Process Monitor 进行监控。

3.分析过程

我上来直接好家伙,这个软件的激活程序是吃豆人…

004016D8   .  52            push edx                                                        ; |FileName = "KwazyWeb.bit"
004016D9   .  E8 1C010000   call <jmp.&KERNEL32.CreateFileA>                                ; \CreateFileA  通过观察 CreateFileA函数的参数,我们可以得知 KeyFile名为 KwazyWeb.bit 
// 我们可以在程序根目录建立一个同名文件,以方便后续的跟进
004016DE   .  83F8 FF       cmp eax,-0x1
004016E1   .  74 64         je short PacMe.00401747
004016E3   .  A3 44344000   mov dword ptr ds:[0x403444],eax
004016E8   .  6A 00         push 0x0                                                        ; /pOverlapped = NULL
004016EA   .  68 48344000   push PacMe.00403448                                             ; |pBytesRead = PacMe.00403448
004016EF   .  6A 01         push 0x1                                                        ; |BytesToRead = 0x1  // 读取一个字节
004016F1   .  68 FA344000   push PacMe.004034FA                                             ; |Buffer = PacMe.004034FA //缓存区地址004034FA
004016F6   .  FF35 44344000 push dword ptr ds:[0x403444]                                    ; |hFile = NULL
004016FC   .  E8 11010000   call <jmp.&KERNEL32.ReadFile>                                   ; \ReadFile

//以上为第一次读取文件,从文件中读取一个字节的大小.从后续的分析我们可以知道这个是用户名的字节长度
00401701   .  0FB605 FA3440>movzx eax,byte ptr ds:[0x4034FA] // 判断字节长度是否为0,若为0 则直接退出
00401708   .  85C0          test eax,eax
0040170A   .  74 3B         je short PacMe.00401747
0040170C   .  6A 00         push 0x0                                                        ; /pOverlapped = NULL
0040170E   .  68 48344000   push PacMe.00403448                                             ; |pBytesRead = PacMe.00403448
00401713   .  50            push eax        ; |BytesToRead = 0x0   //读取eax 个字节,eax来自于上一次读取文件                                 
00401714   .  68 88324000   push PacMe.00403288                                             ; |Buffer = PacMe.00403288 // 缓存地址 00403288
00401719   .  FF35 44344000 push dword ptr ds:[0x403444]                                    ; |hFile = NULL
0040171F   .  E8 EE000000   call <jmp.&KERNEL32.ReadFile>                                   ; \ReadFile
00401724   .  E8 D7F8FFFF   call PacMe.00401000
00401729   .  6A 00         push 0x0                                                        ; /pOverlapped = NULL
0040172B   .  68 48344000   push PacMe.00403448                                             ; |pBytesRead = PacMe.00403448
00401730   .  6A 12         push 0x12     //读18个字节 12h                                                  ; |BytesToRead = 12 (18.)
00401732   .  68 E8344000   push PacMe.004034E8                                             ; |Buffer = PacMe.004034E8  // 缓存位置 004034E8
00401737   .  FF35 44344000 push dword ptr ds:[0x403444]                                    ; |hFile = NULL
0040173D   .  E8 D0000000   call <jmp.&KERNEL32.ReadFile>                                   ; \ReadFile
00401742   .  E8 82F9FFFF   call PacMe.004010C9 // 核心验证函数
00401747   >  FF35 44344000 push dword ptr ds:[0x403444]                                    ; /hObject = NULL
0040174D   .  E8 A2000000   call <jmp.&KERNEL32.CloseHandle>                                ; \CloseHandle

0040100 处函数

00401000 /$ 33C0 xor eax,eax
00401002 |. 33D2 xor edx,edx ; ntdll.KiFastSystemCallRet
00401004 |. 33C9 xor ecx,ecx ; kernel32.7C80189C  //清空寄存器
00401006 |. 8A0D FA344000 mov cl,byte ptr ds:[0x4034FA] // cl = 用户名长度 做为循环变量
0040100C |. BE 88324000 mov esi,PacMe.00403288 ; ASCII "pediy" //用户名入esi
00401011 |> AC /lods byte ptr ds:[esi] // 逐字节读入eax
00401012 |. 03D0 |add edx,eax //  edx = edx+eax
00401014 |.^ E2 FB \loopd short PacMe.00401011 // 循环
00401016 |. 8815 FB344000 mov byte ptr ds:[0x4034FB],dl 将求和结果放入 004034FB
0040101C . C3 retn

004010C9处函数

004010C9  /$  55            push ebp
004010CA  |.  8BEC          mov ebp,esp
004010CC  |.  83C4 FC       add esp,-0x4
004010CF  |.  68 65334000   push PacMe.00403365                                             ; /****************C*......*...****.*.****...*....*.*..**********.*..*....*...*...**.****.*.*...****.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************
004010D4  |.  68 BC314000   push PacMe.004031BC                                             ; |****************C*......*...****.*.****...*....*.*..**********.*..*....*...*...**.****.*.*...****.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************
004010D9  |.  E8 3A070000   call <jmp.&KERNEL32.lstrcpyA>   //复制字符串                                ; \lstrcpyA
004010DE  |.  C705 84314000>mov dword ptr ds:[0x403184],PacMe.004031CC                      ;  C*......*...****.*.****...*....*.*..**********.*..*....*...*...**.****.*.*...****.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************
// 00403184指向吃豆人主角C的位置,简称C位
004010E8  |.  E8 30FFFFFF   call PacMe.0040101D // 对18个字节进行异或运算
004010ED  |.  C645 FE 00    mov byte ptr ss:[ebp-0x2],0x0 //ebp -2 = 0
004010F1  |.  33C0          xor eax,eax
004010F3  |.  33C9          xor ecx,ecx                                                     ;  kernel32.7C80189C
004010F5  |>  C645 FF 08    /mov byte ptr ss:[ebp-0x1],0x8 //ebp-1 = 8
004010F9  |>  806D FF 02    |/sub byte ptr ss:[ebp-0x1],0x2 // [ebp-1] -2 
004010FD  |.  0FB64D FE     ||movzx ecx,byte ptr ss:[ebp-0x2] ecx = [ebp -2]
00401101  |.  81C1 E8344000 ||add ecx,PacMe.004034E8                                        //指向异或后字节[ebp-02]位置 若异或后字节为str, 则此处指向 str[i]
00401107  |.  8A01          ||mov al,byte ptr ds:[ecx]                                      ;  将str[i]的值放于 al中
00401109  |.  8A4D FF       ||mov cl,byte ptr ss:[ebp-0x1]                                  ;  cl = len
0040110C  |.  D2E8          ||shr al,cl                                                     ;  右移len位置
//
0040110E  |.  24 03         ||and al,0x3 取最后三位
00401110  |.  E8 1EFFFFFF   ||call PacMe.00401033 // al为方向,此处正式进入吃豆人
00401115  |.  85C0          ||test eax,eax
00401117  |.  74 11         ||je short PacMe.0040112A
00401119  |.  0FB655 FF     ||movzx edx,byte ptr ss:[ebp-0x1]
0040111D  |.  85D2          ||test edx,edx                                                  ;  ntdll.KiFastSystemCallRet
0040111F  |.^ 75 D8         |\jnz short PacMe.004010F9
00401121  |.  FE45 FE       |inc byte ptr ss:[ebp-0x2]
00401124  |.  807D FE 12    |cmp byte ptr ss:[ebp-0x2],0x12
00401128  |.^ 75 CB         \jnz short PacMe.004010F5
0040112A  |>  C9            leave
0040112B  \.  C3            retn

0040101D处函数,

0040101D /$ 8A15 FB344000 mov dl,byte ptr ds:[0x4034FB] //用户名数据和入dl
00401023 |. B9 12000000 mov ecx,0x12 // ecx = 18 (12h)
00401028 |. B8 E8344000 mov eax,PacMe.004034E8 // 第三次读取的18个字节 入eax
0040102D |> 3010 /xor byte ptr ds:[eax],dl 逐字节对eax 于 用户名和 进行异或
0040102F |. 40 |inc eax ; PacMe.004031BC 指向下一个字节
00401030 |.^ E2 FB \loopd short PacMe.0040102D // 循环
00401032 . C3 retn

00401033 处函数

00401033   $  55            push ebp
00401034   .  8BEC          mov ebp,esp
00401036   .  83C4 F8       add esp,-0x8
00401039   .  8B15 84314000 mov edx,dword ptr ds:[0x403184]                                 ;  C*......*...****.*.****...*....*.*..**********.*..*....*...*...**.****.*.*...****.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************
//edx指向C
0040103F   .  8955 FC       mov dword ptr ss:[ebp-0x4],edx   
//将edx 做为参数备份在 ebp-04处                               ;  PacMe.0040321B
00401042   .  0AC0          or al,al                                                        ;  Switch (cases 0..2)
00401044   .  75 09         jnz short PacMe.0040104F
00401046   .  832D 84314000>sub dword ptr ds:[0x403184],0x10  若为0,向上 走,减去10                              ;  Case 0 of switch 00401042
0040104D   .  EB 1F         jmp short PacMe.0040106E
0040104F   >  3C 01         cmp al,0x1
00401051   .  75 08         jnz short PacMe.0040105B
00401053   .  FF05 84314000 inc dword ptr ds:[0x403184]                                     ;  PacMe.004031CC; Case 1 of switch 00401042 若为1,向右走,加1
00401059   .  EB 13         jmp short PacMe.0040106E
0040105B   >  3C 02         cmp al,0x2
0040105D   .  75 09         jnz short PacMe.00401068
0040105F   .  8305 84314000>add dword ptr ds:[0x403184],0x10       // 若为2,向下走,加10h                         ;  Case 2 of switch 00401042
00401066   .  EB 06         jmp short PacMe.0040106E
00401068   >  FF0D 84314000 dec dword ptr ds:[0x403184]     若为3 向左走,减1                                ;  PacMe.004031CC; Default case of switch 00401042
0040106E   >  8B15 84314000 mov edx,dword ptr ds:[0x403184]                                 ;  C*......*...****.*.****...*....*.*..**********.*..*....*...*...**.****.*.*...****.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************
将走过一步的位置传给edx
00401074   .  8A02          mov al,byte ptr ds:[edx], 将当前位置的值传给al
00401076   .  3C 2A         cmp al,0x2A  判断是否为*, 若为*则退出
00401078   .  75 06         jnz short PacMe.00401080
0040107A   .  33C0          xor eax,eax
0040107C   .  C9            leave
0040107D   .  C3            retn
0040107E   . /EB 33         jmp short PacMe.004010B3
00401080   > |3C 58         cmp al,0x58 //比较是否为X,若为X则成功
00401082   . |75 2F         jnz short PacMe.004010B3
00401084   . |6A 00         push 0x0                                                        ; /Style = MB_OK|MB_APPLMODAL
00401086   . |8D15 59334000 lea edx,dword ptr ds:[0x403359]                                 ; |Success..
0040108C   . |52            push edx                                                        ; |Title = "**.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************"
0040108D   . |8D15 EC324000 lea edx,dword ptr ds:[0x4032EC]                                 ; |Congratulations!\n\rMail me (KwazyWebbit@hotmail.com) how you did it.\n\rDont forget to include your keyfile! =]
00401093   . |52            push edx                                                        ; |Text = "**.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************"
00401094   . |6A 00         push 0x0                                                        ; |hOwner = NULL
00401096   . |8D15 AC174000 lea edx,dword ptr ds:[0x4017AC]                                 ; |
0040109C   . |FFD2          call edx                                                        ; \MessageBoxA
0040109E   . |8D15 7B324000 lea edx,dword ptr ds:[0x40327B]                                 ;  Cracked by :
004010A4   . |52            push edx                                                        ; /Text = "**.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************"
004010A5   . |FF35 20344000 push dword ptr ds:[0x403420]                                    ; |hWnd = 0010074A ('UNREGISTERED!',class='Edit',parent=0010074E)
004010AB   . |8D15 DC174000 lea edx,dword ptr ds:[0x4017DC]                                 ; |
004010B1   . |FFD2          call edx                                                        ; \SetWindowTextA
004010B3   > \8B15 84314000 mov edx,dword ptr ds:[0x403184]                                 ;  C*......*...****.*.****...*....*.*..**********.*..*....*...*...**.****.*.*...****.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************
004010B9   .  C602 43       mov byte ptr ds:[edx],0x43 // 将当前的位置赋值为C,新的C位
004010BC   .  8B55 FC       mov edx,dword ptr ss:[ebp-0x4] //取回备份的上一个位置
004010BF   .  C602 20       mov byte ptr ds:[edx],0x20 将上一个位置用空格替代,代表走过的路
004010C2   .  B8 01000000   mov eax,0x1
004010C7   .  C9            leave
004010C8   .  C3            retn


具体原理为 首先程序 读取第一个字节,此字节为用户名的长度, 然后利用这个长度读出完整的用户名, 下一步用用户名计算出用户名和.第三次读文件,从用户名往后读18个字节,这部分字节是用来操纵吃豆人的行为的. 但是需要和 用户名和进行异或 来解密. 异或以后, 逐字节读取,例如A9 二进制为

10101001

吃豆人的移动指令


10
010
010
001

程序从10101001 中,读3次,第一次为10,即2 ,向下移动,010,也为2,即连着向下移动三次, 001 ,向右移动.

所以代码中的循环为,每个字节读4次,共18个字节, 4*18步,最后吃到X验证成功

5.6网络验证

5.6.1 相关函数

1.send 函数

int WSAAPI send(
  SOCKET     s,
  const char *buf,
  int        len,
  int        flags
);

2.recv 函数

int recv(
  SOCKET s,
  char   *buf,
  int    len,
  int    flags
);

5.6.2 破解网络验证的一般思路

这里似乎要用到IDA…我暂时还不会用,等回头会了在回来看看…(偷懒偷懒…)

5.8 只运行一个实例

5.8.1 实现方法

1.查找窗口法

HWND FindWindowA(
  LPCSTR lpClassName,
  LPCSTR lpWindowName
);
若返回的句柄为0,则初始化程序,然则退出

2.使用互斥对象

HANDLE CreateMutexA(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  BOOL                  bInitialOwner,
  LPCSTR                lpName
);

代码形式:

5.9 常用断点

评论

  1. 大漠苍鹰
    10月前
    2021-1-09 13:53:30

    有些软件要注册码就是这技术吧

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇