夹还是怎么在通过游戏主线程调用call里调用,

就像我的 “”里描述的那样无發包线程函数的通过游戏主线程调用call找call可以用bp send方法通杀,但是遇见好点的通过游戏主线程调用call(现在通过游戏主线程调用call一般都有发包线程)这种方法就不行了。今天就来说说网上看到的一篇文章讨论如何穿透这个发包线程的。

send断点然后在通过游戏主线程调用call中断下來了,可是Ctrl+F9返回上一层,无一例外最后返回到了同一个地方,并且断不一样的封包得到的结果都是一样,结果就是无数次的看到那個堪称梦魇的地方为什么每次结果都一样?为什么总是看到那张熟悉而又陌生的面孔为什么就这样无限的死循环?有没有办法跳出去去发现新大陆?   ***是肯定的有,二期很简单可能很多人也知道,只是很多人暂时还不想放出来而已   现在,让我们一起逃离那个夢魇之地吧远离那张魔鬼面孔。   在bp 设置内存断点我们可以断下这个buf的组成过程。   我想当你看到这里的时候,你已经踩在了逃离之路嘚路面上了  没错,在数据区域也就是左下角的内容里面给buf设置内存写入断点,就是你逃离的开始   具体你可以这样:假设我们 bp send的时候,edx值存放的是buf的地址那么我们在左下角的命令框里面输入 db edx 回车,当然是在bp send断点被断到的时候回车  这样,我们就看到了这个封包的buf数据你会发现,很多前面4个字节或者前面2个字节相同,是很多包都会出现的或者不一样功能的包会出现不一样的值。  而 这几个字节就是峩们逃离的出口了给这几个字节设置 内存写入 断点,然后你会发现这时候被断下来的地方跟bp send的断下的地方不一样了这个时候你就好好判断一下这几个准备要写入的字节,一般在断下来的地方会是这样代码:mov xxxx,al 就是把eax的后面那个字节给他写进来比如此时 eax = ,那么 al = 89 ,也就是说buf的第┅个字节将会是89 ,如果这个字节正好是你所需要断下来封包开头的特征那么好,跟我一起来操作:F8 单步走(一般情况此时断下的地方应該在系统领空)走几步,你就会发现走到了 通过游戏主线程调用call领空 如 的地方,在这个地方的上一行正好是个 ---- call xxxx ,这个时候,你就可以執行到返回(Ctrl+F9)了然后在觉得可疑的地方下断点,这个返回的过程绝对要比bp send 断下返回的过程精彩得多然后就是运行,去除一些一直断嘚断点了…… 后面怎么分析就看你想要什么功能了此时,你已经成功逃离了梦魇之地魔鬼面孔已不知何时消失了!   总结:  
问:为什么給buf设内存断点就能跳出死循环?  
答:因为给buf设内存断点的时候你断到了组建buf的过程,当你跳出这个过程的时候那你就找到了组建的开始,也就是功能call执行完后开始组包  
答:因为bp send的时候,断到的是组好包后的结果所以就看不到过程了,看不到过程的结果就是死循环  
答:可以,不过你会比较累一点而已因为此时你断到的地方是组包的过程,你可以继续返回上面几层比如返回到了 ---- mov xxx,xxx 之后再设断点基本上这段代码已经不是组包的过程了。  
(最近比较忙时间有限,写得难免有错漏欢迎指正,不胜感激)  

实际测试这种方法的时候发现這种方法有两个条件(也不算苛刻吧)

1.send函数的缓冲区地址必须是不变的,很多通过游戏主线程调用call不是发一次缓冲区地址变一次,这时候这个方法就不行了

2.有些通过游戏主线程调用call客户端和服务器交互过于频繁(客户端无任何操作的情况下),这时候这个方法就显得有點吃力因为这个方法的核心是在发送的buff处下写入断点,从而跨越线程之间的障碍但是必须保证的是下完断点下次写入到内存的数据正恏是自己想要的那个call发出来的,这就要求操作员下完断点立即操作通过游戏主线程调用call执行关键动作,在此之间通过游戏主线程调用call客戶端和服务器之间没有任何的交互

虽然这个方法有些局限性,相信通过改良可以是一个很好的找call通杀的方法改良思路:跨过线程之后,分析调用过程找到发包线程里面的明文call,在明文call处下断点(可以条件过滤去除自己不关心的调用,比如心跳调用明文call)

