一个改程序小记

本文记录了一次通过修改程序代码使对话框中编辑框预填充文本的过程。作者通过对程序代码的深入分析和尝试,最终实现了在对话框显示时自动设置编辑框文本的目标。
原文首发 CC98 白洪欢老师答疑版( http://10.71.45.99/dispbbs.asp?boardID=323&ID=1979518 )

今天的改程序小记

昨晚逛 WZ 水区,居然有个人问怎么改程序。是这么一个东西:



文件名叫“补丁.exe”……(那改了后是不是该叫“补丁过的补丁.exe”之类的了?)
按菜单上这个“设置”,会出来这么个框。
那个人说要让那个框出来时编辑框里面就有文字,直接改资源没用。
于是为了抓住锻炼的机会我就开搞啦!

首先是找显示这个对话框的 API。

我原先以为对话框的 CreateDialog 也是调用 CreateWindow 的,可是截不住。
抓到两个 CreateWindow 动作,一个是主窗口。还有一个很奇怪的,类名是 "CicMarshalWndClass",
是点击托盘图标弹出菜单的时候发生的。当时以为是这个对话框,可是这个把我搞得却没头绪了。
于是不知怎么的我就觉得应该不是它。

于是我想,可能 CreateDialog 跟 CreateWindow 是不一样的。可是,CreateDialogA、CreateDialogExA,
都说没有这个函数……郁闷。后来查到了,是这么些:
CreateDialogParamA(W)
CreateDialogIndirectParamA(W)
DialogBoxParamA(W)
DialogBoxIndirectParamA(W)

一个一个试过去,原来是 DialogBoxParamA。然后就到了这里:



MSDN 里对 DialogBoxParamA 函数的说明如下:

Syntax

INT_PTR DialogBoxParam(
    HINSTANCE hInstance,
    LPCTSTR lpTemplateName,
    HWND hWndParent,
    DLGPROC lpDialogFunc,
    LPARAM dwInitParam
);

Parameters
...

Return Value

If the function succeeds, the return value is the value of the nResult parameter specified
in the call to the EndDialog function used to terminate the dialog box.

If the function fails because the hWndParent parameter is invalid, the return value is zero.
The function returns zero in this case for compatibility with previous versions of Microsoft Windows.
If the function fails for any other reason, the return value is –1. To get extended error information, call GetLastError.


跟进去逛了几圈,发现 DialogBoxParamA 里面是调用 ShowWindow 的。
那就没法抢在 ShowWindow 之前去设置文本了。接下来的想法是,那就在 DialogBoxParamA 之后,
紧接着马上去设置编辑框文本。这里地方比较紧凑,又产生了暑假用过的一个想法:
Call 到自己添加的函数;
在自己的函数里面 Call DialogBoxParamA;
然后设置那个编辑框的文本;
返回;

这是昨晚想的事情,当时快 12 点了要熄灯,作罢。接下来是今天考试归来后的。

我开始添加函数了。不是要 SetDlgItemText 吗?那就要 GetDlgItem,于是就需要对话框的句柄。
可是从上面的引文可以看到,DialogBoxParamA 并不返回对话框句柄。
我考虑把它改成 CreateDialogParamA,那就可以得到句柄了。
CreateDialogParamA 函数说明:

Syntax

HWND CreateDialogParam(
    HINSTANCE hInstance,
    LPCTSTR lpTemplateName,
    HWND hWndParent,
    DLGPROC lpDialogFunc,
    LPARAM dwInitParam
);
Parameters
...

Return Value

If the function succeeds, the return value is the window handle to the dialog box.

If the function fails, the return value is NULL. To get extended error information, call GetLastError.


好的,就这么干。为 CreateDialogParamA 准备参数。第一个 dwInitParam 大概可以 NULL 的。
然后是回调函数。到原来 Call DialogBoxParamA 的地方找,函数地址是 401019。
这时突然又有个思路了,既然找到了回调函数,那就到里面给他来个 case WM_INITDIALOG,
就可以在那里设置文本了呀。
401019 处:



这里好像是个函数总目录,呵呵。4020E0 那里才是真正的回调函数体的开始。

发现有个

00402148      52            push    edx
00402149      68 EA030000   push    3EA
0040214E  |.  8B45 08       mov     eax, dword ptr [ebp+8]   
00402151  |.  50            push    eax                      
00402152  |.  FF15 D0C44200 call    dword ptr [<&USER32.SetDl>]

3EA 是 1002,可是资源里面找不到:



那个编辑框的 ID 是 1003。试着把 3EA 改成 3EB,可是没用。

发现紧接着的地方有个:

0040216C  |.  68 EB030000   push    3EB
00402171  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]
00402174  |.  51            push    ecx
00402175  |.  FF15 D4C44200 call    dword ptr [<&USER32.GetDlgItem>]
0040217B  |.  3BFC          cmp     edi, esp
0040217D  |.  E8 FE0F0000   call    00403180
00402182      50            push    eax
00402183      FF15 D8C44200 call    dword ptr [<&USER32.SendMessageA>]
00402189  |.  3BF4          cmp     esi, esp
0040218B  |.  E8 F00F0000   call    00403180

