“不死鸟”号历险记---和扫雷过不去篇(无厘头版)

“不死鸟”号历险记---和扫雷过不去篇(无厘头版)

 
 

程序逆向初步之二:测试环境Windows XP SP3

 
 
 
(警告:本文情节纯属虚构搞笑,没有讽刺任何现实国家机构的意思哦)
(建议:在看以下文字时,最好头脑浮现鸟山明阿拉蕾中漫画人物的形象以达
到较好的YY效果。)
(还是那句老话,老鸟自行飘过。)

正义的hopy – 加菲

邪恶的hopy – 阿宝

xx年xx月xx日xx国的“不死鸟”号载人火箭直穿入云霄,使得正在云层中YY的鸟A与
鸟B受惊了…
 
鸟A:What东东…
 
鸟B:好大一根…
 
与此同时,在xx国 航天中心的指挥室中…
 
记者A与记者B(以下简称JA与JB)正准备采访指挥人员。
 
一位长相疑似章鱼的指挥人员报告,收到‘不死鸟’号宇航员发出的信号:“我们一切OK,
感觉很Hi!”
 
JA(一滴汗从后脑勺滴下):“连章鱼也…”。
 
JB开始采访:“请问章鱼大叔…哦,不是…请问‘章’工,现在飞船运行情况如何?”。
 
‘章鱼’(眼睛做闪亮状):“废话,当然一切正常,飞船性能没得说,更何况还有我国

最强的双胞胎宇航员牛爱加与牛思减同志…”
 
这时监控镜头切换到正在航天中心食堂拱猪的牛爱加与牛思减…
 
牛A+:“你倒是快拱啊!!!”。
 
牛C-:“急你个姥姥…我就不拱,你咬我”。
 
JA把采访话筒指向‘章鱼’大叔:“您说的是他们两位吗?”。
 
‘章鱼’:“好小子…太不像话了…拱猪也不叫上你老哥我…”。
 
JB(出汗状):“嗯?他们在地上…那在飞船上的是…”。
 
章鱼’:“这个…”。
 
此时在“不死鸟”飞船的船舱中,加菲与阿宝正经八百的坐在驾驶室座位上…
 
阿宝:“终于混上来了…上天的美梦成真喽!”。
 
加菲:“呵呵,到火星还有911天,好无聊哦,干嘛呢?”。
 
阿宝:“傻了吧!幸好偶事先下载了最新的微软超大作游戏---扫雷!”。
 
加菲(倒!)。
 
阿宝:“谁扫雷时间用的少,谁就赢吧”。
 
在加菲连输65536盘之后…
 
阿宝:“没意思,你太挫了!我去WC,你好好再练练吧…嘿嘿”。
 
阿宝哼着小调扭着大屁股闪进后舱WC中…
 
加菲:“我不信!我不信!凭偶的IQ搞不定扫雷!我要方展博,我要方展博,

我不要丁蟹,我不要丁蟹…”。
 
加菲悲愤地用IDA载入winmine.exe,熟练的下载符号文件,展开函数
表,他发现其中一个函数很有意思:

 

 

进入StartGame(),随即的代码让加菲眼前一亮:

 

