记录一些学习小事

Work hard

统计

留言簿

阅读排行榜

评论排行榜

spy++和游戏修改器

这几天做了两个东西,spy++ 和游戏修改器。 spy++ 就是模仿 vs的那个工具spy++ 。游戏修改器,就是暴力搜索内存,找到我们关心的数据,然后进行更改。
总之这些东西做过之后感觉就是都不难,但是在做的时候多少会感觉点吃力。唉,功力不够啊。继续修炼……
闲下来无事,记录下它们的过程吧。
spy++ 分析(用vs2005做的——)
spy++ 需要拖动一个小图标 然后移动到别的窗口上(本窗口也可以),然后画出窗口的矩形,得到窗口的一些信息。
拖动的这个小图标,是一个picture conctrl控件 ,属性的type改成icon 也就是加载一个icon图标,在属性imag里把图标选上。
给这个控件添加一个类,在这个类里做这个控件的 小图标拖动操作,即 lbuttondown的时候 把鼠标加载成 小图标,把picture里的icon换成空心的。
当鼠标抬起的时候把鼠标换回来原先的光标,然后再把picture里的icon换成有小图标的那个图标。
这里要主次 要把鼠标设成全局有效,要不然的话,鼠标位置出了这个picture的范围就失效了。函数是 SetCapture()
然后再lbuttonup的时候记得 要释放。ReleaseCapture();
           就是这几个图标。吼吼。
把这个小图标托到 窗口上的时候怎么在窗口上画出矩形呢? 
当然是先得到这个矩形了。我要先得到这个窗口的句柄,我们才能得到windowrect 然后才能画。
所以各 下一步就是WindowFromPoint(Point)传一个鼠标点 然后得到这个点的window的hWnd了 哈,很强大的函数啊。
得到窗口句柄就好办了。GetWindowRect得到矩形区域啊,然后用windowdc 画,用windowdc的原因就是,我们要在整个桌面上都能画。
这里画这个矩形有个技巧,我们要画出来,但是当我们不需要这个矩形的时候我们还要给这个颜色画回原来的背景色。这里算是整个程序的一个难点,考验画图工夫。
dc设备中默认选入的是一个黑色画笔,一个白色画刷,也就是说正常我们画矩形的话会画出黑边白底得矩形,现在我们要透明的底,然后画的黑边还要可画回原先的背景色。有一个函数通过dc调用  dc.SetROP2(R2_NOTXORPEN);参数传这个同或运算,这个函数的意思就是,拿你设备中的画笔画刷 去跟背景色运算,同或运算。
比如背景有个颜色是10010100011   你的画刷颜色是白色 也就是111111111111(比如就这么多位)  白色因为是255,255,255所以都是1  
然后进行同或运算得到的是什么?是10010100011(同或,同则1,不同则0)!也就说 还是背景色,这就做到了画刷透明。然后画笔是黑色,0000000000
跟背景色同或运算的到得是01101011100 这个。然后这个就是真正画到屏幕上显示出来的颜色。怎么在把这个颜色画没呢?大家在用这个黑色的画笔画一下,在同或运算看看结果是什么?10010100011 看看是不是又变回去了?吼吼。等会下面贴代码画图这里的。
这个画图就完事了。而且窗口句柄我们也通过windowfrompoint得到了。有了窗口句柄我们能得到很多东西了。
比如说窗口类名GetClassName
窗口标题GetWindowText
窗口矩形GetWindowRect
窗口id等信息GetWindowLong
进程id   GetWindowThreadProcessId
进程路径 OpenProcess()   GetModuleFileNameEx();

