吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8046|回复: 59
收起左侧

[原创] 萌新的逆向脱壳并编写注册机记录

  [复制链接]
lyl610abc 发表于 2021-3-20 18:58

前言

摸鱼了挺久,发现自己还没发过破解方面的帖子,于是自娱自乐写个程序加壳并逆向分析出注册机

本文主要分为两个部分:

  • 带壳破解
  • 脱壳破解

带壳破解部分侧重于逆向分析及注册机的编写

脱壳破解部分侧重于脱壳

PS:本帖重点在分析,而非结果,单纯的爆破十分简单,稍微难点的地方在脱壳和逆向分析注册机上(虽然都很简单,大佬轻喷)

程序说明

image-20210320142851498


程序载入后会随机生成一个数(机器码)

然后要求输入对应的注册码


错误则弹出消息框 fail:

image-20210320143400039


成功则弹出消息框success:

image-20210320143708846


PS:这个小程序十分简单,有兴趣的可以自己试试去Crack一波

用到的工具

调试工具:OD

脱壳相关工具:

PEID(查壳)

LordPE(转储)

ImpREC(修复IAT)


PS:上述工具在论坛的XP虚拟机中都有集成

image-20210320183207545


带壳破解

先来说说带壳破解,破解之前再说说常用的两个破解手段

常用破解手段

  1. 搜索相关字符串
  2. 下断相关API函数

采用手段

带壳破解这里采用第二种,也就是下断相关API函数进行(因为在带壳的情况下,字符串被加密了,无法通过搜索字符串来检索)

下断哪个API函数?

根据前面程序无论是正确还是错误都会弹出对话框,于是就可以对MessageBoxA这个函数进行下断


开始破解

追踪相关代码

1.将程序载入OD

image-20210320145510086


2.下断相关API函数

通过前面的分析得知要对MessageBoxA这个函数进行下断

于是在窗口的左下角部分输入bp MessageBoxA并回车

bp MessageBoxA

image-20210320145941337


3.触发断点

下完断点以后,按快捷键F9,让程序跑起来,然后返回程序,随便输入一个错误的注册码来触发断点

image-20210320150515116


4.断点触发后

image-20210320150803836


断点成功被触发,此时注意右下角的堆栈窗口,可以看到窗口的相关信息

5.根据堆栈追踪

断点触发后,就可以通过堆栈中的内容来追踪相关的代码了

通过选中堆栈的调用的那一行,然后回车即可跳转到相关代码

image-20210320151055689


6.相关代码

image-20210320151444880


回车后跳转到了MessageBoxA执行完的下一行,向上滑动即可得到相关的代码

分析相关代码

相关代码

通过前面的一系列操作,得到了相关的代码:

004011C6  |.  3B4D F4       cmp ecx,[local.3]
004011C9  |. /75 1F         jnz short crackMe.004011EA
004011CB  |. |8BF4          mov esi,esp
004011CD  |. |6A 00         push 0x0                                 ; /Style = MB_OK|MB_APPLMODAL
004011CF  |. |68 54704200   push crackMe.00427054                    ; |Title = "nice!"
004011D4  |. |68 24704200   push crackMe.00427024                    ; |Text = "success!
"
004011D9  |. |6A 00         push 0x0                                 ; |hOwner = NULL
004011DB  |. |FF15 ECF24200 call dword ptr ds:[0x42F2EC]             ; \MessageBoxA
004011E1  |. |3BF4          cmp esi,esp
004011E3  |. |E8 18020000   call crackMe.00401400
004011E8  |. |EB 1D         jmp short crackMe.00401207
004011EA  |> \8BF4          mov esi,esp
004011EC  |.  6A 00         push 0x0                                 ; /Style = MB_OK|MB_APPLMODAL
004011EE  |.  68 1C814200   push crackMe.0042811C                    ; |Title = "try again!"
004011F3  |.  68 1C704200   push crackMe.0042701C                    ; |Text = "fail!
"
004011F8  |.  6A 00         push 0x0                                 ; |hOwner = NULL
004011FA  |.  FF15 ECF24200 call dword ptr ds:[0x42F2EC]             ; \MessageBoxA
00401200  |.  3BF4          cmp esi,esp

爆破

很显然这里的判断跳转语句

004011C6  |.  3B4D F4       cmp ecx,[local.3]
004011C9  |. /75 1F         jnz short crackMe.004011EA

就是用来判断注册码是否匹配的

只要将这里的跳转语句nop掉,就可以实现爆破,破解就完成了


选中这行反汇编语句,右键    二进制→用NOP填充即可完成爆破

image-20210320152327767


爆破效果

因为先前程序已经验证失败了,于是重新加载程序,提前将判断语句NOP掉

加载完程序后按快捷键Ctrl+G并输入先前判断跳转语句的地址

image-20210320152630339


然后将其Nop掉

image-20210320152811437


此时再随便输入一个注册码试试:

image-20210320152958616


爆破成功! 可以看到爆破十分简单,所以并不是这里的重点,重点在于如何逆向分析并写出注册机

注册机

找到正确的注册码

回到先前的比较语句

004011C6  |.  3B4D F4       cmp ecx,[local.3]
004011C9  |. /75 1F         jnz short crackMe.004011EA

在比较语句这里F2下个断点

image-20210320153425771


然后返回程序随便输入一个注册码触发断点

触发断点后观察ecx和[local.3]的值

ecx

image-20210320153754564

可以看到ECX为F71对应十进制为3953,应该就是正确的注册码

[local.3]

这里的[local.3]其实是OD为了方便查看显示的,其实际的汇编代码为(双击反汇编语句即可看到)

image-20210320153924444

即:

cmp ecx,dword ptr ss:[ebp-0xC]

于是观察ebp-0xC的值,在先前输入bp MessageBoxA的地方输入dd ebp-0xC来查看该地址存放的数据

image-20210320154133595


可以看到存储的数据为0x262对应十进制为610,也就是输入的注册码


到这里,就找到了正确的注册码存储在ecx中,于是向上追溯ecx的来源即可

追溯注册码来源

从比较语句向上追溯ecx的来源:

004011B5  |.  8B55 F8       mov edx,[local.2]
004011B8  |.  52            push edx
004011B9  |.  E8 4CFEFFFF   call crackMe.0040100A
004011BE  |.  83C4 04       add esp,0x4
004011C1  |.  8B45 F8       mov eax,[local.2]
004011C4  |.  8B08          mov ecx,dword ptr ds:[eax]
004011C6  |.  3B4D F4       cmp ecx,[local.3]

向上一行一行分析:

004011C4  |.  8B08          mov ecx,dword ptr ds:[eax]

可以看到ecx=[eax],于是接着找eax的来源


004011C1  |.  8B45 F8       mov eax,[local.2]

eax=[local.2],于是接着找[local.2]的来源


004011B5  |.  8B55 F8       mov edx,[local.2]
004011B8  |.  52            push edx
004011B9  |.  E8 4CFEFFFF   call crackMe.0040100A
004011BE  |.  83C4 04       add esp,0x4

再向上会看到调用了一个call,并且这个call压入的参数就是[local.2],于是有可能这个[local.2]是在这个call中被赋值的

为了验证这一点,得重新运行程序,然后在call这里下个断点

image-20210320155429511


然后随便输入一个注册码,使得程序断下,并观察此时[local.2]的值

image-20210320155641554


此时会发现[local.2]=00931E80,看起来像是个地址,于是再dd 00931E80查看一下

image-20210320161428430

可以看到该地址存储的数据为312F对应十进制为12591,发现它就是machine code

image-20210320161516525


所以得知这里的local.2实际上就是一个指向machine code的指针

又因为注册码一般都是机器码通过某种运算的得到的,这边的这个函数又将机器码作为参数,所以可以推断这个函数是用于计算注册码的,为了验证这一点,可以观察call执行前后local.2指向地址的值的变化


