反调试技术及实例(一)
本帖最后由 云在天 于 2019-3-1 22:46 编辑# 反调试技术及实例(一)
## 前言
就软件而言,逆向工程是研究一个程序的过程,以获得关于它如何工作以及它使用了什么算法,通常大众认为逆向工程被用于非法活动。 例如,生成一个软件的注册机,不经过服务器验证使用软件功能等。
本文考虑了目前流行的Windows下的反调试方法。 想要完全保护软件不被破解是不可能的,各种反逆向的技术的也只是使过程尽可能复杂化(当然这就是大佬们的工作了,我这种小白也就只是说说)。
## 何为反调试技术(Anti-Debug)
当您将调试器载入或附加程序时,它会生成一个中断(Interrupt)。 程序可以用特定的程序来查找这个中断,并在找到它时自动退出。说白了就是不让你很轻松的调试。
## Warning
要理解这里提到的示例和技术,需要汇编知识、一些 WinDbg 经验等。虽然我也没有,如感到不适,请关闭。
## 常用函数
### IsDebuggerPresent
这可能是最简单的反调试方法了,调用 IsDebuggerPresent 函数。 MS给的解释是
>Determines whether the calling process is being debugged by a user-mode debugger.
大体的意思是确定一个进程是否被用户模式下的调试器调试。
下面的代码展示了一个基本保护的例子:
```
int main()
{
if (IsDebuggerPresent())
{
cout <<"兄弟,关了你的调试器吧" <<endl;
exit(-1);
}
```
运行截图如下
!(https://i.imgur.com/6WqP7j6.png)
我们看 IsDebuggerPresent 函数的内部,我们会发现以下代码:
!(https://i.imgur.com/phR61Nz.png)
```
75E0C3F0 >64:A1 30000000mov eax,dword ptr fs:
75E0C3F6 0FB640 02 movzx eax,byte ptr ds:
75E0C3FA C3 retn
```
我们可以看到它把一个未知结构(相对于 fs 段的30偏移)传给了eax,然后读取了这个结构的2个偏移。 经过相关资料我们知道这个结构是PEB(Process Environment Block),如果我们看看 PEB 中的2个偏移量,我们会发现 BeingDebugged 字段:
```
typedef struct _PEB {
BYTE Reserved1;
BYTE BeingDebugged;
BYTE Reserved2;
PVOID Reserved3;
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERSProcessParameters;
PVOID Reserved4;
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9;
BYTE Reserved10;
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11;
PVOID Reserved12;
ULONG SessionId;
} PEB, *PPEB;
```
换句话说,IsDebuggerPresent 函数读取 BeingDebugged 这个字段的值。 如果正在调试进程,则返回1,否则返回0。
#### 怎么反反调试
1. 打开OD,载入程序,切勿运行
2. Ctrl+G,搜索IsDebuggerPresent并在所有调用这个函数的地方下断点,如图!(https://i.imgur.com/attO6K0.png)
!(https://i.imgur.com/iCfrDfv.png)
3. F9运行程序,程序会断到这个地方,F8单步一直到它进入程序的领空,如图。**PS:这里论坛的OD做了些修改,所以这个函数在论坛OD里并没有作用,我换成了x32dbg。**!(https://i.imgur.com/qdcv7TO.png)
我们可以观察到右上角EAX是1,然后这里把它修改成0,然后运行就可以了
!(https://i.imgur.com/cL6JQQU.png)
### CheckRemoteDebuggerPresent
与 IsDebuggerPresent 函数不同,CheckRemoteDebuggerPresent 检查一个进程是否正在被另一个独立进程调试。下面是一个例子。
```
BOOL isDebuggerPresent = false;
if (CheckRemoteDebuggerPresent(GetCurrentProcess(), &isDebuggerPresent))
{
if (isDebuggerPresent)
{
cout <<"兄弟,关了你的调试器吧" <<endl;
system("Pause");
exit(-1);
}
}
```
我们继续看这个函数的内部,它调用了NtQueryInformationProcess,如图
!(https://i.imgur.com/J061exe.png)
这个函数的作用是检索有关指定进程的信息(Retrieves information about the specified process.)
我们看下这个函数的结构
```
__kernel_entry NTSTATUS NtQueryInformationProcess(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength
);
```
我们关注下这个函数里的ProcessInformationClass中的ProcessDebugPort这个值,它检索DWORD_PTR值,该值是进程的调试器的端口号。 非零值表示该进程正在ring3调试器的控制下运行。
### 怎么反反调试
* 如果你是个编程大牛,可以Hook **NTQueryInformationProcess**这个函数的返回值,这里我贴一个大牛的Demo
```
#include <Windows.h>
#include "mhook.h"
typedef NTSTATUS(NTAPI *pfnNtQueryInformationProcess)(
_In_ HANDLE ProcessHandle,
_In_ UINT ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
const UINT ProcessDebugPort = 7;
pfnNtQueryInformationProcess g_origNtQueryInformationProcess = NULL;
NTSTATUS NTAPI HookNtQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ UINT ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
)
{
NTSTATUS status = g_origNtQueryInformationProcess(
ProcessHandle,
ProcessInformationClass,
ProcessInformation,
ProcessInformationLength,
ReturnLength);
if (status == 0x00000000 && ProcessInformationClass == ProcessDebugPort)
{
*((PDWORD_PTR)ProcessInformation) = 0;
}
return status;
}
DWORD SetupHook(PVOID pvContext)
{
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
if (NULL != hNtDll)
{
g_origNtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
if (NULL != g_origNtQueryInformationProcess)
{
Mhook_SetHook((PVOID*)&g_origNtQueryInformationProcess, HookNtQueryInformationProcess);
}
}
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hInstDLL);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)SetupHook, NULL, NULL, NULL);
Sleep(20);
case DLL_PROCESS_DETACH:
if (NULL != g_origNtQueryInformationProcess)
{
Mhook_Unhook((PVOID*)&g_origNtQueryInformationProcess);
}
break;
}
return TRUE;
}
```
* 如果 你不是一个编程大牛,请参照IsDebuggerPresent的方法去操作,这里就不再展开说明了
## Todo
还有其他的一些检测调试器的技术,比如检测断点啊,检测进程之类的,这些会在以后的帖子里讲到,还希望大家多多关注一下
云在天
吾爱破解论坛
2019年02月28日23:05:11 找IsDebuggerPresent 跟 CheckRemoteDebuggerPresent 這二個,簡單易懂,
Hook 這個對小白還是有點難懂,慢慢想想看,謝謝樓主的教學。 苏紫方璇 发表于 2019-3-1 15:59
别的不说,先学了一个单词Waring
同意,我一直以为只有warning
最后一段代码看起来好难受... 顶大佬 。。。。。。 谢老师的无私奉献! 牛,学习了 别的不说,先学了一个单词Waring 感谢云大佬分享。插件帮我们跳过了很多东西,但这些还是必须掌握的基础{:301_1003:} {:1_921:}不错不错,简单明了!我这种小白也能看懂!感谢大牛的教程!希望有更多类似的教程! 感谢大牛的教程! 谢谢分享教程。