armytg 2011-09-18
源地址:http://hi.baidu.com/probill/blog/item/1d07d11efbd641f01ad576f3.html
网络游戏.每一个数据比如你的血值.MP值.怪的血值..在内存中是以16进制的形式存放的.而显示给我们看的是10进制的形式显示的,,[16进制转就是123456789ABCDEF,好比十进制的0123456789,逢16进1,十进制是逢十进1,不想算可以用windows自带的计算器,在附件中有]
网络游戏几乎全是动态内存存放[大话战国居然不是.那天写辅助工具时发现的],,就是每上线一次,数据在内存中分配位置会变,但是.数据间地址的差值是不变的.就是所谓的偏移量..我们要做的工作就是要让动态的,转换成静态的,只要找到一个就成了.别的根据偏移量可以知道.步骤如下:
1.我们进游戏.首先要做的就是找动态内存地址[我以HP值举例],找动态内存的工具软件很多,我推荐金山游侠,FPE,CE,GE等...软件用法很简单..就好比你现在的HP值是1000.你先定位好程序.输入1000搜索,会搜到一串地址..然后让自己的血值变(比如穿件加HP的装备,比如是加了50血),再搜1050,这样试几次就找到了唯一的动态内存地址.[虽然唯一,但一下线就会变的],第一步工作完全了...
2.然后我们要用到调试工具设断点,调试工具很多,比如OD或softice等,我以softice举例:
比如我们搜索到HP的动态地址是044321A7..我们按CRTL+D呼出SOFICE..下命令设断点BPM044321A7W然后按F5退出进游戏..只要HP值一变.就会跳出调试的界面.比如说位置跳到了001B:0047EB17moveax,[edx+000000fc]处..其中..edx是基值[也可以是esi等等],000000fc是一个固定的地址偏移量,每次进游戏在变的就是基值中的数值.
3.到这..有多种方法可以求得静态地址..一种就是内存注入的方法.网上有很多这类的资料..主要是太麻烦..我就不写了.我讲我自己的方法.我是用到指针的指针的思想..就是.我们的HP值是存放在一个动态内存地址中..地址其实也是数据..地址也是存放在地址中的..当然.要基值的地址..所以我们要做的就是找存放基值地址的地址,可以用金山等软件搜索到..不过地址是16进制的..所以要转换成10进制.再找存放基值地址的地址..比如说找到011076EC.好了.我们的工作完成了...现在就到了怎么写读语句..用C语言写起来很简单..用VB也可以写..[别人说VB没指针.但并不代表不能用].因为按键是VBS语言.那我就用VB写..当然.我要调用WIN32API函数
VB调用API要申明,如下:DdeclareFunctionReadProcessMemoryLib"kernel32"(ByValhProcessAsLong,lpBaseAddressAsAny,lpBufferAsAny,ByValnSizeAsLong,lpNumberOfBytesWrittenAsLong)AsLong
dimedxaslong
dimhpaslong
ReadProcessMemorynOK,ByVal&H011076EC,edx,4,0
ReadProcessMemorynOK,ByValedx+000000fc,hp,4,0
就这样两条语句..hp中的数值就是我们的血值了..非常精准的可以得到..比如我血有1000点.我要在666点时加血..你就可以ifhp<666thenkeypress"加血的按键"
补充一点..搜索基址的地址是会搜到不少个的..但是一般来说..第一个就是...------------------------------------------------------------------------------------------------------------------------------
源地址:http://www.wgx4.com/article/html/257.html
从游戏中得到动态内存数据
作者:sbsummer
刚才我玩了几把疯狂坦克,输了好几盘,觉得无聊就搞搞这个,下面开始说说如何得到游戏中的动态数据(地址改变),以得到疯狂坦克中坦克X坐标为例
------------------------------------------------------------------------------
工具:
SoftICE动态调试程序,游戏修改工具(金山游侠),反汇编(W32Dasm),HexWorkshop
------------------------------------------------------------------------------
一、找到内存中坦克X坐标
用金山游侠搜索,方法如下(金山游侠的使用我就不说了)
把坦克往左移动一些,就搜索“减少”;坦克往右移动,就搜索“增大”
反复搜索将会找到一个地址(当然其他游戏可能不止一个),这里是08BFAACC
注:动态的内存分配就是下次你如果再次搜索,地址将不再是08BFAACC
二、找到那条代码修改了这个数据(X坐标)
加载SoftIce
在游戏状态Ctrl+D调出SoftIce,输入BPM08BFAACCW,这里的W表示如果这个地址被写将中断
回到游戏,移动坦克,左移一下,程序中断,SoftIce指向的上面一句是
004640B3MOVDWORDPTR[ESI+000001A4],EAX
这句就是修改坦克坐标的代码,当然右移也能找到一句,这里就不重复了
三、修改程序使动态的数据变成静态
这里说点题外话,修改程序包括两种,一种是直接修改程序,一种是修改内存中的程序(内存补丁),这里由于我懒,所以用了第一种
修改程序:
疯狂坦克程序存在Fortress2.dat当中,如果你把这个文件改名为EXE文件一样可以运行,这里我们就把他修改成Fortress2.exe
打开W32Dasm反汇编,SHIFT+F12跳到004046B3,你看到这几行
004046B38986A4010000MOVDWORDPTR[ESI+000001A4],EAX
004046B98B8644020000MOVEAX,DWORDPTR[ESI+00000244]
004046BFC744241001000000MOV[ESP+10],00000001
刚才我们说了004046B3是修改X坐标的那条语句,现在我们要让他每次修改完程序就能够把X坐标存储到一个固定的地址
现在要让它运行到这里就JMP到一个我们自己的代码的地方,于是在程序的尾部我们找到一段空白的区域00465A52,于是我修改004046BF为代码JMP00465A52,机器码为E98E130600,因为这句的长度不够以前的那句长,所以要加入几个NOP,机器码为90,所以我们打开HEXWorkshop修改程序,CTRL+G跳到位移为000046BF的地方,看到了C744241001000000,我们把它修改为E98E130600909090,现在程序将一运行到这里就跳到00465A52运行我们的代码。
四、实现我们自己的代码,然后跳回
我们的代码要做的是把动态变成静态,
PUSHEAX
MOVEAX,[ESI+000001A4]
MOV[00470000],EAX
POPEAX
JMP004046C7
这样这个数值无论运行多少次,只要你移动(当然右移也要修改)就能在00470000中找到X坐标,这段机器码为508B86A4010000
A30000470058E95BECF9FF
忘了说刚才我们把004046BF替换掉的那句MOV[ESP+10],00000001也必须加上,所以打开HEX
Workshop,CTRL+G跳到00465A52,修改加入
C744241001000000508B86A4010000A30000470058E95BECF9FF
这样动态数据就变成了静态
------------------------------------------------------------------------------
现在回顾一下
首先搜索坐标地址
找到改变这个地址的代码
修改代码让他跳到自己的代码中运行
在程序的空白段加入自己的代码,当然要补上被替换了的那句,还有修改了寄存器,必须先PUSH,再POP
下面的工作就是写一个程序读取这个地址了,我用VC写了一个,顺便贴一下关键代码
------------------------------------------------------------------------------
CProcessm_process;
boolm_ret=m_process.FindProcess("FortressII");
if(m_ret)
{
BYTEtank1xL=m_process.ReadByte(0x00470000);
BYTEtank1xR=m_process.ReadByte(0x00470001);
WORDtank1x=tank1xL+tank1xR*256;
temp=tank1x;
str.Format("%d",temp);
m_tank1x=str;
UpdateData(FALSE);
returnTRUE;
}
elsereturnFALSE;
-----------------------------------------------------------------------------
CProcess是一个我编写的游戏修改类,以下是部分函数代码:
HANDLECProcess::OpenProcess(char*p_ClassName,char*p_WindowTitle)
{
HWNDhWindow;
DWORDpid;
hWindow=FindWindow(p_ClassName,p_WindowTitle);
if(hWindow){
GetWindowThreadProcessId(hWindow,&pid);
return::OpenProcess(PROCESS_ALL_ACCESS,false,pid);
}
returnNULL;
}
boolCProcess::FindProcess(char*p_WindowTitle)
{
if(m_hProcess==NULL){
m_hProcess=this->OpenProcess(NULL,p_WindowTitle);
if(m_hProcess)
m_bGameRunning=true;
returnm_bGameRunning;
}
else
returnfalse;
}
BYTECProcess::ReadByte(DWORDp_Address)
{
DWORDbytes;
BYTEtmpValue;
if(m_bGameRunning){
if(ReadProcessMemory(m_hProcess,(void*)p_Address,
(void*)&tmpValue,1,&bytes)==0)
return0;
else
returntmpValue;
}
return0;
}
-----------------------------------------------------------------------------