钞sir 发表于 2020-7-21 16:49

给程序加一个按钮

# 简介

有时候我们会觉得某个已经编译好了的程序的功能不是那么完美,我们想要再添加一些额外的功能,但是我们又没有源码,不方便直接进行修改重编译打包,这时候我们就可以考虑给程序添加一个新功能的按钮了...

# 思路

思路很简单,无非就是利用子类化技术,直接编写DLL,然后注入到程序当中去,虽然就一句话但是具体的工作还是比较多,这里我就通过给植物大战僵尸程序为例子仔细说说...

# 编写DLL

## 创建按钮
因为我们主要目的是添加一个按钮功能,使用这里主要用到的函数是[`CreateWindow`](https://docs.microsoft.com/en-us/previous-versions/aa931018(v=msdn.10))...

Syntax:

```c
HWND CreateWindow(
    LPCTSTR lpClassName,
    LPCTSTR lpWindowName,
    DWORD dwStyle,
    int x,
    int y,
    int nWidth,
    int nHeight,
    HWND hWndParent,
    HMENU hMenu,
    HANDLE hInstance,
    PVOID lpParam
)
```

通过这个函数我们可以创建一个按钮,主要的参数是`hWndParent`,这个参数表示父窗口句柄,我们可以用`FindWindow`函数来获取,例如:

```c
#define BUTTON_sir_1 3300
HWND hWndParent = FindWindow(NULL,"植物大战僵尸中文版");
CreateWindow(TEXT("BUTTON"),
            TEXT("增加阳光"),
            WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
            600,0,80,30,
            hWndParent,
            (HMENU)BUTTON_sir_1,
            NULL,
            NULL);
```

## 子类化

子类化就是用来改变或者扩展一个已存在的窗口的行为、而不用重新开发的有效途径,这是我们添加按钮,给予按钮处理事件的能力的主要方法,主要方法就是通过`GetWindowLong()`获取窗口旧的消息处理函数(`OldWindowProc`),然后通过`SetWindowLong()`设置新的消息处理函数(`NewWndProc`),进行需要进行的消息处理,其他的交给旧的消息处理函数:

```c
LRESULT CALLBACK NewWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
OldWindowProc = GetWindowLong(hWndParent,GWL_WNDPROC);
SetWindowLong(hWndParent,GWL_WNDPROC,(LONG)NewWndProc);
```
新的消息处理函数中只需要写我们感兴趣的消息,比如这里我们只关心按键增加阳光的功能,其余我们没有写的消息处理通过`CallWindowProc`函数交给原来的消息处理函数处理:

```c
LRESULT CALLBACK NewWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
        int wmId, wmEvent;
        switch(message)
        {
        case WM_COMMAND:
                wmId    = LOWORD(wParam);
                wmEvent = HIWORD(wParam);
                // 分析菜单选择:
                switch (wmId)
                {
                case BUTTON_sir_1:
                        MessageBox(NULL,TEXT("3003"),TEXT("HELLO"),MB_OK);
                        break;
                default:
                        returnCallWindowProc((WNDPROC)OldWindowProc,hWnd,message,wParam,lParam);
                }
                break;
        default:
                returnCallWindowProc((WNDPROC)OldWindowProc,hWnd,message,wParam,lParam);
        }
        return 0;
}
```

这样我们的按键就有相应消息的能力了,剩下的就是完善dll,然后编写dll注入代码了;

# DLL注入

基本步骤是:
1. 获取目标进程句柄
2. 将要注入的dll路径写入目标进程内存
3. 获取LoadLibraryW()API地址
4. 在目标进程中运行远程线程

dll注入的代码确实网上确实到处都是,我这里直接贴出[参考](https://blog.csdn.net/qq_40827990/article/details/89367045),用的是`CreateRemoteThread`的方法;

# 效果
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200623103316201.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwODI3OTkw,size_1,color_FFFFFF,t_0)!(https://img-blog.csdnimg.cn/20200623110035245.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwODI3OTkw,size_1,color_FFFFFF,t_0)
# dll代码
```c
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <stdio.h>
#include <psapi.h>
#define BUTTON_sir_1 3300
#define BUTTON_sir_2 3301
#define BUTTON_sir_3 3302
//extern "C" _declspec(dllexport)
LONG OldWindowProc,Button1Proc;
HWND pro_hwnd;                               //程序句柄
HANDLE hpro;                                //进程句柄
DWORD pro_base = NULL;                //程序基地址
char szBuf = { 0 };
LRESULT CALLBACK NewWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
int kill(){
        pro_hwnd = FindWindow(NULL,"植物大战僵尸中文版"); //植物大战僵尸中文版
        if (pro_hwnd == NULL){        //如果无法获取句柄则报错
                return -1;
        }

        DWORD pro_id;
        GetWindowThreadProcessId(pro_hwnd, &pro_id);        //获取进程ID
        if(pro_id == 0){
                //printf("无法获取进程ID\n");
                return 0;
        }
        //printf("进程id: %d\n",pro_id);
        //打开进程对象,并获取进程句柄
        hpro = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pro_id);
        if (hpro == 0){
                printf("无法获取进程句柄");
        }
        printf("进程句柄id: %d\n",hpro);

        // 获取每一个模块加载基址
        HMODULE hModule = {0};
    DWORD dwRet = 0;
        int num = 0;
    int bRet = EnumProcessModulesEx(hpro, (HMODULE *)(hModule), sizeof(hModule),&dwRet,NULL);
    if (bRet == 0){
      printf("EnumProcessModules");
    }
        // 总模块个数
        num = dwRet/sizeof(HMODULE);
    pro_base = (DWORD)hModule;
        return 0;
}

void sun_add(){
        DWORD sun_addr = pro_base + 0x002A9EC0;
        printf("阳光基址: 0x%p\n",sun_addr);
        DWORD sun_value;
        DWORD new_sun_value;
        ReadProcessMemory(hpro,(PVOID)sun_addr,&sun_addr,4,0);
        sun_addr = sun_addr + 0x768;
        ReadProcessMemory(hpro,(PVOID)sun_addr,&sun_addr,4,0);
        sun_addr = sun_addr + 0x5560;
        ReadProcessMemory(hpro,(PVOID)sun_addr,&sun_value,4,0);
        new_sun_value = sun_value + 2000;
        WriteProcessMemory(hpro, (LPVOID)sun_addr, &new_sun_value, 4, 0);        //修改阳光
}

void kill_all(){
        DWORD kill_1 = pro_base + 0x0013130F;        //0x0053130F
        DWORD code_1 = 0x9090ff29;                                //0x20247c2b
        WriteProcessMemory(hpro, (LPVOID)kill_1, (LPVOID)&code_1, 4, 0);        //普通僵尸秒杀

        DWORD kill_2 = pro_base + 0x00131044;        //0x00531044
        WORD code_2 = 0xC929;                                        //0xc82b
        WriteProcessMemory(hpro, (LPVOID)kill_2, (LPVOID)&code_2, 2, 0);        //帽子僵尸秒杀
}

void no_time(){
        DWORD no_time = pro_base + 0x00087296;        //0x487296
        WORD code_3 = 0x9090;                                        //jle 004872AC
        WriteProcessMemory(hpro, (LPVOID)no_time, (LPVOID)&code_3, 2, 0);        //无冷却时间
}

DWORD APIENTRY Msg(LPVOID lpParameter)
{
        char szBuf = { 0 };
        //hwnd = FindWindow(NULL, TEXT("wintest"));
        CreateWindow(TEXT("BUTTON"),
                                        TEXT("增加阳光"),
                                        WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
                                        600,0,80,30,
                                        pro_hwnd,
                                        (HMENU)BUTTON_sir_1,
                                        NULL, //(HINSTANCE)GetWindowLongPtr((HWND)lpParameter,GWLP_HWNDPARENT)
                                        NULL);
        CreateWindow(TEXT("BUTTON"),
                                        TEXT("僵尸秒杀"),
                                        WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
                                        600,40,80,30,
                                        pro_hwnd,
                                        (HMENU)BUTTON_sir_2,
                                        NULL,
                                        NULL);
        CreateWindow(TEXT("BUTTON"),
                                TEXT("无冷却"),
                                WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
                                600,80,80,30,
                                pro_hwnd,
                                (HMENU)BUTTON_sir_3,
                                NULL,
                                NULL);

        OldWindowProc = GetWindowLong(pro_hwnd,GWL_WNDPROC);
        SetWindowLong(pro_hwnd,GWL_WNDPROC,(LONG)NewWndProc);

        MSG msg;
        while(GetMessage(&msg, NULL, 0, 0))
        {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        }
       
        return 0;
}


LRESULT CALLBACK NewWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
        int wmId, wmEvent;
        switch(message)
        {
        case WM_COMMAND:
                wmId    = LOWORD(wParam);
                wmEvent = HIWORD(wParam);
                // 分析菜单选择:
                switch (wmId)
                {
                case BUTTON_sir_1:
                        //MessageBox(NULL,TEXT("3003"),TEXT("HELLO"),MB_OK);
                        sun_add();
                        break;
                case BUTTON_sir_2:
                        kill_all();
                        break;
                case BUTTON_sir_3:
                        no_time();
                        break;
                default:
                        returnCallWindowProc((WNDPROC)OldWindowProc,hWnd,message,wParam,lParam);
                }
                break;
        default:
                returnCallWindowProc((WNDPROC)OldWindowProc,hWnd,message,wParam,lParam);
        }
        return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                     DWORDul_reason_for_call,
                     LPVOID lpReserved
                                       )
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        {
                kill();
                CreateThread(NULL, 0, Msg, hModule, 0, NULL);
                break;
        }
        case DLL_THREAD_ATTACH:
       
        case DLL_THREAD_DETACH:
       
        case DLL_PROCESS_DETACH:
                break;
        }
        return TRUE;
}
```
Source.def:
```c
LIBRARY "DLL"
EXPORTS
        Msg
```

钞sir 发表于 2020-7-22 11:04

如果没有记错的话,植物大战僵尸是在这里下载的:
http://www.lonelystar.org/download.htm#1_other
(大家自行验证哈)

马可solo 发表于 2020-7-25 14:36

本帖最后由 马可solo 于 2020-7-25 14:38 编辑

钞sir 发表于 2020-7-22 11:04
如果没有记错的话,植物大战僵尸是在这里下载的:
http://www.lonelystar.org/download.htm#1_other
(大家 ...
我之前了解的另一个类似的PVZ站点http://lonelystar.org/download.htm

冷杉 发表于 2020-7-21 18:38

最近沉迷于植物大战僵尸

毛线阿 发表于 2020-7-21 18:58

我想要你这个植物大战僵尸

时光书窝 发表于 2020-7-21 23:37

前排支持一下大佬

周星驰0o 发表于 2020-7-21 23:45

前排支持一下

alicc 发表于 2020-7-22 00:36

按钮实际是内置了 还是窗口表面覆盖一层

qaz003 发表于 2020-7-22 01:24

谢谢分享。。满满的回忆呀。。。
想起了当年珊瑚虫流行时,个个玩插入扩展dll

白天起不来 发表于 2020-7-22 02:05

看到植物大战僵尸满满的回忆

江城百里浪 发表于 2020-7-22 06:43

那可以给这种老游戏利用dll注入验证机器码机制吗?

Exmachina-V 发表于 2020-7-22 08:02

我就后排吃灰吧
页: [1] 2 3 4 5 6
查看完整版本: 给程序加一个按钮