外挂框架,使用mingw编译

这半年来,由于女王将我的台式机占用,只能用可怜的macbook来看看网页之类的。因为 macbook比较老,只有2G内存,在windows上只分了20G的磁盘空间,随便装了几个程序,便 没有空间了。

最近在分析一个程序,正好用到了比如补丁,hook之类的,在很早很早之前的程序中,我是 用C++ Builder来做的,但苦于没有空间,也不想去找一个破解的程序了。就决定还是用 mingw+原生的windows api来做吧。

闲话说完了,开始上代码:

编译的方法

CC_PRE = mingw32-
AS=$(CC_PRE)as
CC=$(CC_PRE)gcc
CPP:=$(CC_PRE)g++
LD=$(CC_PRE)gcc
RES=windres

ASFLAGS=-g
CCFLAGS=-g -D _WIN32_IE=0x0500 -D WINVER=0x500
CPPFLAGS=$(CCFLAGS)
LDFLAGS= -lcomdlg32 -lgdi32 -luser32 -Wl,--subsystem,windows hook.dll

OBJECT=resource.o main.o
TARGET=run.exe

all: ready $(TARGET)

clean:
    rm -rf *.o *.res *.exe

ready:
    cd ../dll && make

$(TARGET): $(OBJECT)
    $(LD) $(OBJECT) -o $(TARGET) $(LDFLAGS) 

%.o: %.cpp
    $(CPP) $(CPPFLAGS) -c -o $@ $<

%.o: %.c
    $(CC) $(CCFLAGS) -c -o $@ $<

resource.o: resource.rc resource.h
    $(RES) -i $< -o $@ 

%.o: %.s
    $(AS) $(ASFLAGS) -o $@ $<

上面的是个很简单的exe的编译脚本,dll和上面的类似,只是LDFLAGS一行换成: LDFLAGS= -lcomdlg32 -lgdi32 -luser32 -Wl,–subsystem,windows -shared -Wl,–out-implib,libhook_dll.a

resource我是使用resedit编辑的,上面只有两个按钮,hook和open,

{ PUSHBUTTON “hook”, IDC_BUTTON2, 73, 7, 50, 14, 0, WS_EX_LEFT PUSHBUTTON “open”, IDC_BUTTON1, 15, 9, 40, 11, 0, WS_EX_LEFT }

接下来是exe程序,也很简单的:

#include <windows.h>
#include "resource.h"

/*
* dll中的函数,打开hook窗口
*/
extern "C" __declspec(dllimport) bool EnableHook(DWORD dwThreadId, char* afilename );

/*
* dll中的函数,关闭hook
*/
extern "C" __declspec(dllimport) bool DisableHook();

/*
* 从文件全路径中获取文件名之外的部分,即文件所在的目录,这里简单写个函数
*/
char* ExtractFileDir(char* filename)
{
        static char szFile[MAX_PATH];
        strcpy(szFile, filename);
        char* pos = szFile+ (strlen(szFile) -1);
        while(pos> szFile) {
                if (*pos == '\\' || *pos == '/' || *pos == ':') {
                        *pos = 0;
                        break;
                }
                pos--;
        }
        return szFile;
}
/*
* 结构体,用来管理不同更能的补丁
*/
typedef struct {
        byte* buf; //!< 补丁的地址
        int size; //!< 补丁的字节长度
        void* addr; //!< 补丁的位置
} TPatch;
byte no_check_buf[] = {0xeb,}; //!< 补丁例子1的补丁内容
TPatch no_check = {
        no_check_buf,
        sizeof(no_check_buf)/sizeof(no_check_buf[1]),
        (void*)0x0046bb1e
}; //!< 补丁例子1的结构填充
TPatch no_check_2 = {
        no_check_buf,
        sizeof(no_check_buf)/sizeof(no_check_buf[1]),
        (void*)0x0046c4f8
}; //!< 补丁例子2的结构填充

TPatch* patch[] =
        {
                &no_check,
                &no_check_2
        }; //!< 这样就可以简单的弄个补丁的数组了

