This commit is contained in:
Linehu
2026-08-09 22:20:01 +08:00
parent 8717ea8f20
commit 2a50fa58c7
185 changed files with 19890 additions and 710 deletions
+229 -11
View File
@@ -54,9 +54,74 @@ typedef struct {
} pe_dll_entry;
static pe_dll_entry g_dll_cache[PE_DLL_CACHE_MAX];
/* ===== 延迟 DllMain 队列 =====
* pe_load_image 触发递归 pe_dll_load 时,PE32+ 原生执行环境尚未就绪:
* SSE 未启用(pe_enable_sse 在 load 之后才调用)、仍跑在 16KB 内核栈上。
* 真实 DLL 的 CRT 初始化含 SSE 指令且栈需求大,立即调用必 #UD/溢栈。
* 故加载阶段只登记 DllMain,由 pe_service_run 在启用 SSE 并切换到
* 1MB PE 大栈后调 pe_dll_run_pending_dllmains() 统一执行。 */
#define PE_DLLMAIN_PENDING_MAX 32
typedef struct {
u64 image_base;
u64 entry_point;
char name[PE_DLL_NAME_MAX];
} pe_dllmain_pending;
static pe_dllmain_pending g_dllmain_pending[PE_DLLMAIN_PENDING_MAX];
static int g_dllmain_pending_count = 0;
static int g_dllmain_defer = 0;
/* enable=1 开启延迟模式并清空队列(每次 pe_service_run 加载前调用);
* enable=0 恢复立即调用(兜底路径用)。 */
void pe_dll_defer_dllmain(int enable) {
g_dllmain_defer = enable;
if (enable) g_dllmain_pending_count = 0;
}
/* 在大栈 + SSE 环境下执行全部已登记的 DllMain(ATTACH)。
* 执行完毕清空队列并关闭延迟模式。 */
void pe_dll_run_pending_dllmains(void) {
for (int i = 0; i < g_dllmain_pending_count; i++) {
pe_dllmain_pending *p = &g_dllmain_pending[i];
typedef int (__attribute__((ms_abi)) *dllmain_fn)(u64, u32, u64);
dllmain_fn dm = (dllmain_fn)p->entry_point;
int ok = dm(p->image_base, 1 /* DLL_PROCESS_ATTACH */, 0);
if (!ok) {
log_warn("[PE] DllMain returned FALSE (init failed)");
log_info(p->name);
} else {
log_info("[PE] DllMain ATTACH ok");
}
}
g_dllmain_pending_count = 0;
g_dllmain_defer = 0;
}
static pe_dll_reader_fn g_dll_reader = 0;
int g_pe_dll_manager_ready = 0;
/* 转发导出递归深度上限(A->B->C 链) */
#define PE_DLL_FWD_DEPTH_MAX 8
/* ---- API set 重定向 ----
* Windows 10+ 的 api-ms-win-crt-*.dll 是 API set 虚拟 DLL,
* 物理上不存在文件,由 loader 按 schema 重定向到 ucrtbase.dll。
* 此处实现等价映射:api-ms-win-crt-* -> ucrtbase.dll。
* 返回目标 DLL 名(静态字符串),或 NULL 表示非 API set。 */
static const char *apiset_redirect(const char *dll_name) {
if (!dll_name) return 0;
/* 大小写不敏感前缀匹配 "api-ms-win-crt-" */
static const char prefix[] = "api-ms-win-crt-";
int i = 0;
while (prefix[i]) {
char c = dll_name[i];
if (!c) return 0;
if (c >= 'a' && c <= 'z') c = (char)(c - 32);
if (c != prefix[i]) return 0;
i++;
}
return "ucrtbase.dll";
}
/* ---- 辅助函数 ---- */
/* ASCII 大小写不敏感字符串比较(freestanding,无 strcmp) */
@@ -134,12 +199,37 @@ static void extract_export_dir(u64 image_base, u32 *out_rva, u32 *out_size) {
*out_size = data_dir[IMAGE_DIRECTORY_ENTRY_EXPORT].size;
}
/* 在已加载 DLL 的导出表中按名称查找函数。
* 返回绝对地址(image_base + func_rva),0=未找到/转发导出。 */
static u64 resolve_export_by_name(u64 image_base, u32 export_dir_rva,
const char *func_name) {
/* 按序号在已加载 DLL 的导出表中查找函数(转发串 "#123" 用)。
* 返回绝对地址,0=未找到/越界。 */
static u64 resolve_export_by_ordinal(u64 image_base, u32 export_dir_rva,
u32 ordinal) {
if (export_dir_rva == 0) return 0;
const u8 *img = (const u8 *)image_base;
const image_export_directory *exp =
(const image_export_directory *)(img + export_dir_rva);
if (ordinal < exp->ordinal_base) return 0;
u32 idx = ordinal - exp->ordinal_base;
if (idx >= exp->number_of_functions) return 0;
const u32 *functions = (const u32 *)(img + exp->address_of_functions);
u32 func_rva = functions[idx];
if (func_rva == 0) return 0;
return image_base + func_rva;
}
/* 解析转发导出串("DLL.Func" 或 "DLL.#123")。
* fwd_str 位于 image_base+func_rva 处(导出目录数据区内)。
* depth 防止 A→B→A 循环转发。返回绝对地址,0=失败。 */
static u64 resolve_forwarded_export(u64 image_base, u32 func_rva, int depth);
/* 在已加载 DLL 的导出表中按名称查找函数。
* 返回绝对地址(image_base + func_rva),0=未找到。
* 转发导出自动跟随解析(PE 规范:func_rva 落在导出目录数据区内即为转发串)。 */
static u64 resolve_export_by_name(u64 image_base, u32 export_dir_rva,
u32 export_dir_size, const char *func_name,
int depth) {
if (export_dir_rva == 0) return 0;
if (depth > PE_DLL_FWD_DEPTH_MAX) return 0;
const u8 *img = (const u8 *)image_base;
const image_export_directory *exp =
(const image_export_directory *)(img + export_dir_rva);
if (exp->number_of_names == 0) return 0;
@@ -155,12 +245,12 @@ static u64 resolve_export_by_name(u64 image_base, u32 export_dir_rva,
if (eat_index >= exp->number_of_functions) return 0;
u32 func_rva = functions[eat_index];
/* 转发导出:func_rva 落在导出表范围内 → 是转发字符串而非代码地址 */
/* 转发导出(PE 规范):func_rva 落在导出目录数据区
* [export_dir_rva, export_dir_rva+export_dir_size) 内 →
* 该处是 "DLL.Func" 转发字符串而非代码地址 */
if (func_rva >= export_dir_rva &&
func_rva < export_dir_rva + exp->number_of_functions * 4) {
/* 粗略范围检查(更精确应使用 export_dir_size,但此处足够) */
log_info("[PE] forwarded export (not supported)");
return 0;
func_rva < export_dir_rva + export_dir_size) {
return resolve_forwarded_export(image_base, func_rva, depth + 1);
}
return image_base + func_rva;
}
@@ -168,6 +258,74 @@ static u64 resolve_export_by_name(u64 image_base, u32 export_dir_rva,
return 0;
}
/* 转发串解析:"NTDLL.RtlInitUnicodeString" 或 "KERNEL32.#123"。
* DLL 名通常不带 .dll 后缀(如 "UCRTBASE"),需补全。 */
static u64 resolve_forwarded_export(u64 image_base, u32 func_rva, int depth) {
if (depth > PE_DLL_FWD_DEPTH_MAX) {
log_warn("[PE] forwarded export chain too deep");
return 0;
}
const char *fwd = (const char *)(image_base + func_rva);
/* 拷贝到本地缓冲并找分隔点 '.'(转发串最大 ~64 字符,防御截断) */
char buf[96];
u32 i = 0;
while (i + 1 < sizeof(buf) && fwd[i] && fwd[i] != ' ') {
buf[i] = fwd[i];
i++;
}
buf[i] = 0;
u32 dot = 0;
while (buf[dot] && buf[dot] != '.') dot++;
if (buf[dot] != '.' || dot == 0 || buf[dot + 1] == 0) {
log_warn("[PE] malformed forwarder string");
return 0;
}
buf[dot] = 0;
const char *target_func = buf + dot + 1;
/* 目标 DLL 名:小写化 + 补 ".dll" 后缀 */
char tdll[PE_DLL_NAME_MAX];
dll_name_lower(buf, tdll, sizeof(tdll));
int tl = 0;
while (tdll[tl]) tl++;
if (tl + 4 < (int)sizeof(tdll)) {
/* 已有 ".dll" 后缀则不重复添加 */
if (!(tl >= 4 && tdll[tl-4] == '.' &&
(tdll[tl-3] == 'd') && (tdll[tl-2] == 'l') && (tdll[tl-1] == 'l'))) {
tdll[tl++] = '.'; tdll[tl++] = 'd';
tdll[tl++] = 'l'; tdll[tl++] = 'l';
tdll[tl] = 0;
}
}
/* 序号转发:"#123" */
if (target_func[0] == '#') {
u32 ord = 0;
const char *p = target_func + 1;
if (*p == 0) return 0;
while (*p >= '0' && *p <= '9') {
ord = ord * 10 + (u32)(*p - '0');
p++;
}
if (ord == 0 || ord > 0xFFFF) return 0;
/* 需先确保目标 DLL 已加载 */
u64 base = 0;
if (pe_dll_load(tdll, &base) != 0) return 0;
int idx = dll_cache_find(tdll);
if (idx < 0) return 0;
u64 addr = resolve_export_by_ordinal(base, g_dll_cache[idx].export_dir_rva, ord);
if (addr == 0) {
log_warn("[PE] ordinal forward target not found");
}
return addr;
}
/* 名称转发:递归走 pe_dll_resolve(带深度计) */
return pe_dll_resolve_ex(tdll, target_func, depth);
}
/* ---- 公共接口 ---- */
void pe_dll_manager_init(pe_dll_reader_fn reader) {
@@ -185,6 +343,10 @@ int pe_dll_load(const char *dll_name, u64 *out_base) {
return -1;
}
/* API set 重定向:api-ms-win-crt-* -> ucrtbase.dll */
const char *redirected = apiset_redirect(dll_name);
if (redirected) dll_name = redirected;
/* 查缓存 */
int idx = dll_cache_find(dll_name);
if (idx >= 0) {
@@ -254,13 +416,49 @@ int pe_dll_load(const char *dll_name, u64 *out_base) {
log_info(dll_name);
log_hex64("[PE] DLL base=", info.image_base);
/* 调用 DllMain(hinst, DLL_PROCESS_ATTACH=1, NULL)。
* 真实 DLL(如 ucrtbase)依赖入口完成 CRT/TLS 初始化。
* 无入口点(AddressOfEntryPoint==0 → entry==image_base)则跳过。
* 返回 0(初始化失败)只记告警不阻断:无状态函数仍可用。
* 延迟模式(pe_service_run 加载阶段):只登记不调用,待 SSE 启用
* 且切换到 1MB PE 大栈后由 pe_dll_run_pending_dllmains 统一执行。 */
if (info.entry_point != info.image_base) {
if (g_dllmain_defer) {
if (g_dllmain_pending_count < PE_DLLMAIN_PENDING_MAX) {
pe_dllmain_pending *p = &g_dllmain_pending[g_dllmain_pending_count++];
p->image_base = info.image_base;
p->entry_point = info.entry_point;
int i = 0;
for (; i < PE_DLL_NAME_MAX - 1 && entry->name[i]; i++) p->name[i] = entry->name[i];
p->name[i] = 0;
log_info("[PE] DllMain deferred");
} else {
log_warn("[PE] DllMain pending queue full, skipped");
}
} else {
typedef int (__attribute__((ms_abi)) *dllmain_fn)(u64, u32, u64);
dllmain_fn dm = (dllmain_fn)info.entry_point;
int ok = dm(info.image_base, 1 /* DLL_PROCESS_ATTACH */, 0);
if (!ok) {
log_warn("[PE] DllMain returned FALSE (init failed)");
log_info(dll_name);
} else {
log_info("[PE] DllMain ATTACH ok");
}
}
}
if (out_base) *out_base = info.image_base;
return 0;
}
u64 pe_dll_resolve(const char *dll_name, const char *func_name) {
u64 pe_dll_resolve_ex(const char *dll_name, const char *func_name, int depth) {
if (!g_pe_dll_manager_ready || !dll_name || !func_name) return 0;
/* API set 重定向:api-ms-win-crt-* -> ucrtbase.dll */
const char *redirected = apiset_redirect(dll_name);
if (redirected) dll_name = redirected;
/* 确保 DLL 已加载 */
int idx = dll_cache_find(dll_name);
if (idx < 0 || g_dll_cache[idx].state != DLL_STATE_LOADED) {
@@ -271,5 +469,25 @@ u64 pe_dll_resolve(const char *dll_name, const char *func_name) {
}
pe_dll_entry *e = &g_dll_cache[idx];
return resolve_export_by_name(e->image_base, e->export_dir_rva, func_name);
return resolve_export_by_name(e->image_base, e->export_dir_rva,
e->export_dir_size, func_name, depth);
}
u64 pe_dll_resolve(const char *dll_name, const char *func_name) {
return pe_dll_resolve_ex(dll_name, func_name, 0);
}
u64 pe_dll_resolve_in_base(u64 image_base, const char *func_name, u32 ordinal) {
if (!g_pe_dll_manager_ready || image_base == 0) return 0;
for (int i = 0; i < PE_DLL_CACHE_MAX; i++) {
pe_dll_entry *e = &g_dll_cache[i];
if (e->state != DLL_STATE_LOADED || e->image_base != image_base) continue;
if (ordinal != 0)
return resolve_export_by_ordinal(e->image_base, e->export_dir_rva, ordinal);
if (func_name)
return resolve_export_by_name(e->image_base, e->export_dir_rva,
e->export_dir_size, func_name, 0);
return 0;
}
return 0;
}
+14
View File
@@ -48,4 +48,18 @@ int pe_dll_load(const char *dll_name, u64 *out_base);
* 返回绝对地址,0=未找到。 */
u64 pe_dll_resolve(const char *dll_name, const char *func_name);
/* 带转发深度的内部变体(转发导出链 A→B→C 递归用,外部勿调)。 */
u64 pe_dll_resolve_ex(const char *dll_name, const char *func_name, int depth);
/* 在已加载 DLL 的模块基址上解析导出(GetProcAddress 真实 DLL 路径用)。
* image_base 必须是 pe_dll_load 返回的基址;func_name 与 ordinal 二选一
* (ordinal != 0 时按序号)。返回绝对地址,0=未找到。 */
u64 pe_dll_resolve_in_base(u64 image_base, const char *func_name, u32 ordinal);
/* 延迟 DllMain 控制(PE32+ 加载期 SSE/大栈未就绪,DllMain 必须延迟)。
* pe_service_run 在 pe_load_image 前调 pe_dll_defer_dllmain(1),
* 切换到 1MB PE 大栈后调 pe_dll_run_pending_dllmains() 统一执行。 */
void pe_dll_defer_dllmain(int enable);
void pe_dll_run_pending_dllmains(void);
#endif /* PE_DLL_MANAGER_H */
+54 -4
View File
@@ -12,6 +12,7 @@
#include "pe_loader.h"
#include "pe_shim.h"
#include "pe_dll_manager.h"
#include "x86emu32.h"
#include "../include/utsm/pe.h"
#include <utsm/arena.h>
@@ -218,19 +219,53 @@ static void resolve_imports(u8 *image, u64 image_base_actual,
shim_idx = pe_shim_lookup(dll_name, func_name);
}
/* 当 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;
}
/* 填充 IAT(image 缓冲区内) */
u8 *iat_ptr = image + iat_rva + (u32)fn_idx * (is_pe32_plus ? 8 : 4);
int resolved_by_real_dll = 0;
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;
} 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();
}
} else {
fn_addr = pe_shim_unimpl_stub();
}
*(u64 *)iat_ptr = fn_addr;
} else {
/* 32位:填合成地址,解释器拦截 */
/* 32位:填合成地址,解释器拦截(不调真实 DLL) */
u32 synth_addr;
if (shim_idx >= 0) {
synth_addr = PE_SHIM_ADDR_BASE | (u32)(shim_idx & 0xFFFF);
@@ -248,11 +283,26 @@ static void resolve_imports(u8 *image, u64 image_base_actual,
}
}
if (shim_idx < 0 && func_name) {
/* shim 未命中且真实 DLL 也未解析:记未实现日志;
* 真实 DLL 解析成功则记来源日志(区分 shim/真实 DLL 路径)。
* 用本地副本(pe_data 可能已被 DLL 加载破坏) */
if (shim_idx < 0 && func_name && !resolved_by_real_dll) {
log_info("[PE] unimplemented import");
serial_write(dll_name);
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);
serial_write("!");
serial_write(func_name);
serial_write(func_buf);
serial_write("\n");
}
}
+3 -2
View File
@@ -1,10 +1,11 @@
pe/pe_loader.o: pe\pe_loader.c pe\pe_loader.h include\utsm\types.h \
include\utsm\pe.h pe\pe_shim.h pe\x86emu32.h pe\..\include\utsm\pe.h \
include\utsm\arena.h include\utsm\log.h
include\utsm\pe.h pe\pe_shim.h pe\pe_dll_manager.h pe\x86emu32.h \
pe\..\include\utsm\pe.h include\utsm\arena.h include\utsm\log.h
pe\pe_loader.h:
include\utsm\types.h:
include\utsm\pe.h:
pe\pe_shim.h:
pe\pe_dll_manager.h:
pe\x86emu32.h:
pe\..\include\utsm\pe.h:
include\utsm\arena.h:
+43 -5
View File
@@ -14,6 +14,7 @@
#include "pe_loader.h"
#include "pe_shim.h"
#include "pe_dll_manager.h"
#include "x86emu32.h"
#include "../include/utsm/pe.h"
#include <utsm/arena.h>
@@ -34,13 +35,20 @@
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);
int pe_service_run_windowed(const void *pe_data, u64 size, const char *cmdline,
u64 *exit_code, const pe_window_host *host);
/* 全局服务表实例 */
/* 全局服务表实例(P5:ABI 尾部追加 run_windowed/inject_*,同镜像一致性
* 假设见 pe.h;旧调用方按前 4 字段使用不受影响) */
static const pe_service g_pe_service = {
PE_SERVICE_MAGIC,
pe_service_load,
pe_service_run,
pe_service_unload,
pe_service_run_windowed,
pe_shim_inject_scancode,
pe_shim_inject_pointer,
pe_shim_inject_input,
};
const pe_service *pe_get_service(void) {
@@ -248,6 +256,14 @@ static void pe_call_on_stack(pe_entry_fn fn, u64 stack_top) {
"r8", "r9", "r10", "r11");
}
/* PE32+ 大栈引导入口:先执行加载期延迟登记的真实 DLL DllMain(ATTACH),
* 再跳 PE 入口。此时 SSE 已启用且运行在 1MB PE 大栈上。 */
static pe_entry_fn g_pe_boot_entry;
static void pe_boot_with_dllmains(void) {
pe_dll_run_pending_dllmains();
g_pe_boot_entry();
}
/* ===== 运行 =====
* 加载 + 执行 + 卸载.返回 0 成功,*exit_code 为进程退出码. */
int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit_code) {
@@ -268,13 +284,18 @@ int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit
if (opt_magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
/* ===== PE32+ : Ring0 原生执行 ===== */
pe_image_info info;
/* SSE 必须在任何 PE/DLL 代码执行前启用:load 期会递归加载真实 DLL,
* MSVC CRT 代码含 SSE 指令,后置启用曾在 DllMain 内触发 #UD。 */
pe_enable_sse();
/* DllMain 延迟到 1MB PE 大栈上执行(16KB 内核栈跑不动 CRT init) */
pe_dll_defer_dllmain(1);
int rc = pe_load_image(pe_data, size, &info);
if (rc != 0) {
pe_dll_defer_dllmain(0);
log_error("[PE] PE32+ load failed");
return -5;
}
log_info("[PE] running PE32+ natively");
pe_enable_sse();
/* 恢复异常可见性:UTSM 交权 DSK 时 idt_halt_all() 将 IDT 全部
* 替换为静默 halt stub,PE 原生执行期间的 CPU 异常(PF/GP/UD)
@@ -297,10 +318,10 @@ int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit
* 残片充当 R12-R15/RBP → 野指针 call → 三重故障). */
u64 code = 0;
if (__builtin_setjmp(pe_shim_exit_jmpbuf()) == 0) {
pe_entry_fn entry = (pe_entry_fn)info.entry_point;
log_hex64("[PE] entry=", (u64)entry);
log_hex64("[PE] entry=", (u64)info.entry_point);
log_hex64("[PE] stack_top=", stack_top);
pe_call_on_stack(entry, stack_top);
g_pe_boot_entry = (pe_entry_fn)info.entry_point;
pe_call_on_stack(pe_boot_with_dllmains, stack_top);
code = 0;
} else {
code = pe_shim_get_exit_code();
@@ -365,3 +386,20 @@ int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit
return -8; /* 未知 PE magic */
}
/* ===== P5 窗口模式运行 =====
* 与 pe_service_run 相同的加载/执行路径,区别仅在 shim 行为:
* 渲染目标重定向到 host->surface,PS/2 轮询关闭,消息空转回调
* host->pump()(desktop 帧)。host==NULL/surface==NULL 时等价 run。 */
int pe_service_run_windowed(const void *pe_data, u64 size, const char *cmdline,
u64 *exit_code, const pe_window_host *host) {
if (host && host->surface) {
pe_shim_set_window_host(host);
log_info("[PE] run_windowed: surface mode");
} else {
pe_shim_set_window_host(0);
}
int rc = pe_service_run(pe_data, size, cmdline, exit_code);
pe_shim_set_window_host(0); /* 防止残留配置串到下一次 run */
return rc;
}
+4 -2
View File
@@ -1,10 +1,12 @@
pe/pe_service.o: pe\pe_service.c pe\pe_loader.h include\utsm\types.h \
include\utsm\pe.h pe\pe_shim.h pe\x86emu32.h pe\..\include\utsm\pe.h \
include\utsm\arena.h include\utsm\idt.h include\utsm\log.h
include\utsm\pe.h pe\pe_shim.h pe\pe_dll_manager.h pe\x86emu32.h \
pe\..\include\utsm\pe.h include\utsm\arena.h include\utsm\idt.h \
include\utsm\log.h
pe\pe_loader.h:
include\utsm\types.h:
include\utsm\pe.h:
pe\pe_shim.h:
pe\pe_dll_manager.h:
pe\x86emu32.h:
pe\..\include\utsm\pe.h:
include\utsm\arena.h:
+264 -38
View File
@@ -25,6 +25,8 @@
*/
#include "pe_shim.h"
#include "pe_dll_manager.h"
#include "../include/utsm/pe.h" /* P5: pe_window_host */
#include <utsm/arena.h>
#include <utsm/log.h>
#include <utsm/types.h>
@@ -69,11 +71,57 @@ static inline u64 gp(u64 va) {
return va;
}
/* ===== P5 窗口模式状态 =====
* 全屏模式(默认):s_wsurf==NULL,shim 直写 Limine fb + 轮询 PS/2。
* 窗口模式:渲染目标为 host surface(伪造 fb),输入靠 inject_*,
* 消息空转时回调 host pump(desktop 帧)。 */
static u8 *s_wsurf = 0;
static u32 s_wsurf_w = 0, s_wsurf_h = 0, s_wsurf_pitch = 0;
static int (*s_host_pump)(void *ud) = 0;
static void *s_host_ud = 0;
static struct limine_framebuffer s_wsurf_fb; /* 伪造 fb(窗口模式) */
static const pe_window_host *s_winhost_pending = 0; /* service 层挂起配置 */
void pe_shim_set_window_host(const pe_window_host *host) {
s_winhost_pending = host;
}
int pe_shim_window_mode(void) { return s_wsurf != 0; }
void pe_shim_init(const char *cmdline) {
g_cmdline = cmdline ? cmdline : "";
g_exit_jmp_set = 0;
g_exit_code = 0;
g_emu_mem_base = 0; /* BUG-007: 默认非解释器上下文(原生路径恒等映射) */
/* P5:应用 service 层挂起的窗口模式配置(在状态重置之后生效) */
if (s_winhost_pending && s_winhost_pending->surface
&& s_winhost_pending->width && s_winhost_pending->height
&& s_winhost_pending->pitch) {
s_wsurf = s_winhost_pending->surface;
s_wsurf_w = s_winhost_pending->width;
s_wsurf_h = s_winhost_pending->height;
s_wsurf_pitch = s_winhost_pending->pitch;
s_host_pump = s_winhost_pending->pump;
s_host_ud = s_winhost_pending->ud;
/* 伪造 fb:32bpp 0xAARRGGBB(与 BitBlt/SetDIBits fast path 掩码一致) */
s_wsurf_fb.address = s_wsurf;
s_wsurf_fb.width = s_wsurf_w;
s_wsurf_fb.height = s_wsurf_h;
s_wsurf_fb.pitch = s_wsurf_pitch;
s_wsurf_fb.bpp = 32;
s_wsurf_fb.memory_model = 1;
s_wsurf_fb.red_mask_shift = 16; s_wsurf_fb.red_mask_size = 8;
s_wsurf_fb.green_mask_shift = 8; s_wsurf_fb.green_mask_size = 8;
s_wsurf_fb.blue_mask_shift = 0; s_wsurf_fb.blue_mask_size = 8;
log_info("[shim] window mode: surface target");
} else {
s_wsurf = 0;
s_wsurf_w = s_wsurf_h = s_wsurf_pitch = 0;
s_host_pump = 0;
s_host_ud = 0;
}
s_winhost_pending = 0;
}
/* 返回 ExitProcess longjmp 目标 jmpbuf(见 pe_shim.h 的 BUG-006 说明).
@@ -697,7 +745,7 @@ static u64 __attribute__((ms_abi)) shim_LoadLibraryA(u64 name_p) {
name_p = gp(name_p);
if (!name_p) return 0;
const char *n = (const char *)name_p;
/* 已知 DLL 返回伪句柄(大小写不敏感子串匹配) */
/* 已知 shim DLL 返回伪句柄(大小写不敏感子串匹配) */
static const char *known[] = { "kernel32", "ntdll", "user32", "gdi32", "msvcrt" };
for (int k = 0; k < 5; k++) {
const char *pat = known[k];
@@ -709,7 +757,22 @@ static u64 __attribute__((ms_abi)) shim_LoadLibraryA(u64 name_p) {
if (a != b) break;
i++;
}
if (pat[i] == 0) return 0x10000020;
if (pat[i] == 0) return PE_SHIM_FAKE_MODULE;
}
}
/* 未知 DLL:尝试从 SYSTEM/lib 加载真实 DLL(句柄=镜像基址) */
if (g_pe_dll_manager_ready) {
char nb[64];
u32 i = 0;
while (n[i] && i + 1 < sizeof(nb)) { nb[i] = n[i]; i++; }
nb[i] = 0;
u64 base = 0;
if (pe_dll_load(nb, &base) == 0 && base != 0) {
log_info("[shim] LoadLibrary real DLL ok");
serial_write(" -> ");
serial_write(nb);
serial_write("\n");
return base;
}
}
g_last_error = 126; /* ERROR_MOD_NOT_FOUND */
@@ -723,13 +786,22 @@ static u64 __attribute__((ms_abi)) shim_lstrlenW(u64 str_p) {
return n;
}
/* GetProcAddress — 在 shim 表内按函数名全局查找(hModule 为伪句柄,不限定 DLL).
* lpProcName 高 16 位为 0 时是 ordinal(不支持,返回 0)。
* Rust std 用它动态探测 Rtl 系列/dbghelp API,命中返回真实 shim 地址,miss 返回 0。 */
/* GetProcAddress — 伪句柄在 shim 表内按函数名全局查找(不限定 DLL);
* 真实 DLL 句柄(pe_dll_load 返回的镜像基址)在该 DLL 导出表内解析。
* lpProcName 高 16 位为 0 时是 ordinal。 */
static u64 __attribute__((ms_abi)) shim_GetProcAddress(u64 module, u64 name_p) {
(void)module;
name_p = gp(name_p);
if (!name_p) return 0;
/* 真实 DLL 模块:按基址解析(支持名称与序号) */
if (module && module != PE_SHIM_FAKE_MODULE && g_pe_dll_manager_ready) {
u32 ord = ((name_p >> 16) == 0) ? (u32)(name_p & 0xFFFF) : 0;
u64 addr = pe_dll_resolve_in_base(module,
ord ? 0 : (const char *)name_p, ord);
if (addr) return addr;
g_last_error = 127; /* ERROR_PROC_NOT_FOUND */
return 0;
}
(void)module;
if ((name_p >> 16) == 0) { /* ordinal 导入 */
g_last_error = 127; /* ERROR_PROC_NOT_FOUND */
return 0;
@@ -1153,8 +1225,47 @@ static void shim_mouse_byte(u8 b) {
s_mouse_btn = nbtn;
}
/* 排空 PS/2 输出缓冲,转成 Win32 消息入队 */
/* 单个 set-1 扫描码 -> Win32 消息入队(P5 抽出:全屏 PS/2 轮询与
* 窗口模式 host 注入共用同一份 vk 映射/修饰键跟踪/WM_CHAR 合成)。
* sc: 原始扫描码(bit7=release);e0: 1=E0 扩展键。 */
static void shim_kbd_inject(u8 sc, int e0) {
if (!e0) {
if (sc == 0x2A || sc == 0x36) { s_kbd_shift = 1; shim_msg_push(SHIM_WM_KEYDOWN, 0x10, sc); return; }
if (sc == 0xAA || sc == 0xB6) { s_kbd_shift = 0; shim_msg_push(SHIM_WM_KEYUP, 0x10, sc); return; }
if (sc == 0x1D) { s_kbd_ctrl = 1; shim_msg_push(SHIM_WM_KEYDOWN, 0x11, sc); return; }
if (sc == 0x9D) { s_kbd_ctrl = 0; shim_msg_push(SHIM_WM_KEYUP, 0x11, sc); return; }
if (sc == 0x38) { s_kbd_alt = 1; shim_msg_push(SHIM_WM_KEYDOWN, 0x12, sc); return; }
if (sc == 0xB8) { s_kbd_alt = 0; shim_msg_push(SHIM_WM_KEYUP, 0x12, sc); return; }
}
if (sc & 0x80) {
u32 vk = shim_scan_vk(sc & 0x7F, e0);
if (vk) {
shim_msg_push(SHIM_WM_KEYUP, vk, sc);
if (s_dbg_key < 16) { s_dbg_key++; log_hex64("[shim] KEYUP vk=", vk); }
}
return;
}
u32 vk = shim_scan_vk(sc, e0);
if (vk) {
shim_msg_push(SHIM_WM_KEYDOWN, vk, sc);
if (s_dbg_key < 16) { s_dbg_key++; log_hex64("[shim] KEYDOWN vk=", vk); }
/* Esc -> 额外合成 WM_CLOSE:无鼠标环境下关闭窗口的唯一途径 */
if (vk == 0x1B && !e0) shim_msg_push(SHIM_WM_CLOSE, 0, 0);
}
if (!e0) {
char c = shim_scan_ascii(sc, s_kbd_shift);
if (c >= ' ' && c < 0x7F) {
shim_msg_push(SHIM_WM_CHAR, (u64)(u8)c, sc);
if (s_dbg_key < 16) { s_dbg_key++; log_hex64("[shim] CHAR ch=", (u64)(u8)c); }
}
}
}
/* 排空 PS/2 输出缓冲,转成 Win32 消息入队。
* P5 窗口模式:输入权归 desktop(经 inject_* 入队),此函数 no-op,
* 绝不触碰 PS/2 端口(避免与 desktop 的输入采集竞争)。 */
static void shim_kbd_poll(void) {
if (s_wsurf) return; /* 窗口模式:host 注入,不轮询端口 */
shim_mouse_init_once();
for (;;) {
u8 st = inb(0x64);
@@ -1167,37 +1278,64 @@ static void shim_kbd_poll(void) {
}
if (sc == 0xE0) { s_kbd_e0 = 1; continue; }
int e0 = s_kbd_e0;
if (!e0) {
if (sc == 0x2A || sc == 0x36) { s_kbd_shift = 1; shim_msg_push(SHIM_WM_KEYDOWN, 0x10, sc); continue; }
if (sc == 0xAA || sc == 0xB6) { s_kbd_shift = 0; shim_msg_push(SHIM_WM_KEYUP, 0x10, sc); continue; }
if (sc == 0x1D) { s_kbd_ctrl = 1; shim_msg_push(SHIM_WM_KEYDOWN, 0x11, sc); continue; }
if (sc == 0x9D) { s_kbd_ctrl = 0; shim_msg_push(SHIM_WM_KEYUP, 0x11, sc); continue; }
if (sc == 0x38) { s_kbd_alt = 1; shim_msg_push(SHIM_WM_KEYDOWN, 0x12, sc); continue; }
if (sc == 0xB8) { s_kbd_alt = 0; shim_msg_push(SHIM_WM_KEYUP, 0x12, sc); continue; }
}
s_kbd_e0 = 0;
if (sc & 0x80) {
u32 vk = shim_scan_vk(sc & 0x7F, e0);
if (vk) {
shim_msg_push(SHIM_WM_KEYUP, vk, sc);
if (s_dbg_key < 16) { s_dbg_key++; log_hex64("[shim] KEYUP vk=", vk); }
}
continue;
}
u32 vk = shim_scan_vk(sc, e0);
if (vk) {
shim_msg_push(SHIM_WM_KEYDOWN, vk, sc);
if (s_dbg_key < 16) { s_dbg_key++; log_hex64("[shim] KEYDOWN vk=", vk); }
/* Esc -> 额外合成 WM_CLOSE:无鼠标环境下关闭窗口的唯一途径 */
if (vk == 0x1B && !e0) shim_msg_push(SHIM_WM_CLOSE, 0, 0);
}
if (!e0) {
char c = shim_scan_ascii(sc, s_kbd_shift);
if (c >= ' ' && c < 0x7F) {
shim_msg_push(SHIM_WM_CHAR, (u64)(u8)c, sc);
if (s_dbg_key < 16) { s_dbg_key++; log_hex64("[shim] CHAR ch=", (u64)(u8)c); }
}
}
shim_kbd_inject(sc, e0);
}
}
/* ===== P5 窗口模式输入注入(pe_service 经 ABI 转发到这里)===== */
int pe_shim_inject_scancode(u32 scancode, u32 e0_prefix) {
if (!s_wsurf) return -1;
shim_kbd_inject((u8)scancode, e0_prefix ? 1 : 0);
return 0;
}
int pe_shim_inject_pointer(i32 x, i32 y, u32 buttons) {
if (!s_wsurf) return -1;
/* 钳位到 surface 范围 */
if (x < 0) x = 0; else if (x >= (i32)s_wsurf_w) x = (i32)s_wsurf_w - 1;
if (y < 0) y = 0; else if (y >= (i32)s_wsurf_h) y = (i32)s_wsurf_h - 1;
u8 nbtn = (u8)(buttons & 0x07);
i32 cx = x - g_wnd_x, cy = y - g_wnd_y; /* 客户区坐标(消息 lparam) */
u64 lp = (u64)(u32)(((u32)(u16)cy << 16) | (u32)(u16)cx);
u64 mk = 0;
if (nbtn & 1) mk |= SHIM_MK_LBUTTON;
if (nbtn & 2) mk |= SHIM_MK_RBUTTON;
if (nbtn & 4) mk |= SHIM_MK_MBUTTON;
if (s_kbd_shift) mk |= SHIM_MK_SHIFT;
if (s_kbd_ctrl) mk |= SHIM_MK_CONTROL;
if (x != s_mouse_x || y != s_mouse_y) {
s_mouse_x = x; s_mouse_y = y;
shim_msg_push_pt(SHIM_WM_MOUSEMOVE, mk, lp, x, y);
}
/* 按钮边沿 */
if ((nbtn & 1) && !(s_mouse_btn & 1)) shim_msg_push_pt(SHIM_WM_LBUTTONDOWN, mk, lp, x, y);
if (!(nbtn & 1) && (s_mouse_btn & 1)) shim_msg_push_pt(SHIM_WM_LBUTTONUP, mk, lp, x, y);
if ((nbtn & 2) && !(s_mouse_btn & 2)) shim_msg_push_pt(SHIM_WM_RBUTTONDOWN, mk, lp, x, y);
if (!(nbtn & 2) && (s_mouse_btn & 2)) shim_msg_push_pt(SHIM_WM_RBUTTONUP, mk, lp, x, y);
if ((nbtn & 4) && !(s_mouse_btn & 4)) shim_msg_push_pt(SHIM_WM_MBUTTONDOWN, mk, lp, x, y);
if (!(nbtn & 4) && (s_mouse_btn & 4)) shim_msg_push_pt(SHIM_WM_MBUTTONUP, mk, lp, x, y);
s_mouse_btn = nbtn;
return 0;
}
int pe_shim_inject_input(u32 msg, u64 wparam, u64 lparam, i32 ptx, i32 pty) {
if (!s_wsurf) return -1;
shim_msg_push_pt(msg, wparam, lparam, ptx, pty);
return 0;
}
/* 消息空转让出点:窗口模式回调 host 泵(desktop 帧),全屏 pause。
* pump 返回非 0 = host 请求退出 → 投递 WM_CLOSE(PE 自愿退出路径)。 */
static void shim_msg_yield(void) {
if (s_host_pump) {
if (s_host_pump(s_host_ud))
shim_msg_push(SHIM_WM_CLOSE, 0, 0);
} else {
__asm__ volatile("pause");
}
}
@@ -1209,6 +1347,8 @@ static u64 __attribute__((ms_abi)) shim_RegisterClassExW(u64 wc_p) {
}
static const struct limine_framebuffer *shim_fb(void) {
/* P5 窗口模式:渲染目标是 host 提供的内存 surface(伪造 fb) */
if (s_wsurf) return &s_wsurf_fb;
if (!g_fb_request.response || g_fb_request.response->framebuffer_count == 0) return 0;
return g_fb_request.response->framebuffers[0];
}
@@ -1315,6 +1455,9 @@ static u64 __attribute__((ms_abi)) shim_PeekMessageW(u64 msg_p, u64 hwnd,
u64 fmin, u64 fmax, u64 remove) {
(void)hwnd; (void)fmin; (void)fmax;
shim_kbd_poll();
/* P5 窗口模式:PeekMessage 驱动的渲染循环(Deaicup 型)下 PE 几乎不
* 调 GetMessage,desktop 只能靠这里的空转让出获得 CPU 跑帧。 */
if (g_mq_count == 0 && s_host_pump) shim_msg_yield();
if (g_mq_count == 0) return 0;
shim_msg_t *m = &g_msgq[g_mq_head];
if (msg_p) *(shim_msg_t *)msg_p = *m;
@@ -1330,7 +1473,7 @@ static u64 __attribute__((ms_abi)) shim_GetMessageW(u64 msg_p, u64 hwnd,
for (;;) { /* 阻塞语义:轮询键盘直到有消息 */
shim_kbd_poll();
if (g_mq_count > 0) break;
__asm__ volatile("pause");
shim_msg_yield(); /* P5: 窗口模式泵 desktop,全屏 pause */
}
shim_msg_t *m = &g_msgq[g_mq_head];
if (msg_p) *(shim_msg_t *)msg_p = *m;
@@ -1434,6 +1577,7 @@ static const char *const g_cursor_bmp[12] = {
};
static void shim_draw_cursor(const struct limine_framebuffer *fb) {
if (s_wsurf) return; /* P5 窗口模式:光标由 desktop 统一绘制 */
if (!s_mouse_ok || !g_wnd_created) return;
i32 wx1 = g_wnd_x + g_wnd_w, wy1 = g_wnd_y + g_wnd_h;
if (s_mouse_x < g_wnd_x || s_mouse_y < g_wnd_y) return;
@@ -1528,6 +1672,87 @@ static u64 __attribute__((ms_abi)) shim_BitBlt(u64 dstdc, u64 x, u64 y, u64 cx,
return 1;
}
/* SetDIBitsToDevice: 不经内存 DC,直接把 DIB 位块贴上窗口 DC.
* 语义(MSDN): (xSrc,ySrc) 是 DIB 源区域左下角(DIB 坐标系 y 向上),
* startScan/numLines 描述 lpvBits 缓冲覆盖的扫描线区间,
* (xDest,yDest,w,h) 是客户区目标矩形(y 向下).
* bottom-up DIB: 缓冲行 0 = DIB 扫描线 startScan(从底数);
* top-down DIB : 缓冲行 0 = 图像顶行 + startScan.
* 仅支持 32bpp BI_RGB;返回实际拷贝的扫描线数. */
static u64 __attribute__((ms_abi)) shim_SetDIBitsToDevice(
u64 hdc, i32 xDest, i32 yDest, u32 w, u32 h,
i32 xSrc, i32 ySrc, u32 startScan, u32 numLines,
u64 bits_p, u64 bmi_p, u64 colorUse) {
(void)colorUse;
if (hdc != SHIM_HDC_WND) return 0; /* 仅支持窗口 DC 目标 */
const struct limine_framebuffer *fb = shim_fb();
if (!fb || !fb->address) return 0;
if (fb->bpp != 32 && fb->bpp != 24) return 0;
if (!bits_p || !bmi_p || w == 0 || h == 0) return 0;
const u8 *bmi = (const u8 *)bmi_p;
i32 bw = *(const i32 *)(bmi + 4); /* biWidth */
i32 bh = *(const i32 *)(bmi + 8); /* biHeight(负=top-down) */
u16 bpp = *(const u16 *)(bmi + 14); /* biBitCount */
u32 comp = *(const u32 *)(bmi + 16); /* biCompression */
if (bw <= 0 || bh == 0 || bpp != 32 || comp != 0 /* BI_RGB */) return 0;
int topdown = 0;
i32 H = bh;
if (H < 0) { H = -H; topdown = 1; }
const u32 *bits = (const u32 *)bits_p;
i32 dx0 = xDest + g_wnd_x;
i32 dy0 = yDest + g_wnd_y;
u8 *fbb = (u8 *)fb->address;
u32 rsh = fb->red_mask_shift, gsh = fb->green_mask_shift, bsh = fb->blue_mask_shift;
u32 rsz = fb->red_mask_size ? fb->red_mask_size : 8;
u32 gsz = fb->green_mask_size ? fb->green_mask_size : 8;
u32 bsz = fb->blue_mask_size ? fb->blue_mask_size : 8;
int fast = (fb->bpp == 32 && rsh == 16 && gsh == 8 && bsh == 0
&& rsz == 8 && gsz == 8 && bsz == 8);
u32 copied = 0;
for (u32 row = 0; row < h; row++) {
/* 目标顶行 row=0 ← DIB 坐标 y=ySrc+h-1(源区域顶边) */
i32 sy = ySrc + (i32)h - 1 - (i32)row;
if (sy < 0 || sy >= H) continue;
/* DIB 扫描线 sy → lpvBits 缓冲行 */
i64 br = topdown ? ((i64)(H - 1 - sy) - (i32)startScan)
: ((i64)sy - (i32)startScan);
if (br < 0 || br >= (i64)numLines) continue;
i32 fy = dy0 + (i32)row;
if (fy < 0 || fy >= (i32)fb->height) continue;
const u32 *src = bits + (u64)br * (u64)bw;
u8 *dst_row = fbb + (u64)fy * fb->pitch;
copied++;
for (u32 col = 0; col < w; col++) {
i32 sx = xSrc + (i32)col;
if (sx < 0 || sx >= bw) continue;
i32 fx = dx0 + (i32)col;
if (fx < 0 || fx >= (i32)fb->width) continue;
u32 px = src[sx];
if (fast) {
((u32 *)dst_row)[fx] = px;
} else {
u32 r = (px >> 16) & 0xFF, g = (px >> 8) & 0xFF, bl = px & 0xFF;
u32 v = ((r >> (8 - rsz)) << rsh)
| ((g >> (8 - gsz)) << gsh)
| ((bl >> (8 - bsz)) << bsh);
if (fb->bpp == 32) {
((u32 *)dst_row)[fx] = v;
} else {
u8 *p = dst_row + (u64)fx * 3;
p[0] = (u8)(v & 0xFF);
p[1] = (u8)((v >> 8) & 0xFF);
p[2] = (u8)((v >> 16) & 0xFF);
}
}
}
}
shim_draw_cursor(fb);
return copied;
}
/* 未实现 stub(64位)— 日志告警(含调用点返回地址)并返回 0 */
static u64 __attribute__((ms_abi)) shim_unimpl64(u64 a0, u64 a1, u64 a2, u64 a3) {
(void)a0; (void)a1; (void)a2; (void)a3;
@@ -1642,6 +1867,7 @@ static const pe_shim_entry g_shim_table[] = {
{"gdi32.dll", "DeleteObject", (void*)shim_DeleteObject, 0},
{"gdi32.dll", "DeleteDC", (void*)shim_DeleteDC, 0},
{"gdi32.dll", "BitBlt", (void*)shim_BitBlt, 0},
{"gdi32.dll", "SetDIBitsToDevice", (void*)shim_SetDIBitsToDevice, 0},
{"gdi32.dll", "GetStockObject", (void*)shim_GetStockObject, 0},
{"gdi32.dll", "GdiFlush", (void*)shim_GdiFlush, 0},
/* msvcrt.dll */
+4 -1
View File
@@ -1,7 +1,10 @@
pe/pe_shim.o: pe\pe_shim.c pe\pe_shim.h include\utsm\types.h \
include\utsm\arena.h include\utsm\log.h pe\..\arch\x86_64\limine.h
pe\pe_dll_manager.h pe\..\include\utsm\pe.h include\utsm\arena.h \
include\utsm\log.h pe\..\arch\x86_64\limine.h
pe\pe_shim.h:
include\utsm\types.h:
pe\pe_dll_manager.h:
pe\..\include\utsm\pe.h:
include\utsm\arena.h:
include\utsm\log.h:
pe\..\arch\x86_64\limine.h:
+23
View File
@@ -16,6 +16,10 @@
#include <utsm/types.h>
/* shim 伪模块句柄(LoadLibrary 对已知 shim DLL 返回值)。
* 真实 DLL 句柄 = pe_dll_load 镜像基址,与此值域区分。 */
#define PE_SHIM_FAKE_MODULE 0x10000020ULL
/* 32位 shim:从模拟栈读取参数(cdecl 语义,调用者清栈)。
* esp 指向模拟栈(第一个参数在 esp[0])。返回值 = EAX。 */
typedef u64 (*pe_shim_fn32)(void *esp);
@@ -82,4 +86,23 @@ const char *pe_shim_get_cmdline(void);
* 完成转换;base=0 表示非解释器上下文(原生 PE32+ 路径),gp() 恒等映射。 */
void pe_shim_set_emu_base(u64 base);
/* ===== P5 窗口模式(desktop 嵌入)=====
* pe_service 在 run_windowed 前调用 pe_shim_set_window_host 挂起配置;
* pe_shim_init(run 开头)重置 shim 状态后应用该配置。host==NULL 或
* host->surface==NULL 时保持全屏独占行为(默认)。 */
struct pe_window_host; /* 定义见 utsm/pe.h */
void pe_shim_set_window_host(const struct pe_window_host *host);
/* 窗口模式输入注入(仅窗口模式生效,全屏返回 -1)。
* inject_scancode: set-1 扫描码(bit7=release),e0_prefix!=0 为 E0 扩展。
* inject_pointer : surface 局部坐标 + 按钮位(bit0/1/2=L/R/M),
* 内部与上次状态比较合成 MOUSEMOVE/BUTTON 边沿。
* inject_input : 直接投递任意 Win32 消息(WM_CLOSE 等宿主控制消息)。 */
int pe_shim_inject_scancode(u32 scancode, u32 e0_prefix);
int pe_shim_inject_pointer(i32 x, i32 y, u32 buttons);
int pe_shim_inject_input(u32 msg, u64 wparam, u64 lparam, i32 ptx, i32 pty);
/* 窗口模式是否激活(1=渲染目标为 host surface)。 */
int pe_shim_window_mode(void);
#endif /* PE_SHIM_H */