Fix: Make the remote chat dialog Unicode and loader-independent

Clients packaged as SC_AES/sRDI shellcode run the payload image as raw
shellcode inside the loader stub, so the chat manager was handed the
stub's module handle. The stub's resource section only carries a
manifest, and Windows resource APIs reject manually mapped images
outright, so creating IDD_CHAT failed and the chat thread exited before
the window ever existed.

The chat window now falls back to an embedded compiled dialog template
that is byte-identical to the IDD_CHAT resource, and the title-bar icon
is rebuilt directly from the module's own resource directory instead of
LoadIcon, bypassing the loader on both paths. The PE walking code checks
every offset against SizeOfImage and the resource directory size, so a
malformed or truncated image returns NULL instead of faulting.
Deployments where the resource lookup already works keep the original
code path unchanged.

The dialog is now created and driven with the wide-character API. The
client is still an MBCS build, but an ANSI dialog renders text with the
system code page, so GBK chat text turned into mojibake on German and
other non-936 systems. Incoming text is converted CP936 to UTF-16 and
outgoing text strictly back to CP936; characters GBK cannot represent
(emoji, accented letters) are refused with a visible notice instead of
silently becoming '?'. Static labels follow the system UI language, and
IDD_CHAT now uses the scalable MS Shell Dlg face so font linking can
supply CJK glyphs.

The window is now positioned explicitly as well. DS_CENTER in the
template put it at the work area origin on the three-monitor host: the
centred position came out negative there and Windows clamps out-of-range
creation coordinates to that origin. It is centred in the work area of
the monitor under the cursor instead, falling back to SPI_GETWORKAREA.

Teardown no longer leaves a hidden window behind either. The dialog
thread blocked in GetMessageW and never saw m_bRun cleared, while the
destructor waited for that thread before sending the message that would
have destroyed the window, so a close command that never arrived (the
sub-connection already gone, or WaitSendIdle timing out before CancelIO)
leaked the window, the dialog thread and the manager loop. The
destructor now destroys the window first, the loop checks m_bRun before
blocking, and it destroys the window itself if it exits first.

Co-Authored-By: deepseek-flash
This commit is contained in:
yuanyuanxianganddeepseek-flash committed 2026-10-23 11:19:21 +02:00
1 parent 3ae6298746
commit fff7c67af6
8 files changed
+425 -52

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
// ChatDialogTemplate.h: IDD_CHAT 的编译后 DLGTEMPLATEEX(内嵌内存模板)
//
// 用途:加壳部署(EXE + SC_AES/sRDI)的客户端里,ChatManager 的 hInstance 是外壳进程
// 映像(GetModuleHandle(NULL)),其资源里只有清单、没有 IDD_CHAT;而载荷 EXE 以裸
// shellcode 在壳进程内运行,未注册进加载器,FindResource/LoadResource/CreateDialogParamW
// 一律取不到资源(实测 Win11 26200 返回 err=1812 / 59)。此时用下面的内存模板直接
// CreateDialogIndirectParamW 建窗,与部署形态无关。
//
// 数据来源:client/Script.rc 的 IDD_CHAT(DIALOGEX)。DLGTEMPLATEEX 的字符串字段
// 一律是 UTF-16,同一份字节既可用于 A 接口也可用于 W 接口;聊天窗口固定用 W 接口
// 创建成 Unicode 对话框,否则 MBCS 客户端在非中文系统(德语等)上会按系统代码页
// 渲染 GBK 字节,中文全部乱码。
//
// 重新生成(改动 Script.rc 的 IDD_CHAT 后必须同步):
// 1. rc /fo dlg.res dlg.rc (dlg.rc 只保留与 Script.rc 相同的 IDD_CHAT 块)
// 2. link /dll /noentry /machine:x64 /out:dlg.dll dlg.res
// 3. 从 dlg.dll 的 RT_DIALOG(5) → 106 → 任意语言 取数据块,按 16 字节一行导出。
// 当前块 240 字节:CAPTION "Remote Chat"、FONT 9 "MS Shell Dlg" charset 1、
// 控件 ID 1001/1002/1003/1004,与 Script.rc 完全一致。
#ifndef YAMA_CHAT_DIALOG_TEMPLATE_H
#define YAMA_CHAT_DIALOG_TEMPLATE_H
// DLGTEMPLATEEX 的 DWORD 字段要求模板指针 4 字节对齐;显式声明对齐,避免依赖
// 编译器把字节数组放在什么边界上。
__declspec(align(4)) static const unsigned char kChatDialogTemplate[] = {
0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x08, 0xCA, 0x80,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00,
0x65, 0x00, 0x6D, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x43, 0x00, 0x68, 0x00,
0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x90, 0x01, 0x00, 0x01, 0x4D, 0x00, 0x53, 0x00,
0x20, 0x00, 0x53, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x20, 0x00, 0x44, 0x00,
0x6C, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x44, 0x08, 0xA1, 0x50, 0x03, 0x00, 0x03, 0x00, 0xE5, 0x00, 0x6B, 0x00, 0xE9, 0x03, 0x00, 0x00,
0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x44, 0x00, 0xA1, 0x50, 0x03, 0x00, 0x71, 0x00, 0xAA, 0x00, 0x26, 0x00, 0xEA, 0x03, 0x00, 0x00,
0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x01, 0x50, 0xB2, 0x00, 0x74, 0x00, 0x36, 0x00, 0x0E, 0x00, 0xEB, 0x03, 0x00, 0x00,
0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x50, 0xB2, 0x00, 0x85, 0x00,
0x36, 0x00, 0x0E, 0x00, 0xEC, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x45, 0x00, 0x6E, 0x00,
0x64, 0x00, 0x20, 0x00, 0x43, 0x00, 0x68, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
};
#endif // YAMA_CHAT_DIALOG_TEMPLATE_H