C++ 逆向 C++编程原理
软件安全 破解与防破解 外挂与反外挂 病毒分析
灵魂起源
创建DLL并且注入窗口中
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//唯一的一个 C我们的DLLApp 对象
C我们的DLLApp theApp;
CMyDialog *PMainDialog; //窗口类
DWORD WINAPI ShowDialog(LPARAM lpData)
{
PMainDialog = new CMyDialog; //给指针分配空间
PMainDialog->DoModal(); //阻塞的方式 模态窗口
delete PMainDialog; //释放空间
FreeLibraryAndExitThread(theApp.m_hInstance,1); //释放DLL退出线程
return TRUE;
}
// C我们的DLLApp 初始化
BOOL C我们的DLLApp::InitInstance()
{
CWinApp::InitInstance();
::CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)ShowDialog,NULL,NULL,NULL); //创建线程
return TRUE;
}
|
读写内存修改血量
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
void CMyDialog::OnBnClickedButton1()
{
// [[[0x00D0DF1C]+1C]+28]+288
//读取内存数据
DWORD a = *(DWORD*)0x00D0DF1C;
a = *(DWORD*)(a+0x1C);
a = *(DWORD*)(a+0x28);
a = *(DWORD*)(a+0x288);
CString Stemp;
Stemp.Format(_T("%d"),a);
MessageBox(Stemp);
//写入内存数据
a = *(DWORD*)0x00D0DF1C;
a = *(DWORD*)(a+0x1C);
a = *(DWORD*)(a+0x28);
*(DWORD*)(a+0x288) = 999;
}
|
函数和call的对应关系
画江山
人物属性结构
1
2
3
4
5
6
7
8
9
10
11
12
13
|
typedef struct TROLE_PROPERTY //人物属性结构
{
char* szpName;
DWORD ndHp;
...
...
TROLE_PROPERTY* GetData(); //获得人物数据
void FindWay(int x,int y);
DWORD GetMaxJy(); //获得人物当前升级最大经验
DWORD GetisCombat(); //获得战斗标志位
DWORD GetComebatNo(); //获得战斗顺序标志位
}_TROLE_PROPERTY;
|
获得人物数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
TROLE_PROPERTY*TROLE_PROPERTY::GetData() //获得人物数据
{
try
{
DWORD Base = GetBase(Offset_RoleProperty1);
DWORD Offset2 = Offset_RoleProperty2;
Base = Base+Offset_RoleProperty2;
DWORD Temp = *(DWORD*)Base;
ndLv = *(DWORD*)(Temp+0x12*8);
}
catch(...)
{
OutputDebugStringA("读取人物信息异常\r\n");
return NULL;
}
return this;
}
|
获得人物基地址+大偏移
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
DWORD GetBase(DWORD offset) //获得人物基地址+大偏移
{
DWORD backeax;
DWORD of = offset;
__asm
{
mov ecx,Base_Role
mov edx,[ecx]
mov eax,of
mov eax,[edx+eax]
call eax
mov backeax,eax
}
return backeax;
}
|
寻路
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
void TROLE_PROPERTY::FindWay(int x,int y) //寻路
{
try
{
DWORD ID = this->GetData()->ndMapId;
DWORD X=x;
DWORD Y=y;
__asm
{
mov ecx,Base_Role
mov eax,[ecx]
mov edx,[eax+Offset_FindWay]
call edx
mov ecx,eax
push 0
push Y
push X
push ID
mov edx,[ecx]
mov eax,[edx+4]
call eax
}
}
catch(...)
{
OutputDebugStringA("寻路异常\r\n");
}
}
|
NPC结构
1
2
3
4
5
6
7
8
9
|
typedef struct TNpc_PROPERTY //NPC结构
{
char* szpName;
DWORD ndID;
DWORD ndX;
DWORD ndY;
DWORD ndOpenNpcID;
DWORD ndAttackSign;
}_TNpc_PROPERTY;
|
NPC列表结构
1
2
3
4
5
6
7
|
typedef struct TNpc_List //NPC列表结构
{
DWORD ndNum;
TNpc_PROPERTY NpcList[1000];
DWORD ndNo;
void GetData();
}_TNpc_List;
|
获得npc列表数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
void TNpc_List::GetData //获得npc列表数据
{
try
{
DWORD Base = Base_NPC;
DWORD Offset = Offset_RoleProperty2;
ndNum = *(DWORD*)(Base+0xC); //ID数组成员数
DWORD Base1 = *(DWORD*)(Base+0x4); //ID数组根
DWORD Id= 0;
int j = 0;
DbgPrint_Mint("NPCID数组大小:%x\r\n",ndNum);
for(int i=0;i<ndNum;i++)
{
Id = *(DWORD*)(Base1+i*0x4);
if(Id>0x100000)
{
Id = Id&0x0FFFFF;
DWORD Temp=*(DWORD*)Base;
Temp=*(DWORD*)(Temp+Id*0x8);
Temp=*(DWORD*)(Temp+Offset);
NpcList[j].ndId=*(DWORD*)(Temp+0x0*0x8);
NpcList[j].ndX=*(DWORD*)(Temp+0x5*0x8);
NpcList[j].ndY=*(DWORD*)(Temp+0x6*0x8);
NpcList[j].ndOpenNpcID=*(DWORD*)(Temp+0x96*0x8);
NpcList[j].ndAttackSign=*(DWORD*)(Temp+0x3*0x8);
DWORD NameAddr=*(DWORD*)(Temp+Ox1*0x8);
NpcList[j].szpName=(char*)(NameAddr+0x4);
j=j+1;
}
}
ndNo=j;
}
catch(...)
{
OutputDebugStringA("获得npc列表数据异常\r\n");
}
}
|
获得战斗标志位
1
2
3
4
5
6
7
|
DWORD TROLE_PROPERTY::GetisCombat() //获得战斗标志位
{
DWORD Back=GetBase(Offset_isCombat1);
Back=Back+Offset_isCombat2;
DWORD n=*(DWORD*)Back;
return n;
}
|
获得战斗顺序标志位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
DWORD TROLE_PROPERTY::GetComebatNo() //获得战斗顺序标志位
{
DWORD Back = GetBase(Offset_CombatNo1)
__asm
{
mov ecx ,Back
mov eax ,[ecx]
mov edx ,[eax+Offset_CombatNo2]
call edx
mov Back,eax
}
Back =*(DWORD*)(Back+4):
return Back;
}
|
获得人物当前升级最大经验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
DWORD TROLE_PROPERTY::GetMaxJy() //获得人物当前升级最大经验
{
DWORD Back1=GetBase (Offset_MaxJingYan);
DWORD* pLv = new DWORD;
DWORD* pBack = new DWORD;
*pLv = this->GetData()->ndLv;
__asm
{
push pLv
push pBack
mov ecx, Back1
add ecx, 4
mov eax, Call_LvtoJy
call eax
}
DWORD Temp=*pBack;
Temp=*(DWORD*)(Temp+Ox10);
return Temp;
}
|
Hook明文包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
void HookGameClearE() //Hook明文包
{
DWORD dwHookAddr = HookClearEAddr; //HOOK地址
DWORD dwTargetAddr = (DWORD)HookGameClearECall; //跳转到的子程序
DbgPrint_Mine("Hook明文包地址%x HookCall地址%x\r\n",dwHookAddr ,dwTargetAddr);
EnableDebugPrivilege(TRUE); //提升权限
DWORD pid =NULL;
DWORD WriteSize =NULL;
GetWindowThreadProcessId(GetGameWndHandle(), &pid); //获得进程ID
hProcess =OpenProcess(PROCESS_ALL_ACCESS,FALSE ,pid ); //打开进程
DbgPrint_Mine("pid:%x\r\n",pid );
DbgPrint_Mine("hProcess:%x\r\n",hProcess );
byte Temp =OxE9; //jmp
WriteProcessMemory(hProcess,(LPDWORD)(dwHookAddr + 0x00),&Temp,1 ,&WriteSize);
DWORD Temp1= dwTargetAddr - dwHookAddr - 5; //跳转的差值
WriteProcessMemory(hProcess,(LDDWORD)(dwHookAddr + 0x01),&Temp1,4 ,&WriteSize);
byte Temp2=0x90; //用nop填充
WriteProcessMemory(hProcess,(LPDWORD)(dwHookAddr + 0x05),&Temp2,1 ,&WriteSize);
}
|
提升权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
BOOL EnableDebugPrivilege (BOOL bEnable) //提升权限OpenProcess
{
BOO fOK =FALSE;
HANDLE hToken;
if(OpenProcessToken(GetCurrentProcess()), TOKEN_ADTUSI_PRIVILEGES, &hToken)) //打开进程访问令牌
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegelalue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED:0;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof (tp), NULL, NULL);
fOK = (GetlastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return fOK;
}
|
HOOK明文包子程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
__declspec(naked) void HookGameCIearECa11()//裸体函数 HOOK明文包子程序
{
__asm
{
//eax包长 ecx包内容 edx socket flags 0
pushad
mov BaoChang, eax
mov ecx, [esi+4]
mov BaoNeiRongAddr, ecx
}
p =new byte[BaoChang];
ReadProcessMemory(hProcess, (LPCVOID)BaoNeiRongAddr, p, BaoChang, O);
DbgPrint_Mine("包长:%x",BaoChang);
for (int i=0;i<(int)BaoChang; i++)
{
sprintf(s, "%02X",p[i]);
strcat_s(a, s);
}
DbgPrint_Mine("%s", a);
sprintf(a, "%s","包内容:");
delete p;
__asm
{
popd
mov ecx,dword ptr [esi+4]
mov edx,dword ptr [edi+0xC]
jmp HookGameClearBackAdrr
}
}
|
还原明文包
1
2
3
4
5
6
7
8
|
void UnHookGameClearE() //还原明文包
{
//8B 4E 04 8B 57 0C 还原代码
DWORD dwHookAddr = HookClearEAddr;
DWORD WriteSize = NULL;
byte Temp[] = {0x8B,0x4E,0x04,0x8b,0x57,0x0C};
WriteProcessMemory(hProcess,(LPDWORD)(dwHookAddr + 0x00),&Temp,6 ,&WriteSize);
}
|
发送封包
发送封包
HexChar转Byte
人物攻击call
攻击call
自动打怪相关
byte数组转string
宝宝攻击call
封装主线程调用
设置主线程和卸载主线程
获得窗口句柄
回调函数
主线程选角色消息 主线程HOOK明文包消息 主线程发包消息 寻路消息
自动打怪
自动打怪参数结构体
1
2
3
4
5
6
7
|
typedef struct TAutomaticKill //自动打怪参数结构体
{
int ndx1;
int ndy1;
int ndx2;
int ndy2;
}_TAutomaticKill;
|
结束自动打怪
StructGame.h声明
自动打怪寻路线程
FindwayThread
循环寻路
自动打怪杀怪线程
任务遍历
void CMainDialogWnd::OnBnClickedButton14()
任务属性
Ttask_PROPERTY
任务列表结构
typedef struct Ttask_List
获得任务列表数据
void Ttask_List::GetData()
任务二叉树遍历
void Ttask_List::taskthree(int threeBase)
检测概论
exe dll 特征码
改写内存或者改写汇编代码
- CRC检测 代码段 下访问断
- 数据检测 数据段 下访问断
- CALL检测
- 堆栈检测
远程注入dll
int _tmain(int argc,_TCHAR* argue[])
InJectDll
劫持注入
劫持工具
收包
发包可以达到功能
收包只能作为判断
收包效率高
recv
WS2_32.recv
收包实例
hook明文包 另一处
还原明文包 另一处
裸体函数 另一处
窗口句柄和帐号
游戏账号
查找到游戏帐号的文本以后
下访问断 退出游戏得到访问代码
发现是一个常量(类似基地址)1670D31C
这个代码在主线程中 主线程入口地址是16670000
那个公式应该是
主线程入口+9C31C
我们用ce搜索主线程入中发现有基地址存放
得到公式
[27AC124]+9D31C
控件遍历
获得控件列表数据 GetData
控件遍历递归 GetData2