关于魔兽改键。。。类似11的
来源:互联网 发布:蓝色妖姬 js特效 编辑:程序博客网 时间:2024/04/30 21:07
事情是这样的……闲的无聊玩了一盘卡尔的DOTA,结果发现卡尔的按键真的是十分XX啊……于是就在各个网站找那种类似于11的魔兽改键工具,不是改快捷键,而是直接按照技能的位置来改键的。可是没有找到,而11的改键也只能适用于网络对战。于是就萌生了自己写一个改键的想法。
做法当然是用HOOK了。一开始选用的是HOOK按键然后再POSTMESSAGE鼠标消息。结果发现不知道为什么魔兽不能立即响应鼠标的消息,导致画面上必须出现鼠标移动到面板上在移回来的过程,这十分影响操作的……
后来也想过11是怎么做到的,用SPY++看了,也没有发现他修改了什么消息,后来突然顿悟到他可能是通过修改网络消息做的,于是作罢。最后,我开始在内存上打主意了。具体想法是,读取魔兽的内存判断当前的面板上技能的快捷键是多少,然后再用改键。
废话了这么多,下面才是正题。因为是第一次hack别的程序的内存,用的是CheatEngine,所以,收获了不少的经验,这里记录下来:
要实现上面提出的目标要解决两个问题
1.什么地址表示技能的快捷键。
2.从那里可以到达这个地址。
具体的做法就是:
1.不断改变技能(同时就改变了快捷键、比如卡尔),利用CE中寻找变动的数值、寻找未变动的数值等功能,找到内存中和这个改变最相关的地址。因为现在程序中用了许多指针、结构体等等的方法,所以找到的这个最相关的地址可能内容不是数据而是某个指针。
2.通过查看“什么指令访问、修改了这个地址”功能,向上追溯,可以找到访问到这个地址的基指针以及偏移,再从这个基指针入手,查找存放这个值的地址,用之前的方法,不断向上找,直到找到一个处于常量区不变的基址,因为这个在每次游戏载入的时候是固定的,所以加上之前用的偏移以及一些猜测(有时候偏移不会是固定的,有可能碰到的是二维数组之类的东西,这就靠猜了),得到了一条固定到这条地址的路径。参考CE教程6。这过程中可能会遇到很多不同的地址存着相同的值,这时候就需要用到各种推理(比如访问频率、内存内容、猜测数据结构……)来筛选出真正的基址。基本就是一个DFS的过程。
3.在第一步找到的指针中浏览其指向的内存区域,做出合理的假设、推断(数据类型、相同的不同的内容、字符串、猜结构、找规律……)来猜出到底什么地方才是真正的快捷键数据,这就靠一点运气了。其实在探索的过程中会发现很多有趣有规律的东西。这些东西可以帮助探索。
4.当然要是你会汇编的话最好,这样直接看代码,减少很多不必要的假设(我很水。。。我不太会T_T)。
5.综合2,3,4步骤最后找到一条从模块载入地址加上偏移开始的若干层指针指向快捷键数据。。。就好比我得到的是([XXX]表示XXX地址指向的内容)
[[[[[[[[[[[[[[[Game.dll+ACC3E4]+8]+4]+0]+8]+8]+4]+0]+8]+3C8]+154]+28]+0]+190]+5AC]=第三行第一列的快捷键。
这就是答案了。
过程蛮艰辛的,不过有种做侦探的感觉,每一次小小的成功都带来无比的兴奋啊!!!
我只是新手,这次真的学了不少东西。顺便提一下,CE的教学程序十分棒,值得一试!
这里顺便贴源代码。还是要感谢CSDN上各种大神的代码啊,这里借用了好多,不一一感谢了,仅以开源以致感谢!!!
不过还有个问题,就是我的代码在VS2010中执行的时候是没有问题的。但是生成的程序自己单独执行就会碰到读不到内存的问题??不知道为什么,求大神指教啊,运行环境win7,是不是什么UAC权限问题?至今未解决。。。
// changeWar3KeyCmd.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h" #include <Windows.h> #include <stdio.h> #include <stdarg.h> #include <ctype.h> #include <WinError.h> #include "TLHelp32.h" //发送消息以及钩子HINSTANCE g_Instance; // Handler of current instance HHOOK g_Hook; // Handler of hook HWND m_war3hWnd; //读取内存需要的DWORD pid=NULL;//魔兽进程IDbool g_bPrivilegeImproved=false; //标记特权是否已提升HANDLE hProcess=NULL; //魔兽进程句柄bool readReady=false; //是否准备好读取BOOL SetHook(); BOOL UnSetHook(); // The hook function (will be called by other processes) static LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); ////////////////////////////////////////////////////////////////////////// //提高特权,使本进程具备debug特权bool ImprovePrivilege(){HANDLE hTokenHandle=NULL;OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hTokenHandle);int bChangePrivSuc=false;if(hTokenHandle){TOKEN_PRIVILEGES tp;LUID luid;int bRes = LookupPrivilegeValue(NULL, TEXT("SeDebugPrivilege"), &luid);if(bRes) //读取luid成功{tp.PrivilegeCount=1;tp.Privileges[0].Luid = luid;tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;bChangePrivSuc = AdjustTokenPrivileges(hTokenHandle,false,&tp,sizeof(tp), NULL, NULL);}}if(bChangePrivSuc)g_bPrivilegeImproved = true;elseg_bPrivilegeImproved=false;return bChangePrivSuc;}bool findGameDll();//判断是否获得读内存权限bool getReadReady(){if(m_war3hWnd==NULL) return false;if (pid==NULL)GetWindowThreadProcessId(m_war3hWnd,&pid); //获取进程的IDfindGameDll();if(!g_bPrivilegeImproved) //如果特权没有提升,则提升权限,否则无法读取魔兽内存ImprovePrivilege();if(g_bPrivilegeImproved) //权限提高成功hProcess = OpenProcess(PROCESS_ALL_ACCESS,false,pid); //打开进程elsereturn false;if (hProcess) return true;else return false;}int gameDllBase=0x6F000000; //game.dll 装入的基地址,应对windows内存随机化保护机制bool findGameDll(){ //寻找game.dll装入地址HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,pid);if (hSnap!=INVALID_HANDLE_VALUE ){MODULEENTRY32 lpme;lpme.dwSize = sizeof(MODULEENTRY32);BOOL bRet = ::Module32First(hSnap, &lpme); while (bRet) { if (!wcscmp((lpme.szModule),TEXT("Game.dll"))) { gameDllBase=(int)lpme.modBaseAddr;// printf("got base: %X !\n",gameDllBase); break;}bRet=::Module32Next(hSnap, &lpme); }::CloseHandle(hSnap);}return 0;}#define HAD_curPanBase (0x00ACC3E4+gameDllBase)#define HAD_charPanBase (0x00AE8450+gameDllBase)#define HAD_getBaseThis (readMemery(readMemery(readMemery(readMemery(readMemery(HAD_curPanBase)+0x08)+0x04)+0x00)+0x08)) //当前选中单位面板的数据的内存地址#define HAD_getSkillPan_LV1 (readMemery(readMemery(readMemery(readMemery(HAD_getBaseThis+0x08)+0x04)+0x00)+0x08))#define HAD_getSkillPan (readMemery(readMemery(readMemery(HAD_getSkillPan_LV1+0x3C8)+0x154)+0x18)) //当前选中单位面板的数据的内存地址#define HAD_getShilPan_HotKey(a,b) (readMemery(readMemery(readMemery(HAD_getSkillPan+44-24*(a)+(b)*4)+0x190)+0x5AC)) //a是行,b是列 a=1-3 b=1-4,快捷键内存地址#define CHAT_ADDRESS_24E (HAD_charPanBase) //1.24E 版本聊天状态的内存地址//读取内存long int readMemery(long int address){if (address==0) return address;if (!hProcess) { //魔兽没有打开if (!getReadReady()){return 0;}}int res=0;int buf = 0;//存储读取的内存地址res = ReadProcessMemory(hProcess,(LPVOID)address ,&buf,4,NULL); //读取内存if(res!=0) {return buf;}else return 0;}int getVK_HotKey(int a,int b){int pointer;pointer= HAD_getShilPan_HotKey(a,b); return pointer;}DWORD skills[6]={87,69,82,70,84,68}; //W E R F T DDWORD goods[6]={81,89,71,VK_TAB,VK_SPACE,66}; //Q Y G Tab SPACE B int vk[]={VK_NUMPAD7,VK_NUMPAD8,VK_NUMPAD4,VK_NUMPAD5,VK_NUMPAD1,VK_NUMPAD2};int skillsPos[6][2]={ 3,1, 3,2, 3,3, 3,4, 2,2, 2,3};char writeTemplate[]={ "**This file is config file of change War3KeyCmd**\r\n" "\r\n" "[SkillHotKey]\r\n" "pos[2,2]=T\r\n" "pos[2,3]=D\r\n" "pos[3,1]=W\r\n" "pos[3,2]=E\r\n" "pos[3,3]=R\r\n" "pos[3,4]=F\r\n" "\r\n" "[PackageHotKey]\r\n" "pos[Num7]=Q\r\n" "pos[Num8]=Y\r\n" "pos[Num4]=G\r\n" "pos[Num5]=TAB\r\n" "pos[Num1]=SPACE\r\n" "pos[Num2]=B\r\n" "\r\n" "**File end! ^_^ !**\r\n"};DWORD analyzeKey(char *chBuffer,bool &isRight){if (chBuffer[0]=='T'){if ((chBuffer[1]=='A')&&(chBuffer[2]=='B')){isRight=true;return VK_TAB;}else {isRight=true;return (DWORD)chBuffer[0];}}else if (chBuffer[0]=='S'){ if ((chBuffer[1]=='P')&&(chBuffer[2]=='A')&&(chBuffer[3]=='C')&&(chBuffer[4]=='E')){isRight=true;return VK_SPACE;}else {isRight=true;return (DWORD)chBuffer[0];}}else if ((chBuffer[0]>='A')&&(chBuffer[0]<='Z')){isRight=true;return (DWORD)chBuffer[0];}else if (chBuffer[0]==' '){chBuffer++;return analyzeKey(chBuffer,isRight);}else return 0;}bool readKey(){ //从配置文件读取按键配置HANDLE file=0;file=::CreateFile(TEXT("war3keyCmd.ini"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); //文件不存在则创建if ( file == INVALID_HANDLE_VALUE) { MessageBox( NULL,TEXT("打开文件失败") ,TEXT("Error"),MB_OK); CloseHandle(file); // 一定注意在函数退出之前对句柄进行释放。 return false; } DWORD fileSize=GetFileSize(file,NULL);if (fileSize==0){ //才创建的文件int len=strlen(writeTemplate); unsigned long b; WriteFile(file,writeTemplate,len,&b,NULL);CloseHandle(file);file=::CreateFile(TEXT("war3keyCmd.ini"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); //文件不存在则创建}fileSize=GetFileSize(file,NULL);char *chBuffer=new char[fileSize];::ZeroMemory(chBuffer,fileSize);unsigned long b; int bRet= ::ReadFile(file,chBuffer,fileSize,&b,NULL);if (!bRet){ MessageBox( NULL,TEXT("读取文件失败") ,TEXT("Error"),MB_OK); CloseHandle(file); // 一定注意在函数退出之前对句柄进行释放。 return false;}else{ bool isRight=false; char pattern[]="pos[1,3]"; for (int i=0;i<6;i++){ pattern[4]=(char)(skillsPos[i][0])+'0'; pattern[6]=(char)(skillsPos[i][1])+'0'; char *temp=strstr(chBuffer,pattern); temp+=9; skills[i]=analyzeKey(temp,isRight); } char pattern1[]="pos[Num7]"; for (int i=0;i<6;i++){ pattern1[7]=7-(i/2)*3+i%2+'0'; char *temp=strstr(chBuffer,pattern1); temp+=10; goods[i]=analyzeKey(temp,isRight); } CloseHandle(file); return isRight;}}LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if(m_war3hWnd==NULL)m_war3hWnd = ::FindWindow(TEXT("Warcraft III"),NULL); HWND ahWnd = GetForegroundWindow(); //当前激活的窗口 if (ahWnd!=m_war3hWnd) return CallNextHookEx(NULL,nCode,wParam,lParam); if(nCode<0) return CallNextHookEx(NULL,nCode,wParam,lParam); if (readMemery(CHAT_ADDRESS_24E)) return CallNextHookEx(NULL,nCode,wParam,lParam); KBDLLHOOKSTRUCT* kb = (KBDLLHOOKSTRUCT*)lParam; if (!kb) return CallNextHookEx(NULL,nCode,wParam,lParam); DWORD key=kb->vkCode; if (key==VK_F12) { //表示刷新配置数据 readKey(); return true; } //技能 for (int i=0;i<6;i++){if (key==skills[i]){ if(wParam==WM_KEYDOWN){ int key=getVK_HotKey(skillsPos[i][0],skillsPos[i][1]); // if ((key>=0x41)&&(key<=0x5a)){ PostMessage(m_war3hWnd, WM_KEYDOWN,key,0); //postMessage不会重复截获了 PostMessage(m_war3hWnd, WM_KEYUP,key,0); }}return true; //结束这个消息的传递 } }//物品栏 for(int i=0;i<6;i++) { if (key==goods[i]) { if(wParam==WM_KEYDOWN){ //产生一次单击事件 用postMessage不会重复截获PostMessage(m_war3hWnd, WM_KEYDOWN,vk[i],0); //按下 } if(wParam==WM_KEYUP){ PostMessage(m_war3hWnd, WM_KEYUP,vk[i],0); //松开 } return true; //结束这个消息的传递 } } // Call next hook in chain return ::CallNextHookEx(g_Hook, nCode, wParam, lParam); } HMODULE base;BOOL WINAPI DllMain( HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){if(fdwReason==1)base=hinstDLL;return 1;} BOOL SetHook() { if (g_Instance || g_Hook) // Already hooked! return TRUE; g_Instance = (HINSTANCE)::GetModuleHandle(NULL); g_Hook = ::SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)KeyboardProc,base,0); //这个是对的if (!g_Hook) { printf("Hook set wrong! %ld\n",GetLastError());Sleep(1000); return FALSE; } return TRUE; // Hook has been created correctly } BOOL UnSetHook() { if (g_Hook) { // Check if hook handler is valid ::UnhookWindowsHookEx(g_Hook); // Unhook is done here g_Hook = NULL; // Remove hook handler to avoid to use it again } return TRUE; // Hook has been removed } int _tmain(int argc, _TCHAR* argv[]) { readKey(); if (!SetHook()) return -1; MSG msg; while(::GetMessage(&msg,NULL,0,0)> 0) { TranslateMessage(&msg); DispatchMessage(&msg); } //clean: UnSetHook(); return 0; }
- 关于魔兽改键。。。类似11的
- 魔兽改键
- 魔兽改键自己做
- 魔兽改键程序修改
- 简单魔兽改键的基本原理及基于MFC实现
- 强大的魔兽改键--可以像11对战平台一样,自动进入BN!支持命令改键;
- 关于魔兽的笑话
- vc实现魔兽3改键程序
- 发一份魔兽改键工具的代码,vc6.0环境,带聊天模式识别功能
- 关于魔兽的电脑优化配置方法
- 关于魔兽守卫军的改进建议
- 魔兽改键助手1.2(附VC源代码)
- c#实现魔兽(warIII)中显血和改键功能
- 关于魔兽 关于sky
- 左手 右手--关于魔兽的小说(1-4)转贴
- 关于VS上魔兽私聊的问题
- c#实现魔兽(warIII)中显血和改键功能 (附源码)(不影响聊天打字)
- c#实现魔兽(warIII)中显血和改键功能 (附源码)(代码参考sohighthesky)
- vim插件使用简介
- Android 把视图转换为图片,截取屏幕
- 随机事件学习笔记
- 关于函数strtok和strtok_r的使用要点和实现原理(一)
- 数据库事务、隔离级别及锁
- 关于魔兽改键。。。类似11的
- 学生管理系统 宏 出错 名字查找很诡异 scanf() getchar() 用法
- 基于STK的星地光通信链路仿真
- [探讨]开发者眼中的PhoneGap体验
- Jlink百科
- C++对象序列化方案对比
- 开始研究OpenSceneGraph(OSG)
- etc/ld.so.conf的使用说明
- 从Log4j迁移到LogBack的理由