mirror of
https://github.com/EddieIvan01/memexec
synced 2024-06-20 14:00:23 +00:00
Add an IAT hooking interface
This commit is contained in:
parent
db55833355
commit
19fc8641de
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
*.exe
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "memexec"
|
name = "memexec"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
authors = ["iv4n <iv4n.cc@qq.com>"]
|
authors = ["iv4n <iv4n.cc@qq.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "A library for loading and executing PE (Portable Executable) without ever touching the disk"
|
description = "A library for loading and executing PE (Portable Executable) from memory without ever touching the disk"
|
||||||
git = "https://github.com/eddieivan01/memexec.git"
|
repository = "https://github.com/eddieivan01/memexec.git"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
hook = []
|
||||||
|
89
README.md
89
README.md
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[![](https://img.shields.io/crates/v/memexec)](https://crates.io/crates/memexec) [![](https://img.shields.io/crates/d/memexec?label=downloads%40crates.io&style=social)](https://crates.io/crates/memexec)
|
[![](https://img.shields.io/crates/v/memexec)](https://crates.io/crates/memexec) [![](https://img.shields.io/crates/d/memexec?label=downloads%40crates.io&style=social)](https://crates.io/crates/memexec)
|
||||||
|
|
||||||
A library for loading and executing PE (Portable Executable) without ever touching the disk
|
A library for loading and executing PE (Portable Executable) from memory without ever touching the disk
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
@ -10,6 +10,7 @@ A library for loading and executing PE (Portable Executable) without ever touchi
|
|||||||
+ Cross-architecture, applicable to x86 and x86-64
|
+ Cross-architecture, applicable to x86 and x86-64
|
||||||
+ Zero-dependency
|
+ Zero-dependency
|
||||||
+ Contains a simple, zero-copy PE parser submodule
|
+ Contains a simple, zero-copy PE parser submodule
|
||||||
|
+ Provides an IAT hooking interface
|
||||||
|
|
||||||
# Install
|
# Install
|
||||||
|
|
||||||
@ -17,12 +18,12 @@ A library for loading and executing PE (Portable Executable) without ever touchi
|
|||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
memexec = "0.1"
|
memexec = "0.2"
|
||||||
```
|
```
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
## Load and execute
|
## Execute from memory
|
||||||
|
|
||||||
**⚠The architecture of target program must be same as current process, otherwise an error will occur**
|
**⚠The architecture of target program must be same as current process, otherwise an error will occur**
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ use std::io::Read;
|
|||||||
/* EXE */
|
/* EXE */
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
File::open("./mimikatz.exe")
|
File::open("./test.exe")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read_to_end(&mut buf)
|
.read_to_end(&mut buf)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -43,6 +44,7 @@ File::open("./mimikatz.exe")
|
|||||||
unsafe {
|
unsafe {
|
||||||
// If you need to pass command line parameters,
|
// If you need to pass command line parameters,
|
||||||
// try to modify PEB's command line buffer
|
// try to modify PEB's command line buffer
|
||||||
|
// Or use `memexec_exe_with_hooks` to hook related functions (see below)
|
||||||
memexec::memexec_exe(&buf).unwrap();
|
memexec::memexec_exe(&buf).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +65,85 @@ unsafe {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## IAT hooking
|
||||||
|
|
||||||
|
Add the `hook` feature in `Cargo.toml`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
memexec = { version="0.2", features=[ "hook" ] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Hook the `__wgetmainargs` function (see `example/hook.rs`)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
File::open("./test.x64.exe")
|
||||||
|
.unwrap()
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut hooks = HashMap::new();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
hooks.insert(
|
||||||
|
"msvcrt.dll!__wgetmainargs".into(),
|
||||||
|
mem::transmute::<
|
||||||
|
extern "win64" fn(
|
||||||
|
*mut i32,
|
||||||
|
*mut *const *const u16,
|
||||||
|
*const c_void,
|
||||||
|
i32,
|
||||||
|
*const c_void,
|
||||||
|
) -> i32,
|
||||||
|
*const c_void,
|
||||||
|
>(__wgetmainargs),
|
||||||
|
);
|
||||||
|
memexec::memexec_exe_with_hooks(&buf, &hooks).unwrap();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The definition of `__wgetmainargs` (notice the calling convention on different archtectures):
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/getmainargs-wgetmainargs?view=msvc-160
|
||||||
|
/*
|
||||||
|
int __wgetmainargs (
|
||||||
|
int *_Argc,
|
||||||
|
wchar_t ***_Argv,
|
||||||
|
wchar_t ***_Env,
|
||||||
|
int _DoWildCard,
|
||||||
|
_startupinfo * _StartInfo)
|
||||||
|
*/
|
||||||
|
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||||
|
extern "win64" fn __wgetmainargs(
|
||||||
|
_Argc: *mut i32,
|
||||||
|
_Argv: *mut *const *const u16,
|
||||||
|
_Env: *const c_void,
|
||||||
|
_DoWildCard: i32,
|
||||||
|
_StartInfo: *const c_void,
|
||||||
|
) -> i32 {
|
||||||
|
unsafe {
|
||||||
|
*_Argc = 2;
|
||||||
|
let a0: Vec<_> = "program_name\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
let a1: Vec<_> = "token::whoami\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
*_Argv = [a0.as_ptr(), a1.as_ptr()].as_ptr();
|
||||||
|
|
||||||
|
// Avoid calling destructor
|
||||||
|
mem::forget(a0);
|
||||||
|
mem::forget(a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Parse PE
|
## Parse PE
|
||||||
|
|
||||||
**PE parser could parse programs which have different architectures from current process**
|
**PE parser could parse programs which have different architectures from current process**
|
||||||
|
126
example/__wgetmainargs_hook.rs
Normal file
126
example/__wgetmainargs_hook.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/getmainargs-wgetmainargs?view=msvc-160
|
||||||
|
/*
|
||||||
|
int __wgetmainargs (
|
||||||
|
int *_Argc,
|
||||||
|
wchar_t ***_Argv,
|
||||||
|
wchar_t ***_Env,
|
||||||
|
int _DoWildCard,
|
||||||
|
_startupinfo * _StartInfo)
|
||||||
|
*/
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "windows"))]
|
||||||
|
extern "cdecl" fn __wgetmainargs(
|
||||||
|
_Argc: *mut i32,
|
||||||
|
_Argv: *mut *const *const u16,
|
||||||
|
_Env: *const c_void,
|
||||||
|
_DoWildCard: i32,
|
||||||
|
_StartInfo: *const c_void,
|
||||||
|
) -> i32 {
|
||||||
|
unsafe {
|
||||||
|
*_Argc = 2;
|
||||||
|
let a0: Vec<_> = "program_name\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
let a1: Vec<_> = "token::whoami\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
*_Argv = [a0.as_ptr(), a1.as_ptr()].as_ptr();
|
||||||
|
|
||||||
|
// Avoid calling destructor
|
||||||
|
mem::forget(a0);
|
||||||
|
mem::forget(a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "windows"))]
|
||||||
|
fn hook_x86() {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
File::open("./test.x86.exe")
|
||||||
|
.unwrap()
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut hooks = HashMap::new();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
hooks.insert(
|
||||||
|
"msvcrt.dll!__wgetmainargs".into(),
|
||||||
|
mem::transmute::<
|
||||||
|
extern "cdecl" fn(
|
||||||
|
*mut i32,
|
||||||
|
*mut *const *const u16,
|
||||||
|
*const c_void,
|
||||||
|
i32,
|
||||||
|
*const c_void,
|
||||||
|
) -> i32,
|
||||||
|
*const c_void,
|
||||||
|
>(__wgetmainargs),
|
||||||
|
);
|
||||||
|
memexec::memexec_exe_with_hooks(&buf, &hooks).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||||
|
extern "win64" fn __wgetmainargs(
|
||||||
|
_Argc: *mut i32,
|
||||||
|
_Argv: *mut *const *const u16,
|
||||||
|
_Env: *const c_void,
|
||||||
|
_DoWildCard: i32,
|
||||||
|
_StartInfo: *const c_void,
|
||||||
|
) -> i32 {
|
||||||
|
unsafe {
|
||||||
|
*_Argc = 2;
|
||||||
|
let a0: Vec<_> = "program_name\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
let a1: Vec<_> = "token::whoami\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
*_Argv = [a0.as_ptr(), a1.as_ptr()].as_ptr();
|
||||||
|
|
||||||
|
// Avoid calling destructor
|
||||||
|
mem::forget(a0);
|
||||||
|
mem::forget(a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||||
|
fn hook_x64() {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
File::open("./test.x64.exe")
|
||||||
|
.unwrap()
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut hooks = HashMap::new();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
hooks.insert(
|
||||||
|
"msvcrt.dll!__wgetmainargs".into(),
|
||||||
|
mem::transmute::<
|
||||||
|
extern "win64" fn(
|
||||||
|
*mut i32,
|
||||||
|
*mut *const *const u16,
|
||||||
|
*const c_void,
|
||||||
|
i32,
|
||||||
|
*const c_void,
|
||||||
|
) -> i32,
|
||||||
|
*const c_void,
|
||||||
|
>(__wgetmainargs),
|
||||||
|
);
|
||||||
|
memexec::memexec_exe_with_hooks(&buf, &hooks).unwrap();
|
||||||
|
}
|
||||||
|
}
|
174
src/lib.rs
174
src/lib.rs
@ -12,12 +12,32 @@ use peloader::{DllLoader, ExeLoader};
|
|||||||
use peparser::PE;
|
use peparser::PE;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
pub use peloader::hook;
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
use peloader::hook::ProcDesc;
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub unsafe fn memexec_exe(bs: &[u8]) -> Result<()> {
|
pub unsafe fn memexec_exe(bs: &[u8]) -> Result<()> {
|
||||||
let pe = PE::new(bs)?;
|
let pe = PE::new(bs)?;
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
let loader = ExeLoader::new(&pe, None)?;
|
||||||
|
#[cfg(not(feature = "hook"))]
|
||||||
let loader = ExeLoader::new(&pe)?;
|
let loader = ExeLoader::new(&pe)?;
|
||||||
Ok(loader.invoke_entry_point())
|
Ok(loader.invoke_entry_point())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
pub unsafe fn memexec_exe_with_hooks(
|
||||||
|
bs: &[u8],
|
||||||
|
hooks: &HashMap<ProcDesc, *const c_void>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let pe = PE::new(bs)?;
|
||||||
|
let loader = ExeLoader::new(&pe, Some(hooks))?;
|
||||||
|
Ok(loader.invoke_entry_point())
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn memexec_dll(
|
pub unsafe fn memexec_dll(
|
||||||
bs: &[u8],
|
bs: &[u8],
|
||||||
hmod: *const c_void,
|
hmod: *const c_void,
|
||||||
@ -25,10 +45,26 @@ pub unsafe fn memexec_dll(
|
|||||||
lp_reserved: *const c_void,
|
lp_reserved: *const c_void,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let pe = PE::new(bs)?;
|
let pe = PE::new(bs)?;
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
let loader = DllLoader::new(&pe, None)?;
|
||||||
|
#[cfg(not(feature = "hook"))]
|
||||||
let loader = DllLoader::new(&pe)?;
|
let loader = DllLoader::new(&pe)?;
|
||||||
Ok(loader.invoke_entry_point(hmod, reason_for_call, lp_reserved))
|
Ok(loader.invoke_entry_point(hmod, reason_for_call, lp_reserved))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
pub unsafe fn memexec_dll_with_hooks(
|
||||||
|
bs: &[u8],
|
||||||
|
hmod: *const c_void,
|
||||||
|
reason_for_call: u32,
|
||||||
|
lp_reserved: *const c_void,
|
||||||
|
hooks: &HashMap<ProcDesc, *const c_void>,
|
||||||
|
) -> Result<bool> {
|
||||||
|
let pe = PE::new(bs)?;
|
||||||
|
let loader = DllLoader::new(&pe, Some(hooks))?;
|
||||||
|
Ok(loader.invoke_entry_point(hmod, reason_for_call, lp_reserved))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -36,9 +72,16 @@ mod tests {
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(not(feature = "hook"))]
|
||||||
fn test_dll() {
|
fn test_dll() {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
File::open("./test.dll")
|
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||||
|
File::open("./test.x64.dll")
|
||||||
|
.unwrap()
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.unwrap();
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "windows"))]
|
||||||
|
File::open("./test.x86.dll")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read_to_end(&mut buf)
|
.read_to_end(&mut buf)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -49,9 +92,16 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(not(feature = "hook"))]
|
||||||
fn test_exe() {
|
fn test_exe() {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
File::open("./test.exe")
|
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||||
|
File::open("./test.x64.exe")
|
||||||
|
.unwrap()
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.unwrap();
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "windows"))]
|
||||||
|
File::open("./test.x86.exe")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read_to_end(&mut buf)
|
.read_to_end(&mut buf)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -60,4 +110,124 @@ mod tests {
|
|||||||
memexec_exe(&buf).unwrap();
|
memexec_exe(&buf).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "windows"))]
|
||||||
|
extern "cdecl" fn __wgetmainargs(
|
||||||
|
_Argc: *mut i32,
|
||||||
|
_Argv: *mut *const *const u16,
|
||||||
|
_Env: *const c_void,
|
||||||
|
_DoWildCard: i32,
|
||||||
|
_StartInfo: *const c_void,
|
||||||
|
) -> i32 {
|
||||||
|
unsafe {
|
||||||
|
*_Argc = 2;
|
||||||
|
let a0: Vec<_> = "program_name\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
let a1: Vec<_> = "token::whoami\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
*_Argv = [a0.as_ptr(), a1.as_ptr()].as_ptr();
|
||||||
|
|
||||||
|
mem::forget(a0);
|
||||||
|
mem::forget(a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "windows"))]
|
||||||
|
fn hook_x86() {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
File::open("./test.x86.exe")
|
||||||
|
.unwrap()
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut hooks = HashMap::new();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
hooks.insert(
|
||||||
|
"msvcrt.dll!__wgetmainargs".into(),
|
||||||
|
mem::transmute::<
|
||||||
|
extern "cdecl" fn(
|
||||||
|
*mut i32,
|
||||||
|
*mut *const *const u16,
|
||||||
|
*const c_void,
|
||||||
|
i32,
|
||||||
|
*const c_void,
|
||||||
|
) -> i32,
|
||||||
|
*const c_void,
|
||||||
|
>(__wgetmainargs),
|
||||||
|
);
|
||||||
|
memexec_exe_with_hooks(&buf, &hooks).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||||
|
extern "win64" fn __wgetmainargs(
|
||||||
|
_Argc: *mut i32,
|
||||||
|
_Argv: *mut *const *const u16,
|
||||||
|
_Env: *const c_void,
|
||||||
|
_DoWildCard: i32,
|
||||||
|
_StartInfo: *const c_void,
|
||||||
|
) -> i32 {
|
||||||
|
unsafe {
|
||||||
|
*_Argc = 2;
|
||||||
|
|
||||||
|
let a0: Vec<_> = "program_name\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
let a1: Vec<_> = "token::whoami\0"
|
||||||
|
.chars()
|
||||||
|
.map(|c| (c as u16).to_le())
|
||||||
|
.collect();
|
||||||
|
*_Argv = [a0.as_ptr(), a1.as_ptr()].as_ptr();
|
||||||
|
|
||||||
|
mem::forget(a0);
|
||||||
|
mem::forget(a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||||
|
fn hook_x64() {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
File::open("./test.x64.exe")
|
||||||
|
.unwrap()
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut hooks = HashMap::new();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
hooks.insert(
|
||||||
|
"msvcrt.dll!__wgetmainargs".into(),
|
||||||
|
mem::transmute::<
|
||||||
|
extern "win64" fn(
|
||||||
|
*mut i32,
|
||||||
|
*mut *const *const u16,
|
||||||
|
*const c_void,
|
||||||
|
i32,
|
||||||
|
*const c_void,
|
||||||
|
) -> i32,
|
||||||
|
*const c_void,
|
||||||
|
>(__wgetmainargs),
|
||||||
|
);
|
||||||
|
memexec_exe_with_hooks(&buf, &hooks).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ pub enum Error {
|
|||||||
MismatchedLoader,
|
MismatchedLoader,
|
||||||
NoEntryPoint,
|
NoEntryPoint,
|
||||||
UnsupportedDotNetExecutable,
|
UnsupportedDotNetExecutable,
|
||||||
|
InvalidProcDescString,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@ -19,3 +20,9 @@ impl std::convert::From<std::str::Utf8Error> for Error {
|
|||||||
Error::InvalidUtf8String
|
Error::InvalidUtf8String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<std::num::ParseIntError> for Error {
|
||||||
|
fn from(_: std::num::ParseIntError) -> Self {
|
||||||
|
Error::InvalidProcDescString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
163
src/peloader/hook.rs
Normal file
163
src/peloader/hook.rs
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#![cfg(feature = "hook")]
|
||||||
|
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Thunk<'a> {
|
||||||
|
Ordinal(isize),
|
||||||
|
Name(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialEq for Thunk<'a> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(&Thunk::Ordinal(s), &Thunk::Ordinal(o)) => s == o,
|
||||||
|
(&Thunk::Name(s), &Thunk::Name(o)) => s == o,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Eq for Thunk<'a> {}
|
||||||
|
|
||||||
|
impl<'a> Hash for Thunk<'a> {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
mem::discriminant(self).hash(state);
|
||||||
|
match *self {
|
||||||
|
Thunk::Ordinal(o) => o.hash(state),
|
||||||
|
Thunk::Name(n) => n.hash(state),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProcDesc<'a> {
|
||||||
|
// Use `String` instead of `&str`,
|
||||||
|
// because we need a case conversion in `new` function
|
||||||
|
dll: String,
|
||||||
|
thunk: Thunk<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialEq for ProcDesc<'a> {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.dll == other.dll && self.thunk == other.thunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Eq for ProcDesc<'a> {}
|
||||||
|
|
||||||
|
impl<'a> Hash for ProcDesc<'a> {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.dll.hash(state);
|
||||||
|
self.thunk.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ProcDesc<'a> {
|
||||||
|
/// Dll name is case insensitive
|
||||||
|
pub fn new(dll: &str, thunk: Thunk<'a>) -> ProcDesc<'a> {
|
||||||
|
ProcDesc {
|
||||||
|
dll: dll.to_ascii_lowercase(),
|
||||||
|
thunk: thunk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Couldn't implement `FromStr` trait here,
|
||||||
|
// because the signature of `from_str` doesn't allow an explicit lifetime hint
|
||||||
|
impl<'a> From<&'a str> for ProcDesc<'a> {
|
||||||
|
fn from(s: &'a str) -> Self {
|
||||||
|
match s.find('!') {
|
||||||
|
Some(i) => {
|
||||||
|
let (dll, proc_name) = s.split_at(i);
|
||||||
|
ProcDesc {
|
||||||
|
dll: dll.to_ascii_lowercase(),
|
||||||
|
thunk: Thunk::Name(&proc_name[1..]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => match s.find('#') {
|
||||||
|
Some(i) => {
|
||||||
|
let (dll, proc_ord) = s.split_at(i);
|
||||||
|
ProcDesc {
|
||||||
|
dll: dll.to_ascii_lowercase(),
|
||||||
|
thunk: Thunk::Ordinal(proc_ord[1..].parse().unwrap_or(0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Failure case
|
||||||
|
None => ProcDesc {
|
||||||
|
dll: String::new(),
|
||||||
|
thunk: Thunk::Ordinal(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
|
trait ProcDescTbl<'a> {
|
||||||
|
fn get(&self, proc_desc: &'a ProcDesc) -> Option<&*const c_void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ProcDescTbl<'a> for HashMap<ProcDesc<'a>, *const c_void> {
|
||||||
|
fn get(&self, proc_desc: &'a ProcDesc) -> Option<&*const c_void> {
|
||||||
|
self.get(proc_desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Implement FNV-1a hash
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct ProcHash(u64, u64);
|
||||||
|
|
||||||
|
impl PartialEq for ProcHash {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0 && self.1 == other.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for ProcHash {}
|
||||||
|
|
||||||
|
impl ProcHash {
|
||||||
|
/// Dll name is case insensitive
|
||||||
|
/// ProcHash::new("kernel32.dll", Thunk::Name("CreateProcessW"))
|
||||||
|
pub fn new(dll: &str, thunk: Thunk) -> ProcHash {
|
||||||
|
// The hash of DLL and thunk is calculated and saved separately,
|
||||||
|
// in order to reduce the collision probability
|
||||||
|
let mut h1: u64 = 0xcbf29ce484222325;
|
||||||
|
|
||||||
|
for c in dll.chars() {
|
||||||
|
h1 ^= c.to_ascii_lowercase() as u64;
|
||||||
|
h1 = h1.wrapping_mul(0x100000001b3);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut h2: u64 = 0xcbf29ce484222325;
|
||||||
|
match thunk {
|
||||||
|
Thunk::Ordinal(ord) => {
|
||||||
|
// #
|
||||||
|
h2 ^= 0x23;
|
||||||
|
h2 = h2.wrapping_mul(0x100000001b3);
|
||||||
|
h2 ^= ord as u64;
|
||||||
|
h2 = h2.wrapping_mul(0x100000001b3);
|
||||||
|
}
|
||||||
|
Thunk::Name(name) => {
|
||||||
|
// !
|
||||||
|
h2 ^= 0x21;
|
||||||
|
h2 = h2.wrapping_mul(0x100000001b3);
|
||||||
|
for c in name.chars() {
|
||||||
|
h2 ^= c as u64;
|
||||||
|
h2 = h2.wrapping_mul(0x100000001b3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcHash(h1, h2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
@ -4,48 +4,24 @@ pub mod winapi;
|
|||||||
|
|
||||||
use crate::peparser::def::*;
|
use crate::peparser::def::*;
|
||||||
use crate::peparser::PE;
|
use crate::peparser::PE;
|
||||||
use def::{DllMain, DLL_PROCESS_ATTACH, MEM_COMMIT, MEM_RESERVE, PVOID};
|
use def::{DllMain, DLL_PROCESS_ATTACH, MEM_COMMIT, MEM_RESERVE};
|
||||||
use error::{Error, Result};
|
use error::{Error, Result};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
unsafe fn load_pe_into_mem(pe: &PE) -> Result<*const c_void> {
|
#[cfg(feature = "hook")]
|
||||||
// Step1: allocate memory for image
|
use hook::{ProcDesc, Thunk};
|
||||||
let mut base_addr = pe.pe_header.nt_header.get_image_base();
|
#[cfg(feature = "hook")]
|
||||||
let size = pe.pe_header.nt_header.get_size_of_image();
|
use std::collections::HashMap;
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
pub mod hook;
|
||||||
|
|
||||||
// ALSR
|
unsafe fn patch_reloc_table(pe: &PE, base_addr: *const c_void) -> Result<()> {
|
||||||
if winapi::nt_alloc_vm(
|
|
||||||
&base_addr as _,
|
|
||||||
&size as _,
|
|
||||||
MEM_RESERVE | MEM_COMMIT,
|
|
||||||
PAGE_READWRITE,
|
|
||||||
)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
base_addr = 0 as PVOID;
|
|
||||||
winapi::nt_alloc_vm(
|
|
||||||
&base_addr as _,
|
|
||||||
&size as _,
|
|
||||||
MEM_RESERVE | MEM_COMMIT,
|
|
||||||
PAGE_READWRITE,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step2: copy sections
|
|
||||||
for section in pe.section_area.section_table {
|
|
||||||
ptr::copy_nonoverlapping(
|
|
||||||
pe.raw.as_ptr().offset(section.PointerToRawData as isize),
|
|
||||||
base_addr.offset(section.VirtualAddress as isize) as *mut u8,
|
|
||||||
section.SizeOfRawData as usize,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step3: handle base relocataion table
|
|
||||||
let reloc_entry = &pe.pe_header.nt_header.get_data_directory()[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
let reloc_entry = &pe.pe_header.nt_header.get_data_directory()[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||||
let image_base_offset = base_addr as usize - pe.pe_header.nt_header.get_image_base() as usize;
|
let image_base_offset = base_addr as isize - pe.pe_header.nt_header.get_image_base() as isize;
|
||||||
|
|
||||||
if image_base_offset != 0 && reloc_entry.VirtualAddress != 0 && reloc_entry.Size != 0 {
|
if image_base_offset != 0 && reloc_entry.VirtualAddress != 0 && reloc_entry.Size != 0 {
|
||||||
let mut reloc_table_ptr =
|
let mut reloc_table_ptr =
|
||||||
base_addr.offset(reloc_entry.VirtualAddress as isize) as *const u8;
|
base_addr.offset(reloc_entry.VirtualAddress as isize) as *const u8;
|
||||||
@ -62,7 +38,7 @@ unsafe fn load_pe_into_mem(pe: &PE) -> Result<*const c_void> {
|
|||||||
if (item >> 12) == IMAGE_REL_BASED {
|
if (item >> 12) == IMAGE_REL_BASED {
|
||||||
let patch_addr = base_addr
|
let patch_addr = base_addr
|
||||||
.offset(reloc_block.VirtualAddress as isize + (item & 0xfff) as isize)
|
.offset(reloc_block.VirtualAddress as isize + (item & 0xfff) as isize)
|
||||||
as *mut usize;
|
as *mut isize;
|
||||||
*patch_addr = *patch_addr + image_base_offset;
|
*patch_addr = *patch_addr + image_base_offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,24 +46,33 @@ unsafe fn load_pe_into_mem(pe: &PE) -> Result<*const c_void> {
|
|||||||
reloc_table_ptr = reloc_table_ptr.offset(reloc_block.SizeOfBlock as isize);
|
reloc_table_ptr = reloc_table_ptr.offset(reloc_block.SizeOfBlock as isize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Step4: resolve import symbols
|
unsafe fn resolve_import_symbols(
|
||||||
|
pe: &PE,
|
||||||
|
base_addr: *const c_void,
|
||||||
|
#[cfg(feature = "hook")] hooks: Option<&HashMap<ProcDesc, *const c_void>>,
|
||||||
|
) -> Result<()> {
|
||||||
let import_entry = &pe.pe_header.nt_header.get_data_directory()[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
let import_entry = &pe.pe_header.nt_header.get_data_directory()[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||||
if import_entry.Size != 0 && import_entry.VirtualAddress != 0 {
|
if import_entry.Size != 0 && import_entry.VirtualAddress != 0 {
|
||||||
for i in 0..(import_entry.Size as usize / mem::size_of::<IMAGE_IMPORT_DESCRIPTOR>()) {
|
let mut import_desc_ptr = base_addr.offset(import_entry.VirtualAddress as isize)
|
||||||
let import_desc =
|
as *const IMAGE_IMPORT_DESCRIPTOR;
|
||||||
&*mem::transmute::<PVOID, *const IMAGE_IMPORT_DESCRIPTOR>(base_addr.offset(
|
loop {
|
||||||
import_entry.VirtualAddress as isize
|
let import_desc = &*import_desc_ptr;
|
||||||
+ (i * mem::size_of::<IMAGE_IMPORT_DESCRIPTOR>()) as isize,
|
if 0 == import_desc.Name
|
||||||
));
|
&& 0 == import_desc.FirstThunk
|
||||||
if 0 == import_desc.Name {
|
&& 0 == import_desc.OriginalFirstThunk
|
||||||
|
&& 0 == import_desc.TimeDateStamp
|
||||||
|
&& 0 == import_desc.ForwarderChain
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dll_name = CStr::from_ptr(base_addr.offset(import_desc.Name as isize) as *const i8)
|
let dll_name = CStr::from_ptr(base_addr.offset(import_desc.Name as isize) as *const i8)
|
||||||
.to_str()?;
|
.to_str()?;
|
||||||
// TODO: implement loading module by calling self recursively
|
// TODO: implement loading module by calling self recursively
|
||||||
let hmod = winapi::load_library(dll_name)?;
|
let hmod = winapi::load_library(&dll_name)?;
|
||||||
|
|
||||||
// Whether the ILT (called INT in IDA) exists? (some linkers didn't generate the ILT)
|
// Whether the ILT (called INT in IDA) exists? (some linkers didn't generate the ILT)
|
||||||
let (mut iat_ptr, mut ilt_ptr) = if import_desc.OriginalFirstThunk != 0 {
|
let (mut iat_ptr, mut ilt_ptr) = if import_desc.OriginalFirstThunk != 0 {
|
||||||
@ -109,31 +94,142 @@ unsafe fn load_pe_into_mem(pe: &PE) -> Result<*const c_void> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let proc_addr;
|
|
||||||
if thunk_data & IMAGE_ORDINAL_FLAG != 0 {
|
if thunk_data & IMAGE_ORDINAL_FLAG != 0 {
|
||||||
// Import by ordinal number
|
// Import by ordinal number
|
||||||
proc_addr = winapi::get_proc_address_by_ordinal(hmod, thunk_data & 0xffff)?;
|
|
||||||
|
#[cfg(not(feature = "hook"))]
|
||||||
|
let proc_addr = winapi::get_proc_address_by_ordinal(hmod, thunk_data & 0xffff)?;
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
let proc_addr = match hooks {
|
||||||
|
Some(hooks) => {
|
||||||
|
match hooks.get(&ProcDesc::new(
|
||||||
|
dll_name,
|
||||||
|
Thunk::Ordinal(thunk_data & 0xffff),
|
||||||
|
)) {
|
||||||
|
Some(addr) => *addr,
|
||||||
|
None => {
|
||||||
|
winapi::get_proc_address_by_ordinal(hmod, thunk_data & 0xffff)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => winapi::get_proc_address_by_ordinal(hmod, thunk_data & 0xffff)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// rust-lang/rust/issues/15701
|
||||||
|
*iat_ptr = proc_addr as IMAGE_THUNK_DATA;
|
||||||
} else {
|
} else {
|
||||||
// TODO: implement resolving proc address by `IMAGE_IMPORT_BY_NAME.Hint`
|
// TODO: implement resolving proc address by `IMAGE_IMPORT_BY_NAME.Hint`
|
||||||
let hint_name_table = &*mem::transmute::<PVOID, *const IMAGE_IMPORT_BY_NAME>(
|
let hint_name_table = &*mem::transmute::<
|
||||||
base_addr.offset(thunk_data),
|
*const c_void,
|
||||||
);
|
*const IMAGE_IMPORT_BY_NAME,
|
||||||
|
>(base_addr.offset(thunk_data));
|
||||||
if 0 == hint_name_table.Name {
|
if 0 == hint_name_table.Name {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
proc_addr = winapi::get_proc_address(
|
#[cfg(not(feature = "hook"))]
|
||||||
|
let proc_addr = winapi::get_proc_address_by_name(
|
||||||
hmod,
|
hmod,
|
||||||
CStr::from_ptr(&hint_name_table.Name as _).to_str()?,
|
CStr::from_ptr(&hint_name_table.Name as _).to_str()?,
|
||||||
)?;
|
)?;
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
let proc_addr = match hooks {
|
||||||
|
Some(hooks) => match hooks.get(&ProcDesc::new(
|
||||||
|
dll_name,
|
||||||
|
Thunk::Name(CStr::from_ptr(&hint_name_table.Name as _).to_str()?),
|
||||||
|
)) {
|
||||||
|
Some(addr) => *addr,
|
||||||
|
None => winapi::get_proc_address_by_name(
|
||||||
|
hmod,
|
||||||
|
CStr::from_ptr(&hint_name_table.Name as _).to_str()?,
|
||||||
|
)?,
|
||||||
|
},
|
||||||
|
None => winapi::get_proc_address_by_name(
|
||||||
|
hmod,
|
||||||
|
CStr::from_ptr(&hint_name_table.Name as _).to_str()?,
|
||||||
|
)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
*iat_ptr = proc_addr as IMAGE_THUNK_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
*iat_ptr = proc_addr as IMAGE_THUNK_DATA;
|
|
||||||
iat_ptr = iat_ptr.offset(1);
|
iat_ptr = iat_ptr.offset(1);
|
||||||
ilt_ptr = ilt_ptr.offset(1);
|
ilt_ptr = ilt_ptr.offset(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import_desc_ptr = import_desc_ptr.offset(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn call_tls_callback(pe: &PE, base_addr: *const c_void) -> Result<()> {
|
||||||
|
let tls_entry = &pe.pe_header.nt_header.get_data_directory()[IMAGE_DIRECTORY_ENTRY_TLS];
|
||||||
|
if tls_entry.Size != 0 && tls_entry.VirtualAddress != 0 {
|
||||||
|
let tls = &*mem::transmute::<*const c_void, *const IMAGE_TLS_DIRECTORY>(
|
||||||
|
base_addr.offset(tls_entry.VirtualAddress as isize),
|
||||||
|
);
|
||||||
|
let mut tls_callback_addr = tls.AddressOfCallBacks as *const *const c_void;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if *tls_callback_addr == 0 as _ {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem::transmute::<*const c_void, PIMAGE_TLS_CALLBACK>(*tls_callback_addr)(
|
||||||
|
base_addr,
|
||||||
|
DLL_PROCESS_ATTACH,
|
||||||
|
0 as _,
|
||||||
|
);
|
||||||
|
tls_callback_addr = tls_callback_addr.offset(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn load_pe_into_mem(
|
||||||
|
pe: &PE,
|
||||||
|
#[cfg(feature = "hook")] hooks: Option<&HashMap<ProcDesc, *const c_void>>,
|
||||||
|
) -> Result<*const c_void> {
|
||||||
|
// Step1: allocate memory for image
|
||||||
|
let mut base_addr = pe.pe_header.nt_header.get_image_base();
|
||||||
|
let size = pe.pe_header.nt_header.get_size_of_image();
|
||||||
|
|
||||||
|
// ASLR
|
||||||
|
if winapi::nt_alloc_vm(
|
||||||
|
&base_addr as _,
|
||||||
|
&size as _,
|
||||||
|
MEM_RESERVE | MEM_COMMIT,
|
||||||
|
PAGE_READWRITE,
|
||||||
|
)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
base_addr = 0 as *const c_void;
|
||||||
|
winapi::nt_alloc_vm(
|
||||||
|
&base_addr as _,
|
||||||
|
&size as _,
|
||||||
|
MEM_RESERVE | MEM_COMMIT,
|
||||||
|
PAGE_READWRITE,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step2: copy sections
|
||||||
|
for section in pe.section_area.section_table {
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
pe.raw.as_ptr().offset(section.PointerToRawData as isize),
|
||||||
|
base_addr.offset(section.VirtualAddress as isize) as *mut u8,
|
||||||
|
section.SizeOfRawData as usize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step3: handle base relocataion table
|
||||||
|
patch_reloc_table(pe, base_addr)?;
|
||||||
|
|
||||||
|
// Step4: resolve import symbols
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
resolve_import_symbols(pe, base_addr, hooks)?;
|
||||||
|
#[cfg(not(feature = "hook"))]
|
||||||
|
resolve_import_symbols(pe, base_addr)?;
|
||||||
|
|
||||||
// Step5: restore sections' protection
|
// Step5: restore sections' protection
|
||||||
for section in pe.section_area.section_table {
|
for section in pe.section_area.section_table {
|
||||||
@ -150,32 +246,14 @@ unsafe fn load_pe_into_mem(pe: &PE) -> Result<*const c_void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step6: call TLS callback
|
// Step6: call TLS callback
|
||||||
let tls_entry = &pe.pe_header.nt_header.get_data_directory()[IMAGE_DIRECTORY_ENTRY_TLS];
|
call_tls_callback(pe, base_addr)?;
|
||||||
if tls_entry.Size != 0 && tls_entry.VirtualAddress != 0 {
|
|
||||||
let tls = &*mem::transmute::<PVOID, *const IMAGE_TLS_DIRECTORY>(
|
|
||||||
base_addr.offset(tls_entry.VirtualAddress as isize),
|
|
||||||
);
|
|
||||||
let mut tls_callback_addr = tls.AddressOfCallBacks as *const PVOID;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if *tls_callback_addr == 0 as _ {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem::transmute::<PVOID, PIMAGE_TLS_CALLBACK>(*tls_callback_addr)(
|
|
||||||
base_addr,
|
|
||||||
DLL_PROCESS_ATTACH,
|
|
||||||
0 as _,
|
|
||||||
);
|
|
||||||
tls_callback_addr = tls_callback_addr.offset(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(base_addr)
|
Ok(base_addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_platform(pe: &PE) -> Result<()> {
|
fn check_platform(pe: &PE) -> Result<()> {
|
||||||
if (mem::size_of::<usize>() == 4 && pe.is_x86()) || mem::size_of::<usize>() == 8 && pe.is_x64()
|
if (mem::size_of::<usize>() == 4 && pe.is_x86())
|
||||||
|
|| (mem::size_of::<usize>() == 8 && pe.is_x64())
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -188,7 +266,10 @@ pub struct ExeLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ExeLoader {
|
impl ExeLoader {
|
||||||
pub unsafe fn new(pe: &PE) -> Result<ExeLoader> {
|
pub unsafe fn new(
|
||||||
|
pe: &PE,
|
||||||
|
#[cfg(feature = "hook")] hooks: Option<&HashMap<ProcDesc, *const c_void>>,
|
||||||
|
) -> Result<ExeLoader> {
|
||||||
check_platform(pe)?;
|
check_platform(pe)?;
|
||||||
if pe.is_dll() {
|
if pe.is_dll() {
|
||||||
return Err(Error::MismatchedLoader);
|
return Err(Error::MismatchedLoader);
|
||||||
@ -202,14 +283,18 @@ impl ExeLoader {
|
|||||||
if entry_point == 0 {
|
if entry_point == 0 {
|
||||||
Err(Error::NoEntryPoint)
|
Err(Error::NoEntryPoint)
|
||||||
} else {
|
} else {
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
let entry_point_va = load_pe_into_mem(pe, hooks)?.offset(entry_point);
|
||||||
|
#[cfg(not(feature = "hook"))]
|
||||||
|
let entry_point_va = load_pe_into_mem(pe)?.offset(entry_point);
|
||||||
Ok(ExeLoader {
|
Ok(ExeLoader {
|
||||||
entry_point_va: load_pe_into_mem(pe)?.offset(entry_point),
|
entry_point_va: entry_point_va,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn invoke_entry_point(&self) {
|
pub unsafe fn invoke_entry_point(&self) {
|
||||||
mem::transmute::<PVOID, extern "system" fn()>(self.entry_point_va)()
|
mem::transmute::<*const c_void, extern "system" fn()>(self.entry_point_va)()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +303,10 @@ pub struct DllLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DllLoader {
|
impl DllLoader {
|
||||||
pub unsafe fn new(pe: &PE) -> Result<DllLoader> {
|
pub unsafe fn new(
|
||||||
|
pe: &PE,
|
||||||
|
#[cfg(feature = "hook")] hooks: Option<&HashMap<ProcDesc, *const c_void>>,
|
||||||
|
) -> Result<DllLoader> {
|
||||||
check_platform(pe)?;
|
check_platform(pe)?;
|
||||||
if !pe.is_dll() {
|
if !pe.is_dll() {
|
||||||
return Err(Error::MismatchedLoader);
|
return Err(Error::MismatchedLoader);
|
||||||
@ -232,8 +320,12 @@ impl DllLoader {
|
|||||||
if entry_point == 0 {
|
if entry_point == 0 {
|
||||||
Err(Error::NoEntryPoint)
|
Err(Error::NoEntryPoint)
|
||||||
} else {
|
} else {
|
||||||
|
#[cfg(feature = "hook")]
|
||||||
|
let entry_point_va = load_pe_into_mem(pe, hooks)?.offset(entry_point);
|
||||||
|
#[cfg(not(feature = "hook"))]
|
||||||
|
let entry_point_va = load_pe_into_mem(pe)?.offset(entry_point);
|
||||||
Ok(DllLoader {
|
Ok(DllLoader {
|
||||||
entry_point_va: load_pe_into_mem(pe)?.offset(entry_point),
|
entry_point_va: entry_point_va,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,6 +336,10 @@ impl DllLoader {
|
|||||||
reason_for_call: u32,
|
reason_for_call: u32,
|
||||||
lp_reserved: *const c_void,
|
lp_reserved: *const c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
mem::transmute::<PVOID, DllMain>(self.entry_point_va)(hmod, reason_for_call, lp_reserved)
|
mem::transmute::<*const c_void, DllMain>(self.entry_point_va)(
|
||||||
|
hmod,
|
||||||
|
reason_for_call,
|
||||||
|
lp_reserved,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ pub fn load_library(lib: &str) -> Result<HMODULE> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_proc_address(hmod: HMODULE, proc_name: &str) -> Result<PVOID> {
|
pub fn get_proc_address_by_name(hmod: HMODULE, proc_name: &str) -> Result<PVOID> {
|
||||||
if let Ok(proc_name) = CString::new(proc_name) {
|
if let Ok(proc_name) = CString::new(proc_name) {
|
||||||
let proc = unsafe { GetProcAddress(hmod, proc_name.as_ptr()) };
|
let proc = unsafe { GetProcAddress(hmod, proc_name.as_ptr()) };
|
||||||
if proc == 0 as PVOID {
|
if proc == 0 as PVOID {
|
||||||
@ -71,7 +71,7 @@ pub unsafe fn nt_alloc_vm(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if p_nt_alloc_vm == 0 as _ {
|
if p_nt_alloc_vm == 0 as _ {
|
||||||
p_nt_alloc_vm =
|
p_nt_alloc_vm =
|
||||||
get_proc_address(load_library("ntdll.dll")?, "NtAllocateVirtualMemory")? as _;
|
get_proc_address_by_name(load_library("ntdll.dll")?, "NtAllocateVirtualMemory")? as _;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ret = mem::transmute::<
|
let ret = mem::transmute::<
|
||||||
@ -119,7 +119,7 @@ pub unsafe fn nt_protect_vm(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if p_nt_protect_vm == 0 as _ {
|
if p_nt_protect_vm == 0 as _ {
|
||||||
p_nt_protect_vm =
|
p_nt_protect_vm =
|
||||||
get_proc_address(load_library("ntdll.dll")?, "NtProtectVirtualMemory")? as _;
|
get_proc_address_by_name(load_library("ntdll.dll")?, "NtProtectVirtualMemory")? as _;
|
||||||
};
|
};
|
||||||
|
|
||||||
let old_protect: ULONG = 0;
|
let old_protect: ULONG = 0;
|
||||||
|
@ -267,6 +267,10 @@ struct_wrapper!(
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
OriginalFirstThunk: DWORD,
|
OriginalFirstThunk: DWORD,
|
||||||
|
// 0 if not bound,
|
||||||
|
// -1 if bound, and real date\time stamp
|
||||||
|
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
|
||||||
|
// O.W. date/time stamp of DLL bound to (Old BIND)
|
||||||
TimeDateStamp: DWORD,
|
TimeDateStamp: DWORD,
|
||||||
ForwarderChain: DWORD, // -1 if no forwarders
|
ForwarderChain: DWORD, // -1 if no forwarders
|
||||||
Name: DWORD,
|
Name: DWORD,
|
||||||
|
@ -3,7 +3,7 @@ use super::error::{Error, Result};
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
// zero copy
|
// Zero copy
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ImageNtHeaders<'a> {
|
pub enum ImageNtHeaders<'a> {
|
||||||
x86(&'a IMAGE_NT_HEADERS32),
|
x86(&'a IMAGE_NT_HEADERS32),
|
||||||
@ -61,7 +61,7 @@ impl<'a> ImageNtHeaders<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// zero copy
|
// Zero copy
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PeHeader<'a> {
|
pub struct PeHeader<'a> {
|
||||||
|
Loading…
Reference in New Issue
Block a user