关于 SendMessageA 和 SendMessageW 的问题

本文讲述了作者在使用SendMessageA和SendMessageW函数时遇到的问题,发现是由于工程属性设置为Unicode导致数据长度被截断。通过指定SendMessage为SendMessageA,解决了文本混乱的问题并给出了修复后的日志对比。

关于SendMessageASendMessageW的问题

再一次陷入AW的问题,用时约四个小时,终于找到原因了。如下图,左边是正确的情况,右边是错误的情况:
在这里插入图片描述
代码在这里:“ysouyno/fake_tide81”。

究其原因其实就是由于current_pos这个变量的值出现了问题,但是到底在哪里出错了呢?分析了一下流程:

  1. SCI_ADDTEXT消息处是最终添加文本的地方,这里通过current_position()获得的current_pos已经出错。
  2. 设置current_pos的值是在SCI_GOTOPOS消息中处理的。
  3. 代码中共有三个向errorlist窗口发送SCI_GOTOPOS的地方,通过添加LPARAM参数筛选发现错误的调用来自:
void TideWindow::output_append_string(char* s, int len) {
  if (-1 == len)
    len = strlen(s);
  send_output(SCI_ADDTEXT, len, (LPARAM)s);
  dprintf("WM_GETTEXTLENGTH get length: %d\n", send_output(WM_GETTEXTLENGTH));
  send_output(SCI_GOTOPOS, send_output(WM_GETTEXTLENGTH));
}
  1. 问题就出在send_output(WM_GETTEXTLENGTH)这里,它返回的值出错了,添加输出:
case WM_GETTEXTLENGTH:
  dprintf("WM_GETTEXTLENGTH set length: %d\n", length());
  return length();

通过日志发现,setget的值不一致,如下:

[21772] WM_GETTEXTLENGTH set length: 1
[21772] WM_GETTEXTLENGTH get length: 1
[21772] WM_GETTEXTLENGTH set length: 22
[21772] WM_GETTEXTLENGTH get length: 21
[21772] WM_GETTEXTLENGTH set length: 23
[21772] WM_GETTEXTLENGTH get length: 21
[21772] WM_GETTEXTLENGTH set length: 48
[21772] WM_GETTEXTLENGTH get length: 21

原来整个工程属性是Use Unicode Character Set,而send_output用的是SendMessage并没有指定A还是W版本,所以默认以SendMessageW发送消息了,这可能造成了数据长度被截段了(我是这么猜想的),所以将SendMessage指定为SendMessageA即可以修复这个问题。再看日志输出就正确了:

[6668] WM_GETTEXTLENGTH set length: 1
[6668] WM_GETTEXTLENGTH get length: 1
[6668] WM_GETTEXTLENGTH set length: 22
[6668] WM_GETTEXTLENGTH get length: 22
[6668] WM_GETTEXTLENGTH set length: 23
[6668] WM_GETTEXTLENGTH get length: 23
[6668] WM_GETTEXTLENGTH set length: 48
[6668] WM_GETTEXTLENGTH get length: 48

commitFix why is the output here messy, like multiple threads writing to the same place?

修复后的效果如下:
在这里插入图片描述在这里插入图片描述

