发包类型:
在游戏或者软件中基本上都是用的以下三种发包方式:
- 明文发包
- 加密发包
- 线程循环发包
我们在分析之前需要先搞懂目标是那种发包方式。
判断游戏发包方式:
- 线程循环发包所有功能堆栈返回地址基本上大同小异
- 加密发包在包文中没有明文内容
首先下send函数头部下断:
移动:
000000CAD7A0FDB8 00007FF660DC18FB 返回到 jx3clientx64.00007FF660DC18FB 自 ???
000000CAD7A0FE28 00007FF660A1A028 返回到 jx3clientx64.00007FF660A1A028 自 ???
000000CAD7A0FEC8 00007FFD8CF0628E 返回到 engine_lua5x64.00007FFD8CF0628E 自 ???
技能:
000000CAD7A0FDB8 00007FF660DC18FB 返回到 jx3clientx64.00007FF660DC18FB 自 ???
000000CAD7A0FE28 00007FF660A1A028 返回到 jx3clientx64.00007FF660A1A028 自 ???
000000CAD7A0FEC8 00007FFD8CF0628E 返回到 engine_lua5x64.00007FFD8CF0628E 自 ???
可以看出来这游戏明显是线程发包,在次在喊话试试:
00000007E2FFE1E8 00007FF660DC18FB 返回到 jx3clientx64.00007FF660DC18FB 自 ???
00000007E2FFE258 00007FF660B09C35 返回到 jx3clientx64.00007FF660B09C35 自 ???
00000007E2FFE298 00007FF660B0815D 返回到 jx3clientx64.00007FF660B0815D 自 jx3clientx64.00007FF660B09B60
00000007E2FFE2F8 00007FF660B0975B 返回到 jx3clientx64.00007FF660B0975B 自 jx3clientx64.00007FF660B07FF0
00000007E2FFE348 00007FF660BD4378 返回到 jx3clientx64.00007FF660BD4378 自 jx3clientx64.00007FF660B09610
00000007E2FFE388 00007FF6609CCDD5 返回到 jx3clientx64.00007FF6609CCDD5 自 jx3clientx64.00007FF660DBEE09
00000007E2FFE3A8 00007FF6609D0498 返回到 jx3clientx64.00007FF6609D0498 自 jx3clientx64.00007FF6609CD220
00000007E2FFE3C8 00007FF6609C84CA 返回到 jx3clientx64.00007FF6609C84CA 自 jx3clientx64.00007FF6609C78E0
00000007E2FFE3F8 00007FF660A6620C 返回到 jx3clientx64.00007FF660A6620C 自 jx3clientx64.00007FF6609C86C0
可以发现他真的很奇怪,喊话居然不是线程发包,查看包内容发下并没有我们所输入的123:
分析喊话call:
通过堆栈返回一直往上追到此call,这个call在下断时只有喊话会断下,所以姑且判断他是喊话call,但观察传参依旧是加密的,所以继续往上追:
00007FF660B08152 | 48:8BD3 | mov rdx,rbx |
00007FF660B08155 | 49:8BCF | mov rcx,r15 |
00007FF660B08158 | E8 031A0000 | call jx3clientx64.7FF660B09B60 |
00007FF660B0815D | 85C0 | test eax,eax |
一直追到:
00007FF660BD436F | 884424 20 | mov byte ptr ss:[rsp+20],al |
00007FF660BD4373 | E8 9852F3FF | call jx3clientx64.********* |
00007FF660BD4378 | 85C0 | test eax,eax |
观察目标传参情况:
近聊发送123456:
1: rcx 0000019B79825EF0 L"呐惢翶"
2: rdx 0000000000000001
3: r8 0000000000000000
4: r9 0000000000000000
5: [rsp+20] 00000007E2FFE000
6: [rsp+28] 00000007E2FFDE50
7: [rsp+30] 0000000000000008
世界发送123456:
1: rcx 0000019B79825EF0 L"呐惢翶"
2: rdx 0000000000000020
3: r8 0000000000000000
4: r9 0000000000000000
5: [rsp+20] 00000007E2FFE500
6: [rsp+28] 00000007E2FFE3A0
7: [rsp+30] 0000000000000008
近聊发送789555:
1: rcx 0000019B79825EF0 L"呐惢翶"
2: rdx 0000000000000001
3: r8 0000000000000000
4: r9 0000000000000000
5: [rsp+20] 00000007E2FFE000
6: [rsp+28] 00000007E2FFDE50
7: [rsp+30] 0000000000000008
从此时可以看出如下:
- RCX不变,应该是一个绝对值
- rdx应该代表频道ID
- rsp+20
- rsp+28
- 内容长度
再次下断查看rsp+28与rsp+20:
rsp+28:
00000007E2FFDE50 0000010033323101 .123....
rsp+20:
00000007E2FFE500 0000019B271529E0 à).'....
此时可以看出rsp+28应该是我们的喊话内容,rsp+20暂时不清楚,但我跟了整个流程未发现程序有对他进行访问或者改写的地方,就随便传一个地址
代码喊话:
int 喊话(const char* Msg, int 频道) {
cout << "---------------------喊话Call调用开始-----------------------" << endl;
PVOID64 参数1 = (PVOID64)(*(ULONG_PTR*)(JX3ClientX64 + 喊话参数偏移) + 喊话参数二级偏移);
cout << "参数1=" << hex << (ULONG_PTR)参数1 << endl;
char* MsgChar = (char*)malloc(strlen(Msg) + 2);
memset(MsgChar, 0x01, strlen(Msg) + 2);
strcpy(MsgChar + 1, Msg);
cout << "MsgChar=" << MsgChar << endl;
char* Addres = (char*)malloc(strlen(Msg) + 2);
memset(Addres, 0, strlen(Msg) + 2);
喊话Call Func = (喊话Call)(JX3ClientX64 + 喊话Call偏移);
cout << "Call Addres=" << hex << (ULONG_PTR)Func << endl;
ULONG_PTR Size = strlen(Msg) + 2;
int ret = Func(参数1, 频道, 0, 0, Addres, MsgChar, Size);
cout << "Func Return=" << ret << endl;
free(MsgChar);
free(Addres);
cout << "---------------------喊话Call调用结束-----------------------" << endl;
return ret;
}
效果: