游戏辅助工具开发教程-从入门到精通之A3_5篇

十二 31st, 2009 | Filed under GameCheat

作者:Figo

上节关于Windows框架的讲解有些仓促,没有尊重认知学规律,所以有疑问的读者可以结合本篇来看。

首先请大家把源码下载到电脑上,然后用VC打开,我下面将对照源码讲解,不再贴源码图。WinMain函数有4个传入参数如下:
int
WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
hInstance-实例句柄,由系统传入。当你双击一个程序时,系统通过PE装载器把该程序装入内存,然后在系统内部,把内存中的和该程序相关的整个部分称作该程序的一个实例,也就是说如果你对于同一个程序执行两次,那么就有两个实例句柄,只不过是在不同的内存中。如果要打比方的话,你可以理解为暂住证,你只有一个你,但是你在北京居住的时候要办暂住证,如果换到深圳,虽然你没有变,但是要重新办理暂住证–这里的暂住证就可以理解为实例句柄:)
hPrevInstance-这个参数在Win32中是个废柴,因为始终为NULL,故不再介绍。
lpCmdLine-以字符串表示的命令参数,比如红警2有一种模式为窗口模式,就是通过传递命令参数-win实现的,见下图:
1
nShowCmd-指定窗口显示方式:正常[SW_SHOWNORMAL],最大化[SW_MAXIMIZE],隐藏[SW_HIDE]等等,具体可选参数参见MSDN,可由程序员指定,如未指定则使用默认参数,取决于进程创建时的启动信息。

进入WinMain函数的第一行代码是:
static TCHAR szAppName[ ] = TEXT(”Model Win Frame”);
注意这个TCHAR和TEXT,前者是数据类型后者是宏定义。这里就涉及到我们要经常打交道的UNICODE字符,还记得我在C++教程中讲ASCII码时候说过的话吧?最初由于没有考虑到语言和符号的广泛性,所以最初的编码方案的容量只有128个符号,后来一些公司和组织联合推出了UNICODE编码方案,思想很简单,就是把容量扩大,由原来的128个符号扩大为65536个,千万不要把它想得太复杂。C语言中为了表示宽字符,重新定义了一种数据类型:
typedef unsigned  short wchar_t;
我们知道unsigned  short的范围是65536;TEXT(  )指示编译器该字符串是宽字符,当然你也可以使用L前缀,如:
static TCHAR szAppName[ ] = L“Model Win Frame”;//L号之间没有空格!!!
两者是等价的。在API函数中末尾有W的表示为宽字符版本,A则表示ASCII版本,如MessageBox函数有MessageBoxW和MessageBoxA两种版本,具体到编译阶段编译器会根据你设定的编译选项来选择具体版本,此处我们可以使用如下语法来指示编译器我们的版本为UNICODE:
#define UNICODE
此时MessageBox为MessageBoxW版本,但如果你程序中强制写成MessageBoxA的时候,编译阶段仍旧是MessageBoxA。因为实现的机制如下:
———传说中的分割线———
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
———传说中的分割线———
对于Windows NT架构来说,在底层全部是UNICODE实现,如果你使用ASCII版本的话,首先会被转化为UNICODE版本然后再执行,这就提醒我们,实际编程中一定要使用UNICODE版本。

继续向下看源码:HWND hwnd;
H是Handle的缩写,WND是Window的缩写,表示此处是定义一个窗口句柄,那么这和前面提到的实例句柄有什么区别呢?确切的说,它们各自表示了一个运行中的程序的不同方面,HWND侧重标识不同的GDI界面,而HINSTANCE侧重于内存中程序的整体表示。我们对于窗口的大部分操作都要通过HWND类型的变量来实现。

定义完窗口句柄紧接着定义消息变量:MSG msg;
MSG是一个结构体,具体定义如下:
typedef struct  tagMSG
{
HWND hwnd;//接收消息的窗口句柄
UINT message;//指定消息号
WPARAM wParam;//附加信息,具体含义取决于消息号
LPARAM lParam;//附加信息,具体含义取决于消息号
DWORD time;//记录消息传递时的时间
POINT pt;//记录消息传递时鼠标的坐标[参照屏幕坐标系]
}    MSG;
随着消息变量的定义结束,引出了窗口类变量定义:WNDCLASS wndclass;
WNDCLASS结构体具体定义如下:
typedef struct tagWNDCLASSW {
UINT style;//指定窗口样式,MSDN2008中提供了12种可选风格,你可以使用|来组合新的样式
WNDPROC lpfnWndProc;//窗口过程的函数指针,即消息处理函数的地址
int cbClsExtra;//指定窗口类结构体后附加字节的数量(BYTE)
int cbWndExtra;//指定窗口实例后附加字节的数量(BYTE)
HINSTANCE hInstance;//实例句柄,由系统传入,实际上是包含窗口过程的实例的句柄
HICON hIcon;//图标句柄,如果不填则使用默认图标
HCURSOR hCursor;//光标句柄,如果不填则使用默认光标
HBRUSH hbrBackground;//客户区背景色刷子句柄,所谓客户区就是你建立的窗口中除了菜单、工具栏和状态栏等以外的区域
LPCWSTR lpszMenuName;//以NULL结尾的资源中表示菜单的字符串
LPCWSTR lpszClassName;//以NULL结尾的窗口类名的字符串,最长为256
} WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW;

