欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]CoolCmd接入教程(某游戏协议破解及完整可编译插件源码)

[原创]CoolCmd接入教程(某游戏协议破解及完整可编译插件源码)

20151012  支持插件的免费版已放出  需要的可进QQ交流群下载版本和插件
------------------------------------20150921新加Begin--------------------------------------------------------------------
新活动,20150921开始,任何人根据此示例接入一款协议加密的游戏,并提交插件给官方,可获得一个永久授权
须进交流QQ群101564837,联系官方确认游戏,防止重复.

------------------------------------20150921新加End----------------------------------------------------------------------
前言:
本有个通杀多款几十万在线游戏的插件教程,效果也不错,奈何多种原因不便公开.
So决定用个国产精品游戏做示例,物色了剑网3,又奈何游戏是收费模式,为了让感兴趣人员可以实际练习,选用剑侠世界2(清爽版)做示例.
任何有逆向和开发基础人员,学习此文,均可自主接入任何网游或其他网络软件,且近期将有机会靠接入必得工具永久授权@千游斩活动(一个工具破解市面所有网游还会远吗?).
CoolCmd是什么?  -->  http://bbs.pediy.com/showthread.php?t=203487
CoolCmd挖掘游戏漏洞?  -->  http://bbs.pediy.com/showthread.php?t=203777

CoolCmd交流Q群  -->  101564837
一、协议破解
1.下载运行游戏,登录账号,进入创建角色界面,OD附加之
    选用创建角色协议的优势:
          a.此时封包少,便于无干扰分析
          b.已知加密前明文(创建的角色名),便于对照分析
2.堆栈回溯法回溯明文发包函数
    ⑴bp send 下断点
    ⑵输入角色名:加密解密,点进入游戏,OD断下
      
    ⑶OD Ctrl + F9  4次即可定位到明文发包函数
       汇编代码:

       00565405    8B45 FC         mov     eax, dword ptr [ebp-4]
       00565408    50              push    eax				                 //压入参数
       00565409    8B4D F4         mov     ecx, dword ptr [ebp-C]
       0056540C    8B51 28         mov     edx, dword ptr [ecx+28]
       0056540F    8B45 F4         mov     eax, dword ptr [ebp-C]
       00565412    8B48 28         mov     ecx, dword ptr [eax+28]	//寄存器参数 
       00565415    8B12            mov     edx, dword ptr [edx]
       00565417    8B42 14         mov     eax, dword ptr [edx+14]
       0056541A    FFD0            call    eax   ; //eax = 0x008551D0	即明文发包函数
       0056541C    8945 F8         mov     dword ptr [ebp-8], eax
       0056541F    8B4D FC         mov     ecx, dword ptr [ebp-4]
       00565422    8B11            mov     edx, dword ptr [ecx]
       00565424    8B45 FC         mov     eax, dword ptr [ebp-4]
       00565427    50              push    eax
       00565428    8B4A 08         mov     ecx, dword ptr [edx+8]
       0056542B    FFD1            call    ecx
       0056542D    837D F8 00      cmp     dword ptr [ebp-8], 0
       00565431    75 23           jnz     short 00565456
       00565433    68 A8E7A800  push    00A8E7A8         //SendMsg2GateWay 

                    
       (对0x008551D0函数头、尾以及send同时下断点,可确认此函数同时具备加密和发送功能)
      
  
      分析下此发包函数,为多语言通用,设定函数原型为:
      typedef DWORD (__stdcall *TMMSendProc)(PSendParamSt pParam);
      参数1         压栈参数        包含明文信息的结构体指针
      对应结构体如下:
      typedef struct _SendParamSt
      {
				DWORD dwMMSend_A;	//参数A,固定基址
				DWORD dwMMSend_B;	//参数B,固定基址
				DWORD dwUnknow;	//未知,默认1
				DWORD dwLen0;		//协议长度
				DWORD dwLen;		//协议长度
				DWORD dwBuff;		//协议数据内存地址
       }TSendParamSt,*PSendParamSt;

       内存断点分析dwMMSend_A,dwMMSend_B来源
       dwMMSend_A = 0x00ABE510
       dwMMSend_B = 0x00ABE4F8
       对应汇编代码:
       0085A88A    C700 10E5AB00      MOV DWORD PTR DS:[EAX],game.00ABE510
       0085A890  C740 04 F8E4AB00   MOV DWORD PTR DS:[EAX+4],game.00ABE4F8
       0085A897    C740 08 01000000   MOV DWORD PTR DS:[EAX+8],1
       0085A89E    8948 14            MOV DWORD PTR DS:[EAX+14],ECX
       0085A8A1    5B                 POP EBX
       0085A8A2    C3                 RETN

       参数2  寄存器ECX        加密Key和加密函数相关的指针
   
       内存断点分析Ecx对应基址和偏移
       基址g_Adr_LoginKey = 0xB65FE4
       对应汇编代码:
       00428657    83F8 0A         CMP EAX,0A
       0042865A    75 07           JNZ SHORT game.00428663
       0042865C    33C0            XOR EAX,EAX
       0042865E    E9 A5040000     JMP game.00428B08
       00428663    A1 E45FB600     MOV EAX,DWORD PTR DS:[B65FE4]//B65FE4即基址
       00428668    8B10            MOV EDX,DWORD PTR DS:[EAX]
       0042866A    8B0D E45FB600   MOV ECX,DWORD PTR DS:[B65FE4]		
       00428670    8B42 10         MOV EAX,DWORD PTR DS:[EDX+10]
       00428673    FFD0            CALL NEAR EAX

       偏移g_Oft_LoginKey1= 0x28
       对应汇编代码:
       0056540C    8B51 28         mov     edx, dword ptr [ecx+28]  //0x28偏移
       0056540F    8B45 F4         mov     eax, dword ptr [ebp-C]
       00565412    8B48 28         mov     ecx, dword ptr [eax+28]	

       ECX = *(PDWORD)(*(PDWORD)(g_Adr_LoginKey) + g_Oft_LoginKey1);

       (此游戏,游戏场景内的Ke使用另外的基址和偏移,分析过程类似,此处直接贴出)

       基址g_Adr_SceneKey = 0x1C285F0
       对应汇编代码:
       005977B4    8B0D F085C201   MOV ECX,DWORD PTR DS:[1C285F0]//1C285F0
       005977BA    8B11            MOV EDX,DWORD PTR DS:[ECX]
       005977BC    8B0D F085C201   MOV ECX,DWORD PTR DS:[1C285F0]
       005977C2    8B02            MOV EAX,DWORD PTR DS:[EDX]
       005977C4    FFD0            CALL NEAR EAX

       偏移g_Oft_SceneKey1 = 0x30;
       对应汇编代码:
       00529F20    8B51 30         MOV EDX,DWORD PTR DS:[ECX+30]
       00529F23    8B45 F4         MOV EAX,DWORD PTR SS:[EBP-C]
       00529F26    8B48 30         MOV ECX,DWORD PTR DS:[EAX+30]  
     
       ECX = *(PDWORD)(*(PDWORD)(g_Adr_SceneKey) + g_Oft_SceneKey1);
       明文发包函数接口和参数分析完毕
3.内存断点追溯明文收包函数
   bp recv,过程类似,此处略去
   函数原型:
         typedef DWORD (__cdecl *TMMRecvProc)(DWORD dwLen,DWORD dwBuff,DWORD dwParam);
         参数1:        dwParam        指向解密Key
         参数2:        dwBuff        存储接受的数据(解密前后都存于此)
         参数3:         dwLen        接收的数据长度(解密前后等长)
       对应汇编代码:
       0085B200    8B4424 0C          MOV EAX,DWORD PTR SS:[ESP+C] //明文收包函数
       0085B204    8B08               MOV ECX,DWORD PTR DS:[EAX]
       0085B206    8B4424 08          MOV EAX,DWORD PTR SS:[ESP+8]
       0085B20A    8D5424 0C          LEA EDX,DWORD PTR SS:[ESP+C]
       0085B20E    52                 PUSH EDX
       0085B20F    894C24 10          MOV DWORD PTR SS:[ESP+10],ECX
       0085B213    8B4C24 08          MOV ECX,DWORD PTR SS:[ESP+8]
       0085B217    50                 PUSH EAX
       0085B218    51                 PUSH ECX
       0085B219    E8 52FFFFFF        CALL game.0085B170
       0085B21E    83C4 0C            ADD ESP,0C
       0085B221    C3                 RETN

       明文收包函数接口和参数分析完毕
二、编写CoolCmd插件:
       只需把分析的内容填充到插件代码中即可,核心代码如下:
       VC版代码:
      