call执行前:local.2指向地址存储的数据为312F对应十进制为12591,存储的数据为machine code,即上面的结果

然后再看看call执行后的结果,按快捷键F8单步步过,观察结果

image-20210320161650910


call执行后:可以看到当call执行后,local.2指向地址存储的数据发生了变化

从312F变成了2D0F(对应十进制11535)为注册码

于是应证了前面的推测:这个call为计算注册码的函数


分析计算注册码函数

找到了计算注册码的函数后,就开始分析

进入函数内部

image-20210320162316334

选中注册码函数,连续按2次回车进入到函数内部


函数内部

函数内部的代码并不多,于是直接贴出代码

004010E0  /> \55            push ebp
004010E1  |.  8BEC          mov ebp,esp
004010E3  |.  83EC 40       sub esp,0x40
004010E6  |.  53            push ebx
004010E7  |.  56            push esi
004010E8  |.  57            push edi
004010E9  |.  8D7D C0       lea edi,[local.16]
004010EC  |.  B9 10000000   mov ecx,0x10
004010F1  |.  B8 CCCCCCCC   mov eax,0xCCCCCCCC
004010F6  |.  F3:AB         rep stos dword ptr es:[edi]
004010F8  |.  8B45 08       mov eax,[arg.1]
004010FB  |.  3E:8B18       mov ebx,dword ptr ds:[eax]
004010FE  |.  B9 10060000   mov ecx,0x610
00401103  |.  2BD9          sub ebx,ecx
00401105  |.  33D9          xor ebx,ecx
00401107  |.  3E:8918       mov dword ptr ds:[eax],ebx
0040110A  |.  5F            pop edi                                  ;  00931E80
0040110B  |.  5E            pop esi                                  ;  00931E80
0040110C  |.  5B            pop ebx                                  ;  00931E80
0040110D  |.  83C4 40       add esp,0x40
00401110  |.  3BEC          cmp ebp,esp
00401112  |.  E8 E9020000   call crackMe.00401400
00401117  |.  8BE5          mov esp,ebp
00401119  |.  5D            pop ebp                                  ;  00931E80
0040111A  \.  C3            retn

分析代码

函数内部初始化堆栈之类的代码略去,提取出关键代码

004010F8  |.  8B45 08       mov eax,[arg.1]
004010FB  |.  3E:8B18       mov ebx,dword ptr ds:[eax]
004010FE  |.  B9 10060000   mov ecx,0x610
00401103  |.  2BD9          sub ebx,ecx
00401105  |.  33D9          xor ebx,ecx
00401107  |.  3E:8918       mov dword ptr ds:[eax],ebx

1.将参数赋值给eax

004010F8  |.  8B45 08       mov eax,[arg.1]

这里的arg.1就是先前压入的参数,也就是先前的local.2


2.取出eax中的值,并赋值给ebx

004010FB  |.  3E:8B18       mov ebx,dword ptr ds:[eax]

结合先前的local.2中存储的为指向机器码的地址,这里其实就是将机器码赋值给ebx


3.将0x610赋值给ecx

004010FE  |.  B9 10060000   mov ecx,0x610

将一个常量赋值给ecx,留作后面运算


4.将ebx减去ecx

00401103  |.  2BD9          sub ebx,ecx

结合前面的分析就是:机器码=机器码-0x610


5.将ebx和ecx进行异或运算,结果保存在ebx中

00401105  |.  33D9          xor ebx,ecx

结合前面的分析就是:机器码= (机器码-0x610) 异或 0x610


6.将ebx赋值给[eax]

00401107  |.  3E:8918       mov dword ptr ds:[eax],ebx

将结果保存到要返回的内容中


得出注册码计算公式

通过上面的分析得到注册码计算公式为:

注册码 = (机器码 - 0x610) xor 0x610
注册机代码

通过注册码计算公式写出对应的注册机代码:

int getRegisterCode(int machineCode){
    return (machineCode-0x610) ^ 0x610;
}

脱壳破解

