其他
2021南极动物厂游戏高赛竞赛决赛分析02
前言
Part 2
0x01 过程
功能分析
例如武器.开火()这样子。
我们可以看到函数内有大量的浮点操作,此时我们慢慢单步,观察这个函数内每一个函数的参数返回值,看看有没有取出来什么坐标之类的,因为这个虚表函数已经被确认为开火函数,并且这个虚表函数除了this之外是没有其他参数的,所以子弹出发的坐标一定是在这个函数里面,通过其他函数取出来的。所以我们要格外认真的去查看这些函数的返回值参数(因为可能是通过参数返回的坐标)。
push xxxx
mov [rsp+4],xxxx
ret
判断下武器指针是否属于我们自己,不是我们自己的话就不修改,是我们自己的话就给Bot坐标,这样就不会影响Bot的开枪了(懒得写了)。
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <iostream>
void DllEntry();
char* GetName(uintptr_t Object);
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
AllocConsole();
freopen("CON", "w", stdout);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)DllEntry, 0, 0, 0);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
uintptr_t GameBase{ 0 };
uintptr_t BotPosPtr{ 0 };
const char BotName[10] = "BotPawn_C";
struct Array
{
uintptr_t* ArrayEntry;
int Count;
};
struct Vec3
{
float x;
float y;
float z;
};
Vec3* GetPos(uintptr_t Object);
void Hook();
void DllEntry()
{
GameBase = reinterpret_cast<uintptr_t>(GetModuleHandleW(L"ShooterClient.exe"));
Hook();
while (true)
{
auto UOjbectArrayPtr = *(uintptr_t*)(GameBase + 0x2F71060);
if (!UOjbectArrayPtr)
continue;
UOjbectArrayPtr = *(uintptr_t*)(UOjbectArrayPtr + 0x30);
if (!UOjbectArrayPtr)
continue;
auto UOjbectArray = *(Array*)(UOjbectArrayPtr + 0x98);//自己定义一个结构
for (int index = 0; index < UOjbectArray.Count; index++)
{
auto Object = UOjbectArray.ArrayEntry[index];
if (Object)
{
auto NamePtr = GetName(Object);
if (NamePtr)
{
if (!strcmp(NamePtr, "BotPawn_C"))
{
auto pPos = GetPos(Object);
memcpy((void*)BotPosPtr, pPos, sizeof(Vec3));
}
//printf("Ptr:%llx Name:%s\n", Object, NamePtr);
}
}
}
}
}
void Hook()
{
uint8_t BulletShellCode[] = "\x81\xC1\x6B\x63\x19\x36\x8B\xC1\x25\xFF\xFF\x7F\x00\x0D\x00\x00\x80\x3F\x89\x85\x00\x01\x00\x00\x50\x51\x48\xB8\x66\x66\x66\x66\x66\x66\x36\x12\x48\x8B\x08\x48\x89\x4C\x24\x70\x8B\x48\x08\x89\x4C\x24\x78\x59\x58\x68\x78\x56\x34\x12\xC7\x44\x24\x04\x34\x12\x00\x00\xC3";
uint8_t JmpShellCode[] = "\x68\x78\x56\x34\x12\xC7\x44\x24\x04\x34\x12\x00\x00\xC3";
BotPosPtr = (uintptr_t)malloc(sizeof(Vec3));
auto HookMemory = (uintptr_t)VirtualAlloc(0, 0x1000, 0x1000, PAGE_EXECUTE_READWRITE);
if (BotPosPtr && HookMemory)
{
auto HookAddress = GameBase + 0x51C162;
auto ReturnAddress = GameBase + 0x51C17A;
*(uintptr_t*)(BulletShellCode + 0x1C) = BotPosPtr;
*(uint32_t*)(BulletShellCode + 0x36) = *(uint32_t*)(&ReturnAddress);
*(uint32_t*)(BulletShellCode + 0x3E) = *(uint32_t*)((uint64_t)(&ReturnAddress) + 4);
memcpy((void*)HookMemory, BulletShellCode, sizeof(BulletShellCode) - 1);
*(uint32_t*)(JmpShellCode + 0x1) = *(uint32_t*)(&HookMemory);
*(uint32_t*)(JmpShellCode + 0x9) = *(uint32_t*)((uint64_t)(&HookMemory) + 4);
DWORD old{ 0 };
VirtualProtect((void*)HookAddress, 0x100, 0x40, &old);
memcpy((void*)HookAddress, JmpShellCode, sizeof(JmpShellCode) - 1);
VirtualProtect((void*)HookAddress, 0x100, old, &old);
}
printf("HookMemory:%llx BotPosPtr:%llx\n", HookMemory, BotPosPtr);
}
char* GetName(uintptr_t Object)
{
if (IsBadReadPtr((void*)Object, 8))
{
return 0;
}
auto NameIndex = *(int*)(Object + 0x18);
if (!NameIndex)
return NULL;
auto NameBase = *(uintptr_t*)(GameBase + 0x2E6E0C0);
if (!NameBase)
return NULL;
auto NameIndexPtr = *(uintptr_t*)(NameBase + 8 * (static_cast<uint64_t>(NameIndex) / 0x4000));
if (!NameIndexPtr)
return NULL;
NameIndexPtr = *(uintptr_t*)(NameIndexPtr + 8 * (static_cast<uint64_t>(NameIndex) % 0x4000));
if (!NameIndexPtr)
return NULL;
return (char*)(NameIndexPtr + 0xC);
//v4 = (*(*(qword_1800091A0 + 8i64 * (*v3 / 0x4000)) + 8i64 * (*v3 % 0x4000)) + 0x10i64); 这里dump出来的dll最后是0x10哦
//这里 0xC 为什么跟 dump出来的 那个0x10不一样呢 因为0x10取出来的名字是不完整的 不知道为什么出题人要这样写
}
Vec3* GetPos(uintptr_t Object)
{
if (IsBadReadPtr((void*)Object, 8))
{
return 0;
}
auto PosPtr = *(uintptr_t*)(Object + 0x158);
return (Vec3*)(PosPtr + 0x164);
}
FLAG
重新回来看FLAG,发现了这里,不清楚是个啥,于是x64dbg,设置RIP,可以看见解密出来以后的字符串 FileName = "flag:%s\n\r"
进到140001010可以看见明显的特征(分析多了,这里其实可以看出来,这是一个printf函数)。
所以在v23为假的情况下会执行这个流程打印v9,v9是从hack.dat解密出来的。
#include <iostream>
#include <Windows.h>
int main()
{
uint8_t flag[] = "2RSRhrofoWtLeLrJCSlTireznrtx.oeLxuehyyAwbpCOZq0tsS7MZyVdOUoE8";
for (int i = 0; i < sizeof(flag); i++)
{
flag[i] += 0x13;
flag[i] ^= 0x3F;
}
HANDLE hFile = CreateFileA("hack.dat", GENERIC_ALL, NULL, NULL, CREATE_ALWAYS, NULL, NULL);
DWORD lpNumberOfBytesWritten{ 0 };
WriteFile(hFile, &flag, sizeof(flag), &lpNumberOfBytesWritten, NULL);
std::cout << "Hello World!\n";
}
0x02 总结
看雪ID:淡然他徒弟
https://bbs.pediy.com/user-home-620278.htm
《安卓高级研修班》2021年6月班火热招生中!
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!