在填充完毕窗口类结构体后我们使用RegisterClass函数来注册窗口类,它的声明为:
ATOM RegisterClass(CONST WNDCLASS *lpWndClass);
其中ATOM的定义为typedef unsigned short ATOM;
该函数接收一个窗口类结构体地址,我们把刚才填充完毕的结构体地址传递给函数:如果注册失败,则返回0;如果成功,则返回一个唯一标识该窗口类的数值,该数值可作为参数传递给以下函数[下面函数具体用法参考MSDN]:
CreateWindow
GetClassInfo
FindWindow
UnregisterClass

继续看源码,窗口类注册成功的话,我们就要以该类为模板建立窗口,使用的API为CreateWindow,其声明如下:
HWND CreateWindow(
LPCTSTR lpClassName,//窗口类名字符串
LPCTSTR lpWindowName,//窗口标题
DWORD dwStyle,//指定窗口样式,如是否有标题,是否为子窗口,是否有最大化选项等等
int x,//窗口左上角的点的X坐标
int y,//窗口左上角的点的Y坐标
int nWidth,//窗口宽度
int nHeight,//窗口长度
HWND hWndParent,//父窗口句柄,如果窗口样式中选择了子窗口,那么需要传递该参数
HMENU hMenu,//窗口菜单句柄
HINSTANCE hInstance,//程序实例句柄
LPVOID lpParam//填充CREATESTRUCT结构的中第一个参数的指针,可以在窗口为绘制前更改窗口的一些参数,更改的位置是处理WM_CREATE消息的地方,在演示程序中我只是显示一个小对话窗,我们可以在此处修改窗口位置等,部分代码见下
);
结构体CREATESTRUCT的定义如下:
typedef struct tagCREATESTRUCTW {
LPVOID lpCreateParams;//即为上面的lpParam
HINSTANCE hInstance;
HMENU hMenu;
HWND hwndParent;
int cy;
int cx;
int y;
int x;
LONG style;
LPCWSTR lpszName;
LPCWSTR lpszClass;
DWORD dwExStyle;
} CREATESTRUCTW, *LPCREATESTRUCTW;

