美文网首页
每日总结-第四十九天-反调试

每日总结-第四十九天-反调试

作者: SamiraG | 来源:发表于2020-06-12 00:18 被阅读0次

参考链接

反调试技术总结
白话windows之四 异常处理机制(VEH、SEH、TopLevelEH...)
动态反调试技术
静态反调试技术
2020 空指针 5月RE公开赛

静态反调试

查看PEB.BeingDebugged的值

IsDebuggerPresent()的实现方式,如果该值为1,则表示当前进程被调试。
PEB访问方法:

  1. 直接获取PEB地址
MOV EAX, DWORD PTR FS:[30] ; FS[30] = address of PEB
  1. 先获取TEB地址,在通过ProcessEnvironmentBlock成员(+30偏移)获取PEB地址
MOV EAX, DWORD PTR FS:[18] ; FS[18] = address of TEB
MOV EAX, DWORD PTR DS:[EAX+30] ; DS[EAX+30] = address of PEB

PEB.Ldr(PEB+0xC)

(仅限在WindowsXP系统,之后的系统无法使用,且以附加形式将无法在堆内存中出现上述标识)
调试进程时,其堆内存会出现一些奇怪的标志,表示它正处于被调试状态。最醒目的是,未使用的堆内存区域全部填充着0xEEFEEEFE,这证明该进程正在被调试。
PEB.Ldr指向_PEB_LDR_DATA结构体的指针,而_PEB_LDR_DATA结构体结构体又恰好在堆内存中创建,所以扫描该区域是否存在0xEEFEEEFE区域即可判断。

  • 破解方法:将相关0xEEFEEEFE区域全部覆盖为NULL即可。

PEB.Process Heap(PEB+0x18)

(仅限在WindowsXP系统,且以附加形式将无法在堆内存中出现上述特征)

PEB.Process Heap成员既可以直接从PEB结构体获取,也可以从GetProcessHeap()API获取。

HEAP(PEB+0x18)地址(黄色方框)为0x00790000


我们访问0x00790000
进程正常运行(非调试运行)时,Heap.Flags成员(+0xC)的值为0x2,Heap.ForceFlags成员(+0x10)的值为0x0.
进程处于被调试状态时,这些值也会随之改变。比较这些值即可判断。
  • 破解方法:将Heap.Flags与Heap.ForceFlags的值分别重新设置为2与0即可。

NtGlobalFalg(PEB+0x68)

(将运行中的进程附加到调试器的时候,NtGlobalFalg值不变)
调试进程时,PEB.NtGlobalFalg成员(+0x68)的值会被设置为0x70.所以,检测该成员的值即可判断进程是否处于被调试阶段.
NtGlobalFalg 0x70是由下列的flags值进行位或(bit OR)运算的结果

FLG_HEAP_ENABLE_TAIL_CHECK(0X10)
FLG_HEAP_ENABLE_FREE_CHECK(0X20)
FLG_HEAP_VALIDATE_PARAMETERS(0X40)

  • 破解方法:设置PEB.NtGlobalFalg为0即可。

NtQueryInformationProcess()系列

函数原型

__kernel_entry NTSTATUS NtQueryInformationProcess(
  IN HANDLE           ProcessHandle,
  IN PROCESSINFOCLASS ProcessInformationClass,
  OUT PVOID           ProcessInformation,
  IN ULONG            ProcessInformationLength,
  OUT PULONG          ReturnLength
);

ProcessInformationClass为enum类型。其中,与调试器探测有关的成员为

  • ProcessDebugPort(0x7)
  • ProcessDebugObjectHandle(0x1E)
  • ProcessDebugFlags(0x1F)

ProcessDebugPort(0x7)

进程处于非调试阶段时,dwDebugPort的值为0,处于调试阶段,则为0xFFFFFFFF
例如

DWORD dwDebugPort=0;
pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, &dwDebugPort, sizeof(dwDebugPort), NULL);
if(dwDebugPort != 0)
        printf("being debuged\n");

即CheckRemoteDebuggerPresent()函数的实现方法,该函数还可以检测其他进程是否处于被调试状态。

ProcessDebugObjectHandle(0x1E)

如果进程处于被调试状态,则调试对象句柄存在,反之,该句柄值为NULL
例如

