SantMat's ReverseME #2 数字游戏
下载地址: http://www.reversemes.de/reversemes/reme2.zip
这个又是让我们到程序里去看任务,主要有三条:一是去掉开始时的窗口,二是把两个按钮的代码填上,每按一次随机出现0-100的数,两个按钮分别对应两个玩家,谁的数大就得到1分,先得50分者为胜,三是用键盘上的“M”和“Z”控制两个按钮。
先反汇编,从开头看:
//********************** Start of Code in Object .text **************
Program Entry Point = 00401000 (SantMat-ReverseMe2.exe File Offset:00001600)
//******************** Program Entry Point ********
:00401000 6A00 push 00000000
* Reference To: KERNEL32.GetModuleHandleA, Ord:0111h
|
:00401002 E859010000 Call 00401160
:00401007 A39C304000 mov dword ptr [0040309C], eax
:0040100C 6A00 push 00000000
* Possible StringData Ref from Code Obj ->"U嬱亇 "
|
:0040100E 6842104000 push 00401042 <-对话框的消息处理
:00401013 6A00 push 00000000
* Possible StringData Ref from Data Obj ->"MEMO"
|
:00401015 6800304000 push 00403000 <-看看,应该是开始时出现的对话框
:0040101A FF359C304000 push dword ptr [0040309C]
* Reference To: USER32.DialogBoxParamA, Ord:0092h
|
:00401020 E81D010000 Call 00401142 <-这叫"基于对话框的程序"吧,我不太懂
:00401025 6A00 push 00000000
* Reference To: KERNEL32.LoadLibraryA, Ord:01A9h
|
:00401027 E840010000 Call 0040116C
:0040102C 6A00 push 00000000
* Reference To: KERNEL32.FreeLibrary, Ord:00A2h
|
:0040102E E827010000 Call 0040115A
:00401033 6A00 push 00000000
:00401035 6A00 push 00000000
* Reference To: KERNEL32.GetProcAddress, Ord:0129h
|
:00401037 E82A010000 Call 00401166
:0040103C 50 push eax
* Reference To: KERNEL32.ExitProcess, Ord:0075h
|
:0040103D E812010000 Call 00401154
:00401042 55 push ebp
:00401043 8BEC mov ebp, esp
:00401045 817D0C10010000 cmp dword ptr [ebp+0C], 00000110
:0040104C 7502 jne 00401050
:0040104E EB66 jmp 004010B6
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040104C(C)
|
:00401050 837D0C10 cmp dword ptr [ebp+0C], 00000010
:00401054 750C jne 00401062
:00401056 6A00 push 00000000
:00401058 FF7508 push [ebp+08]
* Reference To: USER32.EndDialog, Ord:00B8h
|
:0040105B E8E8000000 Call 00401148
:00401060 EB54 jmp 004010B6
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401054(C)
|
:00401062 817D0C11010000 cmp dword ptr [ebp+0C], 00000111
:00401069 7542 jne 004010AD
:0040106B 8B4510 mov eax, dword ptr [ebp+10]
:0040106E 663DEC03 cmp ax, 03EC
:00401072 7542 jne 004010B6
:00401074 C1E810 shr eax, 10
:00401077 660BC0 or ax, ax
:0040107A 752F jne 004010AB
:0040107C 6A00 push 00000000
:0040107E FF7508 push [ebp+08]
* Reference To: USER32.EndDialog, Ord:00B8h
|
:00401081 E8C2000000 Call 00401148 <-至此是第一个对话框
:00401086 6A00 push 00000000
* Reference To: KERNEL32.GetModuleHandleA, Ord:0111h
|
:00401088 E8D3000000 Call 00401160 <-又出现一次,第二个对话框开始了
:0040108D A3A0304000 mov dword ptr [004030A0], eax
:00401092 6A00 push 00000000
* Possible StringData Ref from Code Obj ->"U嬱亇 "
|
:00401094 68BF104000 push 004010BF
:00401099 6A00 push 00000000
* Possible StringData Ref from Data Obj ->"GAME"
|
:0040109B 6805304000 push 00403005
:004010A0 FF35A0304000 push dword ptr [004030A0]
* Reference To: USER32.DialogBoxParamA, Ord:0092h
|
:004010A6 E897000000 Call 00401142
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040107A(C)
|
:004010AB EB09 jmp 004010B6
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401069(C)
|
:004010AD B800000000 mov eax, 00000000
:004010B2 C9 leave
:004010B3 C21000 ret 0010
可见这两个对话框是“独立”的,只要把关于第一个对话框的代码去掉就可以了,怎么去掉?全NOP?当然可以,不过是不是累点?我干脆把程序的入口点改在了401086,前面全没用了,HEHE~~~~
接下来看第二个对话框的消息处理:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004010D1(C)
|
:004010DF 817D0C11010000 cmp dword ptr [ebp+0C], 00000111
:004010E6 7547 jne 0040112F
:004010E8 8B4510 mov eax, dword ptr [ebp+10]
:004010EB 663DED03 cmp ax, 03ED
:004010EF 751B jne 0040110C
:004010F1 C1E810 shr eax, 10
:004010F4 660BC0 or ax, ax
:004010F7 7513 jne 0040110C
:004010F9 6A00 push 00000000
* Possible StringData Ref from Data Obj ->"Number Game"
|
:004010FB 680A304000 push 0040300A
* Possible StringData Ref from Data Obj ->"This is the part you need to change "
->"into a number generator! :)"
|
:00401100 6816304000 push 00403016
:00401105 6A00 push 00000000
* Reference To: USER32.MessageBoxA, Ord:01BBh
|
:00401107 E842000000 Call 0040114E <-按第一个按钮后出现的对话框
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004010EF(C), :004010F7(C)
|
:0040110C 663DEE03 cmp ax, 03EE
:00401110 7526 jne 00401138
:00401112 C1E810 shr eax, 10
:00401115 660BC0 or ax, ax
:00401118 7513 jne 0040112D
:0040111A 6A00 push 00000000
* Possible StringData Ref from Data Obj ->"Number Game"
|
:0040111C 680A304000 push 0040300A
* Possible StringData Ref from Data Obj ->"This is the other part you need "
->"to change into a number generator! "
->":)"
|
:00401121 6856304000 push 00403056
:00401126 6A00 push 00000000
* Reference To: USER32.MessageBoxA, Ord:01BBh
|
:00401128 E821000000 Call 0040114E <-按第二个按钮后出现的对话框
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401118(C)
|
:0040112D EB09 jmp 00401138
很显然只要在显示对话框的地方加上我们的代码就行了,还是老方法,跳到后面去.
改动的地方: :004010F9 E974000000 jmp 00401172
:0040111A A138214000 mov eax, dword ptr [00402138] <-这是我写在.data块里的代码里的第一句,那里看起来是空地,结果一运行就会把这一句吃掉,所以只好写在这里(建议写在自己新建的块里安全一些)
:0040111F E9811F0000 jmp 004030A5 <-跨块跳转(没有地方了:))
下面是我自己写的代码,很长,但实现了一个相当复杂的过程,自豪中....
先说说思路,因为游戏是两个按钮各按一次后再比较大小,如果不停的重复按一个按钮,应该没有作用.所以我把按下按钮后得到的随机数分别存在[402130]和[402138]两个地方,当按其中一个按钮时,如果相应的内存里没有值,就得到一个随机数并放在相应的内存里,如果内存里已经有数就不处理.只有两个地方都有数时才进行比较,比较以后显示结果,把两个地方清零,以进行下次比较.(要是用高级语言写多好!)
下面是按钮1的处理:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004010F9(U) <-从上面跳过来的
|
:00401172 90 nop <-跳的位置有点错位:)
:00401173 A130214000 mov eax, dword ptr [00402130] <-把相应内存中的值取出来
:00401178 85C0 test eax, eax <-看是否为0
:0040117A 7590 jne 0040110C <-如果已经有值就返回
:0040117C FF153F604000 call dword ptr [0040603F] <-这是函数GetTickCount,随机数种子
:00401182 B923000000 mov ecx, 00000023 <-随机数的计算公式:
:00401187 F7E1 mul ecx Ran_Num=[(Ran_Seed*X)+Y] mod Z
:00401189 83C007 add eax, 00000007 其中X,Y为素数,Z为范围
:0040118C B964000000 mov ecx, 00000064
:00401191 F7F1 div ecx
:00401193 42 inc edx <-上面是计算1-100的随机数的过程
:00401194 891530214000 mov dword ptr [00402130], edx <-把结果放好
:0040119A 6A00 push 00000000
:0040119C 52 push edx <-要显示的结果
* Possible Reference to Dialog: GAME, CONTROL_ID:03F0, ""
|
:0040119D 68F0030000 push 000003F0 <-按钮对应的文本框的ID,用资源工具可查
:004011A2 FF7508 push [ebp+08] <-对话框的句柄
:004011A5 FF151B604000 call dword ptr [0040601B] <-函数SetDlgItemInt
:004011AB A138214000 mov eax, dword ptr [00402138] <-取另一个地方的内存
:004011B0 85C0 test eax, eax
:004011B2 0F8454FFFFFF je 0040110C <-如果为空说明对方还没有按,返回
:004011B8 8B1530214000 mov edx, dword ptr [00402130] <-取出己方得到的数
:004011BE 3BC2 cmp eax, edx <-比较
:004011C0 7C18 jl 004011DA
:004011C2 6A00 push 00000000
* Possible StringData Ref from Data Obj ->"Number Game"
|
:004011C4 680A304000 push 0040300A
* Possible StringData Ref from Data Obj ->"Player 2 win !"
|
:004011C9 6856304000 push 00403056 <-这里我把原来的字串改了
:004011CE 6A00 push 00000000
:004011D0 E879FFFFFF call 0040114E <-函数MessageBox,显示"Player 2 win !"
:004011D5 E913000000 jmp 004011ED
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004011C0(C)
|
:004011DA 6A00 push 00000000
* Possible StringData Ref from Data Obj ->"Number Game"
|
:004011DC 680A304000 push 0040300A
* Possible StringData Ref from Data Obj ->"Player 1 win !"
|
:004011E1 6816304000 push 00403016
:004011E6 6A00 push 00000000
:004011E8 E861FFFFFF call 0040114E <-显示"Player 1 win !"
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004011D5(U)
|
:004011ED 33C0 xor eax, eax <-善后工作,EAX=0
:004011EF A330214000 mov dword ptr [00402130], eax
:004011F4 A338214000 mov dword ptr [00402138], eax <-把两处内存都清零,以便下次比较
:004011F9 E90EFFFFFF jmp 0040110C <-走喽
:004011FE 0000 add byte ptr [eax], al <-好险,只剩两个字节的空余了
按钮2的处理十分类似,只是因为地方不够了,在后面403XXX处找了一块空地(本来是.data块,比较危险,不推荐使用,最好新建一个块)在HIEW里这种跨块的长跳转好像有点问题,总是搞不对地址,(不知是HIEW的问题还是我的脑袋的问题,请高手讲解)后来只好用专门的伪代码工具得到机器码再输进去.(痛苦啊!)
改成以后试试,很像样子了。任务只完成了一半,那个得50分胜利和按“M”AND“Z”还没写,不过我实在想歇歇了,而且寒假作业还没写呢。BYE!