逆向工程与软件自我保护

实验相关背景介绍:

(侵权联系删除)

 

 

阶段:软件保护破解

方法一爆破(至少两种方式)

  1. 查找显示注册结果相关代码
  2. 查找注册码验证相关代码
  3. 修改程序跳转

方法二编写注册机

  1. 查找显示注册结果相关代码
  2. 查找注册码验证相关代码
  3. 根据注册码验证代码编写注册机

阶段:软件反动态调试分析

  1. 分析CrackMe1.exe是如何通过父进程检测实现反OllyDbg调试的
  2. 分析除父进程检测外,该程序用到的反动态调试技术

 

阶段:壳

  1. 加壳脱壳深入理解
  2. 尝试手动脱壳

阶段:软件保护破解

方法一爆破(至少两种方式)

 

 

 

  1.  查找显示注册结果相关代码

首先尝试输入Sssss 55555 错误,弹框Bad boy。那么bad boy有关的代码就是注册结果相关代码。

之后用OllyDbg打开crack.exe。

查找所有参考文本子串。

双击 good boy(bad boy 也可)。

就找到了注册结果相关代码。

2. 查找注册码验证相关代码

向上查看代码。

可以看到一段循环后,做cmp,然后jnz,选择是否跳转到Bad boy相关字段。(地址401107

还是正确,直接good boy

即为注册码验证相关代码。

3.修改程序跳转

爆破方法一:修改跳转条件

JNZ 改为JZ

OllyDbg修改相关代码,汇编,F9运行,查看效果。

F9运行

修改为jz。汇编

爆破完成

修改PE文件。

使用Lordpe 查看crack的文件信息。点击sections。 .text节在V偏移,文件中偏移都为1000,所以节偏移为0。之后下图给出了文件偏移地址的计算公式。

 

010Editor工具打开crack。  地址10F9  750C (机器指令),与OD一致

改为jz后可见机器码为 740C

pe文件,改为74

 

运行

爆破成功。

另一种找到地址的方式

OD

004010F9 右键

在下方数据窗口,右键查看可执行文件,可见地址10F9

爆破方法二:

修改比较方式。

由以上分析可知,crack程序验证时,cmp eaxecx,若不等(JNZ 401107)

那么我们修改为相等即可。直接cmp eax eax

 

爆破成功。

 

机器码为3BC0

pe文件的相应位置修改即可。

地址为000010F7.

方法二编写注册机

  1. 查找显示注册结果相关代码

同方法一,不再赘述。

  1. 查找注册码验证相关代码

同方法一,不再赘述。

  1. 根据注册码验证代码编写注册机

我们在上面找到了验证相关代码,现在通过动态调试分析具体的验证过程。

在XOR(异或)  EAX,EAX F2下断点,F9运行。

弹窗,此时输入了name abcdeserial 12345.

之后F7单步运行,观察寄存器值和堆栈信息,分析验证过程。

 

窗口右边有寄存器值。

F7单步运行

单步运行可见ECX值变为abcdeEAX清零,EDX00001908

ECX值变为61”a”ascii

这里cmp eaxebx ebx=5,是arg1的长度。每次循环eax+1,判断输入的是不是5

下方堆栈信息

通过单步运行,观察寄存器,汇编和堆栈可以分析出程序的保护方式是根据name计算serial,然后和用户输入的serial比较。

执行完CALL语句就将输入的字符串转化为整型

循环计算对应的注册码,并判断输入的用户名长度是否合法(必须是5),否则有提示。

Arg3存储输入的nameArg2(ECX)存储计算得到的注册码

Arg4存储输入的注册码,字符串转换为整型后存到EAX

然后就是CMP EAX ECX

计算过程用C++形式表示如下

#include<iostream>
#include<cstring>
using namespace std;

int main()
{
    cout<<"Enter the name"<<endl;
    string name;
    cin>>name;
    string arg3=name;
    int arg2=6408;
    int ebx=arg3.size();
    int ecx;
    for(int eax=0;eax<ebx;eax++)
    {
        arg2+=ebx;
        ecx=arg3[eax];
        ecx*=arg2;
        arg2=ecx;
    }
    cout<<(arg2^0xA9F9FA)<<endl;
    
    
}

编写相关程序,编译运行,输入五位字符,就会计算出相应的注册码。如图所示。

输入name aaaaa,计算出其serial 978652603.

我们在crack程序验证,确实如此。于是就生成了注册码生成机。

 

阶段:软件反动态调试分析

 

 

  1. 分析CrackMe1.exe是如何通过父进程检测实现反OllyDbg调试的

OllyDbg打开,查看调用树。

去地址40192C查看。

 

在这里下断点,开始调试。

Ntdll是获取父进程idpushedi

 

获取父进程名字    GetModuleFileNmeExA

这里有关闭句柄操作。

再往下

可以看到ARG2是作为比较的explorer的路径名称,而arg1即为上面获取到的当前打开craackme1的父进程的路径名称。

Pushedx后,call5027FE去进行比较。

5027FE

在第二个cmp处跳转

Jne 502829

Je没有jump

Jmp 00502705

00502705是大的循环,比较过程。

循环可以分为三个段。

前两个做大小写转换,第三个做比较

cmp19 判断是大写还是小写,是大写+20转化为小写

test eaxeax判断循环是否结束。结束跳转到502738

JE 相同的话跳转到2712继续循环比较后续字符 

注意看右侧的寄存器值

一开始edxcxxxx

一次比较后

c比较过了,就去掉。

比较不等,返回到call5027FE的下一条,进行后续操作。(这时已经判断出有问题了,父进程不是explorer

 2. 分析除父进程检测外,该程序用到的反动态调试技术

该程序用到的关键函数还有

作用分别为:

 

1.GetCurrentProcessId

GetCurrentProcessId(

VOID

);

说明:

获取当前进程的标示符(PID)

返回值:

返回一个标示符(PID)

库文件:kernel32.dll

2.OpenProcess

库文件:Kernel32.dll

OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。

函数原型

HANDLE OpenProcess(

DWORD dwDesiredAccess, 

BOOL bInheritHandle, 

DWORD dwProcessId

);

 

3.GetModuleFileNameExA  通过进程句柄获取进程文件名

DWORD GetModuleFileNameExA(

HANDLE hProcess,

HMODULE hModule,

LPTSTR lpstrFileName,

DWORD nsize

);

库文件:Kernel32.dll

4.CreateToolhelp32Snapshot

可以获取系统中正在运行的进程信息,线程信息

HANDLE WINAPI CreateToolhelp32Snapshot(

DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等

DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0

);

库文件:Kernel32.dll

5.Process32Next

是一个进程获取函数,当我们利用函数CreateToolhelp32Snapshot()获得当前运行进程的快照后,我们可以利用Process32Next函数来获得下一个进程的句柄。

BOOLWINAPIProcess32Next(

__inHANDLEhSnapshot,

__outLPPROCESSENTRY32lppe

);

库文件:Kernel32.dll

 

6.EnumWindows()

函数原型:

BOOL WINAPI EnumWindows(
_In_ WNDENUMPROC lpEnumFunc,
_In_ LPARAM lParam
);

lpEnumFunc: 应用程序定义的回调函数的指针

lParam:         传递给回调函数的应用程序定义的值

MSDN中对EnumWindows的解释:

Enumerates all top-level windows on the screen by passing the handle to each window, in turn, to an application-defined callback function. EnumWindows continues until the last top-level window is enumerated or the callback function returns FALSE.

即:

枚举屏幕上的所有的顶层窗口,轮流地将这些窗口的句柄传递给一个应用程序定义的回调函数。EnumWindows会一直进行下去,直到枚举完所有的顶层窗口,或者回调函数返回了FALSE.

 

库文件:user32.dll

7.GetWindowTextA

GetWindowTextA将指定窗口的标题栏(如果有的话)的文字拷贝到缓冲区内。如果指定的窗口是一个控件(control),那么该控件的text属性将被拷贝(到缓冲区)。但是,GetWindowText 不能取回其他程序中控件的text。

int WINAPI GetWindowText(

_In_ HWND hWnd,

_Out_ LPTSTR lpString,

_In_ int nMaxCount

);

库文件:User32.dll

对该程序反动态调试技术的理解:

该程序被打开后,通过windows api函数获取父进程的路径,存储到寄存器中,然后与explorer.exe(预期的父进程名)的路径作比较,比较时先转换为小写。之后比较相同,则认为没有被动态调试,否则认为是调试器。之后进行操作阻止调试。

阶段:壳

  1. 加壳脱壳深入理解

PEid检测CrackmeUPX,可以发现该程序是UPX方法加壳的。

OllyDbg打开运行,查看壳的加载过程。

409BF0pushad保存现场环境。

单步运行查看右下角堆栈信息。

将信息保存到12FFA4开始的位置,当壳加载完成退出后,在弹出保存好的信息。

F7单步运行查看加载壳的具体过程

完成后Popad弹出保存好的信息。

猜你喜欢

转载自www.cnblogs.com/lqerio/p/10922113.html