#include "CmdApi.h"
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
//SendParam结构体
typedef struct _SendParamSt
{
	DWORD dwMMSend_A;	//参数A,固定基址
	DWORD dwMMSend_B;	//参数B,固定基址
	DWORD dwUnknow;		//未知,默认1
	DWORD dwLen0;		//协议长度
	DWORD dwLen;		//协议长度
	DWORD dwBuff;		//协议数据内存地址
}TSendParamSt,*PSendParamSt;
//明文发送
typedef DWORD (__stdcall *TMMSendProc)(PSendParamSt pParam);
//明文接收
typedef DWORD (__cdecl *TMMRecvProc)(DWORD dwLen,DWORD dwBuff,DWORD dwParam);
TMMSendProc OldMMSend = NULL;
TMMRecvProc OldMMRecv = NULL;
DWORD g_Adr_MMSend = 0;
DWORD g_Adr_MMRecv = 0;
DWORD g_Param_MMSend_A = 0;
DWORD g_Param_MMSend_B = 0;
DWORD g_Adr_LoginKey = 0;
DWORD g_Oft_LoginKey1 = 0x28;
DWORD g_Oft_LoginKey2_Send = 0xC;
DWORD g_Oft_LoginKey2_Recv = 0x10;
//
DWORD g_Adr_SceneKey = 0;
DWORD g_Oft_SceneKey1 = 0x30;
DWORD g_Oft_SceneKey2_Send = 0xC;
DWORD g_Oft_SceneKey2_Recv = 0x10;
DWORD __stdcall NewMMSend(PSendParamSt pParam)
{
	TFilterRet FltRet;
	DWORD dwEcx, dwBuff, dwLen;
	//保存寄存器参数
	_asm{
		mov dwEcx,ecx
	}
	//
	dwLen =  pParam->dwLen;
	dwBuff = pParam->dwBuff;
	//调用滤镜接口,解析序号对应CheckBox创建序号
	g_InitIO.Api.FilterOper((char*)(dwBuff),dwLen,0,&FltRet);
	if (FltRet.bVis){
		if (strlen(FltRet.cName) != 0){
			g_InitIO.Api.WriteLog("Send明",FltRet.cName,(char*)(dwBuff),dwLen,0);
		}
		else{
			g_InitIO.Api.WriteLog("Send明",CN_PUB_DEFHINT,(char*)(dwBuff),dwLen,0);
		}
	}
	//屏蔽发送则直接返回
	if (!FltRet.bCom){
		return 1;
	}
	//恢复寄存器参数
	_asm{
		mov ecx,dwEcx
	}
	//调用原发包函数
	return OldMMSend(pParam);
}
void __stdcall HookMMSend(bool bHook)
{
	void* pRet;
	if (bHook){
		g_InitIO.Api.HookCodeNormal((void*)g_Adr_MMSend, &NewMMSend,&pRet);
		OldMMSend = (TMMSendProc)pRet;
	}
	else{
		g_InitIO.Api.UnHookCodeNormal(OldMMSend);
	}
}
DWORD __cdecl NewMMRecv(DWORD dwLen,DWORD dwBuff,DWORD dwParam)
{ 
	TFilterRet FltRet;
	DWORD dwRet = 0;
	//进解密处理
	dwRet = OldMMRecv(dwLen,dwBuff,dwParam);
	//调用滤镜接口,解析序号对应CheckBox创建序号
	g_InitIO.Api.FilterOper((char*)(dwBuff),dwLen,1,&FltRet);
	if (FltRet.bVis){
		if (strlen(FltRet.cName) != 0){
			g_InitIO.Api.WriteLog("Recv明",FltRet.cName,(char*)(dwBuff),dwLen,0);
		}
		else{
			g_InitIO.Api.WriteLog("Recv明",CN_PUB_DEFHINT,(char*)(dwBuff),dwLen,0);
		}
	}
	//
	if (!FltRet.bCom){
		 //此处可自己通过替换封包的方式变相实现禁止收指定包
		return 1; 
	}
	return dwRet;
}
void __stdcall HookMMRecv(bool bHook)
{
	void* pRet;
	if (bHook){
		g_InitIO.Api.HookCodeNormal((void*)g_Adr_MMRecv, &NewMMRecv,&pRet);
		OldMMRecv = (TMMRecvProc)pRet;
	}
	else{
		g_InitIO.Api.UnHookCodeNormal(OldMMRecv);
	}
}
//自定义的执行函数
//获取SendEcx Key指针
DWORD __stdcall GetSendEcx()
{	
	DWORD dwTmp,dwEcx;
	//登录SendEcx
	if (!IsBadReadPtr((void*)g_Adr_LoginKey,4))
	{
		dwTmp = *(PDWORD)(g_Adr_LoginKey) + g_Oft_LoginKey1;
		if (!IsBadReadPtr((void*)dwTmp,4)){
			dwEcx = *(PDWORD)(dwTmp);
			dwTmp = dwEcx + g_Oft_LoginKey2_Send;
			if (!IsBadReadPtr((void*)dwTmp,4)){
				return dwEcx;
			}
		}
	}
	//游戏内SendEcx
	if (!IsBadReadPtr((void*)g_Adr_SceneKey,4))
	{
		dwTmp = *(PDWORD)(g_Adr_SceneKey) + g_Oft_SceneKey1;
		if (!IsBadReadPtr((void*)dwTmp,4)){
			dwEcx = *(PDWORD)(dwTmp);
			dwTmp = dwEcx + g_Oft_SceneKey2_Send;
			if (!IsBadReadPtr((void*)dwTmp,4)){
				return dwEcx;
			}
		}
	}
	return 0;
}
void __stdcall ExecuteCmd(char* sHexCmd) 
{
	if (NULL == sHexCmd){
		return;
	}
	try{
		DWORD dwEcx = GetSendEcx();
		if (0 == dwEcx){
			return;
		}
		DWORD dwLen = *(WORD*)(sHexCmd);
		DWORD dwBuff = DWORD(&g_ExecuteCmdBuff[0x20]);
		memcpy((void*)dwBuff,&sHexCmd[2],dwLen);
		PSendParamSt pParam = PSendParamSt(&g_ExecuteCmdBuff);
		pParam->dwMMSend_A = g_Param_MMSend_A;
		pParam->dwMMSend_B = g_Param_MMSend_B; 
		pParam->dwUnknow = 1;
		pParam->dwLen0 = dwLen;
		pParam->dwLen = dwLen;
		pParam->dwBuff = dwBuff; 
		//
        	_asm{
            		push ecx
            		push edx
                	push pParam
                	mov ecx,dwEcx
                	mov edx,g_Adr_MMSend
                	call edx
            		pop edx
            		pop ecx
		}
	}
	catch (...){
	}
}
DWORD __stdcall Init_Plugin_MyThread(VOID* lpArgc)
{
	while ((0 == g_Adr_MMSend) || (0 == g_Adr_MMRecv) || (0 == g_Adr_LoginKey) || (0 == g_Adr_SceneKey) || (0 == g_Param_MMSend_A) || (0 == g_Param_MMSend_B)){
		if (0 == g_Adr_MMSend){
			g_Adr_MMSend = g_InitIO.Api.MemSigScan("game.exe", "[1]g_Adr_MMSend,74 ?? 3B F5 74 ?? 8B 16 8B 42 ?? 53,-0x20,0,4");
		}
		if (0 == g_Adr_MMRecv){
			g_Adr_MMRecv = g_InitIO.Api.MemSigScan("game.exe", "[2]g_Adr_MMRecv,8B 44 24 0C 8B 08 8B 44 24 08 ?? ?? ?? 0C,0,0,4");
		}
		if (0 == g_Adr_LoginKey){
			g_Adr_LoginKey = g_InitIO.Api.MemSigScan("game.exe", "[3]g_Adr_LoginKey,83 F8 ?? 75 07 33 C0 E9 ?? ?? ?? ?? A1,0xD,1,4");
		}
		if (0 == g_Adr_SceneKey){
			g_Adr_SceneKey = g_InitIO.Api.MemSigScan("game.exe", "[4]g_Adr_SceneKey,0F B7 51 01 83 C2 01 52,0xE,1,4");
		}
		if (0 == g_Param_MMSend_A){
			g_Param_MMSend_A = g_InitIO.Api.MemSigScan("game.exe", "[5]g_Param_MMSend_A,C7 00 ?? ?? ?? ?? C7 40 ?? ?? ?? ?? ?? C7 40 ?? 01 00 00 00 89 48 ?? ?? C3,2,1,4");
		}
		if (0 == g_Param_MMSend_B){
			g_Param_MMSend_B = g_InitIO.Api.MemSigScan("game.exe", "[6]g_Param_MMSend_B,C7 00 ?? ?? ?? ?? C7 40 ?? ?? ?? ?? ?? C7 40 ?? 01 00 00 00 89 48 ?? ?? C3,9,1,4");
		}
		Sleep(10);
	}
	//
	LogDbg("g_Adr_MMSend:%.8X g_Adr_MMRecv:%.8X g_Adr_LoginKey:%.8X g_Adr_SceneKey:%.8X g_Param_MMSend_A:%.8X g_Param_MMSend_B:%.8X",
			g_Adr_MMSend,g_Adr_MMRecv,g_Adr_LoginKey,g_Adr_SceneKey,g_Param_MMSend_A,g_Param_MMSend_B);
	//调用创建CheckBox接口
    	g_InitIO.Api.CreateCheckBox("Send明", HookMMSend, true);
    	g_InitIO.Api.CreateCheckBox("Recv明", HookMMRecv, true);
	//设置执行发包函数
    	g_InitIO.Api.SetFunc_ExecuteCmd(ExecuteCmd);
	LogDbg("Init_Plugin_MyThread End");
	return true;
}
void Init_Plugin_My()
{
	DWORD dwThreadId = 0;
	HANDLE hTmp = CreateThread(NULL, 0, Init_Plugin_MyThread, NULL, 0, &dwThreadId);
	CloseHandle(hTmp);
}
void Init_Plugin()
{
	LogDbg("_init in");
	if (Init_Plugin_Pub()){
		Init_Plugin_My();
		LogDbg("_init ok");
	}
}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	if (0 == g_hModuleThisDll){
		g_hModuleThisDll = hModule; 	
	}
	switch(ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			Init_Plugin();
        break;
		case DLL_PROCESS_DETACH:
			UnInit_Plugin();
		break;
		default:
			break;
	}
    return TRUE;
}

       Delphi版代码:
library CmdPlugin_JXSJ;
uses
  SysUtils,
  Windows,
  Winsock2 in 'Winsock2.pas',
  Uni_CmdApi in 'Uni_CmdApi.pas';
type
    TMMSendProc = function(dwParam : DWORD):DWORD; stdcall;
    TMMRecvProc = function(dwLen : DWORD; dwBuff : DWORD; dwParam : DWORD):DWORD; cdecl;
{$R *.res}
var
    OldMMSend : TMMSendProc = nil;
    OldMMRecv : TMMRecvProc = nil;
    //
    g_Adr_MMSend : DWORD = 0;
    g_Adr_MMRecv : DWORD = 0;
    //
    g_Param_MMSend_A : DWORD = 0;
    g_Param_MMSend_B : DWORD = 0;
    //
    g_Adr_LoginKey : DWORD  = 0;
    g_Oft_LoginKey1 : DWORD = $28;
    g_Oft_LoginKey2_Send : DWORD = $C;
    g_Oft_LoginKey2_Recv : DWORD = $10;
    //
    g_Adr_SceneKey : DWORD  = 0;
    g_Oft_SceneKey1 : DWORD = $30;
    g_Oft_SceneKey2_Send : DWORD = $C;
    g_Oft_SceneKey2_Recv : DWORD = $10;
type
    TSendParamSt = packed record
        dwMMSend_A : DWORD;	//参数A,固定基址
        dwMMSend_B : DWORD;	//参数B,固定基址
	dwUnknow : DWORD;	//未知,默认1
	dwLen0 : DWORD;		//协议长度
	dwLen : DWORD;		//协议长度
	dwBuff : DWORD;		//协议数据内存地址
    end;
    PSendParamSt = ^TSendParamSt;
function NewMMSend(dwParam : DWORD):DWORD; stdcall;
var
    TmpRet : TFilterRet;
    dwEcx, dwBuff, dwLen : DWORD;
begin
    asm
        mov dwEcx,ecx
    end;
    dwLen :=  pDword(dwParam + $10)^;
    dwBuff := pDword(dwParam + $14)^;
    g_InitIO.Api.FilterOper(PChar(dwBuff),dwLen,0,@TmpRet);
    //显示记录
    if TmpRet.bVis then
    begin
        if TmpRet.cName <> '' then
            g_InitIO.Api.WriteLog('Send明',TmpRet.cName,PChar(dwBuff),dwLen,0)
        else
            g_InitIO.Api.WriteLog('Send明',CN_PUB_DEFHINT,PChar(dwBuff),dwLen,0);
    end;
    if not TmpRet.bCom then
    begin
        Result := 1;
        Exit;
    end;
    asm
        mov ecx,dwEcx
    end;
    Result := OldMMSend(dwParam);
end;
procedure HookMMSend(bHook :Boolean);stdcall;
var
    pRet : Pointer;
begin
    if bHook then
    begin
        g_InitIO.Api.HookCodeNormal(Pointer(g_Adr_MMSend), @NewMMSend,@pRet);
        OldMMSend := pRet;
    end
    else
        g_InitIO.Api.UnHookCodeNormal(@OldMMSend);
end;
function NewMMRecv(dwLen : DWORD; dwBuff : DWORD; dwParam : DWORD):DWORD; cdecl;
var
    TmpRet : TFilterRet;
begin
    Result := OldMMRecv(dwLen,dwBuff,dwParam);
    g_InitIO.Api.FilterOper(PChar(dwBuff),dwLen,1,@TmpRet);
    //显示记录
    if TmpRet.bVis then
    begin
        if TmpRet.cName <> '' then
            g_InitIO.Api.WriteLog('Recv明',TmpRet.cName,PChar(dwBuff),dwLen,0)
        else
            g_InitIO.Api.WriteLog('Recv明',CN_PUB_DEFHINT,PChar(dwBuff),dwLen,0);
    end;
    if not TmpRet.bCom then
    begin
        //此处可自己通过替换封包的方式变相实现禁止收指定包
        Result := 1;
        Exit;
    end;
end;
procedure HookMMRecv(bHook :Boolean);stdcall;
var
    pRet : Pointer;
begin
    if bHook then
    begin
        g_InitIO.Api.HookCodeNormal(Pointer(g_Adr_MMRecv), @NewMMRecv,@pRet);
        OldMMRecv := pRet;
    end
    else
        g_InitIO.Api.UnHookCodeNormal(@OldMMRecv);
end;
function GetSendEcx():DWORD;stdcall;
var
    dwTmp,dwEcx : DWORD;
begin
    Result := 0;
    //登录SendEcx
    if (not IsBadReadPtr(Pointer(g_Adr_LoginKey),4)) then
    begin
        dwTmp := PDWORD(g_Adr_LoginKey)^ + g_Oft_LoginKey1;
	if (not IsBadReadPtr(Pointer(dwTmp),4)) then
        begin
            dwEcx := PDWORD(dwTmp)^;
	    dwTmp := dwEcx + g_Oft_LoginKey2_Send;
            if (not IsBadReadPtr(Pointer(dwTmp),4)) then
            begin
		Result := dwEcx;
                Exit;
            end;
	end;
    end;
	  //游戏内SendEcx
    if (not IsBadReadPtr(Pointer(g_Adr_SceneKey),4)) then
    begin
        dwTmp := PDWORD(g_Adr_SceneKey)^ + g_Oft_SceneKey1;
	if (not IsBadReadPtr(Pointer(dwTmp),4)) then
        begin
            dwEcx := PDWORD(dwTmp)^;
	    dwTmp := dwEcx + g_Oft_SceneKey2_Send;
            if (not IsBadReadPtr(Pointer(dwTmp),4)) then
            begin
		Result := dwEcx;
                Exit;
            end;
	end;
    end;
end;
procedure ExecuteCmd(sHexCmd: PChar); stdcall;
var
    dwLen, dwBuff, dwEcx: DWORD;
    pParam: PSendParamSt;
