首页
论坛
课程
招聘
[原创]给程序加一个按钮
2020-7-22 11:12 4192

[原创]给程序加一个按钮

2020-7-22 11:12
4192

简介

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

思路

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

编写DLL

创建按钮

因为我们主要目的是添加一个按钮功能,使用这里主要用到的函数是CreateWindow)...

 

Syntax:

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函数来获取,例如:

#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),进行需要进行的消息处理,其他的交给旧的消息处理函数:

LRESULT CALLBACK NewWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
OldWindowProc = GetWindowLong(hWndParent,GWL_WNDPROC);
SetWindowLong(hWndParent,GWL_WNDPROC,(LONG)NewWndProc);

新的消息处理函数中只需要写我们感兴趣的消息,比如这里我们只关心按键增加阳光的功能,其余我们没有写的消息处理通过CallWindowProc函数交给原来的消息处理函数处理:

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:
            return  CallWindowProc((WNDPROC)OldWindowProc,hWnd,message,wParam,lParam);
        }
        break;
    default:
        return  CallWindowProc((WNDPROC)OldWindowProc,hWnd,message,wParam,lParam);
    }
    return 0;
}

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

DLL注入

基本步骤是:

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

dll注入的代码确实网上确实到处都是,我这里直接贴出参考,用的是CreateRemoteThread的方法;

效果

12

dll代码

// 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[1024] = { 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[200] = {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[0];
    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[1024] = { 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:
            return  CallWindowProc((WNDPROC)OldWindowProc,hWnd,message,wParam,lParam);
        }
        break;
    default:
        return  CallWindowProc((WNDPROC)OldWindowProc,hWnd,message,wParam,lParam);
    }
    return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_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:

LIBRARY "DLL"
EXPORTS
    Msg

[看雪官方]《安卓高级研修班》线下班,网课(12月)班开始同步招生!!

收藏
点赞2
打赏
分享
最新回复 (9)
雪    币: 3
活跃值: 活跃值 (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zylrocket 活跃值 2020-7-22 13:46
2
0
不错
雪    币: 4813
活跃值: 活跃值 (455)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
白菜大哥 活跃值 2020-7-22 23:29
3
0
精彩,不过有个疑问,就是这样写界面太费劲,不知可否把qt的dll注入进去,现实qt的窗口?
雪    币: 3580
活跃值: 活跃值 (137)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
MsScotch 活跃值 2020-7-23 15:11
4
0
挺好的方法
雪    币: 2205
活跃值: 活跃值 (264)
能力值: ( LV11,RANK:185 )
在线值:
发帖
回帖
粉丝
MTRush 活跃值 1 2020-7-23 17:12
5
0
学习了
雪    币: 571
活跃值: 活跃值 (77)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lylxd 活跃值 2020-7-24 13:03
6
0
学习
雪    币: 2910
活跃值: 活跃值 (144)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
iamasbcx 活跃值 2020-7-25 05:59
7
0
绘制+快捷键方便 
雪    币: 30
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
月影湖 活跃值 2020-8-5 08:22
8
0
学习学习
雪    币: 5819
活跃值: 活跃值 (479)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
黑洛 活跃值 1 2020-8-15 19:25
9
0
白菜大哥 精彩,不过有个疑问,就是这样写界面太费劲,不知可否把qt的dll注入进去,现实qt的窗口?
qt的绘制逻辑是封闭的,注入dll以后处理绘制逻辑需要修改qt的源代码,比较复杂。
雪    币: 520
活跃值: 活跃值 (75)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
闲人_ 活跃值 2020-9-2 21:31
10
0
mark
游客
登录 | 注册 方可回帖
返回