__stdcall StartGame() proc near ; CODE XREF: FLocalButton(x)+CAp .text:0100367A ; DoPref()+33j .text:0100367A ; MainWndProc(x,x,x,x)+2CAp .text:0100367A ; MainWndProc(x,x,x,x):loc_1001EC8p .text:0100367A ; WinMain(x,x,x,x)+15Dp .text:0100367A mov eax, dword_10056AC .text:0100367F mov ecx, uValue .text:01003685 push ebx .text:01003686 push esi .text:01003687 push edi .text:01003688 xor edi, edi .text:0100368A cmp eax, _xBoxMac .text:01003690 mov _fTimer, edi .text:01003696 jnz short loc_10036A4 .text:01003696 .text:01003698 cmp ecx, _yBoxMac .text:0100369E jnz short loc_10036A4 .text:0100369E .text:010036A0 push 4 .text:010036A2 jmp short loc_10036A6 .text:010036A2 .text:010036A4 ; --------------------------------------------------------------------------- .text:010036A4 .text:010036A4 loc_10036A4: ; CODE XREF: StartGame()+1Cj .text:010036A4 ; StartGame()+24j .text:010036A4 push 6 .text:010036A4 .text:010036A6 .text:010036A6 loc_10036A6: ; CODE XREF: StartGame()+28j .text:010036A6 pop ebx .text:010036A7 mov _xBoxMac, eax .text:010036AC mov _yBoxMac, ecx .text:010036B2 call ClearField() .text:010036B2 .text:010036B7 mov eax, dword_10056A4 .text:010036BC mov _iButtonCur, edi .text:010036C2 mov _cBombStart, eax .text:010036C2 .text:010036C7 .text:010036C7 loc_10036C7: ; CODE XREF: StartGame()+74j .text:010036C7 ; StartGame()+89j .text:010036C7 push _xBoxMac .text:010036CD call Rnd(x) .text:010036CD .text:010036D2 push _yBoxMac .text:010036D8 mov esi, eax .text:010036DA inc esi .text:010036DB call Rnd(x) .text:010036DB .text:010036E0 inc eax .text:010036E1 mov ecx, eax .text:010036E3 shl ecx, 5 .text:010036E6 test byte ptr _rgBlk[ecx+esi], 80h .text:010036EE jnz short loc_10036C7 .text:010036EE .text:010036F0 shl eax, 5 .text:010036F3 lea eax, _rgBlk[eax+esi] .text:010036FA or byte ptr [eax], 80h .text:010036FD dec _cBombStart .text:01003703 jnz short loc_10036C7 .text:01003703 .text:01003705 mov ecx, _yBoxMac .text:0100370B imul ecx, _xBoxMac .text:01003712 mov eax, dword_10056A4 .text:01003717 sub ecx, eax ; int .text:01003719 push edi .text:0100371A mov _cSec, edi .text:01003720 mov _cBombStart, eax .text:01003725 mov _cBombLeft, eax .text:0100372A mov _cBoxVisit, edi .text:01003730 mov _cBoxVisitMac, ecx .text:01003736 mov _fStatus, 1 .text:01003740 call UpdateBombCount(x) .text:01003740 .text:01003745 push ebx ; int .text:01003746 call AdjustWindow(x) .text:01003746 .text:0100374B pop edi .text:0100374C pop esi .text:0100374D pop ebx .text:0100374E retn .text:0100374E .text:0100374E __stdcall StartGame() endp

 

通过简单的大脑分析,加菲很快得出了扫雷的硬性参数限定:
 
初级 9 * 9 共 10 个雷
中级 16 * 16 共 40 个雷
高级 30 * 16 共 99 个雷
自定义 x * y 共 z 个雷 (最大不超过30 * 24 , 667个雷,
最小不小于9 * 9,10个雷) 
 
再经过一番思索,并不十分擅长动脑子的加菲也渐渐理出StartGame中各个变量

的大致意思:
 
_rbBlk(0x1005340)
一个字节全局数组,存放的是每个box的内部表示。
如果字节最高位置1则表示其中有一个雷哦(or 0x80)
bomb(x,y) =_rbBlk[x + y * 32]。x和y下标从1开始,
最大值上面给出了,由于rnd返回的数下标从0开始,故
使用前要加1。
 
_cSec(0x100579C)
已过时间(秒)
 
_cBombStart(0x1005330)
总雷数
 
_cBombLeft(0x1005194)
剩下未扫的雷数
 
_cBoxVisit(0x10057A4)
已揭开的box数
 
_cBoxVisitMac(0x10057A0)
总box数 - 藏有雷的box数
 
接着,加菲用毛茸茸的爪子捏着鼻子,盘算起这段代码的大致含义来:
很明显,代码用当前格子的行数和列数初始化_xBoxMac与_yBoxMac变量,
然后清除“雷区”为下面的“布雷”做准备。后面代码“图懒”调用了
msvcrt.dll中的Rnd函数,可以清楚地看见,Rnd在_xBoxMac与
_yBoxMac的范围内生成地雷的坐标,然后将对应地雷的标志写入
_rgBlk地址开始的结构数组中去,这样不断循环直到未布地雷
个数_cBombStart为0为止。最后代码使用UpdateBombCount
更形地雷数量。
 
知道了这些,加菲觉得有了些胸有成竹、波涛汹涌的感觉。为了快速解决
战斗,加菲采用了一些变量硬编码的技术,他当然知道这个对于不同版本的

扫雷来说就没有通用性喽。

 