前面安排了带壳破解,接着安排脱壳破解

脱壳的一般流程

  1. 查壳
  2. 找OEP(程序入口点)
  3. 修复IAT

查壳

打开前面提到的工具PEID,然后载入程序得到:

image-20210320183242538


可以看到是个ORiEN壳

对待不同的壳有不同的技巧,对于ORiEN这种壳,它使用了不少花指令,但可以使用ESP定律脱掉


找OEP(程序入口点)

知道了壳后就用OD载入程序,并开始找OEP

OD载入后

image-20210320165434702

先F8单步步过,跳转到对应的位置


单步过跳转后

image-20210320165612877

可以看到跳转到了pushad指令(一般看到这个指令可以试着用ESP定律尝试一下)

于是再按F8单步步过一次

ESP定律

image-20210320165815902

当程序走过pushad后就可以使用ESP定律来定位OEP了


选中右边寄存器的ESP,右键 HW break [ESP](该操作为对ESP设置硬件断点)

image-20210320165938090


操作后通过 调试→硬件断点,可以看到多了一个对ESP的硬件断点

image-20210320170047917


image-20210320170131348


断点断下

然后按快捷键F9使得程序跑起来,等待断点断下

image-20210320170319800


断点断下后,记得删除先前的硬件断点

image-20210320170400602


接下来按几次F8(单步步过)即可到达OEP

在到达OEP前的最后一个跳转为:

image-20210320170515818

为一个跨段的远跳转(判断达到OEP的一个可能要素)


到达OEP

image-20210320170636626


如何判断OEP:

  • 跨段跳转
  • 根据编译器对应的OEP格式判断(不同编译器的OEP并不相同,可以通过编译器判断OEP特征)

因为程序为C语言编写,上述的OEP也满足C语言的OEP格式,关于各种语言的OEP格式这里就不再罗列

转储文件

找到OEP后,先将文件转储一份出来

打开前面提到的工具LordPE

image-20210320171230286

找到OD正在调试的程序crackme.exe,然后选中并右键 完整转存

image-20210320171417849

image-20210320171347121


修复IAT

转储完后,打开前面提到的工具ImpREC

然后选中crackme.exe

image-20210320171656673


然后返回OD,将OEP的地址复制出来

选中先前找到的OEP首地址,右键 用OllyDump脱壳调试进程

image-20210320171814945


image-20210320171929773


复制完OEP:34A0后再回到ImpREC

image-20210320172157854

先修改OEP,然后依次点击自动查找IAT和获取输入表


获取完后再点击无效函数,发现有两个无效函数

image-20210320172346737

记录下这两个无效的函数地址,需要手动修复

0043EF8A和0043EF21


修复无效函数

先处理0043EF8A

回到OD,重新载入程序,然后先选中内存窗口,然后按Ctrl+G跳转到0043EF8A

image-20210320173135527


然后在跳转得到的地址右键 断点→内存访问

image-20210320173247839


设置完断点后按F9使程序跑起来,注意观察寄存器窗口

image-20210320173513088


第一次断下后并没有什么收获,继续按F9使程序运行,观察寄存器窗口

重复上述操作(按F9后观察寄存器窗口)


在第11次左右断下后,可以观察到寄存器窗口:

image-20210320173731791

此时的eax为kernel32.GetCommandLineA的地址,于是我们得到了要修正的函数为GetCommandLineA


返回ImpREC

双击我们要修复的函数

image-20210320173927499


将前面得到的函数名称填入并修复

image-20210320174152684


修复后:

image-20210320174243931


可以用同样的方法修复剩下的那个无效函数0043EE21

通过内存断点可以得到该函数为ExitProcess,将其修复后:

image-20210320174403851


到此为止,看似好像已经修复完成了,但这里会发现修复的函数只包含了kernel32.dll

而MessageBoxA函数是属于user32.dll的函数,并没有被修复,也就是修复不完全

于是还要修复MessageBoxA函数,IAT函数一般为连续存储,但中间可能会分段,这里的识别不全就是因为少了user32.dll区段的函数识别,于是需要手动补全

