给程序加一个按钮
# 简介有时候我们会觉得某个已经编译好了的程序的功能不是那么完美,我们想要再添加一些额外的功能,但是我们又没有源码,不方便直接进行修改重编译打包,这时候我们就可以考虑给程序添加一个新功能的按钮了...
# 思路
思路很简单,无非就是利用子类化技术,直接编写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
``` 如果没有记错的话,植物大战僵尸是在这里下载的:
http://www.lonelystar.org/download.htm#1_other
(大家自行验证哈) 本帖最后由 马可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 最近沉迷于植物大战僵尸 我想要你这个植物大战僵尸 前排支持一下大佬 前排支持一下 按钮实际是内置了 还是窗口表面覆盖一层 谢谢分享。。满满的回忆呀。。。
想起了当年珊瑚虫流行时,个个玩插入扩展dll 看到植物大战僵尸满满的回忆 那可以给这种老游戏利用dll注入验证机器码机制吗? 我就后排吃灰吧