HANDLE hDebugObject = NULL;
pNtQueryInformationProcess(GetCurrentProcess(),(PROCESSINFOCLASS)30 , &hDebugObject, sizeof(hDebugObject), NULL);
if(hDebugObject != null)
        printf("being debuged\n");

API文档头文件中的ProcessInformationClass成员中找不到此值,故直接写入30作为参数。当然,也可以自己声明好PROCESSINFOCLASS。

ProcessDebugFlags(0x1F)

函数的第二个参数设置为ProcessDebugFlags(0x1F)后,调用函数后,通过第三个参数即可获取调试标志的值。如果为0,则进程处于被调试状态;若为1,则进程处于非调试状态。

BOOL bDebugFlag = TRUE;
    pNtQueryInformationProcess(GetCurrentProcess(),
                               ProcessDebugFlags,
                               &bDebugFlag,
                               sizeof(bDebugFlag),
                               NULL);
    printf("NtQueryInformationProcess(ProcessDebugFlags) = 0x%X\n", bDebugFlag);
    if( bDebugFlag == 0x0  )  printf("  => Debugging!!!\n\n");
    else                      printf("  => Not debugging...\n\n");

注意,此处BOOL为大写,即与int等价(如果用了bool,会导致结果错误)

NtQuerySystemInformation()

API文档
函数原型

__kernel_entry NTSTATUS NtQuerySystemInformation(
  IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
  OUT PVOID                   SystemInformation,
  IN ULONG                    SystemInformationLength,
  OUT PULONG                  ReturnLength
);

  • SystemInformationClass是枚举类型,其成员SystemKernelDebuggerInformation的值为0x23
  • 向函数第一个参数传入0x23,通过获取的第二个参数SystemInformation结构体的DebuggerEnabled和DebuggerInfo的值即可判断。
  • 注:当前(2018.12.26)的API文档中没有了SystemInformation结构体的定义,官方说明NtQuerySystemInformation可能将在未来的Windows版本中发生改变。
  • 破解方法:windows7中在命令行窗口(管理员模式)执行bcdedit /debug off即可。

NtQueryObject()

系统中的调试器调试进程的时候,会创建一个调试对象类型的内核对象。检测该对象是否存在即可判断是否有进程正在被调试。

ZwSetInformationThread()

  • 通过设置当前线程的信息来将自身从调试器中分离出来。
  • 原理:隐藏当前线程,使调试器无法再收到该线程的调试事件。

DebugActiveProcessStop()

用于分离调试器和被调试进程。

动态反调试

异常

  1. SEH
  2. SetUnhandledExceptionFilter()

进程中发生异常,若SEH未处理或者注册的SEH不存在,此时会调用执行系统的kernel32!UnhandledExceptionFilter()API.该函数内部会运行系统的最后一个异常处理器(名为Top Level Exception Filter或Last Exception Filter).系统最后的异常处理器通常会弹出错误消息框,然后终止进程运行。
kernel32!UnhandledExceptionFilter()API内部调用了ntdll!NtQueryInformationProcess(ProcessDebugPort)这个API(静态反调试),来判断是否正在调试进程。若进程正常运行(非调试状态),则运行系统最后的异常处理器,否则将异常派送给调试器。通过kernel32!SetUnhandledExceptionFilter可以修改系统最后的异常处理器。
基于异常的反调试技术中,通常先故意触发异常,然后在新注册的Last Exception Filter内部判断进程是正常运行还是调试运行,并根据判断结果修改EIP。

Timing Check

在调试器中逐行跟踪代码比程序正常运行耗费的时间要长很多,Timing Check技术通过计算运行的时间差异来判断进程是否处于被调试状态。

  1. 时间间隔测量

测量时间间隔的方法有很多,例如
基于计数器

RDTSC (汇编指令)
kernel32!QueryPerformanceCounter() / ntdll!NtQueryPerformanceCounter()
kernel32!GetTickCount()

基于时间

timeGetTime()
__ftime()

计数器的精准程度从高到低:
RDTSC>NtQueryPerformanceCounter()>GetTickCount()

  1. RDTSC

x64CPU中存在一个名为TSC(Time Stamp Counter时间戳计数器)的64位寄存器。RDTSC这个汇编指令将TSC值读入EDX:EAX寄存器(高32位被保存到EDX,低32位被保存到EAX)