试着把这里的 SendMessage 跳过去(jmp 到 402189),却出来一个错误的框框。再接下去的部分是别的东西了,那个错误框框……只能是 0040218B |. E8 F00F0000 call 00403180 这里面有名堂了。可是前面没有 je 之类的判断跳转,cmp 又不影响流程,难道它每次都显示错误不成?所以比较好奇,进去看了一下,原来是:

00403180  /$ /75 01         jnz     short 00403183
00403182  |. |C3            retn
00403183  |> /55            push    ebp
00403184  |.  8BEC          mov     ebp, esp
00403186  |.  83EC 00       sub     esp, 0
00403189  |.  50            push    eax
0040318A  |.  52            push    edx

原来如此,呵呵。现在我知道了,后面所有的
cmp esi, esp
call 00403180
都是在检查函数调用结果。如果出问题就跳出一个错误框。

刚才试改了上面两处都不行,我考虑实现自己原先的思路了,嗯,找 switch (message) 的 case。
函数前面有这么两个 cmp:

004020FE  |.  8B45 0C       mov     eax, dword ptr [ebp+C]
00402101  |.  8945 8C       mov     dword ptr [ebp-74], eax
00402104  |.  817D 8C 10010>cmp     dword ptr [ebp-74], 110
0040210B  |.  74 12         je      short 0040211F
0040210D  |.  817D 8C 11010>cmp     dword ptr [ebp-74], 111
00402114  |.  0F84 C0000000 je      004021DA
0040211A  |.  E9 0E020000   jmp     0040232D

比较,跳转,比较,跳转,跟 switch case 有点像。那么 dword ptr [ebp-74] 就是消息值了。第二个 je 后的 jmp 跳到了:

0040232D  |> /33C0          xor     eax, eax
0040232F  |>  5F            pop     edi
00402330  |.  5E            pop     esi
00402331  |.  5B            pop     ebx
00402332  |.  81C4 B4000000 add     esp, 0B4
00402338  |.  3BEC          cmp     ebp, esp
0040233A  |.  E8 410E0000   call    00403180
0040233F  |.  8BE5          mov     esp, ebp
00402341  |.  5D            pop     ebp
00402342  /.  C2 1000       retn    10

这里已经是函数末尾了。那么我就 jmp 到其他空白地方,在那里比较消息值是不是 WM_INITDIALOG,是的话设置文本,jmp 回来,应该就好了。我查 WM_INITDIALOG 的值,MSDN索引里填进去没有……对,打开一个 Win32 工程看:



110?前面好像打眼过……回去找。

