一、找点击选怪物call以及选怪状态标志
1、ce搜未知数据,选择一个怪,搜变动,换一个,再搜变动,最后找到最像的查看访问地址代码,记下后,od附加跳转下段,返回上一层,用注入软件测试一下。
2、找到后,记下call上下代码和call内部代码,遗迹ce搜到的代码,方便以后定位基地址。
3、一般参数是存放怪物id等信息的地址和另外一个参数(可能是用来存放计算得到的怪物id等信息,就是ce搜到的存放怪物id的内存地址) 向上找,一般格式是mov[变量地址]数值。
4、和选择怪物call相关的动作,ce搜到的数据就可以直接od跳转到调试,向上找就能找到相关的动作call。
5、或者直接bp send。
6、ce搜出来的所点击怪物的id的地址,完后通过它od里找到的是一个call吧。某处地址的数据存入了ce找到的地址里面了。具体这个某处地址的数据是怎么来的,没点击肯定啥也没有。
点击了才会在某个地方计算,点击的坐标和怪物的坐标等等数据,计算结果才会算出这个怪物的id。具体估计是在某个库里的call里面计算,所以你找不到的,点击选怪call,如果要自动运行,就必须先知道要攻击的怪物的ID等信息,所以你找哪里把怪物id存放到那个地址里,不是白费功夫吗。
选怪状态标志:
查找经验:
1、有些游戏如天龙八部,每次调试出现错误,再启动,游戏的反汇编指令地址和特征码都在变动,所以每次找到重要数据,都要记录反汇编指令和特征码还有所在模块名字。
2、选择怪物,ce搜未知初始化数据,完后不选择怪物,搜变动,来回搜 完后跳跃,打坐,搜未变动,几个回合后,剩下四五个了,完后选择一个 找访问的指令,写入的也可以,完后od跳转向上层找来源,发现最后都会有个来源的地址的数据不停地变动,而且那层call不停的断下来,ce也搜不到访问的指令,那就说明只有用户选择了怪物,才会有数据存进那个地址 不然就是不停地变动(可能被其他指令访问存取)。
3、第一种情况遇到后不着急,ce查看另外一个搜到的结果,查看访问的代码 完后od跳转,是这种如上图指令:mov ecx,dword ptr ds:[esi+0xC],完后发现是个二叉树遍历,完后发现是个二叉树数组 。
数组里每个二叉树的指定偏移都是选中怪物状态的标志,od不断下来,那些标志和数据也不变化,那就找到了。完后找树根,比如刚才那个指令下断点,完后内存窗口更随 完后ce搜包含esi的地址,完后搜。
包含esi地址的地址, 以此类推,找到了00745410 |. 8B30 mov esi,dword ptr ds:[eax] 完后发现eax来源ds:[edi+0x10] edi是基地址0x986618 断下来后数据不变动,那就找到了。
二、寻路call 以及 坐标加密
1、ce搜索未知、跑动、改变、不跑、不改变。
2、一般人物跑动状态是bool a=0 1 或者int a = 0不动 1跑动 2 走动 一般搜索0到2之间数字 有可能1是不动 0是跑。
3、ce找到小数字,查看写入的代码,此时ce可能搜索到有很多个貌似得内存地址,一定要看跑的时候往里面写入的指令,比如跑的时候写入0,就监视器中找mov byte ...0之类的语句,选择几个,查看写入的代码,首先看游戏跑一下,监视器中有没有多弹出来的某条指令,如果有,那看是不是mov byte 之类的,因为可能是bool值,一个字节。
如果没有多弹出来的指令,就看每条指令类似mov byte,或者直接吧ce搜出来的地址,锁住,看游戏能跑动不,一般不能跑动的就是要找的了。记下来 od跳转,完后下段 ctrl+f9上一层看这个call参数有没有坐标数值或者是存坐标的结构地址 或者把该call nop掉看看。
4、完后一般寻路call,都是先读取障碍物图call,完后查看坐标是否在障碍物上,完后是寻路call(武林外传的)。
5、call参数如果是个地址,一定要在call内部看看这个地址,哪些偏移被访问了,没有断下来的时候里面存的是不是被访问的数据,如果没有,说明其他地方把数据写入了这地址的偏移里面了,还要再找找哪里写入的。
6,坐标可能加密,一般是SHL SHR。
7,如果坐标加密,当前寻路call下段后,参数里存放的地址,跟随进去后看到的是加密后的坐标,所以要查看哪里写入了这些坐标 一般下断点后,断下来的是读取现在的坐标完后,加每一步的偏移,最后写入那个地址里 而我们要找到的啥时候把现在的坐标写入的,所以要返回人物画面,重新进入游戏 断下来后一步步找。
三、技能call
1、ce搜未知,选择怪物,搜变动,没选择,搜0。
2、访问代码,双击怪物,看ce数量为1的代码,od跳转,一般格式是
while(1):
{
判断鼠标的目标
if(人物在攻击状态)
读取目标id
普攻
}
3、以上找不到,就在技能栏放两个技能,ce搜索未知初始化数据,完后把第一个栏的技能移动到别的栏,完后ce搜0,完后第一个栏放上一个技能, 再搜变动,直到找到几个类似数据,选择较大的ce搜到的三个,最小的数字是技能序号,只查看访问大的的代码。
查看访问的代码。打开监视器。游戏里掉整一下技能位置。看监视器跳出的指令,记下od查找到技能数组,完后数组第一个元素是地址 ce查看方位这个地址的指令,od跳转到地址处,完后附近call里面的某个call就是技能call,一般参数0或者1比较多,有个参数是可以随着技能不同而变化。
4、也可以bp send,完后向上跳转,点击od上的k键,复制下,完后每个call都下断点,没有使用技能就断下来的,就pass掉,完后剩余的nop掉再恢复,看看有没有任何动作,如果有的话,就基本不可能是了。接着向上层call,按同样方法nop再恢复,看有没有啥动作,如果一点都没有,就先在这个call里面开始,遇到的每一个call都同理nop掉在恢复,看有没有动作,没有的就进入同样道理直到找到。
四、找铭文包call
1、bp send 完后游戏里做一些动作,断下来后,返回上一层,查看[esi+4]是封包头部[esi+8]是封包结尾,ce搜+8de地址 查看写入的代码,发言时候断下来od内存中中能否看到发言内容看不懂就是加密call。
2、
发包结构:
sctruct 发包结构
{
dword starsddress
dword lastaddress 1,ce哪里改变
}
void 发包线程()
{
while(1)
if(结构.starsddress==lastaddress)
{
continue;
}
else
{
send();
结构.starsddress=lastaddress
}
}
void 封包写入(int 长度 ,dword 地址)
{
结构地址= 结构.staraddress
int i=0
while(长度!=0)
{
结构地址[i]=地址[i]
i++
}
结构.lastaddress = 结构.startaddress+长度2,跳到这里
}
struct 目标技能结构
{
bool 是否普通攻击
dwordid
dword x
dword y
dword objectID
}
void 目标攻击封包组合函数()
{
组合封包
风暴写入(长度,地址)3,返回这一层
}
void 循环技能()
{
while(1)
{
if(目标技能结构.是否普通攻击==true)
{
普通封包组合函数()4,返回到这一层
}
if(目标技能结构.objectID !=0)
{
技能封包组合函数();
if(目标怪物是否死亡)
目标技能结构id =0;
}
}
}
普通攻击call()
{
}
五、鼠标点击普通攻击call
1,有时候k堆栈表啥都没有,换个od,或者ollyice(大陆od改进版) 删掉调试udd文件再试试。
2,ce搜怪物id内存地址,查看访问的代码,返回游戏,攻击怪物,完后在监视器中查看,最先出来的指令(实在不行就看下一个出来的指令) 完后od跳转,看是不是该条指令下面吧这个怪物id访问出来后,存进了别的内存地址,完后ce 搜这个内存地址,查看访问的指令。
完后,再去攻击怪物监视器中查看,最后出来的访问的指令(不行就倒数第二出来的 以此类推) 完后od跳转,看指令下面的call,进入call开头下断点,返回后删掉这个call,再攻击怪物,看行不行,有没有任何动作(如果死屏的话,估计不是这个call了)完后撤销修改,再看有没有什么动作,有的话估计也不可能是了。
3,bp send 下段后返回,查看堆栈表,复制下来,找到最后的的call(nop掉不能有任何多余动作) 完后在这call里里面,分三层方法:
进入call后逐一排除,只要nop掉,什么动作都没有就行,至于恢复后有什么动作无所谓。
nop掉后,看光标,如果有战斗的光标,点击后不打怪物,的话,可能就是他了。
进入后查看复制的堆栈表,看看哪个call在这个call的里面,完后在他附近找call,nop 一下看看。
六、背包数组和背包物品名称
1、
struct Goords
{
DWORD ID;
char name[32];
int num;
}
可以ce搜 物品id 物品名字 或者物品数量
d1911b526f83
2、提示vp protect 程序发生未知错误,重新启动游戏不登陆,重新打开od 完后提示错误, 完后退出游戏, 在登陆游戏可解决,b不行,就 结束qqprotect,或者od进程,或者换个od。
3、ce搜到药品名字,修改的时候不要改数据类型为text 再修改,会出错。
4、od向上找基地址的时候,一定要注意每次查看的找到的每次偏移,不下断点的时候,还有没有数据,比如找血基地址 [esi+8c]+c 一定要先看+c这个地址有没有原来的数据,完后看esi+8c 里面有没有原来的数据,没有的话就要查找哪里写入了。
5、搜索物品名陈,有时候要搜,金创药(50) 的unicode编码。
6、有时候要用ce搜到的的指令(搜物品名字的unicode 完后查看访问,当鼠标移动到物品上,监视器中弹出来的指令 记下) od跳转过去,遇到[local.x+xx] 或者堆栈内存都存在的,虽然不断下来,里面的一直在变化 ,也可以通过向上条件断点[local.x+xx]==xx 断下来,可以直接记下地址,完后[0x245678c]==xxx 245678c。
可以使内存或者堆栈地址,如果此时里面数据是要找的数据,说明还要往上看往上找断下来没有要找的数据,就是这里存进去的。
7、我猜可能是因为当我鼠标移动到这个物品的时候才会显示物品名称。此时,是更具光标位置,算出移动到哪个格子,才会算出哪个物品,才会显示出名称。
也就是说,此时你根据ce搜索访问那个地址(ce搜到的储存物品名称比如金创药)的代码,完后鼠标移动到金创药上,此时ce监视器中跳出几个mov ax,[ebx] 完后od跳到这里,向上找ebx来源,最终卡到一个地址处,里面即使不断下来,也是很多数据不停地变动。
因为此时你没有吧鼠标移动到金创药上,所以这个地址里面的指针式垃圾指针,只有当你鼠标移动到金疮药上的时候,某个call才会根据光标位置算出具体是在哪个物品上,完后把这个物品的关系地址发到,那个内存地址里,你才会看到,但是因为这个地址里的数据不停地被一些垃圾数据写入,你下断点会无数次断下来,ce搜索出一堆,根本不知道当你鼠标移动到金创药上是哪里,写入数据到那个内存地址里。
8、实在od遇到第七的情况,找不到名称基地址,就ce遍历搜索物品数组取出来的对象。
9、通过搜索背包物品的数量,比如说背包里面有个红药有100个,那么,我们就用CE搜索100。
然后吃掉一个,改变它的数量,再搜索99,以此类推,最后会得到一个或者几个数值,剩下的就慢慢测试吧。
10、用物品在背包格里面的位置来搜索,比如说红药在背包第一个格子里面,我们用CE就搜索未知初始化数值。
然后改变红药在背包的位置,移动到其他位置,搜0,移动回来,搜改变的数值,以此类推。 (c语言编程的特性,物品存在即大于0,不存在为0)
11、ce搜索存储物品名字的地址要范围搜索,因为物品名字前面可能还有一些字符串和它是一体的,完后在结果中找最大的内存地址。完后查看访问这个地址的代码,如果没有看到访问的指令,那就搜索存储这个物品名字的内存地址,可能是访问了这个存储物品名字的指针,完后吃一下药物,看看有没有显示出来的指令。
12、ce搜索物品名字,最好是把文本宽度设置的宽一点,可以看有多余的字符出现,可能性就少了,比如:金创药。
13、可以先找到吃药call,进入后分析找到从物品数组取出的物品结构call。
14、ce搜索名字的地址,要范围搜索,因为物品名字前面可能还有一些字符串和他是一体的,完后结果中找最大的内存地址,完后查看访问这个地址的代码 如果没有看到。
访问的指令,那就搜索存储这个物品名字的内存地址,可能是访问了这个存储物品名字的指针,完后吃一下药物,看看有没有显示出来的指令。
七、人物角色姓名蓝色怒气血量
1、条件断点 eax == a247080 对吗。
2、mov ecx,dword ptr ss:[ebp+0x1C] ebp到上层发现是00ce7cf0,其实只要此时发现程序名字.00ce7cf0 就是基地址了,但是ebp=00ce7cf0,不是ebp = [00ce7cf0] ,别搞错了。
3,遇到这种指令mov ecx,dword ptr ss:[ebp+0x1C],别着急查看哪里写入数据到[ebp+1c]里面了,而是要看没有断下来,此时里面的数据还是不是原来的数据,如果是的话,那就找ebp来源。
4,ce搜素血量完后被怪物打,查看变动的数值,完后修改一下数值,看看修改哪个后血量变动,就是它了 (搜出来的绿色的地址也可以 但是修改数值后不能改动人物血量)。完后查看写入的指令,再被怪物打,完后监视其中看最后的类似于mov [],eax这种。完后od跳转,向上跟, 最后找到后 +4 +8等位置一般为怒气,或者其他相关。
5、ce查看写入的指令的时候,比如遇到mov dword ptr ds:[esi+0x278],edx 找的是esi的来源,而不是edx的来源切记。
找人物名称经验:
1,ce搜索宽字符(unicode)或者窄字符(g2b) unicode:3575505b386e67720d54cf65。
2,ce搜索后找mov ax,word ptr ds:[ecx+ebp*2]这种类型的,一般都是数组形式取出来的,或者不行换一个试试。
3,ce直接查看访问的指令(没有经过附加条件比如鼠标点一下完后才查看ce监视器), 完后od跳转下段,无限断下来,这种情况下就不用条件断点 直接断下来向上找。
八、找明文包
1、
006C5802 |.8B06 mov eax,dword ptr ds:[esi]
006C5804 |.8B4E 04 mov ecx,dword ptr ds:[esi+0x4]
006C5807 |.3BC1 cmp eax,ecx
006C5809 |.74 1A je short elementc.006C5825
找到类似的 完后esi+4的地址下内存写入断点。
2、在send call哪里下入断点,查看data地址处的内存,随便下一个写入断点,od跳转后,到开头看看。
3、弄两个相同动作,查看堆栈表,哪些些call一样,对比出来最后一个一样的call。
九,根据技能和普通攻击分析出游戏大概的循环攻击功能框架
1、
struct 发包结构
{
****
DWORD STARTADRESS;
DWORD LASTADRESS;
****
}
发包结构 结构;
void 发包线程()
{
while(1)
{
if (结构.STARTADRESS == 结构.LASTADRESS)
{
contiune;
}
else
{
send();//发包 ①bp send先断下来 完后上下找到+4 +8的对比发包结构是否有数据的地方 完后下写入断点
结构.STARTADRESS = 结构.LASTADRESS;//将封包结尾地址赋值给首地址
}
}
}
void 封包写入(int 长度,DWORD 地址)③在这里下断点
{
结构地址 = 结构.STARTADRESS;
int i = 0
while(长度!=0)
{
结构地址[i] = 地址[i]; //将要发送的封包写入到结构地址当中去 ②跳到这里
i++;
}
结构.LASTADRESS = 结构.STARTADRESS + 长度
}
struct 目标技能结构
{
bool 是否普攻攻击;
DWORD ID;
DWORD X;
DWORD Y;
DOWRD OBJECTID;
}
void 普通攻击封包组合函数()⑤在这里下断点
{
组合过程
封包写入(长度,地址) ④返回到这里
}
void 技能攻击封包组合函数()
{
组合过程
封包写入(长度,地址)
}
void 循环技能()
{
while(1)
{
if (目标技能结构.是否普攻攻击==真) ⑦向上找到循环 查看条件 发现只有等于真 才会发普攻包 于是在技能结构下断点查看什么时候把真 写
入的
{
普通攻击封包组合函数() ⑥返回到这里
if (目标怪物是否死亡)
{
目标技能结构.ID = 0;
}
}
if (目标技能结构.ID != 0)
{
技能攻击封包组合函数()
if (目标怪物是否死亡)
{
目标技能结构.ID = 0;
}
}
}
}
void 普通攻击CALL()
{
目标技能结构.是否普攻攻击 = 是 ⑧在这里断下 向上找到普攻call
目标技能结构.OBJECTID = 读取目标ID
}
void 技能CALL(DWORD ID,0,0,0,0)
{
目标技能结构.OBJECTID = 读取目标ID
目标技能结构.ID = ID
}
2、bp下断点,完后向上层,复制堆栈k表数据,完后找到上层第一个不停断下的call的下一层call,完后nop掉,如果此时按技能键没有任何效果, 撤销nop,看看有没有变化。
如果有,那么游戏的功能call和封包组合call可能不在一个支线上。可能在别的岔道口的支线上,因为根据循环技能框架分析知道,我们刚才找到的第一个无限断下来的call的所在的call可能是一个无限循环判断是否有技能发生,他的判断条件就是看看某些结构等东西里面是否有某个标志,有的话就会调用封包组合函数,而我们刚才说的nop掉的那个call是一个封包组合call,或者是封包组合call和功能call的集合。
因为此时撤销nop发现,还是有相关动作发生,说明刚才说的那个结构里面的某个标志还存在,所以循环技能不停地调用这个被nop掉的call。你撤销nop,他正好调用了,也说明这个call只是封包组合call,不包含功能call 因为只有功能call才会把那个标志写入某个结构里,完后就在刚才被nop掉的call下断点,完后查看参数(或者向上看看有没有跳转指令,跳过这个被nop的call的,完后断下后和撤销断点后看看里面的值有没有变化。
注意:这里的跳转指令类似于技能框架里的判断某个结构里的某个标志是否存在一个值。如果存在就执行功能封包call,完后就是可能在功能封包call里写入数据到发包结构里,完后发送封包,最后就是恢复那个结构里的某个标志为某个值,也可能是在刚才说的被nop掉的封包组合call的下面恢复那个结构里的某个标志为某个值,具体看这个被nop掉的call下面还有没有类似mov指令。
如果都是pop之类的那恢复那个标志的指令就存在于刚才说的被nop的那个封包组合call里面支线) 内存窗口跟随进去,完后撤销断点,看看里面哪些值发生变化了。完后ce搜索查看写入的指令,完后od跳转,如果此时看到的只是一些简单的赋值指令。估计不是功能call,那就向上层返回,感觉像功能call的话,就nop掉看看,没有任何动作的话,估计就是了 。
而且一般像拾取call的话,参数比较少,一般就一个call就可以完成。 找到这个call后,注入测试如果那个被nop掉的call后,在撤销nop 发现没有任何动作,那么就测试这个call看看是不是功能与封包组合call的集合call 或者进入这个call再细找详细哪个是功能call,注意刚才说的 一般拾取这些call 参数少 一个call就可以实现。
遇到这种call doword ptr ds:[eax+0c]断下来无线断的情况,在他的下层call里找条件判断,查看哪里跳过那个下层call,按照上述方法也可以,此时注意每次查看跳转标志位的值是否改变,要下断点查看的。
注意每次断点后这个标志位内存地址的值还是不是一样,不一样就不行了,有时候不好找到正确的标志位的话,可以看看哪些跳转地方的标志位的内存地址的值每次运行某个功能(比如普攻)的时候就会发生改变,不运行这个功能就不改变的话,就ce查哪里写入了这个改变的值。
od跳转后也可以向上层按照上面方法找到,是在找不到标志位的话,可以试试啥时候啥条件成立就会把eax设置为0049C883,比如只有某个内存(标志位)里的值为1的时候就会把eax的值变为0049C883,那么就找到标志了 剩下就是ce查看哪里写入。
九、怪物坐标、数组等
<坐标>
1、ce搜坐标换文本,换字节,搜不出来就是加密了,就是230,经过加密变成256032。
2、od跳到代码处,向上找,注入工具测试,完后找到以后,进入call,一个看找类似235200这样的数字,完后ce搜索写入地址的代码,找到加密来源。
<数组结构>
1、我最常用的就是通过怪物的血量来找,搜索怪物的血值方法和上面类似。
2、通过怪物的名字搜索,这个方法比较麻烦,有时候要看运气。而且还要看怪物名字是什么类型的,文本/UNICODE,搜一种怪物名字完后跑向另外一种怪物,完后查看那些内存地址变为这种怪物,完后选几个查看写入地址。
3、可以通过人物于怪物的距离。
4、可以通过怪物的等级。
5、选怪call的参数分析。
6、按照技能结构分析的经验 找到选怪call。
7、进入单步执行,找到第一个call,返回一个从数组中选出来的怪物id,完后进入这个call查找从哪里返回出来的,直到找到一个数组。
- End -
原文作者:killpy
原文链接:https://bbs.pediy.com/thread-212398.htm
转载请注明:转自看雪学院
更多阅读:
网友评论