Files

428 lines
17 KiB
C
Raw Permalink Normal View History

2026-07-28 14:35:14 +08:00
/* pe_loader.c — PE32+/PE32 解析、节区映射、重定位、import 解析。
*
* 加载流程:
* 1. 验证 MZ + PE 签名
* 2. 区分 PE32(OptionalHeader.Magic=0x10b)/ PE32+(0x20b)
* 3. 分配 SizeOfImage 字节 RWX 内存(kmem_alloc_aligned)
* 4. 复制 SizeOfHeaders + 各节区 raw data 到 virtual_address
* 5. 应用基址重定位(.reloc 节区:DIR64 for PE32+,HIGHLOW for PE32)
* 6. 解析 import 表,用 shim 函数指针填充 IAT
* 7. 构建 IAT 条目表(供 32位解释器 CALL 拦截)
*/
#include "pe_loader.h"
#include "pe_shim.h"
2026-08-09 22:14:01 +08:00
#include "pe_dll_manager.h"
2026-07-28 14:35:14 +08:00
#include "x86emu32.h"
#include "../include/utsm/pe.h"
#include <utsm/arena.h>
#include <utsm/log.h>
#include <utsm/panic.h>
2026-07-28 14:35:14 +08:00
#include <utsm/types.h>
/* 合成 shim 地址范围(32位解释器用):0x00010000..0x0001FFFF */
#define PE_SHIM_ADDR_BASE 0x00010000u
#define PE_SHIM_ADDR_END 0x00020000u
#define PE_SHIM_UNIMPL 0x0001FFFFu
/* 全局 IAT 表(pe_loader 填充,x86emu32 读取) */
static pe_iat_entry g_iat_entries[256];
static int g_iat_count;
/* ---- 辅助:RVA → 文件偏移 ---- */
static u32 rva_to_offset(const image_section_header *sections, int nsec,
u32 rva, u32 *out_size) {
for (int i = 0; i < nsec; i++) {
u32 va = sections[i].virtual_address;
u32 vs = sections[i].virtual_size;
if (vs == 0) vs = sections[i].size_of_raw_data;
if (rva >= va && rva < va + vs) {
if (out_size) *out_size = vs;
return sections[i].pointer_to_raw_data + (rva - va);
}
}
if (out_size) *out_size = 0;
return 0xFFFFFFFF;
}
/* ---- 解析 NT 头 ---- */
typedef struct {
const image_file_header *file_hdr;
const void *optional_hdr;
int is_pe32_plus;
u64 image_base; /* 链接时首选基址(PE32+ 为 64 位,如 0x140000000,必须全宽保留) */
2026-07-28 14:35:14 +08:00
u32 size_of_image;
u32 size_of_headers;
u32 entry_rva;
const image_data_directory *data_dir;
} pe_nt_info;
static int parse_nt_headers(const void *pe_data, u64 size, pe_nt_info *out) {
if (size < sizeof(image_dos_header)) return -1;
const image_dos_header *dos = (const image_dos_header *)pe_data;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -2;
if (dos->e_lfanew + 4 + sizeof(image_file_header) > size) return -3;
const u8 *nt_base = (const u8 *)pe_data + dos->e_lfanew;
u32 sig = *(const u32 *)nt_base;
if (sig != IMAGE_NT_SIGNATURE) return -4;
const image_file_header *fh = (const image_file_header *)(nt_base + 4);
out->file_hdr = fh;
const void *opt = (const void *)(nt_base + 4 + sizeof(image_file_header));
out->optional_hdr = opt;
u16 magic = *(const u16 *)opt;
if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
const image_optional_header64 *oh = (const image_optional_header64 *)opt;
out->is_pe32_plus = 1;
out->image_base = oh->image_base; /* PE32+: u64 全宽,禁止截断(重定位 delta 依赖高位) */
2026-07-28 14:35:14 +08:00
out->size_of_image = oh->size_of_image;
out->size_of_headers = oh->size_of_headers;
out->entry_rva = oh->address_of_entry_point;
out->data_dir = oh->data_directory;
} else if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
const image_optional_header32 *oh = (const image_optional_header32 *)opt;
out->is_pe32_plus = 0;
out->image_base = oh->image_base;
out->size_of_image = oh->size_of_image;
out->size_of_headers = oh->size_of_headers;
out->entry_rva = oh->address_of_entry_point;
out->data_dir = oh->data_directory;
} else {
return -5;
}
if (out->size_of_image == 0 || out->size_of_image > 64 * 1024 * 1024) return -6;
if (out->entry_rva == 0 || out->entry_rva >= out->size_of_image) return -7;
return 0;
}
/* ---- 应用基址重定位 ---- */
static void apply_relocations(u8 *image, u32 image_size, u64 image_base_actual, u64 image_base_preferred,
2026-07-28 14:35:14 +08:00
const image_data_directory *reloc_dir,
const image_section_header *sections, int nsec,
const void *pe_data, u64 pe_size, int is_pe32_plus) {
if (reloc_dir->virtual_address == 0 || reloc_dir->size == 0) return;
u32 block_rva = reloc_dir->virtual_address;
u32 block_end = block_rva + reloc_dir->size;
i64 delta = (i64)image_base_actual - (i64)image_base_preferred;
if (delta == 0) return; /* 加载到首选基址,无需重定位 */
while (block_rva < block_end) {
u32 dummy_size;
u32 file_off = rva_to_offset(sections, nsec, block_rva, &dummy_size);
if (file_off == 0xFFFFFFFF || file_off + 8 > pe_size) break;
const u8 *block = (const u8 *)pe_data + file_off;
u32 page_rva = *(const u32 *)block;
u32 block_size = *(const u32 *)(block + 4);
if (block_size < 8 || block_rva + block_size > block_end) break;
u32 entry_count = (block_size - 8) / 2;
const u16 *entries = (const u16 *)(block + 8);
for (u32 i = 0; i < entry_count; i++) {
u16 e = entries[i];
u32 type = (e >> 12) & 0xF;
u32 offset = e & 0xFFF;
if (type == IMAGE_REL_BASED_ABSOLUTE) continue;
u32 target_rva = page_rva + offset;
/* image 缓冲区按 RVA 排列(image[0] = RVA 0) */
if (target_rva >= (u32)image_size) continue;
u8 *target = image + target_rva;
if (type == IMAGE_REL_BASED_DIR64) {
u64 val = *(u64 *)target;
val += (u64)delta;
*(u64 *)target = val;
} else if (type == IMAGE_REL_BASED_HIGHLOW) {
u32 val = *(u32 *)target;
val += (u32)delta;
*(u32 *)target = val;
} else {
/* 严格错误策略插桩:未实现的重定位类型静默跳过 = 重定位
* 不完整照跑,零降级 panic(PE-E03)。 */
log_error("[PE] unsupported reloc type");
panic_full("PE-E03 RELOC TYPE NOT IMPLEMENTED",
"PE base-reloc type outside ABSOLUTE/HIGHLOW/DIR64: not implemented", 0);
2026-07-28 14:35:14 +08:00
}
}
block_rva += block_size;
}
(void)is_pe32_plus;
}
/* ---- 解析 imports,填充 IAT ---- */
static void resolve_imports(u8 *image, u64 image_base_actual,
const image_data_directory *import_dir,
const image_section_header *sections, int nsec,
const void *pe_data, u64 pe_size, int is_pe32_plus) {
(void)image_base_actual; /* PE32 IAT 用合成地址,不依赖 actual_base */
if (import_dir->virtual_address == 0 || import_dir->size == 0) return;
g_iat_count = 0;
u32 dummy;
u32 imp_off = rva_to_offset(sections, nsec, import_dir->virtual_address, &dummy);
if (imp_off == 0xFFFFFFFF) return;
const image_import_descriptor *imp = (const image_import_descriptor *)
((const u8 *)pe_data + imp_off);
for (int dll_idx = 0; ; dll_idx++) {
const image_import_descriptor *desc = &imp[dll_idx];
if (desc->name == 0 && desc->first_thunk == 0) break;
/* DLL 名 */
u32 name_off = rva_to_offset(sections, nsec, desc->name, &dummy);
if (name_off == 0xFFFFFFFF || name_off >= pe_size) continue;
const char *dll_name = (const char *)pe_data + name_off;
/* IAT (first_thunk) 与 INT (original_first_thunk) */
u32 iat_rva = desc->first_thunk;
u32 int_rva = desc->original_first_thunk;
if (int_rva == 0) int_rva = iat_rva; /* 无 INT,直接用 IAT */
u32 iat_off = rva_to_offset(sections, nsec, iat_rva, &dummy);
u32 int_off = rva_to_offset(sections, nsec, int_rva, &dummy);
for (int fn_idx = 0; ; fn_idx++) {
u32 iat_entry_off = iat_off + (u32)fn_idx * (is_pe32_plus ? 8 : 4);
if (iat_entry_off + (is_pe32_plus ? 8 : 4) > pe_size) break;
u32 int_entry_off = int_off + (u32)fn_idx * (is_pe32_plus ? 8 : 4);
if (int_entry_off + (is_pe32_plus ? 8 : 4) > pe_size) break;
/* 读 INT 条目判断函数名/序号 */
u64 thunk = 0;
if (is_pe32_plus) {
thunk = *(const u64 *)((const u8 *)pe_data + int_entry_off);
} else {
thunk = *(const u32 *)((const u8 *)pe_data + int_entry_off);
}
if (thunk == 0) break; /* 结束 */
const char *func_name = 0;
if (thunk & 0x80000000ULL) {
/* 严格错误策略插桩:序号导入是未实现路径,零降级 panic
* (PE-E02)。shim 表按名称索引,序号导入无法解析语义。 */
log_error("[PE] ordinal import encountered");
panic_full("PE-E02 ORDINAL IMPORT NOT IMPLEMENTED",
"PE import by ordinal: designed, not implemented", 0);
2026-07-28 14:35:14 +08:00
func_name = "#ordinal";
} else {
/* 名称导入:thunk 是 IMAGE_IMPORT_BY_NAME 的 RVA */
u32 hint_rva = (u32)thunk;
u32 hint_off = rva_to_offset(sections, nsec, hint_rva, &dummy);
if (hint_off != 0xFFFFFFFF && hint_off + 2 < pe_size) {
func_name = (const char *)pe_data + hint_off + 2; /* 跳过 hint(2字节) */
}
}
/* 查 shim 表 */
int shim_idx = -1;
if (func_name && func_name[0] != '#') {
shim_idx = pe_shim_lookup(dll_name, func_name);
}
2026-08-09 22:14:01 +08:00
/* 当 shim 未命中且需要尝试真实 DLL 时,先把 dll_name/func_name
* 复制到本地栈缓冲。原因:pe_dll_resolve 可能触发 fat32_read_path,
* 后者返回的 static 缓冲与外层 pe_data 可能指向同一区域,会被覆盖。 */
char dll_buf[256];
char func_buf[256];
int have_local_names = 0;
if (shim_idx < 0 && is_pe32_plus && g_pe_dll_manager_ready &&
func_name && func_name[0] != '#') {
u32 dl = 0;
while (dll_name[dl] && dl + 1 < sizeof(dll_buf)) {
dll_buf[dl] = dll_name[dl];
dl++;
}
dll_buf[dl] = 0;
u32 fl = 0;
while (func_name[fl] && fl + 1 < sizeof(func_buf)) {
func_buf[fl] = func_name[fl];
fl++;
}
func_buf[fl] = 0;
have_local_names = 1;
}
2026-07-28 14:35:14 +08:00
/* 填充 IAT(image 缓冲区内) */
u8 *iat_ptr = image + iat_rva + (u32)fn_idx * (is_pe32_plus ? 8 : 4);
2026-08-09 22:14:01 +08:00
int resolved_by_real_dll = 0;
2026-07-28 14:35:14 +08:00
if (is_pe32_plus) {
u64 fn_addr;
if (shim_idx >= 0) {
const pe_shim_entry *se = pe_shim_get(shim_idx);
fn_addr = (u64)se->fn64;
2026-08-09 22:14:01 +08:00
} else if (have_local_names) {
/* PE32+ 尝试真实 DLL */
u64 real = pe_dll_resolve(dll_buf, func_buf);
if (real != 0) {
fn_addr = real;
resolved_by_real_dll = 1;
} else {
/* DLL 加载过但函数未找到:填 unimpl stub */
fn_addr = pe_shim_unimpl_stub();
}
2026-07-28 14:35:14 +08:00
} else {
fn_addr = pe_shim_unimpl_stub();
}
*(u64 *)iat_ptr = fn_addr;
} else {
2026-08-09 22:14:01 +08:00
/* 32位:填合成地址,解释器拦截(不调真实 DLL) */
2026-07-28 14:35:14 +08:00
u32 synth_addr;
if (shim_idx >= 0) {
synth_addr = PE_SHIM_ADDR_BASE | (u32)(shim_idx & 0xFFFF);
} else {
synth_addr = PE_SHIM_UNIMPL;
}
*(u32 *)iat_ptr = synth_addr;
/* 记录 IAT 条目供解释器查询 */
if (g_iat_count < 255) {
g_iat_entries[g_iat_count].iat_rva = iat_rva + (u32)fn_idx * 4;
g_iat_entries[g_iat_count].shim_index = shim_idx;
g_iat_entries[g_iat_count].target_addr = synth_addr;
g_iat_count++;
}
}
2026-08-09 22:14:01 +08:00
/* shim 未命中且真实 DLL 也未解析:记未实现日志;
* 真实 DLL 解析成功则记来源日志(区分 shim/真实 DLL 路径)。
* 用本地副本(pe_data 可能已被 DLL 加载破坏) */
if (shim_idx < 0 && func_name && !resolved_by_real_dll) {
2026-07-28 14:35:14 +08:00
log_info("[PE] unimplemented import");
2026-08-09 22:14:01 +08:00
if (have_local_names) {
serial_write(dll_buf);
serial_write("!");
serial_write(func_buf);
} else {
serial_write(dll_name);
serial_write("!");
serial_write(func_name);
}
serial_write("\n");
} else if (resolved_by_real_dll) {
log_info("[PE] import via real DLL");
serial_write(dll_buf);
2026-07-28 14:35:14 +08:00
serial_write("!");
2026-08-09 22:14:01 +08:00
serial_write(func_buf);
2026-07-28 14:35:14 +08:00
serial_write("\n");
}
}
}
}
/* ---- 主加载函数 ---- */
int pe_load_image(const void *pe_data, u64 size, pe_image_info *out) {
if (!pe_data || !out || size < sizeof(image_dos_header)) return -1;
pe_nt_info nt;
int rc = parse_nt_headers(pe_data, size, &nt);
if (rc != 0) {
log_error("[PE] NT header parse failed");
log_hex64("[PE] rc=", (u64)(i64)rc);
return -2;
}
/* 节区头 */
int nsec = nt.file_hdr->number_of_sections;
if (nsec <= 0 || nsec > 96) return -3;
usize opt_size = nt.file_hdr->size_of_optional_header;
const image_section_header *sections = (const image_section_header *)
((const u8 *)nt.optional_hdr + opt_size);
/* 分配 SizeOfImage RWX 内存 */
u32 img_size = nt.size_of_image;
u8 *image = (u8 *)kmem_alloc_aligned(img_size, 0x1000);
if (!image) {
log_error("[PE] alloc image failed");
return -4;
}
/* 清零 */
for (u32 i = 0; i < img_size; i++) image[i] = 0;
/* 复制 headers */
u32 hdr_copy = nt.size_of_headers;
if (hdr_copy > size) hdr_copy = (u32)size;
if (hdr_copy > img_size) hdr_copy = img_size;
for (u32 i = 0; i < hdr_copy; i++) image[i] = ((const u8 *)pe_data)[i];
/* 复制各节区 raw data */
for (int i = 0; i < nsec; i++) {
u32 va = sections[i].virtual_address;
u32 raw_size = sections[i].size_of_raw_data;
u32 raw_off = sections[i].pointer_to_raw_data;
if (va >= img_size) continue;
u32 copy_size = raw_size;
if (va + copy_size > img_size) copy_size = img_size - va;
if (raw_off + copy_size > size) {
copy_size = (u32)(size - raw_off);
}
if (raw_off + copy_size > size) continue;
for (u32 j = 0; j < copy_size; j++) {
image[va + j] = ((const u8 *)pe_data)[raw_off + j];
}
}
/* 基址重定位 */
u64 actual_base = (u64)image;
u64 preferred_base = nt.image_base;
2026-07-28 14:35:14 +08:00
/* image 缓冲区按 RVA 排列(image[0]=RVA 0),重定位写入 image 内 */
/* 注意:apply_relocations 直接操作 image 缓冲区,delta = actual_base - preferred_base */
{
const image_data_directory *reloc_dir = &nt.data_dir[IMAGE_DIRECTORY_ENTRY_BASERELOC];
/* 重定位 target 是 RVA,写入 image[rva]。
* 但 image 加载在 actual_base,代码中绝对地址 = actual_base + RVA。
* 重定位修正量 = actual_base - preferred_base。
* image[rva] 处的值原为 preferred_base + rva_offset(链接时假设),
* 修正后应为 actual_base + rva_offset,即加 delta。 */
apply_relocations(image, img_size, actual_base, preferred_base, reloc_dir,
sections, nsec, pe_data, size, nt.is_pe32_plus);
}
/* 解析 imports */
{
const image_data_directory *import_dir = &nt.data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT];
resolve_imports(image, actual_base, import_dir, sections, nsec,
pe_data, size, nt.is_pe32_plus);
}
out->image_base = actual_base;
out->image_size = img_size;
out->entry_point = actual_base + nt.entry_rva;
out->is_pe32_plus = nt.is_pe32_plus;
out->load_ok = 1;
log_hex64("[PE] loaded base=", actual_base);
log_hex64("[PE] image size=", img_size);
log_hex64("[PE] entry=", out->entry_point);
log_info(nt.is_pe32_plus ? "[PE] PE32+ (64-bit)" : "[PE] PE32 (32-bit)");
return 0;
}
void pe_unload_image(pe_image_info *info) {
if (!info || !info->load_ok) return;
/* kmem_alloc_aligned 无对应 free(arena 分配器),标记为未加载即可 */
info->load_ok = 0;
}
u64 pe_get_image_base(const pe_image_info *info) {
return info ? info->image_base : 0;
}
u64 pe_get_image_size(const pe_image_info *info) {
return info ? info->image_size : 0;
}
/* 供 x86emu32 获取 IAT 表 */
pe_iat_entry *pe_get_iat_entries(int *count) {
if (count) *count = g_iat_count;
return g_iat_entries;
}