本帖最后由 梦幻的彼岸 于 2021-6-8 17:00 编辑
备注
原文地址:https://anti-debug.checkpoint.com/techniques/debug-flags.html
原文标题:Anti-Debug: Debug Flags
更新日期:2021年6月8日
此文后期:根据自身所学进行内容扩充
因自身技术有限,只能尽自身所能翻译国外技术文章,供大家学习,若有不当或可完善的地方,希望可以指出,用于共同完善这篇文章。
目录
- 调试标志寄存器
- 1. 使用Win32 API
- 1.1. IsDebuggerPresent()
- 1.2. CheckRemoteDebuggerPresent()
- 1.3. NtQueryInformationProcess()
- 1.3.1. ProcessDebugPort进程调试端口
- 1.3.2. ProcessDebugFlags进程调试标志寄存器
- 1.3.3. ProcessDebugObjectHandle进程调试对象句柄
- 1.4. RtlQueryProcessHeapInformation()
- 1.5. RtlQueryProcessDebugInformation()
- 1.6. NtQuerySystemInformation()
- 反制措施
- 2. 手工检查
- 2.1. PEB!BeingDebugged标志寄存器
- 2.2. NtGlobalFlag
- 2.3. 堆标志寄存器
- 2.4. 堆保护
- 反制措施
调试标志寄存器
系统表中的特殊标志寄存器,即停留在进程内存中的、由操作系统设置的标志寄存器,可以用来指示进程正在被调试。这些标志寄存器的状态可以通过使用特定的API函数或检查内存中的系统表来验证。
这些技术是恶意软件最常使用的。
1. 使用Win32 API
以下技术使用现有的API函数(WinAPI或NativeAPI),这些函数检查进程内存中的系统结构,以寻找表明进程现在正在被调试的特定标志寄存器。
1.1. IsDebuggerPresent()
函数kernel32!IsDebuggerPresent()确定当前进程是否被用户模式的调试器如OllyDbg或x64dbg调试。一般来说,该函数只检查进程环境块(PEB)的 BeingDebugged 标志寄存器。
如果正在调试进程,可以使用以下代码终止进程:
汇编代码:
[Asm] 纯文本查看 复制代码 call IsDebuggerPresent
test al, al
jne being_debugged
...
being_debugged:
push 1
call ExitProcess
C/C++ 代码:
[C++] 纯文本查看 复制代码 if (IsDebuggerPresent())
ExitProcess(-1);
1.2. CheckRemoteDebuggerPresent()
函数kernel32!CheckRemoteDebuggerPresent()检查一个调试器(在同一台机器的不同进程中)是否连接到当前进程。
C/C++ 代码:
[C++] 纯文本查看 复制代码 BOOL bDebuggerPresent;
if (TRUE == CheckRemoteDebuggerPresent(GetCurrentProcess(), &bDebuggerPresent) &&
TRUE == bDebuggerPresent)
ExitProcess(-1);
x86 汇编:
[Asm] 纯文本查看 复制代码 lea eax, bDebuggerPresent]
push eax
push -1 ; GetCurrentProcess()
call CheckRemoteDebuggerPresent
cmp [bDebuggerPresent], 1
jz being_debugged
...
being_debugged:
push -1
call ExitProcess
x86-64 汇编:
[Asm] 纯文本查看 复制代码 lea rdx, [bDebuggerPresent]
mov rcx, -1 ; GetCurrentProcess()
call CheckRemoteDebuggerPresent
cmp [bDebuggerPresent], 1
jz being_debugged
...
being_debugged:
mov ecx, -1
call ExitProcess
1.3. NtQueryInformationProcess()
函数ntdll!NtQueryInformationProcess()可以从一个进程中检索不同种类的信息。它接受一个ProcessInformationClass参数,该参数指定了你想得到的信息,并定义了ProcessInformation参数的输出类型。
1.3.1. ProcessDebugPort进程调试端口
可以使用ntdll!NtQueryInformationProcess()来检索进程的调试器端口号。有一个记录在案的类ProcessDebugPort,如果进程正在被调试,它会检索到一个等于0xFFFFFFFF(十进制-1)的DWORD值。
C/C++ 代码:
[C++] 纯文本查看 复制代码 typedef NTSTATUS (NTAPI *TNtQueryInformationProcess)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength
);
HMODULE hNtdll = LoadLibraryA("ntdll.dll");
if (hNtdll)
{
auto pfnNtQueryInformationProcess = (TNtQueryInformationProcess)GetProcAddress(
hNtdll, "NtQueryInformationProcess");
if (pfnNtQueryInformationProcess)
{
DWORD dwProcessDebugPort, dwReturned;
NTSTATUS status = pfnNtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugPort,
&dwProcessDebugPort,
sizeof(DWORD),
&dwReturned);
if (NT_SUCCESS(status) && (-1 == dwProcessDebugPort))
ExitProcess(-1);
}
}
x86 汇编:
[Asm] 纯文本查看 复制代码 lea eax, [dwReturned]
push eax ; ReturnLength
push 4 ; ProcessInformationLength
lea ecx, [dwProcessDebugPort]
push ecx ; ProcessInformation
push 7 ; ProcessInformationClass
push -1 ; ProcessHandle
call NtQueryInformationProcess
inc dword ptr [dwProcessDebugPort]
jz being_debugged
...
being_debugged:
push -1
call ExitProcess
x86-64 汇编:
[Asm] 纯文本查看 复制代码 lea rcx, [dwReturned]
push rcx ; ReturnLength
mov r9d, 4 ; ProcessInformationLength
lea r8, [dwProcessDebugPort]
; ProcessInformation
mov edx, 7 ; ProcessInformationClass
mov rcx, -1 ; ProcessHandle
call NtQueryInformationProcess
cmp dword ptr [dwProcessDebugPort], -1
jz being_debugged
...
being_debugged:
mov ecx, -1
call ExitProcess
1.3.2. ProcessDebugFlags进程调试标志寄存器
一个叫做EPROCESS的内核结构代表一个进程对象,它包含NoDebugInherit字段。这个字段的反值可以通过一个无文件记录的类ProcessDebugFlags(0x1f)来检索。因此,如果返回值为0,则说明有调试器存在。
C/C++ 代码:
[C++] 纯文本查看 复制代码 typedef NTSTATUS(NTAPI *TNtQueryInformationProcess)(
IN HANDLE ProcessHandle,
IN DWORD ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength
);
HMODULE hNtdll = LoadLibraryA("ntdll.dll");
if (hNtdll)
{
auto pfnNtQueryInformationProcess = (TNtQueryInformationProcess)GetProcAddress(
hNtdll, "NtQueryInformationProcess");
if (pfnNtQueryInformationProcess)
{
DWORD dwProcessDebugFlags, dwReturned;
const DWORD ProcessDebugFlags = 0x1f;
NTSTATUS status = pfnNtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugFlags,
&dwProcessDebugFlags,
sizeof(DWORD),
&dwReturned);
if (NT_SUCCESS(status) && (0 == dwProcessDebugFlags))
ExitProcess(-1);
}
}
x86 汇编:
[Asm] 纯文本查看 复制代码 lea eax, [dwReturned]
push eax ; ReturnLength
push 4 ; ProcessInformationLength
lea ecx, [dwProcessDebugPort]
push ecx ; ProcessInformation
push 1Fh ; ProcessInformationClass
push -1 ; ProcessHandle
call NtQueryInformationProcess
cmp dword ptr [dwProcessDebugPort], 0
jz being_debugged
...
being_debugged:
push -1
call ExitProcess
x86-64 汇编:
[Asm] 纯文本查看 复制代码 lea rcx, [dwReturned]
push rcx ; ReturnLength
mov r9d, 4 ; ProcessInformationLength
lea r8, [dwProcessDebugPort]
; ProcessInformation
mov edx, 1Fh ; ProcessInformationClass
mov rcx, -1 ; ProcessHandle
call NtQueryInformationProcess
cmp dword ptr [dwProcessDebugPort], 0
jz being_debugged
...
being_debugged:
mov ecx, -1
call ExitProcess
1.3.3. ProcessDebugObjectHandle进程调试对象句柄
当调试开始时,一个叫做 "debug object "的内核对象被创建。可以通过使用无文献记载的ProcessDebugObjectHandle(0x1e)类来查询这个句柄的值。
C/C++ 代码:
[C++] 纯文本查看 复制代码 typedef NTSTATUS(NTAPI * TNtQueryInformationProcess)(
IN HANDLE ProcessHandle,
IN DWORD ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength
);
HMODULE hNtdll = LoadLibraryA("ntdll.dll");
if (hNtdll)
{
auto pfnNtQueryInformationProcess = (TNtQueryInformationProcess)GetProcAddress(
hNtdll, "NtQueryInformationProcess");
if (pfnNtQueryInformationProcess)
{
DWORD dwReturned;
HANDLE hProcessDebugObject = 0;
const DWORD ProcessDebugObjectHandle = 0x1e;
NTSTATUS status = pfnNtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugObjectHandle,
&hProcessDebugObject,
sizeof(HANDLE),
&dwReturned);
if (NT_SUCCESS(status) && (0 != hProcessDebugObject))
ExitProcess(-1);
}
}
x86 汇编:
[Asm] 纯文本查看 复制代码 lea eax, [dwReturned]
push eax ; ReturnLength
push 4 ; ProcessInformationLength
lea ecx, [hProcessDebugObject]
push ecx ; ProcessInformation
push 1Eh ; ProcessInformationClass
push -1 ; ProcessHandle
call NtQueryInformationProcess
cmp dword ptr [hProcessDebugObject], 0
jnz being_debugged
...
being_debugged:
push -1
call ExitProcess
x86-64 汇编:
[Asm] 纯文本查看 复制代码 lea rcx, [dwReturned]
push rcx ; ReturnLength
mov r9d, 4 ; ProcessInformationLength
lea r8, [hProcessDebugObject]
; ProcessInformation
mov edx, 1Fh ; ProcessInformationClass
mov rcx, -1 ; ProcessHandle
call NtQueryInformationProcess
cmp dword ptr [hProcessDebugObject], 0
jnz being_debugged
...
being_debugged:
mov ecx, -1
call ExitProcess
1.4. RtlQueryProcessHeapInformation()
ntdll!RtlQueryProcessHeapInformation()函数可以用来从当前进程的进程内存中读取堆标志寄存器。
C/C++ 代码:
[C++] 纯文本查看 复制代码 bool Check()
{
ntdll::PDEBUG_BUFFER pDebugBuffer = ntdll::RtlCreateQueryDebugBuffer(0, FALSE);
if (!SUCCEEDED(ntdll::RtlQueryProcessHeapInformation((ntdll::PRTL_DEBUG_INFORMATION)pDebugBuffer)))
return false;
ULONG dwFlags = ((ntdll::PRTL_PROCESS_HEAPS)pDebugBuffer->HeapInformation)->Heaps[0].Flags;
return dwFlags & ~HEAP_GROWABLE;
}
1.5. RtlQueryProcessDebugInformation()
ntdll!RtlQueryProcessDebugInformation()函数可用于从被请求进程的进程内存中读取某些字段,包括堆标志寄存器。
C/C++ 代码:
[C++] 纯文本查看 复制代码 bool Check()
{
ntdll::PDEBUG_BUFFER pDebugBuffer = ntdll::RtlCreateQueryDebugBuffer(0, FALSE);
if (!SUCCEEDED(ntdll::RtlQueryProcessDebugInformation(GetCurrentProcessId(), ntdll::PDI_HEAPS | ntdll::PDI_HEAP_BLOCKS, pDebugBuffer)))
return false;
ULONG dwFlags = ((ntdll::PRTL_PROCESS_HEAPS)pDebugBuffer->HeapInformation)->Heaps[0].Flags;
return dwFlags & ~HEAP_GROWABLE;
}
1.6. NtQuerySystemInformation()
ntdll!NtQuerySystemInformation()函数接受一个参数,即要查询的信息类别。大多数类都没有被记录下来。这包括SystemKernelDebuggerInformation(0x23)类,它从Windows NT开始就存在了。SystemKernelDebuggerInformation类返回两个标志寄存器的值。al中的KdDebuggerEnabled,和ah中的KdDebuggerNotPresent。因此,如果内核调试器存在,ah中的返回值为零。
C/C++ 代码:
[C++] 纯文本查看 复制代码 enum { SystemKernelDebuggerInformation = 0x23 };
typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION {
BOOLEAN DebuggerEnabled;
BOOLEAN DebuggerNotPresent;
} SYSTEM_KERNEL_DEBUGGER_INFORMATION, *PSYSTEM_KERNEL_DEBUGGER_INFORMATION;
bool Check()
{
NTSTATUS status;
SYSTEM_KERNEL_DEBUGGER_INFORMATION SystemInfo;
status = NtQuerySystemInformation(
(SYSTEM_INFORMATION_CLASS)SystemKernelDebuggerInformation,
&SystemInfo,
sizeof(SystemInfo),
NULL);
return SUCCEEDED(status)
? (SystemInfo.DebuggerEnabled && !SystemInfo.DebuggerNotPresent)
: false;
}
反制措施
- 对于IsDebuggerPresent()。将进程环境块(PEB)的BeingDebugged标志设置为0。 更多信息请参见BeingDebugged 标志寄存器反制措施。
- 对于CheckRemoteDebuggerPresent()和NtQueryInformationProcess():
由于CheckRemoteDebuggerPresent()调用NtQueryInformationProcess(),唯一的方法是拦截NtQueryInformationProcess()并在返回缓冲区设置以下值。
- 如果是ProcessDebugPort查询,则为0(或除-1外的任何值)。
- 如果是ProcessDebugFlags查询,则为非零值。
- 在ProcessDebugObjectHandle查询的情况下为0。
- 用RtlQueryProcessHeapInformation()、RtlQueryProcessDebugInformation()和NtQuerySystemInformation()函数反制这些检查的唯一方法是拦截它们并修改返回值:
- RTL_PROCESS_HEAPS::HeapInformation::Heaps[0]:标志寄存器改为HEAP_GROWABLE,用于RtlQueryProcessHeapInformation()和RtlQueryProcessDebugInformation()。
- SYSTEM_KERNEL_DEBUGGER_INFORMATION::DebuggerEnabled为0和系统_KERNEL_DEBUGGER_INFORMATION::DebuggerNotPresent为1。NtQuerySystemInformation()函数在查询SystemKernelDebuggerInformation的情况下。
2. 手工检查
以下方法用于验证系统结构中的调试标志。他们手工检查进程的内存,而不使用特殊的调试API函数。
2.1. PEB!正在调试标志寄存器
这个方法只是检查PEB的BeingDebugged标志寄存器[的另一种方法,而不需要调用IsDebuggerPresent()。
32位进程:
[C++] 纯文本查看 复制代码 mov eax, fs:[30h]
cmp byte ptr [eax+2], 0
jne being_debugged
64位进程:
[C++] 纯文本查看 复制代码 mov rax, gs:[60h]
cmp byte ptr [rax+2], 0
jne being_debugged
WOW64 进程:
[C++] 纯文本查看 复制代码 mov eax, fs:[30h]
cmp byte ptr [eax+1002h], 0
C/C++ 代码:
[C++] 纯文本查看 复制代码 #ifndef _WIN64
PPEB pPeb = (PPEB)__readfsdword(0x30);
#else
PPEB pPeb = (PPEB)__readgsqword(0x60);
#endif // _WIN64
if (pPeb->BeingDebugged)
goto being_debugged;
2.2. NtGlobalFlag
进程环境块的NtGlobalFlag字段(32位Windows的0x68偏移,64位Windows的0xBC)默认为0。附加一个调试器并不改变NtGlobalFlag的值。但是,如果进程是由调试器创建的,则将设置以下标志寄存器:
- **_HEAP_ENABLE_TAIL_CHECK (0x10)
- **_HEAP_ENABLE_FREE_CHECK (0x20)
- **_HEAP_VALIDATE_PARAMETERS (0x40)
调试器的存在可以通过检查这些标志寄存器的组合来检测。
32位进程:
[C++] 纯文本查看 复制代码 mov eax, fs:[30h]
mov al, [eax+68h]
and al, 70h
cmp al, 70h
jz being_debugged
64位进程:
[C++] 纯文本查看 复制代码 mov rax, gs:[60h]
mov al, [rax+BCh]
and al, 70h
cmp al, 70h
jz being_debugged
WOW64 进程:
[Asm] 纯文本查看 复制代码 mov eax, fs:[30h]
mov al, [eax+10BCh]
and al, 70h
cmp al, 70h
jz being_debugged
C/C++ 代码:
[C++] 纯文本查看 复制代码 #define **_HEAP_ENABLE_TAIL_CHECK 0x10
#define **_HEAP_ENABLE_FREE_CHECK 0x20
#define **_HEAP_VALIDATE_PARAMETERS 0x40
#define NT_GLOBAL_FLAG_DEBUGGED (**_HEAP_ENABLE_TAIL_CHECK | **_HEAP_ENABLE_FREE_CHECK | **_HEAP_VALIDATE_PARAMETERS)
#ifndef _WIN64
PPEB pPeb = (PPEB)__readfsdword(0x30);
DWORD dwNtGlobalFlag = *(PDWORD)((PBYTE)pPeb + 0x68);
#else
PPEB pPeb = (PPEB)__readgsqword(0x60);
DWORD dwNtGlobalFlag = *(PDWORD)((PBYTE)pPeb + 0xBC);
#endif // _WIN64
if (dwNtGlobalFlag & NT_GLOBAL_FLAG_DEBUGGED)
goto being_debugged;
2.3. 堆标志寄存器
堆包含两个字段,它们会受到调试器存在的影响。具体如何影响,取决于Windows的版本。这些字段是标志寄存器(Flags)和强标志寄存器(ForceFlags)。
标志寄存器和强标志寄存器的值通常分别设置为HEAP_GROWABLE和0。
当调试器出现时,在Windows NT、Windows 2000和32位Windows XP上,标志寄存器字段被设置为这些标志寄存器的组合。
- HEAP_GROWABLE (2)
- HEAP_TAIL_CHECKING_ENABLED (0x20)
- HEAP_FREE_CHECKING_ENABLED (0x40)
- HEAP_SKIP_VALIDATION_CHECKS (0x10000000)
- HEAP_VALIDATE_PARAMETERS_ENABLED (0x40000000)
在64位的Windows XP和Windows Vista及更高版本中,如果有调试器存在,Flags字段会被设置为这些标志寄存器组合:
- HEAP_GROWABLE (2)
- HEAP_TAIL_CHECKING_ENABLED (0x20)
- HEAP_FREE_CHECKING_ENABLED (0x40)
- HEAP_VALIDATE_PARAMETERS_ENABLED (0x40000000)
当调试器出现时,ForceFlags字段被设置为这些标志寄存器的组合:
- HEAP_TAIL_CHECKING_ENABLED (0x20)
- HEAP_FREE_CHECKING_ENABLED (0x40)
- HEAP_VALIDATE_PARAMETERS_ENABLED (0x40000000)
C/C++ 代码:
[C++] 纯文本查看 复制代码 bool Check()
{
#ifndef _WIN64
PPEB pPeb = (PPEB)__readfsdword(0x30);
PVOID pHeapBase = !m_bIsWow64
? (PVOID)(*(PDWORD_PTR)((PBYTE)pPeb + 0x18))
: (PVOID)(*(PDWORD_PTR)((PBYTE)pPeb + 0x1030));
DWORD dwHeapFlagsOffset = IsWindowsVistaOrGreater()
? 0x40
: 0x0C;
DWORD dwHeapForceFlagsOffset = IsWindowsVistaOrGreater()
? 0x44
: 0x10;
#else
PPEB pPeb = (PPEB)__readgsqword(0x60);
PVOID pHeapBase = (PVOID)(*(PDWORD_PTR)((PBYTE)pPeb + 0x30));
DWORD dwHeapFlagsOffset = IsWindowsVistaOrGreater()
? 0x70
: 0x14;
DWORD dwHeapForceFlagsOffset = IsWindowsVistaOrGreater()
? 0x74
: 0x18;
#endif // _WIN64
PDWORD pdwHeapFlags = (PDWORD)((PBYTE)pHeapBase + dwHeapFlagsOffset);
PDWORD pdwHeapForceFlags = (PDWORD)((PBYTE)pHeapBase + dwHeapForceFlagsOffset);
return (*pdwHeapFlags & ~HEAP_GROWABLE) || (*pdwHeapForceFlags != 0);
}
2.3.堆保护
如果在NtGlobalFlag中设置了HEAP_TAIL_CHECKING_ENABLED标志寄存器,序列0xABABABAB将被附加在所分配的堆块的末端(在32位Windows中为2次,在64位Windows中为4次)。
如果在NtGlobalFlag中设置了HEAP_FREE_CHECKING_ENABLED标志寄存器,如果需要额外的字节来填充空的空间,直到下一个内存块,那么将附加序列0xFEEEFEEE。
C/C++ 代码:
[C++] 纯文本查看 复制代码 bool Check()
{
PROCESS_HEAP_ENTRY HeapEntry = { 0 };
do
{
if (!HeapWalk(GetProcessHeap(), &HeapEntry))
return false;
} while (HeapEntry.wFlags != PROCESS_HEAP_ENTRY_BUSY);
PVOID pOverlapped = (PBYTE)HeapEntry.lpData + HeapEntry.cbData;
return ((DWORD)(*(PDWORD)pOverlapped) == 0xABABABAB);
}
反制措施
对于PEB!BeingDebugged标志寄存器:
将BeingDebugged标志设置为0,这可以通过DLL注入完成。如果你使用OllyDbg或x32/64dbg作为调试器,你可以选择各种反调试插件,如ScyllaHide。
[C++] 纯文本查看 复制代码 #ifndef _WIN64
PPEB pPeb = (PPEB)__readfsdword(0x30);
#else
PPEB pPeb = (PPEB)__readgsqword(0x60);
#endif // _WIN64
pPeb->BeingDebugged = 0;
对于NtGlobalFlag:
将NtGlobalFlag设置为0,这可以通过DLL注入完成。如果你使用OllyDbg或x32/64dbg作为调试器,你可以选择各种反调试插件,如ScyllaHide。
[C++] 纯文本查看 复制代码 #ifndef _WIN64
PPEB pPeb = (PPEB)__readfsdword(0x30);
*(PDWORD)((PBYTE)pPeb + 0x68) = 0;
#else
PPEB pPeb = (PPEB)__readgsqword(0x60);
*(PDWORD)((PBYTE)pPeb + 0xBC); = 0;
#endif // _WIN64
对于堆标志寄存器:
设置标志寄存器值为HEAP_GROWABLE,ForceFlags值为0,这可以通过DLL注入完成。如果你使用OllyDbg或x32/64dbg作为调试器,你可以选择各种反调试插件,如ScyllaHide。
[C++] 纯文本查看 复制代码 #ifndef _WIN64
PPEB pPeb = (PPEB)__readfsdword(0x30);
PVOID pHeapBase = !m_bIsWow64
? (PVOID)(*(PDWORD_PTR)((PBYTE)pPeb + 0x18))
: (PVOID)(*(PDWORD_PTR)((PBYTE)pPeb + 0x1030));
DWORD dwHeapFlagsOffset = IsWindowsVistaOrGreater()
? 0x40
: 0x0C;
DWORD dwHeapForceFlagsOffset = IsWindowsVistaOrGreater()
? 0x44
: 0x10;
#else
PPEB pPeb = (PPEB)__readgsqword(0x60);
PVOID pHeapBase = (PVOID)(*(PDWORD_PTR)((PBYTE)pPeb + 0x30));
DWORD dwHeapFlagsOffset = IsWindowsVistaOrGreater()
? 0x70
: 0x14;
DWORD dwHeapForceFlagsOffset = IsWindowsVistaOrGreater()
? 0x74
: 0x18;
#endif // _WIN64
*(PDWORD)((PBYTE)pHeapBase + dwHeapFlagsOffset) = HEAP_GROWABLE;
*(PDWORD)((PBYTE)pHeapBase + dwHeapForceFlagsOffset) = 0;
对于堆保护:
手工修补32位的12字节和64位环境下的20字节的堆。拦截kernel32!HeapAlloc()并在其分配后修补堆。
[C++] 纯文本查看 复制代码 #ifndef _WIN64
SIZE_T nBytesToPatch = 12;
#else
SIZE_T nBytesToPatch = 20;
#endif // _WIN64
SIZE_T nDwordsToPatch = nBytesToPatch / sizeof(DWORD);
PVOID pHeapEnd = (PBYTE)HeapEntry.lpData + HeapEntry.cbData;
for (SIZE_T offset = 0; offset < nDwordsToPatch; offset++)
*((PDWORD)pHeapEnd + offset) = 0;
|