陷阱标志

陷阱标志指EFLAGS寄存器的第九个(Index 8)比特位, Trap Flag,(在x64dbg中表示为TF)
TF设置为1后,CPU将进入单步执行模式。单步执行模式中,CPU执行1条指令后即触发EXCEPTION_SINGLE_STEP异常,然后TF会自动清零(0)。
INT 2D
在调试模式下执行完INT 2D后,下一条指令的第一个字节将被调试器忽略。(od的bug?)(若设置TF为1后再执行INT 2D,则不会忽略)

0xCC探测

API断点0xCC检测

比较校验和

异常处理

SEH: 结构化异常处理
VEH: 向量化异常处理
TopLevelEH:顶层异常处理

EXCEPTION_EXECUTE_HANDLER :该异常被处理。从异常处下一条指令继续执行
EXCEPTION_CONTINUE_SEARCH:不能处理该异常,让别人处理它吧
EXCEPTION_CONTINUE_EXECUTION:该异常被忽略。从异常处处继续执行

//调试器返回值:
DBG_CONTINUE : 等同于EXCEPTION_CONTINUE_EXECUTION
DBG_EXCEPTION_NOT_HANDLED :等同于EXCEPTION_CONTINUE_SEARCH

异常处理器处理顺序流程:

  1. 交给调试器(进程必须被调试)
  2. 执行VEH
  3. 执行SEH
  4. TopLevelEH(进程被调试时不会被执行)
  5. 交给调试器(上面的异常处理都说处理不了,就再次交给调试器)
  6. 调用异常端口通知csrss.exe
// exception.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <stdio.h>
#include <windows.h>
#include "atlstr.h" 

//先介绍一下返回值的意思
//EXCEPTION_EXECUTE_HANDLER         //该指令异常被处理。从下一条指令继续执行
//EXCEPTION_CONTINUE_SEARCH         //不能处理该异常,让别人处理它吧
//EXCEPTION_CONTINUE_EXECUTION      //该指令被忽略。从该指令处继续执行


void ShowExceptionInfo(PEXCEPTION_POINTERS pExcepInfo)
{

}
LONG ShowSelectMessageBox(TCHAR* pTitle)
{
    int nRet = MessageBox(0, _T("--------\n我要“认领”该异常?\n--------\nYES:认领该异常。\n\nNO: 交给别人处理(return EXCEPTION_CONTINUE_SEARCH)"), pTitle, MB_YESNO);
    if (nRet != IDYES)
    {//让别人处理
        _tprintf(_T("[EH.Exe] [SELE] Select EXCEPTION_CONTINUE_SEARCH\n"));
        return EXCEPTION_CONTINUE_SEARCH;
    }
    nRet = MessageBox(0, _T("--------\n是“忽略”还是“处理”该异常?\n--------\nYES:忽略,从该指令处继续执行(return EXCEPTION_CONTINUE_EXECUTION)。\n\nNO: 处理,从下一条指令继续执行(return EXCEPTION_EXECUTE_HANDLER)"), pTitle, MB_YESNO);
    if (nRet != IDYES)
    {//处理
        _tprintf(_T("[EH.Exe] [SELE] Select EXCEPTION_EXECUTE_HANDLER\n"));
        return EXCEPTION_EXECUTE_HANDLER;
    }

    //忽略
    _tprintf(_T("[EH.Exe] [SELE] Select EXCEPTION_CONTINUE_EXECUTION\n"));
    return EXCEPTION_CONTINUE_EXECUTION;
}
LONG NTAPI FirstVectExcepHandler(PEXCEPTION_POINTERS pExcepInfo)
{
    TCHAR* pTitle = _T("*首个* VEH异常处理器");
    _tprintf(_T("[EH.Exe] [VEH][1] in \n"));
    LONG nRet = ShowSelectMessageBox(pTitle);
    if (nRet == EXCEPTION_EXECUTE_HANDLER)
    {
        _tprintf(_T("[EH.Exe] [VEH][1] EXCEPTION_EXECUTE_HANDLER 标志在VEH中无效!,所以第二个VEH被调用。\n"));
    }
    if (nRet == EXCEPTION_CONTINUE_EXECUTION)
    {
        if (MessageBox(0, _T("jmp int3?(跳过INT3指令,否则还会被断下)"), _T("是否修正到下一条指令执行"), MB_YESNO) == IDYES)
        {
            pExcepInfo->ContextRecord->Eip += 1;//跳过int3
            _tprintf(_T("[EH.Exe] [VEH][1] 该异常被处理。 且:jmp int3\n"));
        }
        else
        {
            _tprintf(_T("[EH.Exe] [VEH][1] 该异常被处理\n"));
        }
    }
    _tprintf(_T("[EH.Exe] [VEH][1] out\n"));
    return nRet;
}

