这是我痴迷于破解与接近于游戏开发的阶段. 大概是2005年7月
作者:LiquidX
示例图:
![](http://www.cppblog.com/images/cppblog_com/liquidx/111.jpg)
其实网上有很多的外挂内挂文章我也看了不少,从中得到了很多的帮助。前些时候看到shaker写出的一些传奇外挂的文章之后,再根据自己的研究对外挂知识也算是有了个初步的了解。
外挂分为2种,一种是脱机程序,也就是模拟客户端的程序称为外挂.另一种是利用游戏程序本身的函数对游戏进行一些相关动作的称之为内挂,因为是在游戏进程内部完成任务的。今天要说的是传奇2(虽然这游戏过时了但作为研究来说还是值得的:)内挂的一点点知识,其实我也不太懂,复杂的东西也弄不出来,所以我就把我所学到的一点点知识写了出来,希望更多的人能够了解这方面的知识。
本文没有什么技术可言,但相信对一些未入门的人很有用.
第一步: 首先我们得将传奇的mir.dat脱壳有些私服没有mir.dat那就看看mir.exe,我们查得他是用aspack加的壳,你可以去网上下载相关工具也可以手动脱掉. 这样传奇2现在就是赤裸裸的站在我们面前了:) 现在要做的就是给他开开刀,看他的心肝肠肺都在做些什么,在哪里长着.....
第二步:我们用OLLYDBG加载刚才已经脱壳的mir.dat,然后我们利用插件菜单里的中文字符插件来获得相关信息,如果你没有此插件可以去www.pediy.com找找.不一会儿od给我们呈现出了很多的字符串信息,我们现在就搜索他的“肺”-("攻城区域")我们找到如下图:![](http://www.cppblog.com/images/cppblog_com/liquidx/222.jpg)
在此行双击鼠标左键我们来到:
![](http://www.cppblog.com/images/cppblog_com/liquidx/333.jpg)
经过调试确定这里就是个屏幕输出就是在我们攻城的时候屏幕左上角显示的那几个字.
0047A4B3 . 68 FFFFFF00 push 0FFFFFF //字体颜色
0047A4B8 . 6A 00 push 0 //背景色
0047A4BA . 68 94A54700 push unpacked.0047A594
0047A4BF . 33C9 xor ecx,ecx //x坐标
0047A4C1 . 33D2 xor edx,edx //y坐标
0047A4C3 . 8B45 F8 mov eax,dword ptr ss:[ebp-8] //设备场景句柄
0047A4C6 . E8 D5640200 call unpacked.004A09A0 //内部屏幕输出函数
那么我们就写出这个函数
typedef struct
{
int len;
char text[100];
}DT;
void SText(DWORD eax1)
{
DT dstring;
strcpy(dstring.text,(char*)string);
dstring.len=strlen(string);
txtaddress=(DWORD)&dstring.text[0];
_asm
{
mov eax, eax1
call setshowmode1
call setshowmode2
push TRANSPARENT
push eax
call setshowmode3
push txtcolor
push bkcol
push txtaddress
mov ecx, y
mov edx, x
mov eax, eax1
call ShowTxtcall
}
}
然后定义一些全局变量和一个可以动态修改输出的字符串和颜色与位置的函数:
const DWORD conaddress=0x47A6CC;
const DWORD ShowTxtcall=0x4a09a0;
const DWORD setshowmode1=0x44D8B4,setshowmode2=0x41834C,setshowmode3=0x406434;
DWORD x=0x0, y=0x0,txtcolor=0x0,bkcol=0x1e00ff;
DWORD txtaddress=0x0;
char* string="传奇小外挂--By LiquidX Diy 2005.6.15";
void settxt(char* strings,
DWORD X,DWORD Y,
DWORD TXTCOLOR,DWORD BKCOL)
{
string=strings;
x=X;
y=Y;
txtcolor=TXTCOLOR;
bkcol=BKCOL;
}
现在我们的屏幕输出函数已经模拟出来了,下面要做的就是改掉游戏调用这个函数的入口,使这个函数跳转到我们的函数中来这样就可以由我们来输出想输出的字符串了..
定义一个naked函数 关于naked可以去网上查查..
__declspec(naked) initST()
{
_asm
{
push eax
push edx
push ecx
push ebp//保存参数
mov eax,dword ptr[ebp-0x8] //获得我们当时eax中的值
push eax //传入eax参数
call disfunc //调用我们的函数
pop ebp //恢复堆栈
pop ecx
pop edx
pop eax
mov ecx,9
jmp conaddress //返回游戏函数继续执行
}
}
void __stdcall disfunc(DWORD eax1)
{
SText(eax1);//调用我们的函数
}
好了,现在基本上都完成得差不多了,现在只需要修改机器码了
上面代码中我们看到函数中一直都需要获得当时的eax中的值,经过跟踪分析我选择0x47a6cc处地址...
代码如下:
LRESULT CALLBACK hookproc(int ncode ,WPARAM wparam,LPARAM lparam)
{
if(KEYUP(lparam)&&ncode==HC_ACTION&&wparam==VK_HOME)
{
settxt("ShowText Testing....终于成功啦!!!",0x120,0x80,0x0,0x00ffff);
}
if(KEYUP(lparam)&&ncode==HC_ACTION&&wparam==VK_F12)
{
char buf[MAX_PATH];
::GetClassName(GetActiveWindow(),buf,MAX_PATH);
if (lstrcmpi(buf,"TFrmMain")==0)
{
_asm //改写 地址 跳转到我们的函数
{
lea eax,initST
mov ebx,0x47a6cc //写入这个地址
sub eax,ebx
mov esi,0x47a6c7
mov dword ptr[esi],0xe9 //JMP
mov dword ptr[esi+0x1],eax //合成跳转指令
}
}
}
return ::CallNextHookEx(hook,ncode,wparam,lparam);
}
现在我们的一个屏幕输出的简单内挂就完成了,根据网上提供的一些内存地址你可以给它加上更多的功能。
最后一件事情就是外挂退出时恢复机器码,以免游戏跳转到一个不可用的地址造成崩溃..
代码如下:
void revert()
{
_asm
{
mov esi,0x47A6C7
mov eax,0xb9
mov dword ptr[esi],eax
mov eax,0x09
mov dword ptr[esi+0x1],eax
}
}
全文完! 本人能力有限,有任何错误之处希望告之.以免造成误导...
本文配套代码下载:
http://www.cppblog.com/Files/liquidx/mir20057122103.rar
作者QQ:380000937 mailto:liquidx@163.com
等过些天把我收集的一些外挂源码整理一下提供给大家下载研究 :)
相关交流论坛:www.gameres.com,www.cnesort.com
相关资料
全屏看血
内存地址:47A0D3
75 EB
原版:00000075108B45EC
新版:000000EB108B45EC
}
<P>{强行退出
内存地址:004620E6(7)
74 90
0D 90
原版:2000740D8B45
新版:200090908B45
内存地址:00462162(3)
74 90
0A 90
原版:2000740A8B45
新版:200090908B45
内存地址:4914CA(B)
内存地址:491576(7)
74 90
0E 90
原版:0080782000740EA1
新版:00807820009090A1
}</P>
<P>{免助跑
内存地址:00461BEB(C-F0)
0F 90
8E 90
79 90
FD 90
FF 90
FF 90
原版:E8000F8E79FDFFFF
新版:E800909090909090
内存地址:461BB9(A-E)
0F 90
8C 90
DA 90
00 90
00 90
00 90
原版:00010F8CDA000000A1
新版:0001909090909090A1
}</P>
<P>{跑步砍
内存地址:004634E2
00 01
原版:4F00008D45F0
新版:4F00018D45F0
}</P>
<P>{攻击速度
内存地址:467016(7)
78 E2
05 04
原版:EB0BB87805
新版:EB0BB84805
说明:速度由二位数指定,二位数前后互换为真实数据,数字大为慢小为快
}</P>
<P>{穿人
内存地址:472D17
34 0C
原版:00000034018845
新版:0000000C018845
}</P>
<P>{免蜡
内存地址:471BDE
74 EB
原版:008038007454
新版:00803800EB54
}</P>
<P>{物品闪光
内存地址:471AA6
04
原版:1300007625
新版:0200007625
}</P>
<P>{自动放药
0048C21F F9 68 FD FF
004623A2 76 07 00 00
0048C21F DD 50 B0 01
004623A2 7A ED B2 01
}</P>
<P>{超负重??
Poke 00499A40 EB
004975A8 EB 5C
00499A40 EB 93</P>
<P>{攻击方法修改一
原版
00463425 74 1C
0046344A 74 10
00463463 74 15</P>
<P>半月
00463425 74 1C
0046344A 74 10
00463463 90 90
攻杀
00463425 74 1C
0046344A 90 90
00463463 74 15
烈火
00463425 90 90
0046344A 74 10
00463463 74 15</P>
<P>方法二</P>
<P>半月
Poke 00463363 D0
烈火
Poke 00463363 D1
普通
Poke 00463363 C6</P>
<P>C745E8 C60B 单手砍
C745E8 C70B 双手砍
C745E8 C80B 跳跃砍
C745E8 CA0B 攻杀
C745E8 CB0B 刺杀
C745E8 D00B 半月
C745E8 D10B 烈火
}</P>
<P>{无限刺杀
内存地址:463363
C6 CB
原版:C745E8C60B
新版:C745E8CB0B
内存地址:463373
C7 CB
原版:C745E8C70B
新版:C745E8CB0B
}</P>
<P>{无限攻杀
内存地址:463363
C6 CA
原版:C745E8C60B
新版:C745E8CA0B
内存地址:463373
C7 CA
原版:C745E8C70B
新版:C745E8CA0B
</P>