00402104  |.  817D 8C 10010>cmp     dword ptr [ebp-74], 110
0040210B  |.  74 12         je      short 0040211F
0040210D  |.  817D 8C 11010>cmp     dword ptr [ebp-74], 111
00402114  |.  0F84 C0000000 je      004021DA
0040211A  |.  E9 0E020000   jmp     0040232D
0040211F  |>  8BF4          mov     esi, esp

40211F 之后却就是原来我试改过的地方。晕死了。只好接着刚才的两个地方看下来,看看把代码插在哪里好。却发现:

004021B6      8D45 CC       lea     eax, dword ptr [ebp-34]
004021B9      50            push    eax
004021BA      68 EB030000   push    3EB
004021BF  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]
004021C2  |.  51            push    ecx
004021C3  |.  FF15 D0C44200 call    dword ptr [<&USER32.SetDlgItemTextA>]
004021C9  |.  3BF4          cmp     esi, esp
004021CB  |.  E8 B00F0000   call    00403180
004021D0  |.  B8 01000000   mov     eax, 1

它自己在对编辑框设置文本。[ebp-34] 那里是个 0。终于搞明白了,资源里改编辑框的文本还是有用的,只是程序自己把它清除了。
然后是,把这一段跳过(当然是跳到 call 00403180 的后面,不然又会一个错误框),再在资源里改,哈哈,成功了!



文件修改:
004021B6 8D 45 CC => EB 18 90

发现还是缺乏好多常识,折腾了这么久,比较没技术含量。不过快感还是有滴~~~

2008-01-23
 
### 使用 UniApp 开发小记账本应用程序 #### 项目概述 生活记账小程序通过前端 Vue 和 UniApp 设计开发,后端采用 SpringBoot 提供数据接口支持。主要功能模块包括首页展示、分类记账以及微信登录状态管理[^1]。 #### 创建新项目 首先安装 HBuilderX 或者其他 IDE 工具来创建一个新的 UniApp 项目: ```bash npm install -g @dcloudio/uni-cli uni create myAccountBookProject ``` 进入项目目录并初始化必要的配置文件。 #### 配置 App.vue 生命周期函数 为了更好地控制应用生命周期,在 `App.vue` 中定义如下几个重要钩子函数用于处理不同场景下的逻辑操作: - **onLaunch**: 当整个程序启动时调用此方法, 可以在这里做一些全局性的初始化工作. - **onShow**: 页面每次从前台切到后台再返回前台都会触发这个事件, 合适用来刷新某些实时变化的内容. - **onHide**: 对应于当用户点击 Home 键使 APP 进入后台运行模式时执行的动作. 这些设置有助于提升用户体验流畅度和响应速度[^2]. #### 数据绑定与交互实现 确保所有的业务逻辑都放置在 methods 下面,并且避免 data 属性名称同 method 名冲突以免造成不必要的错误;另外注意检查 HTML 标签内部是否存在重复属性声明尤其是集成第三方 UI 组件库 uView-ui 的时候要格外小心[^3]: ```html <template> <!-- 记录条目输入框 --> </template> <script> export default { name: 'RecordEntry', data() { return { recordType: '', amount: null, date: '' } }, methods:{ addNewRecord(){ // 添加新的财务记录... } } } </script> ``` #### 接口对接与缓存机制 对于前后端分离架构的应用来说,合理的 API 路由规划至关重要。这里推荐使用 RESTful 风格的服务端点配合 axios 发起 HTTP 请求完成增删查等基本 CRUD 功能。与此同时引入 Redis 做为临时存储介质加速频繁访问的数据读取效率降低 MySQL 查询压力。 #### 微信开放能力接入 如果计划让这款理财工具具备社交分享特性,则需按照官方文档指引注册成为微信公众平台开发者账号获得 appid 权限认证之后才能正常使用诸如支付等功能服务。此外还可以考虑加入直播营销插件吸引更多潜在客户群体关注产品动态[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值