lies2014 发表于 2024-1-18 23:10

教程:修改x64dbg插件 Multiline Ultimate Assembler 完美支持中文

本帖最后由 lies2014 于 2024-1-18 23:21 编辑

修改x64dbg插件 Multiline Ultimate Assembler 完美支持中文

一、分析篇
    Multiline Ultimate Assembler 是一个调试器多行汇编插件,但它对中文的支持不好,体现在:
    (1)不能显示中文注释,键盘输入中文会显示乱码;
    (2)从调试器向插件同步中文注释会显示乱码;
    (3)从插件向调试器同步中文注释会显示乱码。
    近期对通过 x64dbg 插件存在的以上问题进行分析,最终实现了中文的支持。
    通过网络搜索类似问题,发现这个插件的 OD 版本有人实现了中文显示,是通过修改插件的二进制文件,默认使用中文字体就能正常显示中文,但也只能显示,未能解决输入和同步中文的问题。既然有了前人的经验就试着按 OD 版本的修改方法修改二进制文件,修改后发现完全没有作用,不管改成什么字体界面显示字体都没有变化,就像没有改过一样,怀疑二进制里设置的字体并不是关键。最后把目光定位到调试器的配置文件,打开 x64dbg.ini,果然发现有小节,其中就有“font_name=Lucida Console”的项目,改成中文字体“font_name=Microsoft YaHei UI”,再打开插件,果然可以显示中文了,从外部粘贴中文过去也能正常显示。猜想二进制里的字体只是初始化设置,ini 里的才是实际使用的字体(后来看到源码也印证了猜想)。
    接着是输入的问题,因为系统输入是正常的,显然插件自己处理了字符信息,只要不让插件处理 WM_CHAR 消息,交给系统去处理就好了(感谢网友 csjwaman 提供的思路,还有不明白插件为什么要自己处理输入字符)。
    插件同步乱码的问题,(2)(3)其实是类似的问题,解决了一个另一个就迎刃而解。
    查询 x64dbg 用户手册,在开发接口函数里发现有 DbgGetCommentAt 这个函数,虽然没有更进一步的介绍,但从字面上可以猜得出这是从调试器获取注释信息的。既然要从调试器同步注释过来第一步肯定是先获取注释,我们先看看取过来的注释是否正常,就用 x64dbg 自己来分析一下吧。
    先打开一个 x64dbg,随便载入一个程序,在某条反汇编注释处输入中文字符串,再打开第二个 x64dbg,附加到第一个 x64dbg 上,找到 multiasm_x64dbg.dp64 里的跨模块调用,定位到 DbgGetCommentAt 函数并下断点。
    在第一个 x64dbg 里中文注释的反汇编指令右键选择“Multiline Ultimate Assembler”,第二个 x64dbg 成功断下,F8 单步执行 DbgGetCommentAt 函数,看寄存器已经出现了我们输入的中文字符串,看起来一切正常。

    一般乱码我们第一时间想到的就是编码问题,这时内存中的中文编码是 UTF-8,这和插件界面的编码是否一致呢,我们设定字体后显示的中文是正常的,我们把它们复制出来粘贴到 UltraEdit 去看,编码是 GBK,答案已经呼之欲出了。

    我们手工将内存里取到的注释改成 GBK 编码,继续执行,发现同步到插件里的中文显示正常了。

    同样,插件同步到调试器,我们定位到 DbgSetCommentAt 函数,在函数执行前将参数的 GBK 转换为 UTF-8 就可以正常显示中文。
    这样,几个问题如何修改,我们就有了一个大致的思路:
    (1)修改默认字体,使支持中文显示;
    (2)拦截插件 WM_CHAR 消息,交给系统处理;
    (3)DbgGetCommentAt 函数执行后将注释信息转码(UTF-8 -> GBK);
    (4)DbgSetCommentAt 函数执行前将注释信息转码(GBK -> UTF-8)。

二、修改篇
(一)环境准备
1、源码下载
https://github.com/m417z/Multiline-Ultimate-Assembler

2、搭建环境
    源码原来是 VS2015 的工程,搭配 Win SDK 7,支持 WinXP。
    搭建环境这个环节踩了不少坑,原先图省事先后安装了 VS2015 便携版和 VS2017 便携版,结果编译一堆问题,还把注册表搞得乱七八槽,影响到后面的 VS2019 安装,最终清了半天注册表才搞定。想编译的朋友直接用 VS2019 在线安装就好,模块化的安装方式,增加删除模块都很方便。
    VS2019 安装界面勾选“使用C++的桌面开发“,然后在“单个组件”下勾选下图的项目:

    安装完成后将:
    C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.16.27023\atlmfc\include\afxres.h
    复制一份到:
    C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\atlmfc\include
    否则编译可能会出现“error RC1015: cannot open include file 'afxres.h'“错误。
    以上完成后打开项目解决方案,会出现以下提示,确定即可。

    如果没出现上面提示,可以手动在工程属性中设置 SDK 版本和平台工具集设置如下:


    这时可以编译一下,编译成功就可以着手进行代码修改了。

    安装选项参考:
    https://learn.microsoft.coAm/en-us/cpp/build/configuring-programs-for-windows-xp?view=msvc-170&viewFallbackFrom=vs-2019

(二)源码修改
1、修改默认字体(微软雅黑)
文件:assembler_dlg.c
找到 SetRAEditDesign 函数,修改如下:

    if(!MyGetstringfromini(hInstance, _T("font_name"), lfLogFont.lfFaceName, LF_FACESIZE))
    {
      lstrcpy(lfLogFont.lfFaceName, _T("Microsoft YaHei UI")); // Default font
      MyWritestringtoini(hInstance, _T("font_name"), lfLogFont.lfFaceName);
    }


2、插件输入中文乱码
文件:assembler_dlg.c
找到 AssemblerPreTranslateMessage 函数,修改如下:

BOOL AssemblerPreTranslateMessage(LPMSG lpMsg)
{
    if(hAsmDlg)
    {
      HWND hWnd = hAsmDlg;
      HWND hFindReplaceWnd = AsmDlgParam.hFindReplaceWnd;
      if(hFindReplaceWnd && IsDialogMessage(hFindReplaceWnd, lpMsg))
            return TRUE;
      if(GetActiveWindow() == hWnd)
      {
            if (lpMsg->message == WM_CHAR)
                return FALSE;
            if(hAccelerators && TranslateAccelerator(hWnd, hAccelerators, lpMsg))
                return TRUE;
      }
      if(IsDialogMessage(hWnd, lpMsg))
            return TRUE;
    }
    return FALSE;
}

3、从x64dbg同步中文注释
文件:plugin_x64dbg.c
找到 GetComment 函数,修改如下:

void Utf8ToGbk(char* utf8String, char* gbkString)
{
    gbkString = utf8String;
    wchar_t* unicodeStr = NULL;
    int Len = 0;
    Len = MultiByteToWideChar(CP_UTF8, 0, utf8String, -1, NULL, 0);
    unicodeStr = (wchar_t*)malloc(Len * sizeof(wchar_t));
    MultiByteToWideChar(CP_UTF8, 0, utf8String, -1, unicodeStr, Len);
    Len = WideCharToMultiByte(CP_ACP, 0, unicodeStr, -1, NULL, 0, NULL, 0);
    WideCharToMultiByte(CP_ACP, 0, unicodeStr, -1, gbkString, Len, NULL, 0);
    free(unicodeStr);
}
int GetComment(DWORD_PTR addr, TCHAR *name)
{
    if(!DbgGetCommentAt(addr, name))
      return 0;
    if(name == '\1') // Automatic comment
      return 0;
    char* gbkString;
    Utf8ToGbk(name, gbkString);
    return lstrlen(name);
}

4、向x64dbg同步中文注释
文件:plugin_x64dbg.c
找到 QuickInsertComment 函数,修改如下:

void GbkToUtf8(char* gbkString, char* utf8String)
{
    utf8String = gbkString;
    wchar_t* unicodeStr = NULL;
    int Len = 0;
    Len = MultiByteToWideChar(CP_ACP, 0, gbkString, -1, NULL, 0);
    unicodeStr = (wchar_t*)malloc(Len * sizeof(wchar_t));
    MultiByteToWideChar(CP_ACP, 0, gbkString, -1, unicodeStr, Len);
    Len = WideCharToMultiByte(CP_UTF8, 0, unicodeStr, -1, NULL, 0, NULL, 0);
    WideCharToMultiByte(CP_UTF8, 0, unicodeStr, -1, utf8String, Len, NULL, 0);
    free(unicodeStr);
}
BOOL QuickInsertComment(DWORD_PTR addr, TCHAR *s)
{
    char* utf8String;
    GbkToUtf8(s, utf8String);
    return DbgSetCommentAt(addr, s);
}

如果中文显示乱码请将x32dbg.ini和x64dbg.ini文件中
小节下“font_name”一项删除