begin
    try
        if not Assigned(sHexCmd) then
            Exit;
        dwEcx := GetSendEcx();
        if dwEcx = 0 then
            Exit;
        //
        dwLen := pWord(sHexCmd)^;
        dwBuff := Dword(@g_ExecuteCmdBuff[$20]);
        CopyMemory(Pointer(dwBuff),Pointer(DWORD(sHexCmd) + 2), dwLen);
        //
        pParam := PSendParamSt(@g_ExecuteCmdBuff);
	pParam^.dwMMSend_A := g_Param_MMSend_A;
	pParam^.dwMMSend_B := g_Param_MMSend_B;
	pParam^.dwUnknow := 1;
	pParam^.dwLen0 := dwLen;
	pParam^.dwLen := dwLen;
	pParam^.dwBuff := dwBuff;
        //
        asm
            push ecx
            push edx
                push pParam
                mov ecx,dwEcx
                mov edx,g_Adr_MMSend
                call edx
            pop edx
            pop ecx
        end;
    except
    end;
end;
procedure Init_Plugin_My();
procedure Init_Plugin_MyThread();
begin
    while ((g_Adr_MMSend = 0) or (g_Adr_MMRecv = 0) or (g_Adr_LoginKey = 0) or (g_Adr_SceneKey = 0) or (g_Param_MMSend_A = 0) or (g_Param_MMSend_B = 0)) do
    begin
        if g_Adr_MMSend = 0 then
            g_Adr_MMSend := g_InitIO.Api.MemSigScan('game.exe', '[1]g_Adr_MMSend,74 ?? 3B F5 74 ?? 8B 16 8B 42 ?? 53,-$20,0,4');
        if g_Adr_MMRecv = 0 then
            g_Adr_MMRecv := g_InitIO.Api.MemSigScan('game.exe', '[2]g_Adr_MMRecv,8B 44 24 0C 8B 08 8B 44 24 08 ?? ?? ?? 0C,0,0,4');
        if g_Adr_LoginKey = 0 then
            g_Adr_LoginKey := g_InitIO.Api.MemSigScan('game.exe', '[3]g_Adr_LoginKey,83 F8 ?? 75 07 33 C0 E9 ?? ?? ?? ?? A1,$D,1,4');
        if g_Adr_SceneKey = 0 then
            g_Adr_SceneKey := g_InitIO.Api.MemSigScan('game.exe', '[4]g_Adr_SceneKey,0F B7 51 01 83 C2 01 52,$E,1,4');
        if g_Param_MMSend_A = 0 then
            g_Param_MMSend_A := g_InitIO.Api.MemSigScan('game.exe', '[5]g_Param_MMSend_A,C7 00 ?? ?? ?? ?? C7 40 ?? ?? ?? ?? ?? C7 40 ?? 01 00 00 00 89 48 ?? ?? C3,2,1,4');
        if g_Param_MMSend_B = 0 then
            g_Param_MMSend_B := g_InitIO.Api.MemSigScan('game.exe', '[6]g_Param_MMSend_B,C7 00 ?? ?? ?? ?? C7 40 ?? ?? ?? ?? ?? C7 40 ?? 01 00 00 00 89 48 ?? ?? C3,9,1,4');
        Sleep(10);
    end;
    LogDbg('g_Adr_MMSend:%.8X g_Adr_MMRecv:%.8X g_Adr_LoginKey:%.8X g_Adr_SceneKey:%.8X g_Param_MMSend_A:%.8X g_Param_MMSend_B:%.8X',
          [g_Adr_MMSend,g_Adr_MMRecv,g_Adr_LoginKey,g_Adr_SceneKey,g_Param_MMSend_A,g_Param_MMSend_B]);
    //
    g_InitIO.Api.CreateCheckBox('Send明', HookMMSend, True);
    g_InitIO.Api.CreateCheckBox('Recv明', HookMMRecv, True);
    //
    g_InitIO.Api.SetFunc_ExecuteCmd(ExecuteCmd);
    LogDbg('Init_Plugin_MyThread End',[]);
end;
var
    DwHnd, dwTid: DWord;
begin
    DwHnd := CreateThread(nil, 0, @Init_Plugin_MyThread, nil, 0, dwTid);
    CloseHandle(DwHnd);
end;
procedure Init_Plugin();
begin
    LogDbg('_init in',[]);
    if (Init_Plugin_Pub()) then
    begin
	Init_Plugin_My();
	LogDbg('_init ok',[]);
    end;
end;
procedure MyDLLMain(ul_reason_for_call: Integer);
begin
    case ul_reason_for_call of
        DLL_PROCESS_ATTACH:
            begin
                Init_Plugin();
            end;
        DLL_THREAD_ATTACH:
            begin
            end;
        DLL_THREAD_DETACH:
            begin
            end;
        DLL_PROCESS_DETACH:
            begin
                UnInit_Plugin();
            end;
    end;
end;
begin
    DLLProc := @MyDLLMain;
    MyDLLMain(DLL_PROCESS_ATTACH);
end.

       编译插件,丢进CoolCmd的Plugin目录,至此,针对该游戏明文协议的修改测试、屏蔽测试、时序逻辑测试、模糊测试等功能均可正常使用.
      
