forked from 0xAlexei/WindowsDefenderTools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
OutputDebugStringAHook.patch
243 lines (235 loc) · 8.06 KB
/
OutputDebugStringAHook.patch
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
From eae5317c7e8473f37902a89365b487c26dc0ac8a Mon Sep 17 00:00:00 2001
---
Makefile | 8 ++++-
ODSHook.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ODSHook.h | 28 +++++++++++++++
call.asm | 25 ++++++++++++++
mpclient.c | 5 +++
5 files changed, 179 insertions(+), 1 deletion(-)
create mode 100644 ODSHook.c
create mode 100644 ODSHook.h
create mode 100644 call.asm
diff --git a/Makefile b/Makefile
index 7d6f0dd..a49c876 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,13 @@ mpscript.o: script.h
intercept/hook.o: intercept
-mpclient: mpclient.o intercept/hook.o | peloader
+call.o: call.asm
+ nasm -f elf $^
+
+ODSHook.o: ODSHook.c ODSHook.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+mpclient: mpclient.o intercept/hook.o ODSHook.o call.o | peloader
$(CC) $(CFLAGS) $^ -o $@ $(LDLIBS) $(LDFLAGS)
# mpscript requires libreadline-dev:i386
diff --git a/ODSHook.c b/ODSHook.c
new file mode 100644
index 0000000..f4c409f
--- /dev/null
+++ b/ODSHook.c
@@ -0,0 +1,114 @@
+#include "ODSHook.h"
+
+
+// This structure contains relative virtual addresses for three key addresses
+// we need to make the OutputDebugStringA trick work. These addresses are relative
+// from the base of mpengine.dll
+//
+// Note: these RVAs are specific to the 6/25/2018 mpengine.dll release
+// MD5: e95d3f9e90ba3ccd1a4b8d63cbd88d1b
+RVAS RVAs_June_2018 = {
+
+ .MPVERNO = "MP_6_25_2018",
+
+ // This function gets the parameter passed a 1 parameter emulated
+ // function from the pe_vars_t * passed to every emulated function
+ .RVA_Parameters1 = 0x46e5d5,
+
+ // This function translates the virtual memory address of a string to a
+ // native char * that we can print
+ .RVA_pe_read_string_ex = 0x3e59f3,
+
+ // The address of the function pointer to KERNEL32_DLL_OuputDebugStringA
+ // in the g_syscalls table. It should look something like this:
+ /*
+ .text:5A11A0D8 dd offset ?KERNEL32_DLL_CopyFileWWorker@@YAXPAUpe_vars_t@@@Z ; KERNEL32_DLL_CopyFileWWorker(pe_vars_t *)
+ .text:5A11A0DC dd 0B27D5174h
+ .text:5A11A0E0 dd offset ?KERNEL32_DLL_OutputDebugStringA@@YAXPAUpe_vars_t@@@Z ; KERNEL32_DLL_OutputDebugStringA(pe_vars_t *)
+ .text:5A11A0E4 dd 0B28014BBh
+ .text:5A11A0E8 dd offset ?NTDLL_DLL_NtGetContextThread@@YAXPAUpe_vars_t@@@Z ; NTDLL_DLL_NtGetContextThread(pe_vars_t *)
+ .text:5A11A0EC dd 0B363A610h
+ */
+ .RVA_FP_OutputDebugStringA = 0x1af88L,
+};
+
+//
+// Call into pe_read_string_ex to translate the virtual address of a
+// string in the emulated address space to a real char * that we can
+// interact with. Len value returns the length of the string
+//
+// Due to an annoying calling convention (fastcall but with a 64-bit
+// value in one of the first 2 arguments), trampolining through assembly
+// is easier than setting GCC compiler attributes
+//
+// __cdecl calling convention (implicitly on GCC, but if you have any problems
+// linking or running, double check this)
+char * ASM_pe_read_string_ex(uint32_t * , void *, uint64_t, uint32_t *);
+
+static char * GetString(void * V, uint64_t Param, uint32_t * Len)
+{
+ //trampoline through assembly with wierd calling convention
+ return ASM_pe_read_string_ex(FP_pe_read_string_ex, V, Param, Len);
+}
+
+//
+// Hook for OutputDebugStringA - just print a string to stdout
+//
+static void __cdecl KERNEL32_DLL_OutputDebugStringA_hook(void * v)
+{
+ uint64_t Params[1] = {0};
+ char * str = NULL;
+ DWORD len = 0;
+
+ // void * v is a pe_vars_t *, a huge structure containing all
+ // the information for given emulation session. We don't have
+ // the full structure definition, so we just treat it as a void *
+ printf("[+] OutputDebugStringA(pe_vars_t * v = 0x%p)\n", v);
+
+ // use Defender's internal Parameters<1>::Parameters<1> function
+ // to retrieve the one parameter passed to kernel32!OutputDebugStringA
+ // inside the emulator. This will give us the virtual address of a char *
+ // inside the emulator
+ Parameters1(Params, v);
+
+ // print out the virtual address of the char * argument
+ printf("[+] Params[1]:\t0x%llx\n", Params[0]);
+
+ // use Defender's internal pe_read_string_ex function to translate a
+ // virtual address to a real address we can interact with
+ str = GetString(v, Params[0], &len);
+
+ // now that we finally have a real pointer we can interact with, print
+ // the string out to stdout
+ printf("[+] OutputDebugStringA: \"%s\"\n", str);
+
+ return;
+}
+
+//
+// Set hooks and calculate offsets for functions we will call
+//
+void SetHooks(const uint32_t ImageBase, const uint32_t ImageSize)
+{
+ uint32_t * pOutputDebugStringA;
+
+ printf("[+] MpEngine.dll base at 0x%x\n", ImageBase);
+ printf("[+] Setting hooks and resolving offsets\n");
+
+ // resolve the address of Parameters<1>::Parameters<1>, we will be calling it
+ Parameters1 = (void*)((unsigned char*)ImageBase + RVAs_June_2018.RVA_Parameters1);
+ printf("[+] Parameters<1>::Parameters<1>\tRVA: 0x%06x\tAddress: %p\n", RVAs_June_2018.RVA_Parameters1, Parameters1);
+
+ // resolve the address of pe_read_string_ex, we will be calling it
+ FP_pe_read_string_ex = (void*)((unsigned char*)ImageBase + RVAs_June_2018.RVA_pe_read_string_ex);
+ printf("[+] pe_read_string_ex:\t\t\tRVA: 0x%06x\tAddress: %p\n", RVAs_June_2018.RVA_pe_read_string_ex, FP_pe_read_string_ex);
+
+ // resolve the address of a function pointer to KERNEL32_DLL_OutputDebugStringA, we are replacing it
+ pOutputDebugStringA = (void*)((unsigned char*)ImageBase + RVAs_June_2018.RVA_FP_OutputDebugStringA);
+ printf("[+] OutputDebugStringA FP:\t\tRVA: 0x%06x\tAddress: 0x%x\n", RVAs_June_2018.RVA_FP_OutputDebugStringA, *(pOutputDebugStringA));
+ *pOutputDebugStringA = (uint32_t)KERNEL32_DLL_OutputDebugStringA_hook;
+ printf("[+] OutputDebugStringA FP replaced: \t0x%x\n", *(pOutputDebugStringA));
+
+
+ printf("[+] Done setting hooks and resolving offsets!\n");
+}
diff --git a/ODSHook.h b/ODSHook.h
new file mode 100644
index 0000000..cb6a112
--- /dev/null
+++ b/ODSHook.h
@@ -0,0 +1,28 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "peloader/winnt_types.h"
+#include "peloader/util.h"
+
+
+void SetHooks(const uint32_t ImageBase, const uint32_t ImageSize);
+
+//Typedef for templated Parameters<> calls
+typedef uint32_t __thiscall(* ParametersCall)(void * params, void * v);
+ParametersCall Parameters1;
+
+// address of pe_read_string_ex
+uint32_t * FP_pe_read_string_ex;
+//struct to store offsets
+typedef struct _RVAS {
+ char * MPVERNO;
+ uint32_t RVA_Parameters1;
+ uint32_t RVA_pe_read_string_ex;
+ uint32_t RVA_FP_OutputDebugStringA;
+} RVAS, *PRVAS;
+
diff --git a/call.asm b/call.asm
new file mode 100644
index 0000000..7086010
--- /dev/null
+++ b/call.asm
@@ -0,0 +1,25 @@
+; hacking this together was less annoying than dealing with
+; calling convention declarations
+
+global ASM_pe_read_string_ex
+
+;
+; __cdecl calling convention
+;
+ASM_pe_read_string_ex:
+ push ebp
+ mov ebp, esp
+
+ mov eax, dword [ebp+0x8] ; function pointer we are calling
+ mov ecx, [ebp+0xc] ; set up parameter
+
+ push dword [ebp+0x18] ; set up parameter
+ push dword [ebp+0x14] ; set up parameter (QWORD high)
+ push dword [ebp+0x10] ; set up parameter (QWORD low)
+
+ call eax ; actually call the function
+
+ add esp, 0xc ; stack cleanup after the call to pe_read_string_ex
+ pop ebp
+ ret
+
diff --git a/mpclient.c b/mpclient.c
index 4cfeaaa..a1ff821 100644
--- a/mpclient.c
+++ b/mpclient.c
@@ -50,6 +50,7 @@
#include "scanreply.h"
#include "streambuffer.h"
#include "openscan.h"
+#include "ODSHook.h"
// Any usage limits to prevent bugs disrupting system.
const struct rlimit kUsageLimits[] = {
@@ -169,6 +170,10 @@ int main(int argc, char **argv, char **envp)
}
}
+ //Set our hooks for OutputDebugStringA
+ SetHooks((uint32_t)image.image, (uint32_t)image.size);
+
+
if (get_export("__rsignal", &__rsignal) == -1) {
errx(EXIT_FAILURE, "Failed to resolve mpengine entrypoint");
}
--
2.7.4