软件保护破解
crack.exe,该软件逻辑是,输入正确的用户名便返回Good Boy!否则返回Bad Boy!
现在我们没有正确用户名,随意输几个值,看看软件返回结果

由于软件是判断输入内容的正确性,我们直接使用动态分析工具,分析他的跳转逻辑。我们可以查找其程序内所有Strings所在位置,并且观察哪些操作引用了该字符串,从而找到跳转部分


可以看到此处跳转逻辑使用的是jnz。那么我在不改变PE文件结构的情况下,可以颠倒其逻辑,改为je,应该就可以走到Good Boy!分支了
经过测试,确实成功弹出Good Boy!

将修改后的文件保存,运行之,随便输入数据,确实成功破解了


软件反动态调试分析
我们首先观察正常情况下运行的软件:首先会跳出CrackME的弹窗,点击以后会向用户显示hello,记下此时的软件操作

接下来我们尝试使用Ollydbg打开软件,看看他是否具有反动态调试功能,发现F9运行过后没有任何的窗口弹出,而是直接结束运行,因此判断该程序有反动态调试功能

遇到该功能,我们首先要想到,可能是通过判断父进程名字进行校验的。通常情况下,被我们直接运行的进程应该是由任务管理器explorer.exe打开的。但是通过调试器打开的软件其父进程为调试程序的名字。比如该例,软件父进程名字应该为ollydbg.exe
于是软件便可以通过判断父进程名字是否为explorer.exe或者是否为最常见的几个调试器,从而确定当前是否为调试状态
注意,如果你是通过Ollydbg中的File->Open打开的,那么可能会判定其父进程为explorer,可以直接拖动避免该问题
又由于软件如果想要获取父进程名字,必不可少的函数便是GetCurrentProcessId了,因此我们在此下断点,进行调试


接下来运行软件,发现第一个断点处操作如下。通过几个函数名,不难看出,此处是在检测程序运行的性能,可能是想通过调试器与主机中的性能差别来检测是否在调试器中运行

第二个断点处的操作如下,首先通过NtQueryInformationProcess分析当前进程的信息获取父进程有关的信息,然后再后续使用GetModuleFileName获取对应父进程的名字
// 对应的结构体(未公开,但已被逆向)
typedef struct _PROCESS_BASIC_INFORMATION {
PVOID Reserved1;
PVOID PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId; // 当前进程PID
ULONG_PTR InheritedFromUniqueProcessId; // 父进程PID
} PROCESS_BASIC_INFORMATION;
// 函数调用
NtQueryInformationProcess(
hProcess, // edi - 当前进程句柄
ProcessBasicInformation, // 0x0 - 查询基本信息
pInfoBuffer, // ecx - 指向接收缓冲区的指针
sizeof(PROCESS_BASIC_INFORMATION), // 0x18=24 字节(结构体大小)
pReturnLength // edx - 返回实际写入的字节数
);

获取名字以后将会对父进程名字进行比较,如果有不一样的部分直接退出循环。此时Sub eax,ecx不为0,因此后续不弹窗

基础手动脱壳
首先我们将程序放到PEID中进行分析,发现程序带有UDP壳

接下来我们进入到Ollydbg中对该函数的真正起始点进行分析,发现此处开始时使用了pushad,那么结束该段壳返回到main函数时,也必定会使用popad恢复堆栈

我们在执行过pushad后,在数据窗口中跟随esp(恢复堆栈时必然会调用该寄存器),此时在esp处设置一个硬件断点

然后执行,此时将自动运行到popad的后一条指令处。我们可以发现,正好应该是401000处的main函数

接下来我们对其脱壳,将入口点地址修改为1000

将脱完壳后的程序放到PEID中进行分析,果然已经可以去除壳了
