2026-07-31 13:53:49 +08:00
|
|
|
/* pe_service.c — PE 兼容层服务装配.
|
2026-07-28 14:35:14 +08:00
|
|
|
*
|
2026-07-31 13:53:49 +08:00
|
|
|
* 将 pe_loader + pe_shim + x86emu32 整合为统一的 pe_service,
|
|
|
|
|
* 通过 pe_get_service() 暴露给 DSK/cmd.elf(经 dsk_boot_context.reserved[4]).
|
2026-07-28 14:35:14 +08:00
|
|
|
*
|
2026-07-31 13:53:49 +08:00
|
|
|
* 执行策略:
|
|
|
|
|
* PE32+ (64位):pe_load_image 加载(actual_base 重定位),
|
|
|
|
|
* IAT 填 ms_abi shim 函数地址,Ring0 原生执行入口.
|
|
|
|
|
* ExitProcess 经 __builtin_longjmp 跳回 pe_service_run.
|
|
|
|
|
* PE32 (32位):直接以 preferred_base 加载到一块含栈/堆的大缓冲区,
|
|
|
|
|
* 无需重定位.IAT 填合成地址(0x00010000 | shim_idx),
|
|
|
|
|
* x86emu32 解释执行,CALL 合成地址时拦截分发到 shim32.
|
2026-07-28 14:35:14 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "pe_loader.h"
|
|
|
|
|
#include "pe_shim.h"
|
|
|
|
|
#include "x86emu32.h"
|
|
|
|
|
#include "../include/utsm/pe.h"
|
|
|
|
|
#include <utsm/arena.h>
|
|
|
|
|
#include <utsm/log.h>
|
|
|
|
|
#include <utsm/types.h>
|
|
|
|
|
|
|
|
|
|
/* 32位 PE 用的栈与堆 */
|
|
|
|
|
#define PE32_STACK_SIZE (64 * 1024) /* 64KB 栈 */
|
|
|
|
|
#define PE32_HEAP_SIZE (256 * 1024) /* 256KB 堆 */
|
|
|
|
|
#define PE32_MAX_IAT 256
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
/* 合成 shim 地址范围(与 pe_loader.c 一致) */
|
2026-07-28 14:35:14 +08:00
|
|
|
#define PE32_SHIM_ADDR_BASE 0x00010000u
|
|
|
|
|
#define PE32_SHIM_UNIMPL 0x0001FFFFu
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
/* 前向声明(服务表引用) */
|
2026-07-28 14:35:14 +08:00
|
|
|
int pe_service_load(const void *pe_data, u64 size, pe_image_info *out);
|
|
|
|
|
int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit_code);
|
|
|
|
|
void pe_service_unload(pe_image_info *info);
|
|
|
|
|
|
|
|
|
|
/* 全局服务表实例 */
|
|
|
|
|
static const pe_service g_pe_service = {
|
|
|
|
|
PE_SERVICE_MAGIC,
|
|
|
|
|
pe_service_load,
|
|
|
|
|
pe_service_run,
|
|
|
|
|
pe_service_unload,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const pe_service *pe_get_service(void) {
|
|
|
|
|
return &g_pe_service;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
/* ===== 加载(不执行)===== */
|
2026-07-28 14:35:14 +08:00
|
|
|
int pe_service_load(const void *pe_data, u64 size, pe_image_info *out) {
|
|
|
|
|
if (!pe_data || !out) return -1;
|
|
|
|
|
return pe_load_image(pe_data, size, out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pe_service_unload(pe_image_info *info) {
|
|
|
|
|
pe_unload_image(info);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
/* ===== PE32 专用加载器(加载到指定缓冲区,base=preferred_base,无重定位)===== */
|
2026-07-28 14:35:14 +08:00
|
|
|
typedef struct {
|
|
|
|
|
u8 *mem; /* 大缓冲区起始 */
|
|
|
|
|
u32 mem_size; /* 缓冲区总大小 */
|
|
|
|
|
u32 image_base; /* = preferred_base */
|
|
|
|
|
u32 image_size;
|
|
|
|
|
u32 entry_point; /* 绝对入口 = image_base + entry_rva */
|
|
|
|
|
pe_iat_entry iat[PE32_MAX_IAT];
|
|
|
|
|
int iat_count;
|
|
|
|
|
} pe32_interp_image;
|
|
|
|
|
|
|
|
|
|
static int pe32_load_into(const void *pe_data, u64 size, pe32_interp_image *img) {
|
|
|
|
|
const u8 *base = (const u8 *)pe_data;
|
|
|
|
|
if (size < sizeof(image_dos_header)) return -1;
|
|
|
|
|
const image_dos_header *dos = (const image_dos_header *)base;
|
|
|
|
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -2;
|
|
|
|
|
if ((u64)dos->e_lfanew + 4 + sizeof(image_file_header) > size) return -3;
|
|
|
|
|
|
|
|
|
|
const u8 *nt = base + dos->e_lfanew;
|
|
|
|
|
if (*(const u32 *)nt != IMAGE_NT_SIGNATURE) return -4;
|
|
|
|
|
const image_file_header *fh = (const image_file_header *)(nt + 4);
|
|
|
|
|
const void *opt = (const void *)(nt + 4 + sizeof(image_file_header));
|
|
|
|
|
u16 magic = *(const u16 *)opt;
|
|
|
|
|
if (magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) return -5; /* 仅 PE32 */
|
|
|
|
|
|
|
|
|
|
const image_optional_header32 *oh = (const image_optional_header32 *)opt;
|
|
|
|
|
u32 preferred_base = oh->image_base;
|
|
|
|
|
u32 image_size = oh->size_of_image;
|
|
|
|
|
u32 entry_rva = oh->address_of_entry_point;
|
|
|
|
|
if (image_size == 0 || image_size > 64 * 1024 * 1024) return -6;
|
|
|
|
|
if (preferred_base > 0x10000000u) return -7; /* 解释器地址空间限制 */
|
|
|
|
|
|
|
|
|
|
int nsec = fh->number_of_sections;
|
|
|
|
|
if (nsec <= 0 || nsec > 96) return -8;
|
|
|
|
|
const image_section_header *sections = (const image_section_header *)
|
|
|
|
|
((const u8 *)opt + fh->size_of_optional_header);
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
/* 分配大缓冲区:[0 .. preferred_base + image_size + stack + heap] */
|
2026-07-28 14:35:14 +08:00
|
|
|
u32 total = preferred_base + image_size + PE32_STACK_SIZE + PE32_HEAP_SIZE;
|
|
|
|
|
u8 *mem = (u8 *)kmem_alloc_aligned((u64)total, 0x1000);
|
|
|
|
|
if (!mem) return -9;
|
|
|
|
|
for (u32 i = 0; i < total; i++) mem[i] = 0;
|
|
|
|
|
|
|
|
|
|
/* 复制 headers */
|
|
|
|
|
u32 hdr_copy = oh->size_of_headers;
|
|
|
|
|
if (hdr_copy > size) hdr_copy = (u32)size;
|
|
|
|
|
if (hdr_copy > image_size) hdr_copy = image_size;
|
|
|
|
|
u8 *img_start = mem + preferred_base;
|
|
|
|
|
for (u32 i = 0; i < hdr_copy; i++) img_start[i] = base[i];
|
|
|
|
|
|
|
|
|
|
/* 复制各节区 */
|
|
|
|
|
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 >= image_size) continue;
|
|
|
|
|
u32 copy_size = raw_size;
|
|
|
|
|
if (va + copy_size > image_size) copy_size = image_size - va;
|
|
|
|
|
if ((u64)raw_off + copy_size > size) copy_size = (u32)(size - raw_off);
|
|
|
|
|
for (u32 j = 0; j < copy_size; j++) img_start[va + j] = base[raw_off + j];
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
/* 解析 imports,填充 IAT(合成地址) */
|
2026-07-28 14:35:14 +08:00
|
|
|
img->iat_count = 0;
|
|
|
|
|
const image_data_directory *import_dir = &oh->data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
|
|
|
|
if (import_dir->virtual_address != 0 && import_dir->size != 0) {
|
2026-07-31 13:53:49 +08:00
|
|
|
/* RVA->offset 辅助 */
|
2026-07-28 14:35:14 +08:00
|
|
|
for (int dll_idx = 0; ; dll_idx++) {
|
|
|
|
|
u32 imp_rva = import_dir->virtual_address + (u32)dll_idx * sizeof(image_import_descriptor);
|
|
|
|
|
/* imp_rva 在 image 内 */
|
|
|
|
|
if (imp_rva + sizeof(image_import_descriptor) > image_size) break;
|
|
|
|
|
const image_import_descriptor *desc = (const image_import_descriptor *)(img_start + imp_rva);
|
|
|
|
|
if (desc->name == 0 && desc->first_thunk == 0) break;
|
|
|
|
|
|
|
|
|
|
/* DLL 名 */
|
|
|
|
|
u32 name_rva = desc->name;
|
|
|
|
|
const char *dll_name = 0;
|
|
|
|
|
if (name_rva < image_size) dll_name = (const char *)(img_start + name_rva);
|
|
|
|
|
|
|
|
|
|
u32 iat_rva = desc->first_thunk;
|
|
|
|
|
u32 int_rva = desc->original_first_thunk;
|
|
|
|
|
if (int_rva == 0) int_rva = iat_rva;
|
|
|
|
|
|
|
|
|
|
for (int fn_idx = 0; ; fn_idx++) {
|
|
|
|
|
u32 int_entry_rva = int_rva + (u32)fn_idx * 4;
|
|
|
|
|
if (int_entry_rva + 4 > image_size) break;
|
|
|
|
|
u32 thunk = *(const u32 *)(img_start + int_entry_rva);
|
|
|
|
|
if (thunk == 0) break;
|
|
|
|
|
|
|
|
|
|
const char *func_name = 0;
|
|
|
|
|
if (thunk & 0x80000000u) {
|
|
|
|
|
func_name = "#ordinal";
|
|
|
|
|
} else {
|
|
|
|
|
u32 hint_rva = thunk;
|
|
|
|
|
if (hint_rva + 2 < image_size) {
|
|
|
|
|
func_name = (const char *)(img_start + hint_rva + 2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int shim_idx = -1;
|
|
|
|
|
if (func_name && func_name[0] != '#') {
|
|
|
|
|
shim_idx = pe_shim_lookup(dll_name, func_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 synth_addr;
|
|
|
|
|
if (shim_idx >= 0) {
|
|
|
|
|
synth_addr = PE32_SHIM_ADDR_BASE | (u32)(shim_idx & 0xFFFF);
|
|
|
|
|
} else {
|
|
|
|
|
synth_addr = PE32_SHIM_UNIMPL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 iat_entry_rva = iat_rva + (u32)fn_idx * 4;
|
|
|
|
|
if (iat_entry_rva + 4 <= image_size) {
|
|
|
|
|
*(u32 *)(img_start + iat_entry_rva) = synth_addr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (img->iat_count < PE32_MAX_IAT) {
|
|
|
|
|
img->iat[img->iat_count].iat_rva = iat_entry_rva;
|
|
|
|
|
img->iat[img->iat_count].shim_index = shim_idx;
|
|
|
|
|
img->iat[img->iat_count].target_addr = synth_addr;
|
|
|
|
|
img->iat_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shim_idx < 0 && func_name) {
|
|
|
|
|
log_info("[PE32] unimplemented import");
|
|
|
|
|
if (dll_name) serial_write(dll_name);
|
|
|
|
|
serial_write("!");
|
|
|
|
|
serial_write(func_name);
|
|
|
|
|
serial_write("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
img->mem = mem;
|
|
|
|
|
img->mem_size = total;
|
|
|
|
|
img->image_base = preferred_base;
|
|
|
|
|
img->image_size = image_size;
|
|
|
|
|
img->entry_point = preferred_base + entry_rva;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
/* ===== SSE/SSE2 启用 =====
|
|
|
|
|
* UTSM 自身用 -mno-sse -msoft-float 编译(启动早期安全),但 PE32+ 原生
|
|
|
|
|
* 代码(MSVC/LLVM x86_64 ABI)假定 SSE2 可用——浮点参数走 XMM0-3,
|
|
|
|
|
* egui 的 f32/f64 运算也全部编译为 SSE 指令.跳转 PE 入口前必须:
|
|
|
|
|
* CR0.EM=0, CR0.TS=0, CR0.MP=1, CR0.NE=1
|
|
|
|
|
* CR4.OSFXSR=1, CR4.OSXMMEXCPT=1
|
|
|
|
|
* MXCSR=0x1F80(屏蔽全部异常、round-to-nearest,Windows 默认)
|
|
|
|
|
* 内核 C 代码不带 SSE 指令,开启后对内核自身无副作用. */
|
|
|
|
|
static void pe_enable_sse(void) {
|
|
|
|
|
u64 cr0, cr4;
|
|
|
|
|
__asm__ volatile("mov %%cr0, %0" : "=r"(cr0));
|
|
|
|
|
cr0 &= ~((1ULL << 2) | (1ULL << 3)); /* EM=0, TS=0 */
|
|
|
|
|
cr0 |= (1ULL << 1) | (1ULL << 5); /* MP=1, NE=1 */
|
|
|
|
|
__asm__ volatile("mov %0, %%cr0" :: "r"(cr0));
|
|
|
|
|
__asm__ volatile("mov %%cr4, %0" : "=r"(cr4));
|
|
|
|
|
cr4 |= (1ULL << 9) | (1ULL << 10); /* OSFXSR=1, OSXMMEXCPT=1 */
|
|
|
|
|
__asm__ volatile("mov %0, %%cr4" :: "r"(cr4));
|
|
|
|
|
__asm__ volatile("fninit");
|
|
|
|
|
{
|
|
|
|
|
u32 mxcsr = 0x1F80;
|
|
|
|
|
__asm__ volatile("ldmxcsr %0" :: "m"(mxcsr));
|
|
|
|
|
}
|
|
|
|
|
log_info("[PE] SSE/SSE2 enabled for native PE32+ execution");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ===== PE 专用大栈 =====
|
|
|
|
|
* 调用链 UTSM->DSK->cmd->pe_service 全部运行在 16KB 内核栈上,
|
|
|
|
|
* Rust/egui 渲染(tessellate + 软光栅)栈需求远超 16KB,
|
|
|
|
|
* 因此跳转 PE 入口前切换到 arena 分配的 1MB 独立栈.
|
|
|
|
|
* 正常 ret 时由 trampoline 恢复旧 RSP;ExitProcess longjmp 路径
|
|
|
|
|
* 由 setjmp 时保存的内核 RSP 恢复,两条路径都安全. */
|
|
|
|
|
#define PE_STACK_BYTES (1024ULL * 1024ULL)
|
|
|
|
|
static u64 g_pe_saved_rsp;
|
|
|
|
|
|
|
|
|
|
typedef void (*pe_entry_fn)(void);
|
|
|
|
|
|
|
|
|
|
static void pe_call_on_stack(pe_entry_fn fn, u64 stack_top) {
|
|
|
|
|
__asm__ volatile(
|
|
|
|
|
"mov %%rsp, %0\n\t"
|
|
|
|
|
"mov %1, %%rsp\n\t"
|
|
|
|
|
"call *%2\n\t"
|
|
|
|
|
"mov %0, %%rsp\n\t"
|
|
|
|
|
: "=m"(g_pe_saved_rsp)
|
|
|
|
|
: "r"(stack_top), "r"(fn)
|
|
|
|
|
: "memory", "cc",
|
|
|
|
|
"rax", "rcx", "rdx", "rsi", "rdi",
|
|
|
|
|
"r8", "r9", "r10", "r11");
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-28 14:35:14 +08:00
|
|
|
/* ===== 运行 =====
|
2026-07-31 13:53:49 +08:00
|
|
|
* 加载 + 执行 + 卸载.返回 0 成功,*exit_code 为进程退出码. */
|
2026-07-28 14:35:14 +08:00
|
|
|
int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit_code) {
|
|
|
|
|
if (!pe_data || size == 0) return -1;
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
/* 初始化 shim 状态(cmdline for GetCommandLineA) */
|
2026-07-28 14:35:14 +08:00
|
|
|
pe_shim_init(cmdline ? cmdline : "");
|
|
|
|
|
|
|
|
|
|
/* 先解析 PE 判断 32/64 位 */
|
|
|
|
|
if (size < sizeof(image_dos_header)) return -2;
|
|
|
|
|
const image_dos_header *dos = (const image_dos_header *)pe_data;
|
|
|
|
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -3;
|
|
|
|
|
const u8 *nt_base = (const u8 *)pe_data + dos->e_lfanew;
|
|
|
|
|
if (*(const u32 *)nt_base != IMAGE_NT_SIGNATURE) return -4;
|
|
|
|
|
const void *opt = (const void *)(nt_base + 4 + sizeof(image_file_header));
|
|
|
|
|
u16 opt_magic = *(const u16 *)opt;
|
|
|
|
|
|
|
|
|
|
if (opt_magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
|
|
|
|
/* ===== PE32+ : Ring0 原生执行 ===== */
|
|
|
|
|
pe_image_info info;
|
|
|
|
|
int rc = pe_load_image(pe_data, size, &info);
|
|
|
|
|
if (rc != 0) {
|
|
|
|
|
log_error("[PE] PE32+ load failed");
|
|
|
|
|
return -5;
|
|
|
|
|
}
|
|
|
|
|
log_info("[PE] running PE32+ natively");
|
2026-07-31 13:53:49 +08:00
|
|
|
pe_enable_sse();
|
|
|
|
|
|
|
|
|
|
/* 独立 1MB 栈(内核栈仅 16KB,egui 渲染会溢出) */
|
|
|
|
|
u8 *pe_stack = (u8 *)kmem_alloc_aligned(PE_STACK_BYTES, 4096);
|
|
|
|
|
if (!pe_stack) {
|
|
|
|
|
log_error("[PE] stack alloc failed");
|
|
|
|
|
return -9;
|
|
|
|
|
}
|
|
|
|
|
u64 stack_top = (u64)pe_stack + PE_STACK_BYTES; /* 4096 对齐 -> 16 对齐 */
|
2026-07-28 14:35:14 +08:00
|
|
|
|
|
|
|
|
u64 code = 0;
|
|
|
|
|
int jumped = pe_shim_setup_exit();
|
|
|
|
|
if (jumped == 0) {
|
|
|
|
|
pe_entry_fn entry = (pe_entry_fn)info.entry_point;
|
|
|
|
|
log_hex64("[PE] entry=", (u64)entry);
|
2026-07-31 13:53:49 +08:00
|
|
|
log_hex64("[PE] stack_top=", stack_top);
|
|
|
|
|
pe_call_on_stack(entry, stack_top);
|
2026-07-28 14:35:14 +08:00
|
|
|
code = 0;
|
|
|
|
|
} else {
|
|
|
|
|
code = pe_shim_get_exit_code();
|
|
|
|
|
log_hex64("[PE] ExitProcess code=", code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pe_unload_image(&info);
|
|
|
|
|
if (exit_code) *exit_code = code;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
} else if (opt_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
|
|
|
/* ===== PE32 : x86-32 解释器执行 ===== */
|
|
|
|
|
pe32_interp_image img;
|
|
|
|
|
int rc = pe32_load_into(pe_data, size, &img);
|
|
|
|
|
if (rc != 0) {
|
|
|
|
|
log_error("[PE] PE32 interp load failed");
|
|
|
|
|
log_hex64("[PE] rc=", (u64)(i64)rc);
|
|
|
|
|
return -6;
|
|
|
|
|
}
|
|
|
|
|
log_info("[PE] running PE32 via x86emu32");
|
|
|
|
|
|
|
|
|
|
u32 stack_top = img.image_base + img.image_size + PE32_STACK_SIZE - 16;
|
|
|
|
|
|
|
|
|
|
x86emu_state emu;
|
|
|
|
|
int irc = x86emu32_init(&emu, img.mem, img.mem_size,
|
|
|
|
|
img.image_base, img.image_size,
|
|
|
|
|
img.iat, img.iat_count, stack_top);
|
|
|
|
|
if (irc != 0) {
|
|
|
|
|
log_error("[PE32] emu init failed");
|
|
|
|
|
return -7;
|
|
|
|
|
}
|
|
|
|
|
emu.eip = img.entry_point;
|
|
|
|
|
|
|
|
|
|
u64 code = 0;
|
|
|
|
|
int jumped = pe_shim_setup_exit();
|
|
|
|
|
if (jumped == 0) {
|
|
|
|
|
int rrc = x86emu32_run(&emu, &code);
|
|
|
|
|
if (rrc != 0) {
|
|
|
|
|
log_error("[PE32] emulator error");
|
|
|
|
|
code = 0xFFFFFFFFULL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
code = pe_shim_get_exit_code();
|
|
|
|
|
log_hex64("[PE32] ExitProcess code=", code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (emu.error && !emu.exited) {
|
|
|
|
|
log_warn("[PE32] emulator stopped with error");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (exit_code) *exit_code = code;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -8; /* 未知 PE magic */
|
|
|
|
|
}
|