re | 植物大战僵尸年度版gdi僵尸位置画框

发布时间 2024-01-04 12:55:58作者: Mz1

re | 植物大战僵尸年度版gdi僵尸位置画框

最近写的东西,随便练练手,绘制的部分上一个博客中有精简版本。
程序使用的是僵尸结构体线性数组,取僵尸的部分写的非常优雅,感兴趣的可以去逆一下看看。
效果:
image

代码如下:

#include <Windows.h>

WCHAR szTitle[] = L"mzbox";
WCHAR szWindowClass[] = L"mzclazz";
HINSTANCE g_hInst;
HWND g_hWnd;

LONG g_lWndWidth, g_lWndHeight;

// 游戏数据绘制
struct Zombie {
    FLOAT x, y;
    DWORD hp;
};
DWORD g_zombie_num = 0;
Zombie g_zombies[100];

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    g_hInst = hInstance; // 将实例句柄存储在全局变量中
    // ex: 置顶、分层、鼠标穿透
    HWND hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, szWindowClass, szTitle, WS_POPUP, 200, 200, 200, 200, NULL, NULL, hInstance, NULL);
    g_hWnd = hWnd;    // 保存全局句柄
    if (!hWnd)
    {
        return FALSE;
    }
    //SetLayeredWindowAttributes(hWnd, 0, (255 * 50) / 100, LWA_ALPHA);  // 整个窗口透明
    SetLayeredWindowAttributes(hWnd, RGB(255, 255, 255), 0, LWA_COLORKEY);
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // 分析菜单选择:
        switch (wmId)
        {
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 在此处添加使用 hdc 的任何绘图代码...
        HPEN hPenRed, hPenGreen;
        hPenRed = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
        hPenGreen = CreatePen(PS_SOLID, 3, RGB(0, 255, 0));
        SelectObject(hdc, hPenRed);
        // SelectObject(hdc, CreateSolidBrush(RGB(0, 255, 0)));
        Rectangle(hdc, 0, 0, g_lWndWidth, g_lWndHeight);     //  标记目标窗口
        // 画僵尸框
        int length = g_zombie_num;
        int x = 0;
        int y = 0;
        SelectObject(hdc, GetStockObject(NULL_BRUSH));
        SelectObject(hdc, hPenGreen);
        for (int i = 0; i < length; i++) {
            x = (int)(g_zombies[i].x * 0.68);
            y = (int)(g_zombies[i].y * 0.65+25);
            Rectangle(hdc, x, y, x+50, y+50);
        }

        DeleteObject(hPenRed);
        DeleteObject(hPenGreen);
        EndPaint(hWnd, &ps);
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}



ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex = { 0 };

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = NULL;
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = szWindowClass;

    return RegisterClassExW(&wcex);
}

// 刷新函数,在这里编写辅助的逻辑
DWORD WINAPI ThreadProc(
    _In_ LPVOID lpParameter
) {
    int x = 0;
    int y = 0;
    int width = 0;
    int height = 0;
    Sleep(1000);   // 启动延迟
    // 获取基本信息
    HWND pvz_hWnd = FindWindow(NULL, L"Plants vs. Zombies");   // 获取窗口句柄
    RECT rect = { 0 }; // 窗口RECT
    

    DWORD pid = NULL;
    WCHAR buf[512] = { 0 };
    GetWindowThreadProcessId(pvz_hWnd, &pid);
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
    wsprintf(buf, L"ok: handle: %p \n", hProcess);
    OutputDebugString(buf);

    while (1) {
        // 移动窗口位置, 跟随游戏窗口
        GetWindowRect(pvz_hWnd, &rect);
        x = rect.left;
        y = rect.top;
        width = rect.right - rect.left;
        height = rect.bottom - rect.top;
        g_lWndWidth = width;
        g_lWndHeight = height;
        SetWindowPos(g_hWnd, g_hWnd, x, y, width, height, SWP_NOZORDER);
        DWORD game_base = 0;    // 整个游戏对象
        DWORD game_struct = 0;   // 一局游戏的结构体
        DWORD sun = 0;
        // 获取游戏信息
        ReadProcessMemory(hProcess, (LPCVOID)0x755e0c, (LPVOID)&game_base, 4, NULL);
        ReadProcessMemory(hProcess, (LPCVOID)(game_base + 0x868), (LPVOID)&game_struct, 4, NULL);
        ReadProcessMemory(hProcess, (LPCVOID)(game_struct + 0x5578), (LPVOID)&sun, 4, NULL);  // 阳光数值
        wsprintf(buf, L"sun: %d \n\0", sun);
        OutputDebugString(buf);
        // 获取僵尸数组开头地址和大小
        CONST DWORD ZOMBIE_SIZE = 0x168;   // 僵尸结构体大小
        DWORD zombie_array_length = 0;
        DWORD zombie_array_addr = 0;
        BYTE zombie_array_buffer[0x10000] = { 0 };   // 用于存放僵尸数组内容
        FLOAT x = y = 0;
        ReadProcessMemory(hProcess, (LPCVOID)(game_struct + 0xac), (LPVOID)&zombie_array_length, 4, NULL);
        ReadProcessMemory(hProcess, (LPCVOID)(game_struct + 0xa8), (LPVOID)&zombie_array_addr, 4, NULL);
        wsprintf(buf, L"zombie_num: %d \n\0", zombie_array_length);
        OutputDebugString(buf);
        // 获取僵尸数组内容
        ReadProcessMemory(hProcess, (LPCVOID)(zombie_array_addr), (LPVOID)zombie_array_buffer, ZOMBIE_SIZE * zombie_array_length, NULL);
        // 遍历有效僵尸
        DWORD alive_num = 0;
        for (int i = 0; i < zombie_array_length; i++) {
            DWORD alive = (*(DWORD*)((zombie_array_buffer + 0x168 * i)+ 0x164)) & 0xffff0000;  // 僵尸是否存活
            /*wsprintf(buf, L"  [%d] alive: %d \n\0", i,alive);
            OutputDebugString(buf);*/
            if (alive != 0) {   // 活着
                // 读取位置
                x = (*(FLOAT*)((zombie_array_buffer + 0x168 * i) + 0x2c));
                y = (*(FLOAT*)((zombie_array_buffer + 0x168 * i) + 0x30));
                wsprintf(buf, L"  [%d] pos: %d, %d \n\0", i, (int)x, (int)y);
                OutputDebugString(buf);
                g_zombies[alive_num].x = x;
                g_zombies[alive_num].y = y;
                alive_num += 1;
            }
        }
        g_zombie_num = alive_num;   // 保存最大存活数量

        InvalidateRect(g_hWnd, NULL, true);   // 刷新画面重新绘制
        Sleep(200);
    }
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    MyRegisterClass(hInstance);
    // 执行应用程序初始化:
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }
    // 启动刷新线程
    CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

    MSG msg;
    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;

}
``