LONG NTAPI LastVectExcepHandler(PEXCEPTION_POINTERS pExcepInfo)
{
    TCHAR* pTitle = _T("*最后* VEH异常处理器");
    _tprintf(_T("[EH.Exe] [VEH][2] in \n"));
    LONG nRet = ShowSelectMessageBox(pTitle);
    if (nRet == EXCEPTION_EXECUTE_HANDLER)
    {
        _tprintf(_T("[EH.Exe] [VEH][2] EXCEPTION_EXECUTE_HANDLER 标志在VEH中无效!,所以SEH被调用。\n"));
    }
    if (nRet == EXCEPTION_CONTINUE_EXECUTION)
    {
        if (MessageBox(0, _T("jmp int3?(跳过INT3指令,否则还会被断下)"), _T("是否修正到下一条指令执行"), MB_YESNO) == IDYES)
        {
            pExcepInfo->ContextRecord->Eip += 1;//跳过int3
            _tprintf(_T("[EH.Exe] [VEH][2] 该异常被处理。 且:jmp int3\n"));
        }
        else
        {
            _tprintf(_T("[EH.Exe] [VEH][2] 该异常被处理\n"));
        }
    }
    _tprintf(_T("[EH.Exe] [VEH][2] out \n"));
    return nRet;
}

LONG NTAPI TopLevelExcepFilter(PEXCEPTION_POINTERS pExcepInfo)
{
    TCHAR* pTitle = _T("*顶级* 异常处理器");
    _tprintf(_T("[EH.Exe] [TOP] in \n"));
    LONG nRet = ShowSelectMessageBox(pTitle);
    _tprintf(_T("[EH.Exe] [TOP] out \n"));;
    return nRet;
}

LONG FirstSEHer(PEXCEPTION_POINTERS pExcepInfo)
{
    TCHAR* pTitle = _T("第一个SEH处理器");
    _tprintf(_T("[EH.Exe] [SEH][1] in \n"));
    LONG nRet = ShowSelectMessageBox(pTitle);
    _tprintf(_T("[EH.Exe] [SEH][1] out \n"));
    return nRet;
}
LONG SecondSEHer(PEXCEPTION_POINTERS pExcepInfo)
{
    TCHAR* pTitle = _T("第二个SEH处理器");
    _tprintf(_T("[EH.Exe] [SEH][2] in \n"));
    LONG nRet = ShowSelectMessageBox(pTitle);
    _tprintf(_T("[EH.Exe] [SEH][2] out \n"));;
    return nRet;
}

LONG ThirdSEHer(PEXCEPTION_POINTERS pExcepInfo)
{
    TCHAR* pTitle = _T("第三个SEH处理器");
    _tprintf(_T("[EH.Exe] [SEH][3] in \n"));
    LONG nRet = ShowSelectMessageBox(pTitle);
    _tprintf(_T("[EH.Exe] [SEH][3] out \n"));;
    return nRet;
}

void ExcepFunction()
{
    __try
    {
        __try
        {
            _tprintf(_T("[EH.Exe] *[CALL] int 3\n"));
            __asm int 3;
        }
        __except (FirstSEHer(GetExceptionInformation()))
        {
            _tprintf(_T("[EH.Exe] [SEH][1] 被俺处理了~(只有返回EXCEPTION_EXECUTE_HANDLER才会走到这里)\n"));
        }
    }
    __except (SecondSEHer(GetExceptionInformation()))
    {
        _tprintf(_T("[EH.Exe] [SEH][2] 被俺处理了(只有返回EXCEPTION_EXECUTE_HANDLER才会走到这里)\n"));
    }
}

