游戏网络架构逆向分析--2

游戏底层功能对接类GameProc的实现

这是一个函数的虚函数,这个ecx和发送数据&接收数据都有很大关系,这里截取主要就是要拿到ECX,其实更好的方法就是去找它的基址,但基址不好分析,有一大堆函数指针和虚函数,还和Lua有交互,找基址就套浪费时间了。
edx就是这里虚表的地址

可以直接在虚函数这做一个HOOK,通过改虚表来直接调  直接改这 10617c90的数据就能改变eax结果,就可以跳到任何想去的地方

这个类原型名字就叫 GameWinSock  ::Connect(char* ip, int port)  很容易看出 返回值是bool

 vtable[0x34 / 4] = (unsigned)&GameWinSock::OnConnect;
这句在编译器里是不能直接强转
如果是个普通函数就可以这么直接干了,但这个是类的成员函数。

先试试用类的函数指针能不能得到它来赋值
proc _p = &GameWinSock::OnConnect;
    vtable[0x34 / 4] = (unsigned)p;
这样也不行。

只好来个骚操作了:

union
    {
        unsigned val;
        bool(GameWinSock::* _proc)(char*, unsigned);
    }vproc;

    vproc._proc=&GameWinSock::OnConnect;
    vtable[0x34 / 4] = vproc.val;

用联合体直接安排他

修改虚表要注意访问权限 
用 VirtualProtect

hook的时候发现网络模块还没加载,就手动帮他加载下

 截取成功

#pragma once
class GameWinSock
{
	typedef bool(GameWinSock::* PROC)(char* ip, unsigned port);
public:
	static PROC _Connect;
	bool OnConnect(char* ip, unsigned port);

};




#include "pch.h"
#include "GameWinSock.h"
#include"extern_all.h"

GameWinSock::PROC GameWinSock::_Connect{};

bool GameWinSock::OnConnect(char* ip, unsigned port)
{
	WinSock = this;
	MessageBoxA(0, ip, ip, MB_OK);
	return (this->*_Connect)(ip,port);
}
#include "pch.h"
#include "GameProc.h"
#include"extern_all.h"


bool _OnConnect(HOOKREFS2)//回调
{
	//虚函数表做HOOK
	//截取ECX -》 winsock指针

	unsigned* vtable =(unsigned*) _EDX;
	//vtable[0x34/4]= (unsigned)&GameWinSock::OnConnect;
	union
	{
		unsigned val;
		bool(GameWinSock::* _proc)(char*, unsigned);
	}vproc;

	vproc._proc=&GameWinSock::OnConnect;
	InitClassProc(&GameWinSock::_Connect, vtable[0x34 / 4]);
	DWORD oldPro,backPro;
	VirtualProtect(vtable, 0x100, PAGE_EXECUTE_READWRITE, &oldPro);//修改访问权限
	vtable[0x34 / 4] = vproc.val;
	VirtualProtect(vtable, 0x100, oldPro,&backPro);//恢复访问权限


	return true;
}


GameProc::GameProc()
{
	
	//类的构造
	hooker = new htd::hook::htdHook2();
	Init();
	InitInterface();
}

void GameProc::LoadBase()
{
	LoadLibraryA("fxnet2.dll");
}

void GameProc::Init()
{
}

void GameProc::InitInterface()
{
	LoadBase();
	//10617046
	hooker->SetHook((LPVOID)0x10617046, 0x1, _OnConnect);
}

游戏发送数据包接管 

 

发送数据的函数也是通过ecx,依然可以通过虚函数表做。

进去看看,很容易能看出也是一个bool的返回值
原型:bool GameWinSock::Send(char* buff, int len);

直接还原函数  看看数据包:试着调用


 

char buf[] = {
		0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x01,0x00,0x00,0x00,0x02,0x01,0x00,0x00,
		0x00,0x07,0x0C,0x00,0x00,0x00,0x31,0x00,0x31,0x00,0x31,0x00,0x31,0x00,0x31,0x00,
		0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	};

	WinSock->OnSend(buf, sizeof(buf));

成功通过封包来发送消息

 

	VirtualProtect(vtable, 0x100, PAGE_EXECUTE_READWRITE, &oldPro);//修改访问权限

	vproc._proc = &GameWinSock::OnConnect;
	InitClassProc(&GameWinSock::_Connect, vtable[0x34 / 4]);
	vtable[0x34 / 4] = vproc.val;

	vproc._proc = &GameWinSock::OnSend;
	InitClassProc(&GameWinSock::_OnSend, vtable[0x3C / 4]);
	vtable[0x3C / 4] = vproc.val;

	VirtualProtect(vtable, 0x100, oldPro,&backPro);//恢复访问权限






GameWinSock::PROC GameWinSock::_OnSend{};




bool GameWinSock::OnSend(char* buf, unsigned len)
{
	/*
	监控
	*/
	return (this->*_OnSend)(buf,len);
}

 实现其实和Connect没啥区别。

游戏接收数据包接管

接收数据的函数就没办法用虚表来搞了,因为优化/封装的缘故,用的eax来代替了ecx,就要想别的办法

就要用汇编去传eax或者搞个函数封装一下,不过又涉及到两个参数的传递,可能会把eax用掉

调用的时候就尴尬,用eax做了this指针  用汇编倒是最简单的

也很明显 返回值是个bool   

 

在这个点做HOOK  到时候就能截取到 我们自己模拟的消息了

还得直接hook到返回值到这个ret这
 

截一个说话的数据 

直接试试

char buf[] = {
    0x1E,0x06,0x00,0x06,0x05,0x00,0x00,0x00,0x63,0x68,0x61,0x74,0x00,0x02,0x01,0x00,
    0x00,0x00,0x07,0x08,0x00,0x00,0x00,0x1F,0x77,0x1F,0x77,0x31,0x00,0x00,0x00,0x07,
    0x6E,0x00,0x00,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,
    0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,
    0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,
    0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,
    0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,
    0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,
    0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,
    0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x02,0x01,0x00,0x00,0x00
    };
    WinSock->OnRecv(buf, sizeof(buf));

成功 

bool _OnRecv(HOOKREFS2)//回调
{
	unsigned* _esp = (unsigned*)_ESP;
	_EAX = WinSock->RecvPoint;
	return WinSock->OnRecving((char*)_esp[1], _esp[2]);
}



hooker->SetHook((LPVOID)0x10618480, 0x1, _OnRecv,(LPVOID)0x10618502);

	InitClassProc(&GameWinSock::_OnRecv, 0x10618480);


bool GameWinSock::OnRecving(char* buf, unsigned len)
{
	return true;
}

bool GameWinSock::OnRecv(char* buf, unsigned len)
{
	return (this->*_OnRecv)(buf,len);
}

这样就可以了 就是浪费了个ecx..
这个游戏也存在很多问题,比如他用明文字符串直接来打日志,直接就被我们逆向分析的给直接看到了,应该直接用内部编码来规范

而且数据发送阶段 未对数据进行加密,不能有效对抗中间人攻击,别抓个包就寄了,这个游戏直接截取send函数就直接能看到用户的账号密码,基本的加密还是必要的就算只是随便异或了下

关键代码也可以用VM虚拟机加密

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

#A#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值