Break instruction exception - code 80000003 (first chance) 0033:00007ff7`ca9816a3 cc int 3 kd> g Hook system initialized Hooking SendMessageW at address: 0x00007FFB8F95D5B0 Hook set successfully Test window created Sending test message… 没有输出"Message hooked at: …"(其中…是消息参数的地址)。#include <Windows.h> #include <vector> #include <string> #include <intrin.h> #include <mutex> // 回调函数类型定义 typedef bool (*HOOKBACK2)(PCONTEXT); // 硬件断点结构 struct DbgPoint { LPVOID Address; HOOKBACK2 DestCall; LPVOID AddressRet; }; // 硬件断点管理器 class DbgPointManager { private: std::vector<DbgPoint> points; std::mutex mtx; // 线程安全锁 public: // 添加硬件断点 bool AddHookPoint(LPVOID Address, HOOKBACK2 hookBack, LPVOID AddressRet = nullptr) { std::lock_guard<std::mutex> lock(mtx); if (points.size() >= 4) return false; // DR0-DR3只有4个 points.push_back({ Address, hookBack, AddressRet }); return true; } // 查找断点 DbgPoint* FindPoint(LPVOID Address) { std::lock_guard<std::mutex> lock(mtx); for (auto& point : points) { if (point.Address == Address) return &point; } return nullptr; } // 启用硬件断点 void OpenDbg(PCONTEXT context) { std::lock_guard<std::mutex> lock(mtx); context->Dr0 = 0; context->Dr1 = 0; context->Dr2 = 0; context->Dr3 = 0; if (points.size() > 0) context->Dr0 = (ULONG_PTR)points[0].Address; if (points.size() > 1) context->Dr1 = (ULONG_PTR)points[1].Address; if (points.size() > 2) context->Dr2 = (ULONG_PTR)points[2].Address; if (points.size() > 3) context->Dr3 = (ULONG_PTR)points[3].Address; // 设置断点类型:执行断点 (00 = 执行, 01 = 写, 10 = IO, 11 = 读写) context->Dr7 = 0; if (points.size() > 0) context->Dr7 |= (1 << 0); // L0 if (points.size() > 1) context->Dr7 |= (1 << 2); // L1 if (points.size() > 2) context->Dr7 |= (1 << 4); // L2 if (points.size() > 3) context->Dr7 |= (1 << 6); // L3 } }; // Hook管理器 class htdHook2 { private: static htdHook2* instance; DbgPointManager DbgPoints; PVOID handler; bool initialized = false; // 触发初始化的函数(使用编译器内部函数替代内联汇编) static void ThreadTrap() { // 使用编译器内部函数替代内联汇编 __nop(); __nop(); __nop(); } public: // 单例模式 static htdHook2* getInstance() { if (!instance) { instance = new htdHook2(); } return instance; } htdHook2() { handler = AddVectoredExceptionHandler(1, VectoredHandler); } ~htdHook2() { if (handler) RemoveVectoredExceptionHandler(handler); instance = nullptr; } // 设置硬件断点Hook bool SetHook(LPVOID Address, HOOKBACK2 hookBack, LPVOID AddressRet = nullptr) { return DbgPoints.AddHookPoint(Address, hookBack, AddressRet); } // VEH异常处理 static LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo) { PCONTEXT ctx = ExceptionInfo->ContextRecord; DWORD exceptionCode = ExceptionInfo->ExceptionRecord->ExceptionCode; auto manager = getInstance(); // 获取指令指针(兼容32/64位) ULONG_PTR eip = 0; #if defined(_M_X64) eip = ctx->Rip; #else eip = ctx->Eip; #endif char debugMsg[256]; if (exceptionCode == STATUS_BREAKPOINT) { // 0x80000003 sprintf_s(debugMsg, "Breakpoint at: 0x%p\n", (void*)eip); OutputDebugStringA(debugMsg); // 检查是否是我们设置的断点 if (eip == (ULONG_PTR)ThreadTrap) { // 恢复原始代码 DWORD oldProtect; VirtualProtect((LPVOID)ThreadTrap, 1, PAGE_EXECUTE_READWRITE, &oldProtect); *(BYTE*)ThreadTrap = 0x90; // NOP VirtualProtect((LPVOID)ThreadTrap, 1, oldProtect, &oldProtect); // 设置硬件断点 manager->DbgPoints.OpenDbg(ctx); // 确保从下一条指令继续执行 eip++; #if defined(_M_X64) ctx->Rip = eip; #else ctx->Eip = eip; #endif return EXCEPTION_CONTINUE_EXECUTION; } } else if (exceptionCode == STATUS_SINGLE_STEP) { // 0x80000004 sprintf_s(debugMsg, "Single step at: 0x%p\n", (void*)eip); OutputDebugStringA(debugMsg); if (auto point = manager->DbgPoints.FindPoint((LPVOID)eip)) { if (point->DestCall(ctx)) { // 继续单步模式 ctx->Dr7 = 0; ctx->EFlags |= 0x100; // 设置TF标志 } else { // 清除单步标志 ctx->EFlags &= ~0x100; } return EXCEPTION_CONTINUE_EXECUTION; } // 重新激活所有断点 manager->DbgPoints.OpenDbg(ctx); // 清除单步标志 ctx->EFlags &= ~0x100; return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; } // 初始化硬件断点环境 void Init() { if (initialized) return; // 设置临时断点获取CONTEXT DWORD oldProtect; VirtualProtect((LPVOID)ThreadTrap, 1, PAGE_EXECUTE_READWRITE, &oldProtect); BYTE originalByte = *(BYTE*)ThreadTrap; // 保存原始字节 *(BYTE*)ThreadTrap = 0xCC; // INT3 // 触发异常 __debugbreak(); // 恢复原始代码 *(BYTE*)ThreadTrap = originalByte; VirtualProtect((LPVOID)ThreadTrap, 1, oldProtect, &oldProtect); initialized = true; } }; // 初始化静态成员 htdHook2* htdHook2::instance = nullptr; // 示例:消息Hook回调函数(兼容32/64位) bool __stdcall HookMsgCallback(PCONTEXT ctx) { // 获取消息内容(兼容不同架构) DWORD_PTR msgAddr = 0; #if defined(_M_X64) // x64调用约定:第一个参数在RCX msgAddr = ctx->Rcx; #else // x86调用约定:第一个参数在[ESP+4] // 使用安全的指针访问替代内联汇编 DWORD espValue = ctx->Esp; if (espValue) { msgAddr = *(DWORD*)(espValue + 4); } #endif // 在实际应用中解析消息结构 char buf[256]; sprintf_s(buf, "Message hooked at: 0x%p\n", (void*)msgAddr); OutputDebugStringA(buf); return true; // 继续Hook } int main() { OutputDebugStringA("Application started\n"); // 初始化Hook系统 auto hookMgr = htdHook2::getInstance(); hookMgr->Init(); OutputDebugStringA("Hook system initialized\n"); // 设置Hook HMODULE hUser32 = LoadLibraryA("user32.dll"); if (!hUser32) { OutputDebugStringA("Failed to load user32.dll\n"); return 1; } LPVOID targetFunc = GetProcAddress(hUser32, "SendMessageW"); if (!targetFunc) { char errMsg[256]; sprintf_s(errMsg, "Failed to find SendMessageW: error %d\n", GetLastError()); OutputDebugStringA(errMsg); return 1; } char infoMsg[256]; sprintf_s(infoMsg, "Hooking SendMessageW at address: 0x%p\n", targetFunc); OutputDebugStringA(infoMsg); if (!hookMgr->SetHook(targetFunc, HookMsgCallback)) { OutputDebugStringA("Failed to set hook (hardware breakpoint limit reached?)\n"); } else { OutputDebugStringA("Hook set successfully\n"); } // 创建测试窗口 WNDCLASSA wc = { 0 }; wc.lpfnWndProc = DefWindowProcA; wc.hInstance = GetModuleHandle(NULL); wc.lpszClassName = "TestWindowClass"; if (!RegisterClassA(&wc)) { OutputDebugStringA("Failed to register window class\n"); return 1; } HWND hWnd = CreateWindowA("TestWindowClass", "Hardware Breakpoint Demo", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, GetModuleHandle(NULL), NULL); if (hWnd) { ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); OutputDebugStringA("Test window created\n"); } else { OutputDebugStringA("Failed to create window\n"); return 1; } // 发送测试消息 if (hWnd) { OutputDebugStringA("Sending test message...\n"); SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"Hello, Hardware Breakpoint!"); // 额外发送消息确保触发 SendMessageA(hWnd, WM_PAINT, 0, 0); SendMessageA(hWnd, WM_NCPAINT, 1, 0); } // 主消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } OutputDebugStringA("Application exiting\n"); return 0; }
最新发布
07-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值