/*
* 打开进程, 同时, 在打开进程的时候,对进程进行内存打补丁操作
*/
BOOL OpenProcess(char* filename, HANDLE& process_handle, DWORD& process_threadhandle)
{
        if (filename == 0)
                return FALSE;
        PROCESS_INFORMATION pi;
        STARTUPINFO si;
        si.cb=sizeof(si);
        si.lpReserved=NULL;
        si.lpDesktop=NULL;
        si.lpTitle=NULL;
        si.cbReserved2=0;
        si.lpReserved2=NULL;
        si.dwFlags=STARTF_USEPOSITION;
        si.dwX=0;
        si.dwY=0;

        if (CreateProcess(filename, NULL, NULL, NULL, FALSE, \
                          CREATE_SUSPENDED, NULL, ExtractFileDir(filename), \
                          &si, &pi)) {
                process_handle = pi.hProcess;
                process_threadhandle = pi.dwThreadId;
                for (int i = 0; i < sizeof(patch)/sizeof(patch[1]); i++) {
                        if(WriteProcessMemory(process_handle,patch[i]->addr, \
                                              patch[i]->buf,patch[i]->size, \
                                              NULL)==false
                           ) {
                                char error[2] = {0};
                                MessageBox( NULL, "cann't write", "error", 0);
                        }
                }
                ResumeThread(pi.hThread);
                return TRUE;
        }

        return FALSE;
}
/*
* 打开文件对话框
*/
char* OpenFileDialog(HWND hwnd)
{

        static char szFile[MAX_PATH];
        OPENFILENAME ofn;
        memset(szFile, 0, sizeof(szFile));
        memset(&ofn, 0, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.lpstrFile = szFile;
        ofn.hwndOwner = hwnd;
        ofn.nMaxFile = sizeof(ofn);
        ofn.lpstrFilter = ("Demo.exe\0*.exe\0All files(*.*)\0*.*\0");
        ofn.lpstrFile[1] = 0;
        if(GetOpenFileName((LPOPENFILENAME)&ofn)) {
                return szFile;
        } else {
                return 0;
        }
}
HANDLE process_handle = 0; 
DWORD thread_handle = 0; 

/*
* 主exe的消息循环
*/
BOOL CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        switch(uMsg) {
        case WM_CREATE:
                //MessageBox(hwnd, "Hello", "fdas", 0);
                break;
        case WM_COMMAND:
                switch(LOWORD(wParam)) {
                case IDC_BUTTON1: {
                        //MessageBox(hwnd, "press", "test", 0);
                        if (OpenProcess(OpenFileDialog(hwnd), process_handle, thread_handle) == true) {

                        }
                }
                        break;
                case IDC_BUTTON2: {
                        //MessageBox(hwnd, "press", "button", 0);
                        if (thread_handle == 0) {
                                MessageBox(hwnd, "press", "button ==0", 0);
                        } else {
                        EnableHook(thread_handle, "test");
                        }
                }
                        break;
                }
                break;
        case WM_CLOSE:
                DestroyWindow(hwnd);
                break;
        case WM_DESTROY:
                PostQuitMessage(0);
                break;
                /*default:
                  return DefWindowProc(hwnd, uMsg, wParam, lParam);*/
        }
        return 0;
}

/*
* 入口函数
*/

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nCmd)
{
        DialogBox(hInst, MAKEINTRESOURCE(IDD_DLG1), NULL, WndProc);
        return 0;
}

我尽量写的直观简单,上述代码只做两件事情,打开进程和打开hook功能,接下来是 hook.dll的代码:

#include "windows.h"
#include "stdio.h"
#include "resource.h"
HHOOK g_hHook = NULL; //hook句柄
HINSTANCE DllHinst = NULL; //dll实例
HWND gamehWnd; //进程handle
HANDLE hThread = NULL; //!< 线程handle,用于运行dll中的对话框
HWND wghandle = NULL;
HANDLE gamehandle;
HINSTANCE gameInstance;
DWORD ThreadID;
LRESULT CALLBACK KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam);