还可以向窗口中发消息 sendmessage(这个比较好玩,可以拿你的spy++去关闭别人的窗口。
然后把他设置到窗口上就可以了。
代码贴一小段。

void CMyPic::OnTimer(UINT_PTR nIDEvent)
{
    POINT p;
    ::GetCursorPos(
&p);
    hWnd
=::WindowFromPoint(p);
    CRect rect;
    ::GetWindowRect(hWnd,
&rect);
    CWindowDC dc(NULL);
    CPen redpen(PS_SOLID,
3,RGB(255,0,0));
    dc.SelectObject(
&redpen);
    dc.SetROP2(R2_NOTXORPEN);
    dc.Rectangle(rect);
    
//显示回去 就向没画一样
    Sleep(300);
    dc.Rectangle(rect);
    ReleaseDC(
&dc);
    CStatic::OnTimer(nIDEvent);
}

这个画图在timer里画,能做到 闪动的巨型。(我选的红色画笔,你可以把红色跟背景同或算算,跟黑色是一样的,在画一次就能画回来背景色)
下面的代码是 写我的spy++的第一页的代码

void CMyspyDlg::UpdataWindowNormal(HANDLE hWnd)
{
 CString str;
 int state=((CButton *)GetDlgItem(IDC_CHECK1))->GetCheck();
 //窗口句柄
 if (state==BST_CHECKED)
 {
  str.Format(_T("%p"),hWnd);
 }
 else
 {
  str.Format(_T("%d"),hWnd);
 }
 m_TabCtrl.m_dlg[0].GetDlgItem(IDC_EDIT_HANDLE)->SetWindowText(str);
 //窗口类名
 TCHAR tempTC[50];
 GetClassName((HWND)hWnd,tempTC,49);
 m_TabCtrl.m_dlg[0].GetDlgItem(IDC_EDIT_CLASSNAME)->SetWindowText(tempTC);
 //窗口标题
 ZeroMemory(tempTC,100);
 ::GetWindowText((HWND)hWnd,tempTC,49);
 if (*tempTC==_T('\0'))
 {
  m_TabCtrl.m_dlg[0].GetDlgItem(IDC_EDIT_TITLE)->SetWindowText(_T("无"));
 }
 else
 {
  m_TabCtrl.m_dlg[0].GetDlgItem(IDC_EDIT_TITLE)->SetWindowText(tempTC);
 }
 //窗口矩形
 CRect rect;
 ::GetWindowRect((HWND)hWnd,&rect);
 str.Empty();
 if (state==BST_CHECKED)
 {
  str.Format(_T("x=%x,y=%x,width=%x,hight=%x"),rect.left,rect.top,rect.Width(),rect.Height());
 }
 else
 {
  str.Format(_T("x=%d,y=%d,width=%d,hight=%d"),rect.left,rect.top,rect.Width(),rect.Height());
 }
 m_TabCtrl.m_dlg[0].GetDlgItem(IDC_EDIT_WINRECT)->SetWindowText(str);
 //窗口id
 str.Empty();
 LONG l=::GetWindowLong((HWND)hWnd,GWL_ID);
 str.Format(_T("%ld"),l);
 m_TabCtrl.m_dlg[0].GetDlgItem(IDC_EDIT_WINID)->SetWindowText(str);
 //进程id
 DWORD dword;
 ::GetWindowThreadProcessId((HWND)hWnd,&dword);
 str.Empty();
 if (state==BST_CHECKED)
 { 
  str.Format(_T("%p"),dword);
 }
 else
 {
  str.Format(_T("%d"),dword);
 }
 m_TabCtrl.m_dlg[0].GetDlgItem(IDC_EDIT_PROCESSID)->SetWindowText(str);
 //程序路径
 HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dword);
 TCHAR src[200];
 GetModuleFileNameEx(hProcess,NULL,src,199);
  m_TabCtrl.m_dlg[0].GetDlgItem(IDC_EDIT_PROSRC)->SetWindowText(src);
}

======================================================================
效果图

======================================================================

好了 下面记录下游戏修改器
其实我用的方法很笨的,但是比较简单。改成功了植物大战僵尸中的太阳值。
=======================================================================
效果图

=======================================================================
这个游戏修改 就是读内存,值进行比较,然后找到一些跟你输入的值相同的地址,记录在链表中,然后让这个值变化,在对链表进行搜索
如果链表中的地址中的值也变化了,正确的地址就在这链表里,继续变化值,之后最后地址确定下来为止,地址确定下来之后就可以修改了。
其中用到得函数 先是 快照,(我在前面做任务管理器中写到过) 得到进程id 进程名等。
然后用openprocess打开方式用要可读,可写,或者获得所有权PROCESS_ALL_ACCESS。得到进程的句柄。
然后通过进程的句柄读进程中的数据ReadProcessMemory因为虚拟内存共有4gb  后2gb是系统用,还有前多少K(不同系统不一样)也系统用。所以我们只搜索前两gb ,但是前面那些系统的我们就忽略了,毕竟是小数,不在乎多读那点了。
ReadProcessMemory这个函数第一个参数是进程handle,第二个参数 是基址(即从那个位置开始读,是一个地址),第三个参数是读出的buff 第四个是读的大小。 由于内存页是4kb 我们为了读的速度快 所以我们每次就读4kb。 读出这4kb 放入buff中 然后 拿我们要找的数值比如是100,(我们假定我们的数是2字节),在4kb中1字节1字节的向后走,两字节两字节的比较 如果等于100则加入链表
DWORD dOneGB=1024*1024*1024;//1gb的地址
 DWORD dBase=0;//基址是0
 DWORD dOnePage=4*1024;//一个内存页4kb
 BYTE buffer[4*1024];//装一个内存也
 WORD value;
 CString str;
 int pos=0;
 POSITION listpos;