因为IAT函数为连续存储,所以可以从先前IAT函数的地址继续往下找

先前IAT函数的的地址和大小为:

image-20210320181154032

于是可以从RVA:2F1A8+F0=2F298 继续向下找有没有遗漏的IAT函数

回到OD,重新加载完程序后按F9让程序跑起来后选中内存窗口按Ctrl+G,再选中RVA 填入地址后跳转

image-20210320181411491


可以看到跳转到了kernel32模块的函数尾部

image-20210320181706619


从这里继续向下寻找是否有遗漏的IAT函数

然后可以找到遗漏了一个MessageBoxA,于是需要将MessageBoxA也修复进去

image-20210320182108501

记下这里的地址为0042F2EC,RVA为:2F2EC


回到ImpREC

修改RVA为前面得到的2F2EC

Size为4(因为只有1个MessageBoxA函数)

image-20210320182002920

然后点击获取输入表即可

PS:因为用OD重新加载了程序所以这里也要重新附加,并且重复一遍之前修复无效函数的操作


获取完输入表后可以看到:

image-20210320182245825

到此为止,IAT就已经修复完毕了

导出修复后的文件

image-20210320182500142

点击转储到文件,并选择之前转储的文件dumped.exe,确定即可

image-20210320182605651

到此为止就脱壳完毕了


验证脱壳

脱壳完毕后,验证一下能否正常运行

打开脱壳后的文件,发现可以正常运行

image-20210320182740212


然后再用PEID查一下壳

image-20210320183106112

可以看到此时的壳已经成功脱掉了


脱壳后的破解

前面带壳破解时限于壳的保护没有采用直接搜索字符串的方式来破解

于是在脱壳后再用OD载入,采用字符串搜索进行破解


OD载入后可以看到程序直接停在了OEP处,也验证了脱壳的成功

image-20210320183606253


然后右键 中文搜索引擎→搜索ASCII

image-20210320183730717


然后在中文搜索引擎中就可以轻松找到相关的字符串,后面的破解也就不再赘述

image-20210320183928426

正向代码

附上程序的正向代码,方便大家对比逆向

// crackMe.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void getRand(int* n){
    srand((unsigned)time(NULL));
    *n=rand();
}

void encode(int* n){
    _asm{
        mov eax,n
        mov ebx,dword ptr ds:[eax]
        mov ecx,0x610
        xor ebx,ecx
        add ebx,ecx
        mov dword ptr ds:[eax],ebx
    }

}

void decode(int* n){
    _asm{
        mov eax,n
        mov ebx,dword ptr ds:[eax]
        mov ecx,0x610
        sub ebx,ecx
        xor ebx,ecx
        mov dword ptr ds:[eax],ebx      
    }
}

int getRegisterCode(int machineCode){
    return (machineCode-0x610) ^ 0x610;
}

int main(int argc, char* argv[])
{
    int* a=(int*)malloc(sizeof(int));
    int* b=(int*)malloc(sizeof(int));
    int input;
    getRand(a);
    encode(a);
    printf("your machine code is:%d\n",*a);
    printf("please enter the register code\n");
    *b=*a;  
    printf("%d\n",getRegisterCode(*a));
    scanf("%d",&input);
    decode(b);
    if(*b==input){
        MessageBoxA(0,"success!\n","nice!",0);
    }else{
        MessageBoxA(0,"fail!\n","try again!",0);
    }
    return 0;
}

补充

本人的逆向水平有限,属于萌新水准,文中可能会有谬误之处,望大家指正

这篇文章希望能帮助到想要入门的萌新们

这里的CrackMe为本人用VC6.0编写,为了方便理解并没有采用很复杂的加密

对于CrackMe的加壳也是使用论坛虚拟机中集成的ORiEN v2.12

image-20210320184629876