后记:
       再深入分析下会发现,此游戏加解密算法是相同的,只需Hook一个加解密函数实现明文首发包相关工具.
       汇编代码如下(算法很简单,汇编代码可快速扣出使用):
0085B170    53                       PUSH EBX
0085B171    55                       PUSH EBP
0085B172    8B6C24 14                MOV EBP,DWORD PTR SS:[ESP+14]
0085B176    8B4D 00                  MOV ECX,DWORD PTR SS:[EBP]
0085B179    56                       PUSH ESI
0085B17A    8B7424 10                MOV ESI,DWORD PTR SS:[ESP+10]
0085B17E    8BDE                     MOV EBX,ESI
0085B180    83E3 03                  AND EBX,3
0085B183    C1EE 02                  SHR ESI,2
0085B186    57                       PUSH EDI
0085B187    8B7C24 18                MOV EDI,DWORD PTR SS:[ESP+18]
0085B18B    74 28                    JE SHORT game.0085B1B5
0085B18D    8D49 00                  LEA ECX,DWORD PTR DS:[ECX]
0085B190    83EE 01                  SUB ESI,1
0085B193    8D0431                   LEA EAX,DWORD PTR DS:[ECX+ESI]
0085B196    33D2                     XOR EDX,EDX
0085B198    F735 6CCEB500            DIV DWORD PTR DS:[B5CE6C]
0085B19E    83C7 04                  ADD EDI,4
0085B1A1    8B0C95 B075B500          MOV ECX,DWORD PTR DS:[EDX*4+B575B0]
0085B1A8    81C1 C1236D2E            ADD ECX,2E6D23C1
0085B1AE    314F FC                  XOR DWORD PTR DS:[EDI-4],ECX
0085B1B1    85F6                     TEST ESI,ESI
0085B1B3  ^ 77 DB                    JA SHORT game.0085B190
0085B1B5    33D2                     XOR EDX,EDX
0085B1B7    8BC3                     MOV EAX,EBX
0085B1B9    F735 6CCEB500            DIV DWORD PTR DS:[B5CE6C]
0085B1BF    330C95 B075B500          XOR ECX,DWORD PTR DS:[EDX*4+B575B0]
0085B1C6    85DB                     TEST EBX,EBX
0085B1C8    76 15                    JBE SHORT game.0085B1DF
0085B1CA    8D9B 00000000            LEA EBX,DWORD PTR DS:[EBX]
0085B1D0    300F                     XOR BYTE PTR DS:[EDI],CL
0085B1D2    83EB 01                  SUB EBX,1
0085B1D5    83C7 01                  ADD EDI,1
0085B1D8    C1E9 08                  SHR ECX,8
0085B1DB    85DB                     TEST EBX,EBX
0085B1DD  ^ 77 F1                    JA SHORT game.0085B1D0
0085B1DF    8B45 00                  MOV EAX,DWORD PTR SS:[EBP]
0085B1E2    8BC8                     MOV ECX,EAX
0085B1E4    C1E1 05                  SHL ECX,5
0085B1E7    2BC8                     SUB ECX,EAX
0085B1E9    5F                       POP EDI
0085B1EA    81C1 05840808            ADD ECX,8088405
0085B1F0    5E                       POP ESI
0085B1F1    894D 00                  MOV DWORD PTR SS:[EBP],ECX
0085B1F4    5D                       POP EBP
0085B1F5    B8 01000000              MOV EAX,1
0085B1FA    5B                       POP EBX
0085B1FB    C3                       RETN

于是插件可以另外一种写法(Hook1个函数:自己加密发送):
核心代码如下:
      VC版:
DWORD __cdecl NewMMSend(DWORD dwLen,DWORD dwBuff,DWORD dwParam)
{ 
	TFilterRet FltRet;
	DWORD dwRet = 0;
	if (CheckSend(*(PDWORD)(dwParam))){
		//进加密相关处理
		if (g_B_MMSend){
			g_InitIO.Api.FilterOper((char*)(dwBuff),dwLen,0,&FltRet);
			if (FltRet.bVis){
				if (strlen(FltRet.cName) != 0){
					g_InitIO.Api.WriteLog("Send明",FltRet.cName,(char*)(dwBuff),dwLen,0);
				}
				else{
					g_InitIO.Api.WriteLog("Send明",CN_PUB_DEFHINT,(char*)(dwBuff),dwLen,0);
				}
			}
			//
			if (!FltRet.bCom){
				//此处可自己通过替换封包的方式实现禁止发包
				return 1; 
			}
		}
		return OldMMSend(dwLen,dwBuff,dwParam);
	}
	else{
		//进解密相关处理
		dwRet = OldMMSend(dwLen,dwBuff,dwParam);
		if (g_B_MMRecv){
			g_InitIO.Api.FilterOper((char*)(dwBuff),dwLen,1,&FltRet);
			if (FltRet.bVis){
				if (strlen(FltRet.cName) != 0){
					g_InitIO.Api.WriteLog("Recv明",FltRet.cName,(char*)(dwBuff),dwLen,0);
				}
				else{
					g_InitIO.Api.WriteLog("Recv明",CN_PUB_DEFHINT,(char*)(dwBuff),dwLen,0);
				}
			}
			//
			if (!FltRet.bCom){
				//此处可自己通过替换封包的方式实现禁止收包
				return 1; 
			}
		}
		return dwRet;
	}	
}
void __stdcall ExecuteCmd(char* sHexCmd) 
{
	static int cn_send_oft = 2;
	if (NULL == sHexCmd){
		return;
	}
	try{
		DWORD dwKey = GetSendKey();
		if (0 == dwKey){
			return;
		}
		DWORD dwLen = *(WORD*)(sHexCmd);
		memcpy(&g_ExecuteCmdBuff[cn_send_oft],&sHexCmd[2],dwLen);
		DWORD dwBuff = DWORD(&g_ExecuteCmdBuff[cn_send_oft]);
		//执行加密组包
		__asm{
			pushad
			lea edx,dwKey
			push edx
			push dwBuff
			push dwLen
			mov edx,g_Adr_MMSend
			call edx
			ADD ESP,0x0C
			popad
		}
		dwLen = dwLen + cn_send_oft;
		*(WORD*)(&g_ExecuteCmdBuff) = dwLen;
		//执行发送
		if (g_InitIO.Api.GetSendSock() != 0){
			send(g_InitIO.Api.GetSendSock(),g_ExecuteCmdBuff,dwLen,0);
		}
		else{
			send(g_ExecuteCmd_Sock,g_ExecuteCmdBuff,dwLen,0);
		}
	}
	catch (...){
	}
}

      Delphi版:
