云在天 发表于 2019-3-1 15:06

反调试技术及实例(一)

本帖最后由 云在天 于 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

a48602 发表于 2019-3-1 17:15

找IsDebuggerPresent 跟 CheckRemoteDebuggerPresent 這二個,簡單易懂,
Hook 這個對小白還是有點難懂,慢慢想想看,謝謝樓主的教學。

KaQqi 发表于 2019-3-1 21:46

苏紫方璇 发表于 2019-3-1 15:59
别的不说,先学了一个单词Waring
同意,我一直以为只有warning

最后一段代码看起来好难受...

lcwxxf 发表于 2019-3-1 15:15

顶大佬 。。。。。。

Hnldxjh 发表于 2019-3-1 15:24

谢老师的无私奉献!

bioboom 发表于 2019-3-1 15:52

牛,学习了

苏紫方璇 发表于 2019-3-1 15:59

别的不说,先学了一个单词Waring

kikyoulin 发表于 2019-3-1 16:00

感谢云大佬分享。插件帮我们跳过了很多东西,但这些还是必须掌握的基础{:301_1003:}

haogl 发表于 2019-3-1 16:01

{:1_921:}不错不错,简单明了!我这种小白也能看懂!感谢大牛的教程!希望有更多类似的教程!

天舞宝轮 发表于 2019-3-1 16:42

感谢大牛的教程!

klamauk 发表于 2019-3-1 17:49

谢谢分享教程。
页: [1] 2 3 4
查看完整版本: 反调试技术及实例(一)