———传说中的分割线———
…  …
switch(msg)
{
case WM_CREATE:
{
CREATESTRUCT *lpcs = (LPCREATESTRUCT)lParam;
lpcs->x = iNewX;
lpcs->x = iNewY;
}
return 0;

case WM_PAINT:
…  …
———传说中的分割线———

如果窗口创建成功则返回窗口句柄,否则返回NULL。CreateWindow函数调用执行之前,系统会传递WM_CREATE消息给窗口过程。执行完该函数就已经在内存中分配了空间,只是没有显示出来。

我们继续看源码,窗口创建完成还需要指定显示方式,此时调用下面函数:
ShowWindow(hwnd, nShowCmd);
第一个参数是窗口句柄,第二个是显示方式,我们也可以自己修改,比如有的木马会传递SW_HIDE给第二个参数,这样的话程序即使执行用户也看不到。
此函数调用完毕只是指定了窗口显示的方式,为了显示窗口还需要执行:
UpdateWindow(hwnd);
此函数的作用在于产生WM_PAINT消息,让窗口的绘制过程开始。绘制的具体实现我会在后续章节讲解。希望这次的补充能帮助大家理解Windows框架的来龙去脉。

转载请注明出处:http://www.figoyao.com/blog/2009/12/30/285/

标签: , , ,
  1. 小星
    五 12th, 201020:14

    //www.figoyao.com
    //copyleft(C)Figo Yao
    #include “resource.h”
    #include
    #include
    #include

    #pragma comment(lib,”winmm.lib”)

    #define ID_TIMER_ONE 10250

    DWORD g_dwPreGamePID = 0,g_dwCurGamePID = 0;
    BOOL g_bFirstRun = TRUE, g_bGameStart = FALSE;
    char g_strGameName[] = “Game.exe”;
    HICON g_hIcon = NULL;
    const int g_iTime = 1000;

    BOOL CheatGame(DWORD dwAddress,BYTE byData);
    DWORD GetGamePID(char strGameName[]);
    BOOL WINAPI DlgProcMain(HWND,UINT,WPARAM,LPARAM);

    int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
    {
    g_hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_ICON_MAIN);

    int nResult = DialogBoxParam(hInstance,(LPCTSTR)IDD_DIG_MAIN,NULL,DlgProcMain,NULL);

    return 0;
    }

    BOOL WINAPI DlgProcMain(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
    {
    static DWORD dwPID = 0;

    switch (msg)
    {
    case WM_INITDIALOG:
    {
    SendMessage(hwnd, WM_SETICON,ICON_BIG,(long)g_hIcon);
    SetTimer(hwnd, ID_TIMER_ONE, 200, NULL);
    }
    break;
    case WM_TIMER:
    {
    switch(wParam)
    {
    case ID_TIMER_ONE:
    {
    g_dwCurGamePID = GetGamePID(g_strGameName);
    if (g_dwCurGamePID)
    {
    if (g_bFirstRun)
    {
    g_bFirstRun = FALSE;
    g_dwPreGamePID = g_dwCurGamePID;
    CheatGame(0×4e48f9,0×03);
    SetWindowText(GetDlgItem(hwnd,IDC_STATIC_STATE),TEXT(”-=无限金钱=-”));
    }

    else if (g_dwPreGamePID != g_dwCurGamePID)
    {
    CheatGame(0×4e48f9,0×03);
    g_dwPreGamePID = g_dwCurGamePID;
    SetWindowText(GetDlgItem(hwnd,IDC_STATIC_STATE),TEXT(”-=无限金钱=-”));
    }
    }
    else
    {
    if(g_bGameStart)
    {
    PlaySoundA((LPCTSTR)IDR_WAVE_SYS,NULL,SND_RESOURCE | SND_ASYNC);
    g_bGameStart = FALSE;
    }
    SetWindowText(GetDlgItem(hwnd,IDC_STATIC_STATE),TEXT(”游戏没有启动”));
    }
    }
    break;
    }
    }
    break;

    case WM_COMMAND:
    switch(LOWORD(wParam))
    {
    case ID_BTN_EXIT:
    {
    EndDialog(hwnd, 0);
    KillTimer (hwnd, ID_TIMER_ONE);
    }
    break;
    case ID_BTN_BLOG:
    {
    ShellExecute((HWND)GetModuleHandle(NULL), “open”, “iexplore.exe”, “http://www.figoyao.com/blog”, NULL, SW_SHOWNORMAL);
    }
    break;
    }
    break;

    case WM_CLOSE:
    {
    EndDialog(hwnd, 0);
    KillTimer (hwnd, ID_TIMER_ONE);
    }
    break;
    }

    return 0;
    }

    BOOL CheatGame(DWORD dwAddress,BYTE byData)
    {
    HANDLE hProGame = OpenProcess(PROCESS_ALL_ACCESS, NULL, g_dwCurGamePID);

    if(hProGame)
    {
    if(WriteProcessMemory(hProGame, (LPVOID)dwAddress, &byData, sizeof(BYTE), NULL))
    {
    PlaySoundA((LPCTSTR)IDR_WAVE_INTEL,NULL,SND_RESOURCE | SND_ASYNC);
    g_bGameStart = TRUE;
    return TRUE;
    }
    else
    return FALSE;
    }

    return FALSE;
    }

    DWORD GetGamePID(char strGameName[])
    {
    HANDLE hProcessSnap;
    PROCESSENTRY32 pe32;

    hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    if( hProcessSnap == INVALID_HANDLE_VALUE )
    {
    MessageBox(NULL,TEXT(”快照函数出错”),TEXT(”发生错误”),MB_OK|MB_ICONERROR);
    return 0;
    }

    pe32.dwSize = sizeof( PROCESSENTRY32 );

    if( !Process32First( hProcessSnap, &pe32 ) )
    {
    MessageBox(NULL,TEXT(”快照数据结构出错”),TEXT(”发生错误”),MB_OK|MB_ICONERROR);
    CloseHandle( hProcessSnap );
    return 0;
    }

    do
    {
    if (strcmp(strGameName, pe32.szExeFile) == 0)
    {
    return pe32.th32ProcessID;
    }
    } while(Process32Next(hProcessSnap, &pe32 ));

    return 0;
    }
    教程里很多代码,源代码里看不到。