function NewMMSend(dwLen : DWORD; dwBuff : DWORD; dwParam : DWORD): DWORD; cdecl;
var
    TmpRet : TFilterRet;
begin
    //判断是否发送
    if CheckSend(pDword(dwParam)^) then
    begin
        //勾选了明文发送
        if g_B_MMSend then
        begin
            g_InitIO.Api.FilterOper(PChar(dwBuff),dwLen,0,@TmpRet);
            //显示记录
            if TmpRet.bVis then
                if TmpRet.cName <> '' then
                    g_InitIO.Api.WriteLog('Send明',TmpRet.cName,PChar(dwBuff),dwLen,0)
                else
                    g_InitIO.Api.WriteLog('Send明',CN_PUB_DEFHINT,PChar(dwBuff),dwLen,0);
            if not TmpRet.bCom then
            begin
                //此处可自己通过替换封包的方式实现禁止发包
                Result := 1;
                Exit;
            end;
        end;
        //执行加密
        Result := OldMMSend(dwLen,dwBuff,dwParam);
    end
    else
    begin
        //执行解密
        Result := OldMMSend(dwLen,dwBuff,dwParam);
        //勾选了明文接收
        if g_B_MMRecv then
        begin
            g_InitIO.Api.FilterOper(PChar(dwBuff),dwLen,1,@TmpRet);
            //显示记录
            if TmpRet.bVis then
                if TmpRet.cName <> '' then
                    g_InitIO.Api.WriteLog('Recv明',TmpRet.cName,PChar(dwBuff),dwLen,0)
                else
                    g_InitIO.Api.WriteLog('Recv明',CN_PUB_DEFHINT,PChar(dwBuff),dwLen,0);
            if not TmpRet.bCom then
            begin
                //此处可自己通过替换封包的方式实现禁止收包
                Result := 1;
                Exit;
            end;
        end;
    end;
end;
procedure ExecuteCmd(sHexCmd: PChar); stdcall;
const
    cn_send_oft = 2;
var
    dwLen, dwBuff, dwKey: DWORD;
begin
    try
        if not Assigned(sHexCmd) then
            Exit;
        //
        dwKey := GetSendKey();
        if dwKey = 0 then
            Exit;
        //
        dwLen := pWord(sHexCmd)^;
        CopyMemory(@g_ExecuteCmdBuff[cn_send_oft],Pointer(DWORD(sHexCmd) + 2), dwLen);
        dwBuff := Dword(@g_ExecuteCmdBuff[cn_send_oft]);
	//执行加密组包
        asm
          pushad
              lea edx,dwKey
              push edx
              push dwBuff
              push dwLen
              mov edx,g_Adr_MMSend
              call edx
              ADD ESP,$0C
          popad
        end;
        //
        dwLen := dwLen + cn_send_oft;
        PWORD(@g_ExecuteCmdBuff)^ := dwLen;
	//执行发送
        if g_InitIO.Api.GetSendSock <> 0 then
            send(g_InitIO.Api.GetSendSock,pDword(@g_ExecuteCmdBuff)^,dwLen,0)
        else
            send(g_ExecuteCmd_Sock,pDword(@g_ExecuteCmdBuff)^,dwLen,0);
    except
    end;
end;

      此游戏有多级函数均可获取明文以及完成加密发送,其中亦不乏很多虚函数,改代码或不改代码等多种花式实现插件功能可自行尝试,估计写18种处理插件都可以,当然用VB、易语言写插件也同样是支持的.

上传的附件 p1.jpg
p2.jpg
p3.jpg
四个完整可编译插件源码.rar

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功