/*
* 导出函数,开启hook
*/
extern "C" __declspec(dllexport)bool EnableHook(DWORD dwThreadId, char*
afilename );
/*
* 导出函数,关闭hook
*/
extern "C" __declspec(dllexport)bool DisableHook();

/*
* dll入口函数,标准dll程序
*/
extern "C" BOOL WINAPI DllMain(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
        DllHinst = hinst;
        return 1;
}

extern "C" __declspec(dllexport)bool EnableHook(DWORD dwThreadId, char* afilename )
{
  if (g_hHook == NULL)
  {
    g_hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardHook, DllHinst,
      dwThreadId);
  }
  if (g_hHook)
  {
    return true;
  }
    char temp[3] = {0};
    sprintf(temp, "%d:%d:%d", GetLastError(), DllHinst, dwThreadId);
  MessageBox(NULL, "error", temp, 0);

  return false;
}

extern "C" __declspec(dllexport)bool DisableHook() 
{
  if (g_hHook != NULL)
  {
    UnhookWindowsHookEx(g_hHook);
    g_hHook = NULL; 
    return true;
  }
  return false;
}
BOOL CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        switch(uMsg) {
        case WM_CREATE:
                //MessageBox(hwnd, "Hello", "fdas", 0);
                break;
        case WM_COMMAND:
                switch(LOWORD(wParam)) {
                case IDC_BUTTON1: {
                        MessageBox(hwnd, "press in dll", "test", 0);

                }
                        break;
                case IDC_BUTTON2: {
                        MessageBox(hwnd, "press in dll", "button", 0);
                }
                        break;
                }
                break;
        case WM_CLOSE:
                DestroyWindow(hwnd);
                break;
        case WM_DESTROY:
                PostQuitMessage(0);
                break;
                /*default:
                  return DefWindowProc(hwnd, uMsg, wParam, lParam);*/
        }
        return 0;
}
/*
* 对话框线程
*/
DWORD WINAPI Thread1(PVOID param)
{
        DialogBox(DllHinst, MAKEINTRESOURCE(IDD_DLG1), NULL, WndProc);
  return 1;
}
/*
* 键盘hook回调,123是F12,因此,按下F12后,会弹出dll中的对话框,这个对话框和想要注入的进程是在同一个进程中。可以在这里面对进程做任何事情
*/
LRESULT CALLBACK KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
  if (nCode >= 0)
  {
    if (wParam == 123)
    {
      if (hThread == NULL)
      {
              //MessageBox(NULL, "press2", "button", 0);

        gamehWnd = GetActiveWindow();
        gamehandle =GetCurrentProcess();
        gameInstance = (HINSTANCE)GetWindowLong(gamehWnd, GWL_HINSTANCE);
        hThread = CreateThread(NULL, 0, Thread1, NULL, NULL, &ThreadID);

      }
    }
  }
  return (CallNextHookEx(g_hHook, nCode, wParam, lParam));
}

主要内容已经注释清楚了,代码也是尽可能精简。上述的dll中包含了一个对话框,我用的 是和exe相同的对话框,因此,当成功之后,会弹出两个一样的对话框,不过,点了dll中的 哪个,会弹出上面的messagebox中的提示内容。

dll由于是在目标进程的进程空间中,因此,可以调用目标进程中的任意函数,访问任意内 存变量,可以通过这个dll对目标进程进行任何方便的事情。dll中的对话框是运行在独立的 线程中的,因此,dll中对话框的任何显示并不会对原来的进程造成阻塞或者干扰。

这样编译出来的dll和exe都非常小,按照我的哪个makefile脚本,两个加起来大约100KB左 右,如果去掉调试的-g,并添加-O2或者-O3的话,可能还会更小,而且,仅依赖系统的dll, 不用什么其他的附加库。

上述程序编译出来的exe和dll,在xp上可以直接运行,在win vista/7上,需要使用管理员 权限运行exe,不然的话,无权限对进程进行操作,谁有兴趣的话,可以在exe里面做个提权 的操作。

Footnotes:

1 DEFINITION NOT FOUND: 0

2 DEFINITION NOT FOUND: 20

3 DEFINITION NOT FOUND: 10