【文章标题】: 天堂1窗口化的方式
【文章作者】: dengkeng
【作者邮箱】: poppig#sohu.com
【软件名称】: 天堂1
【下载地址】: 自己搜索下载
【加壳方式】: Themida
【保护方式】: Themida
【使用工具】: IDA,OD,StrongOD.dll
【操作平台】: xp,sp2
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
天堂1窗口化的方式
这个游戏也算是老游戏了,可能因为过于太老吧,没有一些设置选项的东西.
(现在的游戏大部分都有)所以要想着怎么样窗口化这个游戏.通用的工具大家
都知道D3DWindower这个程序,但是使用起来还是有点问题,游戏虽然给窗口化
了,但是鼠标的操作确实怪怪的,貌似现在的Gua提供的窗口化的功能要缴费以
后才能够使用.尽量在网上google,找到几个关于传奇的窗口的代码,主要部分
我摘录了一下.
1. Hook的时机,Hook时首先截获DirectDrawCreat函数,从中得到产生的设备
对象的指针的指针.这个指针是放在堆栈中的,它是一个指向com虚表指针的指针.
2.截获这个指针后,将虚表中的地址SetCooperatelever 和 SetDisplayMode
函数替换成我的SetCooperatelever 和 SetDisplayMode函数地址.
3.下面就是写SetCooperatelever 和 SetDisplayMode两个涵数了
SetDisplayMode 屏掉,因为DirectX中窗口化方式下显示模式不能设置.
在SetCooperatelever 中设置显示分鞭率为1024*768
当让这个是别人总结的,我们就拿来主义了.经过后面的实验,发现3还是有点
问题.(虽然屏幕给窗口化了,但是鼠标的操作还是有点问题,实际还是全屏幕,
只是游戏给窗口化了,有点别扭吧,^_^,就是如果你操作游戏,实际上鼠标是点不
了游戏窗口以外的东西,要运行其他的东西还是需要Alt+Tab进行切换.
好了,下面就说说方法吧.
方法1:分析别人的外挂程序.挂的名字不说了.开启外挂,然后定位到游戏代码
DirectDrawCreate的部分.IDA分析字符串.
___:00423660 sub_423660 proc near ; CODE XREF: sub_4234A0p
___:00423660
___:00423660 var_120 = byte ptr -120h
___:00423660 var_20 = dword ptr -20h
___:00423660 var_1C = dword ptr -1Ch
___:00423660 var_10 = dword ptr -10h
___:00423660 var_C = dword ptr -0Ch
___:00423660 var_8 = dword ptr -8
___:00423660 var_4 = dword ptr -4
___:00423660
___:00423660 push ebp
___:00423661 mov ebp, esp
___:00423663 sub esp, 120h
___:00423669 call sub_423450
___:0042366E cmp eax, 10h
___:00423671 jz short loc_42367A
___:00423673 mov g_IsFullWindow, 1
___:0042367A
___:0042367A loc_42367A: ; CODE XREF: sub_423660+11j
___:0042367A mov eax, dword_67F9F8
___:0042367F test eax, eax
___:00423681 jnz short loc_4236B3
___:00423683 push eax
___:00423684 push offset dword_67F9F8
___:00423689 push eax
___:0042368A call sub_4D53B8
___:0042368F ; ---------------------------------------------------------------------------
___:0042368F test eax, eax
___:00423691 jz short loc_4236AE
___:00423693 push eax
___:00423694 push offset aDirectdrawcrea ; "DirectDrawCreate failed err=%d"
___:00423699
___:00423699 loc_423699: ; CODE XREF: sub_423660+7Aj
___:00423699 lea eax, [ebp+var_120]
___:0042369F push eax
___:004236A0 nop
___:004236A1 call near ptr 77C0F931h
___:004236A6 add esp, 0Ch
___:004236A9 jmp loc_423752
___:004236AE ; ---------------------------------------------------------------------------
___:004236AE
___:004236AE loc_4236AE: ; CODE XREF: sub_423660+31j
___:004236AE mov eax, dword_67F9F8
___:004236B3
___:004236B3 loc_4236B3: ; CODE XREF: sub_423660+21j
___:004236B3 mov cl, g_IsFullWindow
___:004236B9 test cl, cl
___:004236BB mov ecx, [eax]
___:004236BD jz short loc_4236C3
___:004236BF push 11h
___:004236C1 jmp short loc_4236C5
___:004236C3 ; ---------------------------------------------------------------------------
___:004236C3
___:004236C3 loc_4236C3: ; CODE XREF: sub_423660+5Dj
___:004236C3 push 8
___:004236C5
___:004236C5 loc_4236C5: ; CODE XREF: sub_423660+61j
___:004236C5 mov edx, g_GamehWnd
___:004236CB push edx
___:004236CC push eax
___:004236CD call dword ptr [ecx+50h]
___:004236D0 test eax, eax
___:004236D2 jz short loc_4236DC
___:004236D4 push eax
___:004236D5 push offset aSetcooperative ; "SetCooperativeLevel failed err=%d"
___:004236DA jmp short loc_423699
___:004236DC ; ---------------------------------------------------------------------------
___:004236DC
___:004236DC loc_4236DC: ; CODE XREF: sub_423660+72j
___:004236DC mov al, g_IsFullWindow
___:004236E1 test al, al
___:004236E3 jz loc_42376A
___:004236E9 mov ecx, g_GamehWnd
___:004236EF push 0FFFFFFF0h
___:004236F1 push ecx
___:004236F2 call near ptr 2F30387h ; GetWindowLongA
___:004236F7 nop
___:004236F8 mov edx, g_GamehWnd
___:004236FE and eax, 0FF3DFFFFh
___:00423703 or eax, 80000000h
___:00423708 push eax
___:00423709 push 0FFFFFFF0h
___:0042370B push edx
___:0042370C call near ptr 2F305BDh ; SetWindowLongA
___:00423711 nop
___:00423712 mov eax, dword_67F9F8
___:00423717 push 10h
___:00423719 push 1E0h
___:0042371E mov ecx, [eax]
___:00423720 push 280h
___:00423725 push eax
___:00423726 call dword ptr [ecx+54h]
___:00423729 test eax, eax
___:0042372B jge loc_423885
___:00423731 push eax
___:00423732 lea edx, [ebp+var_120]
___:00423738 push offset aSetmodeFailedE ; "SetMode failed err=%x"
正常的流程,也就是全屏幕的过程,g_IsFullWindow,这个地方的地址,是为1(当然这
个后面分析出的结果,我用IDA给它重新命名了),所以4236BD,4236E3这2个地方它不会
跳走,所以就执行了
call dword ptr [ecx+50h]
call dword ptr [ecx+54h]
这2个函数,通过后面的提示,以及网上的一些搜索.ecx+0x50应该就是SetCooperativeLevel
的函数的地址,而ecx+0x54就是SetDisplayMode,现在看看挂是怎么给窗口化的?重新再来,开游戏
你会发现如果用挂,这个地方的流程就变化了g_IsFullWindow这个地方的值给改变了为0了,而不是
原来的1.所以方法1很简单Hook DirectDrawCreat这个函数,然后在里面修改g_IsFullWindow这个
地址的值(因为紧跟着就是判断调用,所以我们在Hook的DirectDrawCreat里面做下手脚)
HRESULT WINAPI MyDirectDrawCreate(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD,IUnknown FAR* pUnkOuter)
{
HRESULT hResult;
__asm
{
push pUnkOuter
push lplpDD
push lpGUID
Call [g_DirectDrawCreate_Call]
mov hResult,eax
}
*(byte *)g_IsWindow = 0;
return hResult;
}
方法1的特点,简单,但是缺点就是需要随着游戏的更新而更新这个全局变量的地址,来适应游戏的
升级.为了达到通用版本(针对这个游戏),所以我们想到了方法2,实际上这个游戏也不是不能够自己
窗口化,只是他自己没有给出让玩家自己设定而已(想不通,为什么呢?难道非要一心一意的玩游戏?)
好了,接下来我们在上面MyDirectDrawCreate里面返回4236BD这个地址跳走了,原来是不会跳走的
push 11现在跳走了然后push 8,这个是个什么意思呢???google一下这个函数,需要对Dircet编程有
点了解,你会发现SetCooperativeLevel的调用方式
HRESULT SetCooperativeLevel(HWND hwnd,DWORD dwLevel);
第一个是游戏的HWND,创建窗口得来的(这个也很重要,后面会讲到),第2个就是我们前面看到的2次
跳转的不同的值,默认的是0x11,窗口化的这个地方传入0x08
0x11 = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN
0x08 = DDSCL_NORMAL
看到了吗?这个就是全屏幕的调用,窗口则传入DDSCL_NORMAL的方式,接下来我们继续走4236E3这个
地方,因为已经修改掉了,所以就是0了,它就跳到了42376A,代码见下面,其实就处理一些窗口的东西
了.
___:0042376A loc_42376A: ; CODE XREF: sub_423660+83j
___:0042376A mov eax, g_GamehWnd
___:0042376F push esi
___:00423770 push 12CA0000h
___:00423775 push 0FFFFFFF0h
___:00423777 push eax
___:00423778 call near ptr 2F305BDh ; SetWindowLongA
___:0042377D nop
___:0042377E lea ecx, [ebp+var_10]
___:00423781 push 1E0h
___:00423786 push 280h
___:0042378B push 0
___:0042378D push 0
___:0042378F push ecx
___:00423790 call near ptr 2F30737h ; SetRect
___:00423795 nop
___:00423796 mov edx, g_GamehWnd
___:0042379C mov esi, g_GetWindowLongA
___:004237A2 push -20 ; GWL_EXSTYLE
___:004237A4 push edx
___:004237A5 call esi ; g_GetWindowLongA
___:004237A7 push eax ; eax = 0x40100
___:004237A8 mov eax, g_GamehWnd
___:004237AD push eax
___:004237AE call near ptr 2F30965h ; GetMenu
___:004237B3 nop
___:004237B4 mov ecx, g_GamehWnd
___:004237BA neg eax
___:004237BC sbb eax, eax
___:004237BE neg eax
___:004237C0 push eax
___:004237C1 push -16 ; GWL_STYLE
___:004237C3 push ecx
___:004237C4 call esi ; g_GetWindowLongA
___:004237C6 lea edx, [ebp+var_10]
___:004237C9 push eax
___:004237CA push edx
___:004237CB call near ptr 2F30D21h ; AdjustWindowRectEx
___:004237D0 nop
___:004237D1 mov eax, [ebp+var_4]
___:004237D4 mov edx, [ebp+var_C]
___:004237D7 mov ecx, [ebp+var_8]
___:004237DA sub eax, edx
___:004237DC push 16h ; _DWORD
___:004237DE mov edx, g_GamehWnd
___:004237E4 push eax ; _DWORD
___:004237E5 mov eax, [ebp+var_10]
___:004237E8 mov esi, g_SetWindowPos
___:004237EE sub ecx, eax
___:004237F0 push ecx ; _DWORD
___:004237F1 push 0 ; _DWORD
___:004237F3 push 0 ; _DWORD
___:004237F5 push 0 ; _DWORD
___:004237F7 push edx ; _DWORD
___:004237F8 call esi ; g_SetWindowPos
___:004237FA mov eax, g_GamehWnd
___:004237FF push 13h ; _DWORD
___:00423801 push 0 ; _DWORD
___:00423803 push 0 ; _DWORD
___:00423805 push 0 ; _DWORD
___:00423807 push 0 ; _DWORD
___:00423809 push 0FFFFFFFEh ; _DWORD
___:0042380B push eax ; _DWORD
___:0042380C call esi ; g_SetWindowPos
___:0042380E lea ecx, [ebp+var_20]
___:00423811 push 0
___:00423813 push ecx
___:00423814 push 0
___:00423816 push 30h
___:00423818 call near ptr 2F40000h ; SystemParametersInfoA
___:0042381D nop
___:0042381E mov eax, g_GamehWnd
___:00423823 lea edx, [ebp+var_10]
___:00423826 push edx
___:00423827 push eax
___:00423828 call near ptr 2F40402h ; GetWindowRect
___:0042382D nop
___:0042382E mov eax, [ebp+var_10]
___:00423831 mov ecx, [ebp+var_20]
___:00423834 cmp eax, ecx
___:00423836 jge short loc_42383D
___:00423838 mov eax, ecx
___:0042383A mov [ebp+var_10], eax
___:0042383D
___:0042383D loc_42383D: ; CODE XREF: sub_423660+1D6j
___:0042383D mov ecx, [ebp+var_C]
___:00423840 mov edx, [ebp+var_1C]
___:00423843 cmp ecx, edx
___:00423845 jge short loc_42384C
___:00423847 mov ecx, edx
___:00423849 mov [ebp+var_C], ecx
___:0042384C
___:0042384C loc_42384C: ; CODE XREF: sub_423660+1E5j
___:0042384C push 15h ; _DWORD
___:0042384E push 0 ; _DWORD
___:00423850 push 0 ; _DWORD
___:00423852 push ecx ; _DWORD
___:00423853 mov ecx, g_GamehWnd
___:00423859 push eax ; _DWORD
___:0042385A push 0 ; _DWORD
___:0042385C push ecx ; _DWORD
___:0042385D call esi ; g_SetWindowPos
___:0042385F mov edx, g_GamehWnd
___:00423865 mov dword_682088, 0
___:0042386F push 1
___:00423871 push 0
___:00423873 push edx
___:00423874 mov dword_68208C, 0
___:0042387E call near ptr 2F71B9Fh ; InvalidateRect
好了,既然知道了游戏的处理流程,我们就可以自己实现了.Hook DirectDrawCreate是
肯定的了,接下来就是要Hook SetCooperativeLevel跟SetDisplayMode了
HRESULT WINAPI MyDirectDrawCreate(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD,IUnknown FAR* pUnkOuter)
{
HRESULT hResult;
__asm
{
push pUnkOuter
push lplpDD
push lpGUID
Call [g_DirectDrawCreate_Call]
mov hResult,eax
}
//这个看开头的说明
DWORD dwDD = (DWORD)*lplpDD;
dwDD = *(DWORD *)dwDD;
Hook_Api(*(DWORD *)(dwDD+0x50)
,(DWORD)MySetCooperativeLevel,
&g_str_inline_hook_SetCooperativeLevel,&g_SetCooperativeLevel_Call);
Hook_Api(*(DWORD *)(dwDD+0x54)
,(DWORD)MySetDisplayMode,
&g_str_inline_hook_SetDisplayMode,&g_SetDisplayMode_Call);
return hResult;
}
HRESULT WINAPI MySetCooperativeLevel(DWORD dwThis,HWND hwnd,DWORD dwLevel)
{
HRESULT hResult;
__asm
{
push 0x00000008
push hwnd
push dwThis
Call [g_SetCooperativeLevel_Call]
mov hResult,eax
}
return hResult;
}
MySetCooperativeLevel,我们需要把参数人为的修改为0x08
HRESULT WINAPI MySetDisplayMode(DWORD dwThis,DWORD dwWidth,DWORD dwHeight,DWORD dwBit)
{
HRESULT hResult = 1;
// __asm
// {
// push dwBit
// push dwHeight
// push dwWidth
// push dwThis
// Call [g_SetDisplayMode_Call]
// mov hResult,eax
// }
return hResult;
}
MySetDisplayMode函数里面,我们什么都不做,OK,让他直接返回,就像开头说的一样
回忆一下,SetDisplayMode 屏掉,因为DirectX中窗口化方式下显示模式不能设置.然后
再开游戏,dll注入进去,看看是否已经窗口化掉了???但是这个只是个假象,游戏虽然
窗口化掉了,但是点了窗口外面的东西没有反应,这个就是窗口化后,后面的部分没有处理
,也就是跳到42376A开始的往下执行部分,没有执行.因为默认的Hook调以后,执行完
SetDisplayMode以后,在42372B处会跳到结尾结束.所以我们需要自己补齐那部分的
处理代码,位置嘛,当然是在MySetDisplayMode里面,毕竟在它里面什么都不做,那我们
就来做点东东了.也就是帮他执行42376A后面的代码.代码如下:
HRESULT WINAPI MySetDisplayMode(DWORD dwThis,DWORD dwWidth,DWORD dwHeight,DWORD dwBit)
{
HRESULT hResult = 1;
RECT rcSect;
SetWindowLong(g_GamehWnd,GWL_STYLE,0x12CA0000);
SetRect(&rcSect,0,0,0x280,0x1E0);
DWORD dwExstyle = GetWindowLong(g_GamehWnd,GWL_EXSTYLE);
HMENU hMenu = GetMenu(g_GamehWnd);
DWORD dwstyle = GetWindowLong(g_GamehWnd,GWL_STYLE);
AdjustWindowRectEx(&rcSect,dwstyle,(BOOL)hMenu,dwExstyle);
long cy = rcSect.bottom - rcSect.top;
long cx = rcSect.right - rcSect.left;
SetWindowPos(g_GamehWnd,0,0,0,cx,cy,0x16);
SetWindowPos(g_GamehWnd,(HWND)0x0FFFFFFFE,0,0,0,0,0x13);
RECT rcWorkArea;
SystemParametersInfo(SPI_GETWORKAREA,0,&rcWorkArea,0);
GetWindowRect(g_GamehWnd,&rcSect);
if(rcSect.left < rcWorkArea.left)
{
rcSect.left = rcWorkArea.left;
}
if(rcSect.top < rcWorkArea.top)
{
rcSect.top = rcWorkArea.top;
}
SetWindowPos(g_GamehWnd,(HWND)0,rcSect.left,rcSect.top,0,0,0x15);
InvalidateRect(g_GamehWnd,NULL,TRUE);
return hResult;
}
在进游戏看看,是不是已经能够操作游戏窗口外的东西了,当然还有一点要说明,以为
后面的代码处理部分用到了g_GamehWnd,所以为了达到通用,你可以自己处理下或者在
MySetCooperativeLevel里面保存一下hwnd到g_GamehWnd,也可以Hook CreateWindowExA
来得到g_GamehWnd,当然这个游戏用Themida处理过了,一些Api的调用都不在系统空间执
行了当然你可以继续在CreateWindowExA往下Hook.
好了方法2也说完了,方法2的特点就是通用天1的所有版本,以后游戏更新也不怕了,
辛苦一次,幸福以后啊.:)至于传奇的是否可行,这个没有下游戏我就不知道了,我想应该
也应该可以吧.
在此感谢fengyue的StrongOD,仅以此文献给,在网上找不到处理游戏窗口化的方式跟
MySetDisplayMode代码的XDJM
--------------------------------------------------------------------------------
【经验总结】
有效合理运用已知外Gua跟Google,:)
--------------------------------------------------------------------------------
【版权声明】: 转载请注明作者并保持文章的完整, 谢谢!
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!