首页 / 技术类 / 逆向 / 一个改程序小记

一个改程序小记

2008-01-26 12:21:00

原文首发 CC98 白洪欢老师答疑版(http://10.71.45.99/dispbbs.asp?boardID=323&ID=1979518)

今天的改程序小记

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

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

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

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

于是我想,可能 CreateDialogCreateWindow 是不一样的。可是,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 那里才是真正的回调函数体的开始。

发现有个

100402148      52            push    edx
200402149      68 EA030000   push    3EA
30040214E  |.  8B45 08       mov     eax, dword ptr [ebp+8]    
400402151  |.  50            push    eax                       
500402152  |.  FF15 D0C44200 call    dword ptr [<&USER32.SetDl>]

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

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

发现紧接着的地方有个:

 10040216C  |.  68 EB030000   push    3EB
 200402171  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]
 300402174  |.  51            push    ecx
 400402175  |.  FF15 D4C44200 call    dword ptr [<&USER32.GetDlgItem>]
 50040217B  |.  3BFC          cmp     edi, esp
 60040217D  |.  E8 FE0F0000   call    00403180
 700402182      50            push    eax
 800402183      FF15 D8C44200 call    dword ptr [<&USER32.SendMessageA>]
 900402189  |.  3BF4          cmp     esi, esp
100040218B  |.  E8 F00F0000   call    00403180

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

100403180  /$ /75 01         jnz     short 00403183
200403182  |. |C3            retn
300403183  |> /55            push    ebp
400403184  |.  8BEC          mov     ebp, esp
500403186  |.  83EC 00       sub     esp, 0
600403189  |.  50            push    eax
70040318A  |.  52            push    edx

原来如此,呵呵。现在我知道了,后面所有的

1cmp esi, esp
2call 00403180

都是在检查函数调用结果。如果出问题就跳出一个错误框。

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

1004020FE  |.  8B45 0C       mov     eax, dword ptr [ebp+C]
200402101  |.  8945 8C       mov     dword ptr [ebp-74], eax
300402104  |.  817D 8C 10010>cmp     dword ptr [ebp-74], 110
40040210B  |.  74 12         je      short 0040211F
50040210D  |.  817D 8C 11010>cmp     dword ptr [ebp-74], 111
600402114  |.  0F84 C0000000 je      004021DA
70040211A  |.  E9 0E020000   jmp     0040232D

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

 10040232D  |> /33C0          xor     eax, eax
 20040232F  |>  5F            pop     edi
 300402330  |.  5E            pop     esi
 400402331  |.  5B            pop     ebx
 500402332  |.  81C4 B4000000 add     esp, 0B4
 600402338  |.  3BEC          cmp     ebp, esp
 70040233A  |.  E8 410E0000   call    00403180
 80040233F  |.  8BE5          mov     esp, ebp
 900402341  |.  5D            pop     ebp
1000402342  /.  C2 1000       retn    10

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

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

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

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

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

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

文件修改:

004021B6 8D 45 CC => EB 18 90

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

2008-01-23


公网首发:https://blog.csdn.net/cnStreamlet/article/details/2066836



NoteIsSite/0.4