//得到我们窗口上输入的值 value
 GetDlgItem(IDC_EDIT_INPUT)->GetWindowText(str);
 value=_wtoi(str);
 for (dBase;dBase<dOneGB*2;dBase+=dOnePage)//遍历2gb每次 加一页  4kb
 {
  if(ReadProcessMemory(hProcess,(LPCVOID)dBase,buffer,4*1024,NULL))//读一个页,因为这个读取是不一定成功的所以加if(有的内存系统不让你读)
  { 
//进行比较 如果相同加入链表CList m_Data;
   for(DWORD b=0;b<dOnePage-1;b++)
   {
    WORD tempword=*((WORD *)(buffer+b));
    if (tempword==value)
    {
     if (pos==0)//如果是头结点就 加在头上,之后就加在后边
     {
      listpos = m_Data.AddHead(dBase+b);
      pos++;
     }
     else
     {
      listpos = m_Data.InsertAfter(listpos,dBase+b);
     }
    }
   }
  }
 }

这样这个第一次搜索就完成了。搜出了一堆 等于100的值 的地址,放入了链表
下次就是   改变100为150,然后再链表中搜索等于150的。
int dataCount=m_Data.GetCount();
 pp=m_Data.GetHeadPosition();
  for (int i=0;i<dataCount;i++)
  {
   DWORD tempvalue=m_Data.GetNext(pp);
//根据基址读出两个字节的数,然后跟value比较
   ReadProcessMemory(hProcess,(LPCVOID)tempvalue,&buff,2,NULL);
   if(buff==value)
   {
   if (head)
   {
    li.AddHead(tempvalue);
    head=FALSE;
   }
   else
   {
    li.AddTail(tempvalue);
   }
   }
  }
哈哈,然后最后就搜到了那个值得地址,,然后根据这个地址改变 其中的值就行了

WriteProcessMemory(hProcess,(LPVOID)address,&newValue,2,NULL);
思路倒是很简单,写的时候总会出错误,或是内存错误或是中断的,要耐心调调。
为了植物大战讲师的无限阳光,吼吼。go


 

posted on 2011-08-09 15:06 陈晓 阅读(3956) 评论(8)  编辑 收藏 引用

评论

# re: spy++和游戏修改器 2011-08-10 10:05 zuhd

改变100为150,然后再链表中搜索等于150的。

这样啊,牛!  回复  更多评论   

# re: spy++和游戏修改器[未登录] 2011-08-10 12:09 heroboy

先用VirtualQueryEx得到内存的属性。有些内存区域可能是代码或者文件的映射,不需要查询。  回复  更多评论   

# re: spy++和游戏修改器[未登录] 2011-08-10 12:50 陈晓

对!这位兄台说的很对,呵呵,当时就为了练读取函数了,没有弄这些细节,如果把你这个条件方里的话会快一些的。@heroboy
  回复  更多评论   

# re: spy++和游戏修改器[未登录] 2011-08-10 12:51 陈晓

恩呢,如果用150搜了之后还是还有好些地址的话,那么就继续改变这个值,在搜,我搜植物大战讲师里的阳光值的话,一般3次就能找到正确的那个。@zuhd
  回复  更多评论   

# re: spy++和游戏修改器 2011-08-10 14:02 coolypf

可以做个金山游侠2012版!  回复  更多评论   

# re: spy++和游戏修改器[未登录] 2011-08-10 14:57 陈晓

@coolypf
额楼上的哥们说笑啦,哈哈,小弟水平不行啊,哈哈。我写的这点东西都是很简单的,难得也不会啊- -我是现学现卖。。。  回复  更多评论   

# re: spy++和游戏修改器 2011-08-10 17:23 他她女鞋

看起来还挺不错的东东。  回复  更多评论   

# re: spy++和游戏修改器[未登录] 2011-08-12 21:41 陈晓

@他她女鞋
谢谢支持…  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理