DWORD __stdcall ExcepThread(LPVOID lpThreadParameter)
{
    _tprintf(_T("[EH.Exe] [ExcepThread] in\n"));
    __try
    {
        ExcepFunction();
    }
    __except (ThirdSEHer(GetExceptionInformation()))
    {
        _tprintf(_T("[EH.Exe] [SEH][3] 被俺处理了(只有返回EXCEPTION_EXECUTE_HANDLER才会走到这里)\n"));
    }
    _tprintf(_T("[EH.Exe] [ExcepThread] out\n"));
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    _tprintf(_T("[EH.Exe] Add VEH.\n"));
    AddVectoredExceptionHandler(1, &FirstVectExcepHandler);
    _tprintf(_T("[EH.Exe] Add VEH.\n"));
    AddVectoredExceptionHandler(0, &LastVectExcepHandler);
    _tprintf(_T("[EH.Exe] Add Top LEF.\n"));
    SetUnhandledExceptionFilter(&TopLevelExcepFilter);

    HANDLE hThread = CreateThread(NULL, 0, &ExcepThread, NULL, 0, NULL);
    if (hThread)
    {
        WaitForSingleObject(hThread, INFINITE);
    }
    _tprintf(_T("[EH.Exe] 进程退出\n"));
    return 0;
}

例子

空指针六月re公开赛这道题用了很多反调试的技术。

  1. TLS回调函数中进行反调试检测

TLS回调函数是指,每当创建/终止进程的线程时,会自动调用执行的函数。创建或终止某线程时,TLS回调函数都会自动调用执行,前后共两次。

int __stdcall TlsCallback_0(int a1, int a2, int a3)
{
  HMODULE v3; // eax
  FARPROC v4; // eax
  int result; // eax
  int v6; // [esp+DCh] [ebp-8h]

  v6 = 0;
  v3 = GetModuleHandleW(L"ntdll.dll");
  v4 = GetProcAddress(v3, "NtQueryInformationProcess");
  result = ((int (__stdcall *)(signed int, signed int, int *, signed int, _DWORD))v4)(-1, 7, &v6, 4, 0);
  if ( result )
    return result;
  if ( v6 )
  {
    result = TlsIndex;
    *(_DWORD *)(*(_DWORD *)(__readfsdword(0x2Cu) + 4 * TlsIndex) + 4) = 0xDEADBEEF;
  }
  else
  {
    result = TlsIndex;
    *(_DWORD *)(*(_DWORD *)(__readfsdword(0x2Cu) + 4 * TlsIndex) + 4) = 0;
  }
  return result;
}

NtQueryInformationProcess,等于7检测ProcessDebugPort。

  1. NtGlobalFalg(PEB+0x68)

调试进程时,PEB.NtGlobalFalg成员(+0x68)的值会被设置为0x70