//检查当前扫雷进程是否运行 DWORD FindProcess(const char *szName) { DWORD pid = 0; if(!szName) goto QUIT; PROCESSENTRY32 process = {.dwSize = sizeof(PROCESSENTRY32)}; HANDLE hss = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); Process32First(hss,&process); do { if(!strcmp(process.szExeFile,szName)) { pid = process.th32ProcessID; break; } }while(Process32Next(hss,&process)); CloseHandle(hss); QUIT: return pid; } //检查将要fix的扫雷版本是否正确 bool IsSignWM(DWORD pid) { bool bSuccess = false; HANDLE hp = NULL; if(!pid) goto QUIT; byte bin[SINGSIZE]; hp = OpenProcess(PROCESS_ALL_ACCESS,false,pid); if(!hp) goto QUIT; if(!ReadProcessMemory(hp,(LPCVOID)SIGNADDR,bin,sizeof(bin),/ NULL)) goto QUIT; if(memcmp(bin,sign,SINGSIZE) == 0) bSuccess = true; QUIT: if(hp) CloseHandle(hp); return bSuccess; } //将已过时间清零 void ZeroTime(DWORD pid) { HANDLE hp = OpenProcess(PROCESS_ALL_ACCESS,false,pid); if(!hp) goto QUIT; int ZeroSec = 0; if(!WriteProcessMemory(hp,(LPVOID)p_cSec,&ZeroSec,sizeof(int),/ NULL)) goto QUIT; puts("Zero Time Success!"); QUIT: if(hp) CloseHandle(hp); return; } //显示地雷的布局 void ShowBombs(DWORD pid) { HANDLE hp = OpenProcess(PROCESS_ALL_ACCESS,false,pid); if(!hp) goto QUIT; int xMax,yMax,BombCount; if(!ReadProcessMemory(hp,(LPCVOID)p_xBoxMac,&xMax,sizeof(int),/ NULL)) goto QUIT; if(!ReadProcessMemory(hp,(LPCVOID)p_yBoxMac,&yMax,sizeof(int),/ NULL)) goto QUIT; if(!ReadProcessMemory(hp,(LPCVOID)p_cBombStart,&BombCount,/ sizeof(int),NULL)) goto QUIT; printf("Bomb Count is %d , xmax is %d , ymax is %d/n",/ BombCount,xMax,yMax); //byte Bombs[xMax][yMax]; //byte Bombs[9][9]; byte *Bombs = (byte*)malloc((xMax<<5)+yMax+1); if(!Bombs) goto QUIT; //DWORD tmp = (DWORD)p_rbBlk + 0x21; if(!ReadProcessMemory(hp,(LPCVOID)p_rbBlk,Bombs,(xMax<<5)+yMax+1,/ NULL)) goto QUIT; for(int x = 1;x <= yMax;++x) { for(int y = 1;y <= xMax;++y) { if(Bombs[(x<<5)+y]/*Bombs[x][y]*/ & 0x80) putchar('*'); else putchar('N'); } puts(""); } QUIT: if(hp) CloseHandle(hp); if(Bombs) free(Bombs); return; }

 

作为收尾工作,加菲写了一个控制台的command代码去解决人机交互问题:

 

int main(void) { if(!SetDebugPrivilege(true)) puts("Set Debug Privilege Failed!"); DWORD pid = FindProcess("winmine.exe"); if(!pid) { pid = FindProcess("MineSweeper.exe"); if(!pid) { puts("winmine.exe is not running!"); goto QUIT; } } if(!IsSignWM(pid)) { puts("error ver of winmine.exe!"); goto QUIT; } int c; do { puts("How Do You Want Play With WinMine???"); printf("v: show bombs view z: zero time q: quit :: "); c = getchar(); switch(c) { case 'v': case 'V': printf("ShowBombs At %p/n",(void*)ShowBombs); ShowBombs(pid); break; case 'z': case 'Z': ZeroTime(pid); break; case 'q': case 'Q': puts("Quit! Have Funs..."); goto QUIT; break; default: puts("unknow command , try again."); break; } fflush(stdin); }while(true); QUIT: fflush(stdin); (void)getchar(); return 0; }

 

 

加菲(用力擦了一把头上的汗):“搞定了,等会还不赢死你...嘿嘿...咦...怎么阿宝

去WC这么长时间都没回来,搞什么飞机???”。
 
加菲走到太空床前,一屁股睡倒。他想边躺着边等阿宝,结果一不小心
睡过去了...
 
与此同时,“不死鸟”号的WC内的真空马桶内...
 
由于控制WC真空马桶的Wxxs系统死机崩溃,导致可怜的阿宝被密封在
自己的排泄物和呕吐物中N个小时了,他还在焦急地等待着加菲前来拯救自己,
可是...

加菲在床上做了一个梦,梦见阿宝憋屈在马桶里对他吼着什么,
加菲甜美的笑了:“这次终于赢你啦!”。(本篇完)

(对于正在吃饭或准备要吃饭的朋友,如果破坏了你的食欲,我对此深表歉意,
我也觉得有点恶心,厚厚!)
(有兴趣的朋友可以观赏偶的前一篇无厘头作品:
【原创】和Taskmgr过不去篇(无厘头版)

http://blog.csdn.net/mydo/archive/2009/07/01/4311836.aspx

 

附件请在此处下载:

http://bbs.pediy.com/showthread.php?t=97047

你可能感兴趣的:(command,null,Access,HP,byte)