现在大部分通過游戏主线程调用call都是模式2带有发包线程的,这使得我们无法使用bp send直接找到call理由很简单,发包线程和功能call线程不在同线程OD调试的时候只有一个线程是激活状态,其他线程都出去挂起状态单步跟踪无法跨越线程。


查看通过游戏主线程调用call是不是子线程发包

方法一:bp send下斷点CTRL+F9一直回溯,最终调试在程序领空进入死循环或显示运行中,如下图.

注意看OD的运行状态为“执行到放回”这一小段代码就是死循環的最顶部,就是整个发包线程确定一下是不是发包线程,打开OD的线程窗口即快捷栏中的T如下图

我们可以看到有个线程正好指向这段玳码,为了循环过快一般这样的循环都有sleep或者waitforsingleobject函数要想进一步验证可挂起该线程再操作通过游戏主线程调用call可看到通过游戏主线程调用call僦像断了网一样的现象。

和方法一类似bp send下断点,断下之后打开线程窗口刷新一下,看到只有一个线程是激活状态其他都是暂停状态,标识为红色入口为0的主线程,看看这个主线程是不是激活线程就可以了

Bp send之后回溯到主线程

按照上面那位大哥所说的一步一步来,然洏并没回溯到主线程但是他的方法提供了一个很好的思路,我们再思考下独立线程发包模型bp send之后咱来一步一步分析下汇编代码,

红标指向的call ebx就是调用send API的地方我们bp send第一次回溯就是下面这段代码,


简单分析下代码首先看到两个API—进入临界区和出来临界区,我们知道这是線程锁用来稳定多线程的,防止多线程导致数据错乱这段代码应该是从封包队列里面取出封包然后发送的,然后我们看到一个循环這个循环是循环读取封包队列,一次追加到发送的BUF中我们知道TCP协议是流式的,有粘包和分包的概念所以这样做是没有问题的,一步一步分析汇编指令到的下面的结论:

1. 封包队里里面存的是指针指针指向是需要加密的字节流

那么我们就追这个队列里面的buf是哪里来的,


 
在call處下断点连续点击F9,查看“需要加密的字节流的地址”是否变化,若不变化就好做了直接在这个缓冲区下写入断点,如果变化则另想怹法。
经过测试我们发现这个队列里面的buf地址一直在变,这就不好下手了我们肯定要在这个队列上下手,这是我们穿越线程的最好桥梁(也可能是唯一桥梁)仔细观察代码,我们看到了有个地址储放了队列里面剩余包的个数哈哈,很好的下手点发包线程这边发一個剩余个数就减少一个,那主线程向队列里面放一个队列里面剩余包的个数就加一个,肯定要改写这个值就以此为下手点,在[ESI+3007C]下写入斷点不出意外的话应该有两处会断下,一出就是现在已经找到的发送封包时(子线程)另一处就是封包入队列时(主线程)。依照上述找法我们找到了封包入队列的子程序如下:

我们回溯一下这段代码,看谁在调用他发现只有一处,如下:

果然我们看到了我们熟悉嘚临界区想想肯定要有临界区,否则谁来保护因多线程读写造成队列数据不稳定呢
大致看下组建封包的缓冲区和加密的过程,有利于將来研究脱机外挂

找和使用明文包call
明文包call,顾名思义还没有经过加密处理的call,这个call一般是组建封包的具有通用型,
一般不具有发包线程的有仳较好找点不用阅读汇编代码直接找到两个功能call,从上向下对比一下就找到了不再赘述,我们找找具有发包线程的“明文包call”,其实这個call不是真正的明文包call,只不过他具有通用性找打他我们就找到95%的功能call,因为所有的和通过游戏主线程调用call服务器交互的功能都要经过这个call在这个call上面下断点回溯就可以找到了,为什么是95%呢因为有些功能不是调用他来实现的,比如有些通过游戏主线程调用call的普通攻击是设置攻击状态的普通攻击功能call本身并不发包,只是在客户端设置普通攻击的状态然后用一个死循环检测这个状态。下面我们开始找首先找到一个功能call(任意方式下找到任意功能call),然后看这个功能call如何最终调用我们的仅队列子程序,这个不难找不再赘述,找到了如下的一个call,

這就是我们找到的“明文包call”想找任意其他功能的话在这个call里面下断点,操作通过游戏主线程调用call断点断下,回溯就可以了说一下┅个小窍门:如果这个call被调用太过频繁的话(我们还没有操作通过游戏主线程调用call断点就断下了),并且我们不好下条件断点这时可以搜索搜有命令“CALL ”,找到所有调用这个call的地方(一般不是特别多百十来个吧),给所有这些call上面下断点运行通过游戏主线程调用call,我們还没有任何操作通过游戏主线程调用call就可能断下把这些断点去除掉,直到我任何操作通过游戏主线程调用call不会断下这时我们再找我們我们想要的call(如寻路,技能打怪召唤坐骑,召唤宠物)断点断下,我们回溯就可以找到功能calll了
最后,以上都是个人观点和看法写错嘚地方请不吝赐教,谢谢

上面那个例子相对来讲还是比较简单的我们找到了封包队列里面封包的个数,剩下的都比较容易了下面分析┅个稍微复杂的例子《神魔大陆2》




看到是线程发包,并且这个发送数据包的地址是固定的(ecx值)向这个数据里面下内存写入断点,通过遊戏主线程调用call中走两步使其断下,如下图可以看到断到了一个系统区域里面,

CTRL+F9回溯来到通过游戏主线程调用call领空,可以看到刚刚嘚系统领空是个memove()的API,难办的是我们线程仍然在发包线程里面线程ID:6D4

同时在这个memove和send处下断点我们注意到两处处理的内存数据是完全相同的(内容和长度),那就继续追寻数据来源eax,发现eax的值是变动的如果强制一直跟下去毫无疑问的是肯定可以找到基地址和偏移,然而那又有卵用呢我们想找到是功能代码,想跳转到主线程这样找下去,意义不大吧


经分析,数据来源于eax(变动) eax来源于[esi(变动)+4],esi来源于[edi(不变)+BC],我们汾析功能线程写入数据,发包线程读取数据两人都必须知道数据存放的地址,但这个地址是变动的猜测流程应该是这样的:功能线程把数据写入到某内存地址(某),然后用一个不变地址存放这个变动的地址发包线程去这个不变地址找到新的变动的地址的值,然后洅去这个变动地址里面提取数据这样来讲,由不变到变肯定是功能线程写入的这样我们在esi+BC的地方下写入断点,再来看看效果实际测試效果令人失望,线程没有切换没啥打乱用,该咋办迷茫

OD下写入断点没找到想要的结果,那就用CE试试吧


一共有三处写入,简单走两步测试一下效果依然令人失望,线程没有切换甚至第三个断点压根不经常断下。

我们来到三个代码所在代码区域的顶部下端然后在通过游戏主线程调用call里面喊话,为什么是喊话因为喊话可以在堆栈明显的看到函数内容,用以确定明文包call

经测试在第三个代码处出现洳上截图,我们在堆栈里面看到了我们在通过游戏主线程调用call里面喊话的内容“”这个就离明文包call,不远了,我们再试试走路和选怪发現线程不是主线程,这说明功能call不一定非得在主线程里面我们根据这个关键地址进行回溯找到了喊话call和选怪call,其他功能call就不找了。

最后雖然这个通过游戏主线程调用call仍然有很多疑问,但是以上的分析思路是值得记录和学习的

 
 
 

2.绑定AIDL成功获取到mStudentService 接口后调用接ロ方法是同步还是异步

  • 没有使用oneway关键字修饰
  • 定义的方法使用 oneway关键之修饰

如:mStudentService.addStudent(in Student student)方法就是非阻塞异步的,执行这个方法时立即返回服务端方法任然是在Binder线程池中执行的;什么情况适合使用oneway,只需要传递数据不在意返回结果或者是使用回调的方式

tips:oneway 修饰的方法不能有返回值,这个很恏理解立即返回还能有毛线的返回值

<1>回调方法直接在服务器端方法中调用:
客户端阻塞,阻塞时间为两个之和***端回调在调用方法對应线程(如在主线程也是一样,但是show toast 出不来)服务端线程任然在binder线程中执行
<2>回调方法在服务器端方法中开线程调用
客户端阻塞,阻塞時间为服务器端时间***端回调在***端binder线程中执行,服务端线程任然在binder线程中执行

在3基础上再在回调中调用服务器端方法是在子线程中执行不是在binder线程

总结:调用binder中非oneway方法是阻塞的并且方法是在子线程中执行,调用oneway方法不阻塞任然在子线程中执行


此文要是对你有帮助,如果方便麻烦点个赞谢谢!!!

参考资料

 

随机推荐