🔹 1. 手写 FFI
#[link(name="kernel32")]
extern"system" {
fnGetModuleHandleA(...) -> ...;
}
👉 调用的是Kernel32.dll里的函数
👉 比如GetModuleHandleA
✅ 特点
❌ 缺点
- 容易写错(参数 / ABI / 类型)
- 不全(你得自己补 API)
- 不方便维护
🔹 2. “WinAPI 库调用”(老方式)
Rust 里通常指这个 crate:
👉winapi crate
用法类似:
use winapi::um::libloaderapi::
GetModuleHandleA;
✅ 本质
👉 它其实还是FFI,只是别人帮你写好了
⚠️ 特点
- API 很全(覆盖 Win32)
- 类型安全比手写好一点
❌ 缺点(现在基本被淘汰)
- 设计比较旧
- 模块结构混乱
- 宏很多,可读性差
- 和新 Windows SDK 不同步
👉 已经逐渐被官方方案替代
🔹 3.windows-sys(现在推荐🔥)
👉windows-sys crate
用法:
use windows_sys::Win32:
:System::LibraryLoader::
GetModuleHandleA;
✅ 本质
👉自动生成的 FFI(基于 Windows Metadata)
换句话说:
🧠 = 官方帮你写的“标准化 FFI”
🚀 优点(重点)
- ✔ 官方维护(微软生态)
- ✔ 和 Windows SDK 同步
- ✔ 无额外封装(接近裸调用)
- ✔ 比
winapi更清晰 - ✔ 零运行时开销
❗ 和windowscrate 区别
顺便说一句:
windows-sys→纯 FFI(底层)windows→带封装(更高级)
调用 Windows API(Win32 API)的动态链接库函数
#[link(name = "kernel32")]
extern "system" {
fn GetModuleHandleA(lpModuleName: *const i8) -> *mut core::ffi::c_void;
}
🔹 1.#[link(name = "kernel32")]
这是一个编译期属性(attribute),作用是:
➡️ 告诉 Rust 编译器去链接 Windows 系统库
👉 这里的kernel32指的是Kernel32.dll
🔹 2.extern "system"
这是关键点之一:
👉 表示使用系统调用约定(ABI)
在 Windows 上:
"system"= WinAPI 默认调用约定(通常是stdcall或 x64 的统一调用约定)
所以这是:
➡️Win32 API 调用约定绑定
🔹 3.extern { fn ... }
这部分表示:
👉 声明一个外部函数(不是 Rust 实现的)
也就是:
- Rust 只“声明”
- 实现来自 DLL(这里就是 kernel32)
🔹 4.GetModuleHandleA
这是一个典型的 Win32 API:
👉GetModuleHandleA
作用是:
FFI(Foreign Function Interface,外部函数接口)调用
无#[link]+ 手动解析 API
不让编译器帮你导入 DLL,而是运行时自己解析函数地址
#[link(name="kernel32")]
extern "system" {
fn GetModuleHandleA(...) -> ...;
}
👉 会生成IAT(导入表)
而“无 link”就是:
❌ 不使用导入表
✅ 自己拿到函数地址 → 再调用
🚀 实现路径(两种主流)
✅ 方式一:用GetModuleHandle + GetProcAddress
虽然“无 link”,但你仍然可以动态获取:
👉GetProcAddress
👉GetModuleHandleA
💣 真·无 link(完全隐藏)
✅ 方式二:PEB 遍历(经典免导入)
👉 不依赖任何 API,直接从进程结构拿
核心步骤:
- 通过寄存器拿 PEB
- 遍历模块链表
- 找到Kernel32.dll基址
- 解析导出表(Export Table)
- 找到函数地址(如 GetModuleHandleA)
🧩 Rust 示例(简化版,无 link)
use core::ffi::c_void;
type GetModuleHandleAFn = unsafe extern "system" fn(*const i8) -> *mut c_void;
unsafe fn get_kernel32_base() -> *mut u8 {
let peb: *mut u8;
#[cfg(target_arch = "x86_64")]
core::arch::asm!(
"mov {}, gs:[0x60]",
out(reg) peb
);
// 这里只是示意(真实需要解析 PEB_LDR_DATA)
peb
}
unsafe fn get_proc_address_manual(
module_base: *mut u8,
name: &str,
) -> *mut c_void {
// 👉 这里需要:
// 1. 解析 DOS Header
// 2. 找 NT Headers
// 3. 找 Export Table
// 4. 遍历函数名匹配
core::ptr::null_mut() // 简化
}
fn main() {
unsafe {
let kernel32 = get_kernel32_base();
let addr = get_proc_address_manual(kernel32, "GetModuleHandleA");
let func: GetModuleHandleAFn = core::mem::transmute(addr);
let handle = func(core::ptr::null());
println!("Handle: {:?}", handle);
}
}
关键技术点
🧷 1. PEB 获取
- x64:
gs:[0x60] - x86:
fs:[0x30]
🧷 2. 三层结构
PEB
└── PEB_LDR_DATA
└── InMemoryOrderModuleList
└── LDR_DATA_TABLE_ENTRY
🧷 3. PE 解析:手动解析:
- DOS Header (
MZ) - NT Headers (
PE) - Export Directory
“无 link”本质是:
🔥自己实现一个迷你版GetProcAddress+GetModuleHandle