雾都孤尔 发表于 2024-1-18 23:23

操作成功了,感谢分享。

liushuaijie123 发表于 2024-1-20 21:50

公布一下我修改的结果:
注释掉源代码
```
                //if(IsDialogMessage(hWnd, lpMsg))
                        //return TRUE;
```


IsDialogMessage文档说明:
Determines whether a message is intended for the specified dialog box and, if it is, processes the message.
翻译:确定消息是否用于指定的对话框,如果是,则处理该消息。

原理:
通过逆向此函数发现IsDialogMessage发现其内部有自己的消息循环实现,只要调用了IsDialogMessage这个函数,则对应的窗口消息过程就会被接管。因为MUA作者多此一举,导致raedit一些中间过程被IsDialogMessage接管,raedit的WM_CHAR接受到的字符就会是unicode。如图所示:

结论:IsDialogMessage是一个很危险的函数,MUA的作者调用这函数的原因未知。我猜测可能是为了处理快捷键从别处借鉴来的代码。

冥界3大法王 发表于 2024-1-18 23:58

本帖最后由 冥界3大法王 于 2024-1-19 00:04 编辑

@lies2014 最头大的就是这个参数1 (duint=》转过去 就是 UInt64)

我试着 搞了Delphi的插件去注释

就放一个按钮

procedure TDLLForm1.commentClick(Sender: TObject);
var
a1: UInt64;
begin
   a1 := $4198420;                        <<=================不报错,但也没效果,是何故?
//a1 := StrToUInt64(Edit1.Text);
DbgGetCommentAt(a1, 'LaLaLa');
end;

冥界3大法王 发表于 2024-1-19 00:08


https://gitee.com/suxuss/DELPHI-x96dbg-Plugins-SDK/blob/master/bridgemain.pas
他这里的注释是这样说的
{BRIDGE_IMPEXP bool}    function DbgIsValidExpression(const expression: PAChar): Boolean; cdecl; external x32_BRIDGE; //检测CTRL+G里面将输入的内容是不是有效
我怀疑他给的注释可能也不太对。
感觉字面意思该是测试表达式,是否有效啊。


procedure TDLLForm1.Button1Click(Sender: TObject);   //还有这个不报错,但与测试结果不相符。
begin
if DbgIsValidExpression(('{x:rbx} != 0')) = True then
begin
    DbgCmdExec('log True');
    DbgCmdExec('StepInto');
end
else
begin
    DbgCmdExec('log False');
    DbgCmdExec('StepOver');
end;
end;

冥界3大法王 发表于 2024-1-19 00:11

难道这个 addr不代表一个地址?
DWORD_PTR addr
必须使用 DWORD型的?
是这么解释吧?

turmasi1234 发表于 2024-1-19 07:39

感谢楼主的分享

冥界3大法王 发表于 2024-1-19 07:47

楼主楼主,我举报你了,优秀帖子必须全力加分!{:301_1006:}

lyliucn 发表于 2024-1-19 08:54

楼主上个成品。

lies2014 发表于 2024-1-19 09:02

冥界3大法王 发表于 2024-1-18 23:58
@lies2014 最头大的就是这个参数1 (duint=》转过去 就是 UInt64)

我试着 搞了Delphi的插件去注释


duint 不一定是 UInt64,在32位系统下是 ULong,在 bridgemain 里有定义的,你直接用 duint 就成了
不报错也没效果有时很难直观从代码看到问题,这时用调试器跟进去,从汇编层面可能可以看出端倪,先看看函数执行前参数是否都正确,然后函数入口是不是正确,这两样都没问题,不可能执行没有结果
比如我上面修改的编码转换,在32位下没有问题,但是64位就会失去响应,对C又不熟,看了半天看不出代码问题,最后还是汇编进去发现最后一个 WideCharToMultiByte 第5个参数没有分配地址,所以第一句多加了一句赋值才解决问题

lies2014 发表于 2024-1-19 09:18

冥界3大法王 发表于 2024-1-19 00:08
https://gitee.com/suxuss/DELPHI-x96dbg-Plugins-SDK/blob/master/bridgemain.pas
他这里的注释是这样 ...

x64dbg 这个函数的定义:
BRIDGE_IMPEXP bool DbgIsValidExpression(const char* expression)
{
    duint value = 0;
    return _dbg_valfromstring(expression, &value);
}
应该就是像你理解的一样判断一个字符串表达式是否有效
页: [1] 2 3 4 5
查看完整版本: 教程:修改x64dbg插件 Multiline Ultimate Assembler 完美支持中文