CrackMe程序本身肯定是没有问题的,但鉴于其跑在虚拟机中,怕会被其它病毒感染,所以想要研究的小伙伴们可以选择自己编译一份(正向代码在前面已经给出),然后自己加壳;或者也可以下载我提供的文件,但建议在虚拟机中运行

提供的附件包含了加壳程序CrackMe.exe、转储文件dumped.exe、脱壳后的程序dumped_.exe

crackMe.zip

213.39 KB, 下载次数: 62, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 27威望 +1 吾爱币 +48 热心值 +24 收起 理由
大明星 + 1 用心讨论,共获提升!
skyward + 1 + 1 谢谢@Thanks!
acing + 1 感谢您的宝贵建议,我们会努力争取做得更好!
IMivory + 1 + 1 谢谢@Thanks!
sam喵喵 + 1 谢谢@Thanks!
执骨哟 + 1 + 1 写的真的细,逐步逐句的分析翻译,看的热血沸腾,不多说,上手!
梦爱 + 1 + 1 用心讨论,共获提升!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
犹豫就会白给 + 1 + 1 用心讨论,共获提升!
debug_cat + 1 + 1 来支持大佬的文章,收藏打卡必备
By阿清 + 1 + 1 我很赞同!
winddyj + 1 + 1 用心讨论,共获提升!
Razuri + 1 + 1 谢谢@Thanks!
朱朱你堕落了 + 3 + 1 现在的萌新起点这么高吗?
bjxiaoyao + 1 + 1 谢谢@Thanks!
YMYS + 1 + 1 我很赞同!
solly + 1 + 1 用心讨论,共获提升!
小笨菜鸟 + 1 + 1 谢谢@Thanks!
cool147852369 + 1 用心讨论,共获提升!
Carrot-Chou + 3 + 1 我很赞同!
mo211683 + 1 + 1 大佬又更新了,来支持一下
shiniankanchai + 1 + 1 用心讨论,共获提升!
鸠山一茶 + 1 + 1 用心讨论,共获提升!
qianshang666 + 1 + 1 谢谢@Thanks!
XXE + 1 热心回复!
泰罗 + 1 + 1 用心讨论,共获提升!
国际豆哥 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

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

 楼主| lyl610abc 发表于 2021-4-5 19:38
sam喵喵 发表于 2021-4-5 19:22
字里行间透露出老鸟的气息,大佬脱壳也出个系列吧

脱壳系列以后会考虑的,下个系列已经暂定为:保护模式了
有太多坑要填了
 楼主| lyl610abc 发表于 2021-3-22 12:36
yamika 发表于 2021-3-22 09:21
萌新问下local.2的地址ebp-0x8是怎么得到的啊,看好像没写

帖子中在local3那里已经写了,双击反汇编语句即可看到
[local.3]:

这里的[local.3]其实是OD为了方便查看显示的,其实际的汇编代码为(双击反汇编语句即可看到
zouhuangfa 发表于 2021-3-20 19:10
 楼主| lyl610abc 发表于 2021-3-20 19:11
zouhuangfa 发表于 2021-3-20 19:10
能写注册机就不算萌新了

自己写的程序写得出注册机很正常吧
不是逆向别人的
国际豆哥 发表于 2021-3-20 19:11
我来了来了!!看见这位大佬我酒店进来啦
aonima 发表于 2021-3-20 19:16
学习学习
你猫临死前 发表于 2021-3-20 19:21
膜拜,现在萌新的定义都那么高了吗
时不予 发表于 2021-3-20 20:26
厉害厉害,大佬你这是太谦虚了
qianshang666 发表于 2021-3-20 20:43
你管这叫萌新?侮辱我?
大表哥1991 发表于 2021-3-20 20:59
你管这叫萌新?侮辱我?
 楼主| lyl610abc 发表于 2021-3-20 21:15
qianshang666 发表于 2021-3-20 20:43
你管这叫萌新?侮辱我?

逆向脱壳破解这块我确实算是萌新
看我以前的帖子就知道之前主要都是针对游戏安全和驱动开发的学习,对于脱壳确实只能算入门
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-23 02:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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