__int64 sub_402740()
{
  int v0; // edx
  __int64 v1; // ST00_8
  signed int i; // [esp+D0h] [ebp-6Ch]
  int v4[24]; // [esp+DCh] [ebp-60h]

  v4[22] = 0;
  v4[22] = *(_DWORD *)(__readfsdword(0x30u) + 0x68) & 0x70;
  v4[0] = 31;
  v4[1] = 43;
  v4[2] = 14;
  v4[3] = 87;
  v4[4] = 34;
  v4[5] = 64;
  v4[6] = 15;
  v4[7] = 7;
  v4[8] = 56;
  v4[9] = 32;
  v4[10] = 67;
  v4[11] = 98;
  v4[12] = 23;
  v4[13] = 45;
  v4[14] = 87;
  v4[15] = 63;
  v4[16] = 25;
  v4[17] = 74;
  v4[18] = 68;
  v4[19] = 88;
  if ( v4[22] != 23 )
  {
    for ( i = 0; i < 20; ++i )
      byte_452F80[i] = LOBYTE(v4[i]) ^ (23 - LOBYTE(v4[22]));
  }
  AddVectoredExceptionHandler(0, Handler);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
  1. GetTickCount

获取时间间隔

int sub_402AB0()
{
  __int16 v0; // STE8_2

  v0 = GetTickCount();
  return (((unsigned __int16)GetTickCount() - v0) & 0xFF00) >> 8;
}
  1. 校验和
    这里不仅是代码不能修改,也不能下断点,因为断点的0xcc也会被检测
int __cdecl sub_4029E0(unsigned __int8 *a1, unsigned int a2)
{
  int v3; // [esp+D0h] [ebp-8h]

  v3 = 0;
  while ( (unsigned int)a1 < a2 )
    v3 += *a1++;
  return v3;
}
__int64 sub_402470()
{
  signed int v0; // edx
  __int64 v1; // ST00_8
  signed int j; // [esp+D0h] [ebp-DCh]
  signed int i; // [esp+DCh] [ebp-D0h]
  int v5[24]; // [esp+E8h] [ebp-C4h]
  int v6; // [esp+150h] [ebp-5Ch]
  int v7; // [esp+15Ch] [ebp-50h]
  int v8; // [esp+168h] [ebp-44h]
  int v9; // [esp+174h] [ebp-38h]
  int v10; // [esp+180h] [ebp-2Ch]
  int v11; // [esp+18Ch] [ebp-20h]
  int v12; // [esp+198h] [ebp-14h]
  int v13; // [esp+1A4h] [ebp-8h]

  v13 = sub_402A30();
  v12 = sub_402AB0();
  v10 = sub_4029E0((unsigned __int8 *)sub_402B50 + 80, (unsigned int)sub_402B50 + 188);
  v9 = sub_4029E0((unsigned __int8 *)main + 16, (unsigned int)main + 206);
  v8 = sub_4029E0((unsigned __int8 *)sub_402E90 + 50, (unsigned int)sub_402E90 + 139);
  v7 = sub_4029E0((unsigned __int8 *)TopLevelExceptionFilter, (unsigned int)TopLevelExceptionFilter + 300);
  v6 = sub_4029E0((unsigned __int8 *)sub_402DC0 + 123, (unsigned int)sub_402DC0 + 136);
  v0 = (unsigned __int8)v8;
  v11 = (unsigned __int8)v6
      + (unsigned __int8)v8
      + (unsigned __int8)v7
      + (unsigned __int8)v10
      + (unsigned __int8)v9
      + (*(_DWORD *)(*(_DWORD *)(__readfsdword(0x2Cu) + 4 * TlsIndex) + 4) & 0xFF)
      + v12
      + v13;
  v5[0] = 31;
  v5[1] = 43;
  v5[2] = 14;
  v5[3] = 87;
  v5[4] = 34;
  v5[5] = 64;
  v5[6] = 15;
  v5[7] = 7;
  v5[8] = 56;
  v5[9] = 32;
  v5[10] = 67;
  v5[11] = 98;
  v5[12] = 23;
  v5[13] = 45;
  v5[14] = 87;
  v5[15] = 63;
  v5[16] = 25;
  v5[17] = 74;
  v5[18] = 68;
  v5[19] = 88;
  v5[20] = 90;
  v5[21] = 113;
  v5[22] = 57;
  v5[23] = 97;
  if ( v11 )
  {
    for ( i = 0; i < 24; ++i )
    {
      v0 = i;
      byte_452F80[i] = LOBYTE(v5[i]) ^ v11;
    }
  }
  else
  {
    for ( j = 0; j < 24; ++j )
    {
      LOBYTE(v0) = v5[j];
      byte_452F80[j] = v0;
    }
  }
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
  1. VEH
AddVectoredExceptionHandler(0, Handler);
  1. SetUnhandledExceptionFilter
unsigned int sub_402DC0()
{
  DWORD flOldProtect; // [esp+D0h] [ebp-2Ch]
  LPVOID lpAddress; // [esp+DCh] [ebp-20h]
  SIZE_T dwSize; // [esp+E8h] [ebp-14h]
  HMODULE v4; // [esp+F4h] [ebp-8h]

  v4 = GetModuleHandleW(0);
  SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TopLevelExceptionFilter);
  dwSize = 188;
  lpAddress = sub_402B50;
  dword_452FA0 = (int)sub_402B50;
  VirtualProtect(sub_402B50, 0xBCu, 0x40u, &flOldProtect);
  return sub_4032A0((int)lpAddress, dwSize, (int)&byte_452F90, 16);
}

相关文章

网友评论

      本文标题:每日总结-第四十九天-反调试

      本文链接:https://www.haomeiwen.com/subject/qhovtktx.html