Sna1lGo

反调试——9——调试器原理

反调试——9——调试器原理

调试一个程序分两种情况:

1 打开这个程序。

2:这个程序已经是一个运行状态了,将其进程进行附加。

打开进程

通过打开运行进程方式来调试进程需要调用一个API:

BOOL CreateProcessA(
  LPCSTR                lpApplicationName,
  LPSTR                 lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL                  bInheritHandles,
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCSTR                lpCurrentDirectory,
  LPSTARTUPINFOA        lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);
//dwCreatetionFlags需要设置为DEBUG_PROCESS
//打开进程示例程序
    STARTUPINFOA sw{ 0 };
    PROCESS_INFORMATION pInfo{ 0 };
    auto retCP = CreateProcessA("F:\\Sublime Text 3\\sublime_text.exe",
        NULL,NULL,NULL,FALSE,
        DEBUG_PROCESS,NULL,NULL,&sw,&pInfo);
    if (retCP == 00)
    {
        cout << "打开进程失败" << endl;
        return;
    }

 

 

附加进程

通过DebugActiveProcess这个API来附加到进程。

BOOL DebugActiveProcess(
  DWORD dwProcessId
);

 

细节

无论是通过打开进程还是附加进程来实现调试,都只是开始调用的方式不一样,在调试器和操作系统之间的交互方式都是相同的。

创建了调试进程后接下来就是死循环等待调试事件

当调试进程时,被调试进程执行的一些操作事件将会被通知给调试器,比如dll的加载和卸载,thread的创建和销毁,异常信息等等。当这些事件需要被发送到调试器时,Windows内核将首先挂起进程中的所有线程,然后把发生的事件通知给调试器,等待调试器的处理。

调试器通过WaitForDebugEvent API来等待调试事件,调试事件被封装到了DEBUG_EVENT结构体中,调试器需要处理的就是循环接受调试事件然后处理DEBUG_EVENT结构体中传递过来的不同调试信息。

在发送事件event给调试器debugger时,被调试进程会被挂起,直到调试器调用了continueDebugEvent函数

利用调试器原理实现附加反调试

利用调试器的原理,我们可以通过创建一个调试模式下的进程,那么这个以调试模式创建的进程就不能被其它进程拿去调试了,因为它已经在被一个我们自己的进程以调试模式创建了。

#include<iostream>
#include<Windows.h>
using namespace std;
​
​
void TestDebugger()
{
    STARTUPINFOA sw{ 0 };
    PROCESS_INFORMATION pInfo{ 0 };
    auto retCP = CreateProcessA("E:\\test\\Debug\\02 CStaticText.exe",
        NULL, NULL, NULL, FALSE,
        DEBUG_PROCESS, NULL, NULL, &sw, &pInfo);
    if (retCP == 0)
    {
        cout << "打开进程失败" << endl;
        return;
    }
    while (TRUE)
    {
        DEBUG_EVENT debugEvent{ 0 };
        auto rDebugEvent = WaitForDebugEvent(&debugEvent, -1);
        if (rDebugEvent)
        {
            cout << debugEvent.dwDebugEventCode << endl;
            //dwDebugEventCode是用来区分不同事件的事件码,用来判断事件
        }
        ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId,
            DBG_CONTINUE);//在发送事件event给调试器debugger时,被调试进程会被挂起,直到调试器调用了continueDebugEvent函数
    }
}
​
int main()
{
​
    TestDebugger();
    system("pause");
    return 0;
}

 

然后来测试一下,这样启动后,是否还能被调试器附加上:

 

 

这样一来就不会被调试器附加上了。

 

 

相关文章: