短暂的双节一下子就过去了....
闲话不多扯,我们进入正题!
WoW怀旧服(x64)是可以一款最近比较火热的炒冷饭作品,我们把它扒开来瞧瞧它的工作做得如何。
一开始还是非常轻松的,直接就是系统send发包 没有重写发包函数、也没有线程发包,大概就相当于白送。
Rpg类型那肯定得通过封包来实现点什么功能,那么我们需要分析它的封包结构,想要分析封包结构 我们肯定要HOOK(拦截)明文包内容方便观察,要拦截明文包 那么需要先找到它,那我们开始吧!
发包流程分析:
由于Od木有64版本的,使用xdbg64一个与od十分相似的工具,附加上之后直接跳转到send头部下一个断点。
等一会发现游戏断下有一个包长0x1A的心跳包,按F9让游戏继续运行 切换到游戏窗口 发现游戏再次断下,这次的包长为0x13很明显这是一个激活窗口包,比较干扰我们分析的差不多就这2个了,我们可以通过条件断点根据包长排除这2个干扰。
下条件断点,老套路我们到游戏里喊一长串话。
PS:更不上节奏的小伙伴可以看看我之前的几篇文章。
然后游戏会因为喊话包断下。
可以看到这次包长为0x4F应该是我们的喊话包。(send函数第一个参数为套接字、第二个为包地址、第三个为包长,64函数调用约定前四个参数由rcx、rdx、r8、r9四个寄存器传递)
注意,一定要确定是因为喊话断下的,因为心跳之类的封包走得代码可能与功能封包略有不同,而我们当然是希望追踪功能的,所以要确定是因为喊话断下,避免我们被引向错误的方向!
使用快捷键Ctrl+F9逐层返回,并标注注释第一层、第二层...如此反复 如下图:
我们返回个7、8层哈,都这样标注上先了解一下大概的情况。
然后我们开始从最外层下断点逐层分析,看看他们是功能call 还是 明文包 或者是加密包,这样我们才能对这个游戏的情况开始有一些了解。
如果在第七层下断你会发现 无论走路、放技能还是干什么其他的事情都不会触发断点,只有喊话会断下 而且只断一次,很明显这是喊话功能call。
而在第六层下断,干什么都断 只要在游戏里面一做任何会发送封包的动作,就会使游戏断下,毋庸置疑这是最外层的明文包call,所有的功能都要在组包之后通过它一层一层向下传递可能加点料、加密然后通过send发送给服务器。
在这里我们细心点可以发现,窗口激活在这里是瞧不见它的。很明显窗口激活包不走这里,那么当初如果我们是通过窗口激活包断下的...那么现在我们会被引向错误的位置,牢记分析封包一定是要功能断下的才可信,分析功能包都需要如此。
现在我们已经对结构流程有了大概的了解,但还不够。第六层虽然是明文包,但是它很可能是残缺的,后续的call可能会对其进行一些其他的加工,那我们开始继续分析!再次来到第一层。
第一层这里的call就是send函数,我们可以看到包长来自于rbp(64基本摒弃了栈底,把rbp当成一个普通的寄存器就好了),而包地址来自于r14。
像之前一样我们下一个条件断点,喊话使游戏断下,然后在数据窗口Ctrl+G 输入r14 回车查看包地址的内容,如下图:
我们并没有看到我们熟悉的31313131...而且这个包如此的杂乱无章,唯一比较清爽的就是开头的4字节 0x24,这可能是一个未加密的包头很可能和包长有关,其他暂时就看不出啥了,很明显这里的包内容是加密的...当然这基本是句废话了...下面就是send发送了,封包还不加密的,难道发明文么?不过主流游戏这样的憨憨也不是没有,听说某洞某鸡有一段时间通讯就是明文未经加密的,好家伙直接被热心网友把整个协议给整理出来了,公开在某著名代码代理网站上....
既然如此,我们继续往上面推,如图:
可以看到包地址r14来自于[rdx],然后[rdx]来自于[rdx+10],再往上面就是函数头部,根据x64函数调用约定rdx传递call的第二个参数,那么我们返回一层去分析。
在一层rdx来自于rdi,于是[rdi+10]是我们的包地址了,顺便在旁边注释纪录一下 要养成好习惯 不然保准回头就忘了。
再次下断,确认喊话使游戏断下,我们Ctrl+G 输入[rdi+10]回车 查看一下包内容,如图:
和上一层相差不大还是加密包,基本就是一模一样的,让人失望没,没什么用。
那我们只能继续往上分析和纪录,重复过程就不写出来了,终于在第四层call,如图:
下断点,老套路喊一大串111111111111111111111111111...
然后游戏断下,查看一下包内容,如图:
一大串313131....很好我们已经找到最内层明文包了,接下来可以接着向上面几层推进分析和纪录,这样我们对wow怀旧服的发包流程就有了一些掌握了。
另类HOOK明文包:
为了方便分析封包,准备做一个最简单的inline hook来拦截封包内容供我们从容的分析,inline hook需要破坏游戏的代码,但是....
使用xdbg64修改任意指令,都会失败...大家可能在想..那我写代码啊 修改内存页属性再修改它...没有用 工具修改不了自己写代码更加修改不了...
好厉害啊,这花里胡哨的...估计只有上驱动读写了...,而且就一个hook明文包的事情,就上驱动读写太没有必要了。
很明显wow怀旧对其代码段进行了某些保护,堵死了我们hook的路...
那我们就真的一点办法都没有了吗?...不,它可以处理自己的代码段,但是系统的代码它没办法处理得那么严格!而从send返回七层call 到 调用send这期间一定会在堆栈中给我们留下无数线索。
那么我们可以在send头部hook,然后到堆栈中读取我们想要的,前提是我们需要先找到!
那我们开始吧!先来到send处下断点,确认为喊话一大串的“11111111”断下,如下图:
来到堆栈窗口,双击栈顶地址处,以相对栈顶偏移的形式来显示地址。
我们开始试图从堆栈中找出我们需要的蛛丝马迹,当然堆栈中的东西太多太乱 我们不能毫无头绪的寻找,首先经过我们上面的分析第6层返回到第4层返回才是明文包出现的范围,我们只可能在这个范围内才可能找得到明文包,那怎么确定第4层的位置?
很简单调用call时会往堆栈压入返回地址,工具也会帮助我们标注出来(当然工具有可能会欺骗我们,最保险自己跳转过去看看),然后我们数4个返回到 下面的内容就是第4层call的局部变量了(相对于栈顶 也就是RSP +250~+360的范围内,往下就是功能call了,不同的功能call代码是不一样的 在堆栈中留下的痕迹自然也不一样 导致下面的内容会变动而不可信)。
那接下来就麻烦了...┭┮﹏┭┮,观察一下在这个范围内没有发现肉眼可见的明文包,很正常...
那我们继续...只要在RSP +250~+360的范围内像是地址的值,我们就到内存窗口中去看看里面有没有我们要找的明文包...
找啊找....找啊找...终于,如下图:
在RSP+0x320的地方,我们发现了明文包地址,如下图:
可以看到一大片的313131... 我们喊话的内容是“11111111” 31是 “1”的编码,很明显这是明文包内容,虽然看起来有点奇怪...
那么现在我们可以得到总结,在send头部hook ,寄存器R8里我们可以得到包长,RSP+0x320我们可以得到明文包地址,进而读取明文包内容!
修复残缺明文包:
写好hook代码来校验一下效果,老套路喊一长串话,如下图:
(ps:跟不上的小伙伴可以看看我之前的几篇文章哈)
利用我们写的hook拦截并输入封包内容,如下图:
但是我们抓到的内容里面却只要313131 和 323232 那“3”的编码33哪去了呢?很明显我们在堆栈中读到的这个明文包是残次品,他是加工过程中的中间产品...
现在我们只能得到这种残次品,需要想办法修复它...还记得我们之前找到这个残次品的位置么?如下图:
上面有个返回到,我们跳转到代码区去看一下,如下图:
是我们的send第六层返回代码不多,就这么一丢丢吾心甚慰...我们直接在第六层返回上下断,更具我们之前的分析 rsp+0x28是包地址,进去一看包内容是正常的。
那现在我们知道那个残缺的明文包是这段代码的局部变量,是一个半成品,而直接在send第六层返回上下断得到的包内容却是成品...那么在这段代码的头部到send第六层之间有一道工序将半成品加工成了成品!
这种加工毋庸置疑不可能只有寥寥几句汇编代码,那么答案只有可能在2个调用的call里面,那么那个才是加工半成品的call呢?
很简单!如果你要加工半成品 你肯定需要把半成品的地址传入call内!那么我们只需要分析这2个call的参数有没有我们的半成品就能知道答案了!
终于在我把第二个call的第一个参数RCX扔进内存窗口查看时得到了我们想要的答案!
(PS:分析时牢记必须是我们喊话使游戏断下的)
熟悉的配方,这就是我们那个半成品!!!那么第二个call就是我们要找的,这个call还有一个rdx参数,游戏中是之间传入了一个堆栈地址进去,猜一猜就知道,成品封包内容会被写入到这个地址中,实际分析也是八九不离十。
那么现在我们明白了,只需要我们调用一下这个call就能够把我们的半成品明文包转换成 成品明文包,阿妹惊 太棒了!
喊话封包分析:
一波三折,我们终于得到了完美的明文包内容,试试它好不好使!老套路,喊话!
产生如下内容:
包长: 1C 内容:E737070000000A003131323233333434353536363737383839393030
包长: 11 内容:E737070000000480303030393939313131
喊话封包还是很容易看出来的:E73707000000 是包头 0A00 和 0480 是喊话的长度的一半,喊话长度除以2 有余数带80 没余数不带,后面就是我们的喊话内容了,内容和我们喊的东西也对得上,没问题!
随便写点代码把我们自己的私货也发送出去,如下图:
也没有问题,完美!
那么再会,我的朋友们!~