本帖最后由 云在天 于 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);
}
运行截图如下
我们看 IsDebuggerPresent 函数的内部,我们会发现以下代码:
75E0C3F0 > 64:A1 30000000 mov eax,dword ptr fs:[0x30]
75E0C3F6 0FB640 02 movzx eax,byte ptr ds:[eax+0x2]
75E0C3FA C3 retn
我们可以看到它把一个未知结构(相对于 fs 段的30偏移)传给了eax,然后读取了这个结构的2个偏移。 经过相关资料我们知道这个结构是PEB(Process Environment Block),如果我们看看 PEB 中的2个偏移量,我们会发现 BeingDebugged 字段:
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;
换句话说,IsDebuggerPresent 函数读取 BeingDebugged 这个字段的值。 如果正在调试进程,则返回1,否则返回0。
怎么反反调试
-
打开OD,载入程序,切勿运行
-
Ctrl+G,搜索IsDebuggerPresent并在所有调用这个函数的地方下断点,如图
-
F9运行程序,程序会断到这个地方,F8单步一直到它进入程序的领空,如图。PS:这里论坛的OD做了些修改,所以这个函数在论坛OD里并没有作用,我换成了x32dbg。
我们可以观察到右上角EAX是1,然后这里把它修改成0,然后运行就可以了
CheckRemoteDebuggerPresent
与 IsDebuggerPresent 函数不同,CheckRemoteDebuggerPresent 检查一个进程是否正在被另一个独立进程调试。下面是一个例子。
BOOL isDebuggerPresent = false;
if (CheckRemoteDebuggerPresent(GetCurrentProcess(), &isDebuggerPresent))
{
if (isDebuggerPresent)
{
cout <<"兄弟,关了你的调试器吧" <<endl;
system("Pause");
exit(-1);
}
}
我们继续看这个函数的内部,它调用了NtQueryInformationProcess,如图
这个函数的作用是检索有关指定进程的信息(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 |