记一次win7下逆向扫雷到DLL(HOOK)注入
本帖最后由 Follow丶Me 于 2018-10-9 13:41 编辑10.1国庆宅在家里,没事干就调戏了扫雷。
这是在Windows 7下的32位扫雷程序,有空在看看64位。
首先理清逆向思路:
1.载入OD,下消息断点201 (后来发现此路不通....)
2.后来枚举所有函数,发现了Rand这个有趣的东东,然后OD中 bp rand
3.最后游戏结束的时候发现对话框,可以直接给 DialogBoxParamW下断
上面是我自己的开始的思路,当然有大神提供思路的,请留言,十分感谢。
第一步,当然是载入OD,bp rand
接着程序会断这里
之后OD会不停的断在这,我们在堆栈窗口找到上层CALL的调用地址,再搜索哪些地方调用了当前的CALL,每个调用的地方都F2
就会得出有8个地方调用了,把那个一直调用rand的断点的去掉,还有把bp rand那个断也去掉
去掉之后再点击格子,此时回到OD如当前图片的断点,去这个CALL的头部从上往下分析。
经过分析,得知当前CALL是一个“布雷函数”
movedi,edi ;布雷函数
pushebp
movebp,esp
pushecx
pushecx
pushebx
pushesi
pushedi
movesi,ecx
callMineSweeper.0034D816 ;全局变量到EAX
pushdword ptr ds: ;push 5BB9B276 == push eax
movdword ptr ss:,eax ;mov ecx, eax
callMineSweeper.0034D821 ;这个参数也就是eax 即为随机序列种子
push0x00000010
callMineSweeper.0034DCA7 ;申请4个DWORD大小的空间(malloc)
popecx ;变相的平衡堆栈 ecx = eax = 0x5BB9B276
testeax,eax
jeMineSweeper.003400F1
push0x00000010 ;初始化函数第一个参数0x10
movecx,eax ;ecx = 01FD6948即为第一次申请的空间首地址
callMineSweeper.0033F959 ;上面四个参数初始化为:0x0 0x0 0x10 0x0
movdword ptr ss:,eax ;ecx = eax 即为01FD6948
jmpMineSweeper.003400F5
anddword ptr ss:,0x00000
moveax,dword ptr ds: ;获取扫雷的行 rows
imuleax,dword ptr ds: ;获取扫雷的列 eax = rows * columns -> 9+9 = 0x51
xoredi,edi ;i=0 edi置零
testeax,eax
jleMineSweeper.0034014B
movecx,dword ptr ds: ;ecx = columns.....=3DC248
moveax,edi ;eax = i
cdq
idivecx ;此时ecx为columns i/dwColumn=eax行号....edx列号
xorecx,ecx ;ecx置零
subedx,dword ptr ss: ;sub 0,0 鼠标点击的列号
subeax,dword ptr ss: ;sub 0,0 鼠标点击的行号
testedx,edx ;列号!!!
setnlcl ;根据列号是否正负,取绝对值
leaecx,dword ptr ds:
imulecx,edx
cmpecx,0x01
jnleMineSweeper.00340136 ;第一个差值绝对值<=1,判断第二个差值
xorecx,ecx ;ecx置零
testeax,eax ;行号!!!
setnlcl ;根据行号是否正负,取绝对值
leaecx,dword ptr ds:
imulecx,eax
cmpecx,0x01
jleMineSweeper.0034013F ;第二个差值绝对值>=1
movecx,dword ptr ss:
pushedi
callMineSweeper.00355A51
moveax,dword ptr ds: ;行数
imuleax,dword ptr ds: ;列数 -> 行数 * 列数 = 0x51
incedi ;i++
cmpedi,eax ;判断是否布满81个格子
jlMineSweeper.00340102 ;没有布满就继续
push0x00000010
callMineSweeper.0034DCA7 ;第二次4个DWORD大小的申请空间
popecx ;变相平衡堆栈 ecx = 0x10
testeax,eax
jeMineSweeper.00340165 ;可能是申请失败时的操作
pushdword ptr ds: ;雷数入栈
movecx,eax ;ecx为第二次申请的首地址
callMineSweeper.0033F959 ;第二次申请空间初始化 0x0,0x0,0xA,0x0
movedi,eax ;edi 为第二次申请的首地址
jmpMineSweeper.00340196 ;无条件跳
xoredi,edi
jmpMineSweeper.00340196
moveax,dword ptr ss: ;eax = 第一次申请的首地址
moveax,dword ptr ds: ;eax = 0x4D = 77
testeax,eax
jbeMineSweeper.0034019D ;判断雷是否布完了
deceax
pusheax ;循环从0x4D 开始减一 入栈
push0x00000000
callMineSweeper.0034D7F3 ;rand函数 随机数是:0-0x4C之间 即0-76之间
movebx,eax ;ebx = 雷的随机数
moveax,dword ptr ss: ;第一个申请的首地址
moveax,dword ptr ds: ;eax = 0436F980
pushdword ptr ds: ;找到随机布局中放雷的位置
movecx,edi ;ecx = 第二个申请的首地址
callMineSweeper.00355A51 ;将数据保存到第二个申请空间里
movecx,dword ptr ss: ;ecx第一个申请的首地址
pushebx
callMineSweeper.0034EC9E
moveax,dword ptr ds: ;eax的值是存放布雷数
cmpeax,dword ptr ds: ;判断雷数
jneMineSweeper.00340169 ;雷数=第二次申请空间的第一个属性值
xorecx,ecx
cmpdword ptr ds:,ecx
jbeMineSweeper.003401C4
moveax,dword ptr ds: ;获取存雷数组的地址
moveax,dword ptr ds: ;取第i(ecx)个雷的位置数
movebx,dword ptr ds: ;扫雷初始化数据中第四个属性是列数
cdq
idivebx ;雷的位置数 / 列数 = 真实布雷的地址(EAX行号,EDX列号)
movebx,dword ptr ds: ;地址表
movebx,dword ptr ds: ;[+0xc] 第二层地址表
incecx ;i++
movedx,dword ptr ds:
movedx,dword ptr ds: ;根据雷所在列号取第edx(列号)个地址
movbyte ptr ds:,0x01 ;为雷时,填充0x1
cmpecx,dword ptr ds: ;判断是否布完了
jcMineSweeper.003401A3 ;布雷完毕
push0x00000001
movecx,edi
callMineSweeper.0033FC96 ;free
movecx,dword ptr ss:
popedi
popesi
popebx
testecx,ecx
jeMineSweeper.003401DE
push0x00000001
callMineSweeper.0033FC96 ;free
pushdword ptr ss:
callMineSweeper.0034D821
leave
retn0x0008
这还没完,在堆栈窗口找到向上2层的CALL那个才是左键点击的CALL
movedi,edi ;左键单击函数
pushebp
movebp,esp
moveax,dword ptr ds: ;这玩意就是基址
pushebx
pushesi
movesi,dword ptr ss:
pushedi
movbyte ptr ds:,0x01
pushdword ptr ds: ;这个是y 坐标
movedi,ecx
pushdword ptr ds: ;这个是x 坐标
movecx,dword ptr ds: ;this 指针
xorbl,bl
callMineSweeper.00341418 ;判断单击的是不是雷,传X,Y坐标
testeax,eax ;比较返回值
jnleMineSweeper.00346FF1
movdword ptr ds:,esi
incbl
jmpMineSweeper.00346FF9
pusheax
movecx,edi
callMineSweeper.00346BCD ;后续CALL
movbyte ptr ds:,0x01
popedi
popesi
moval,bl
popebx
popebp
etn0x0004
再分析一下这个CALL里面“主要”做了什么:callMineSweeper.00341418 --> 判断单击的是不是雷,传X,Y坐标
movedi,edi
pushebp
movebp,esp
ecx,dword ptr ds: ;this指针偏移+0x10(第5个属性地址)
popebp
jmpMineSweeper.00340C50 ;无条件跳
movedi,edi
pushebp
movebp,esp
pushecx ;this->第5个属性地址
pushebx
movebx,dword ptr ss: ;x坐标
pushesi ; = 存放x,y坐标的地址
movesi,ecx ;此时esi=ecx=this指针->第5个属性地址
moveax,dword ptr ds: ;this->某个对象
moveax,dword ptr ds: ;某个对象->属性
moveax,dword ptr ds: ;ebx = x坐标
moveax,dword ptr ds: ;这个eax指向的地址就是存储所有列状态的地址表
pushedi
movedi,dword ptr ss: ;y 坐标
moveax,dword ptr ds: ;edi = y坐标
xorecx,ecx
movdword ptr ss:,ecx
cmpeax,0x09 ;未点击
jeMineSweeper.00340C95
cmpeax,0x0B ;问号
jeMineSweeper.00340C95
moveax,dword ptr ds:
cmpbyte ptr ds:,cl
jeMineSweeper.00340CE5
pushecx
pushecx
pushecx
callMineSweeper.003505E9
xorecx,ecx
jmpMineSweeper.00340CE5
cmpdword ptr ds:,ecx ;this->第七属性
jneMineSweeper.00340CBA ;判断是否第一次鼠标点击
pushedi
pushebx
movecx,esi
callMineSweeper.003400BB ;布雷CALL
push0x00000000
pushedi
pushebx
push0x00000000
pushedi
pushebx
movecx,esi
callMineSweeper.00340A42
movdword ptr ds:,ebx
movdword ptr ds:,edi
jmpMineSweeper.00340CDD
moveax,dword ptr ds:
moveax,dword ptr ds:
moveax,dword ptr ds: ;这个ecx里面就是存储所有列有雷的地址表
moveax,dword ptr ds:
cmpbyte ptr ds:,cl ;判断是否为雷 ->edi=行eax=地址偏移 cl=0(cl值不变)
jeMineSweeper.00340CD0 ;不为雷跳转
最后分析得出:
真实基址 = 扫雷起始地址 + 0x868B4(RVA)
格子数据 = [[[[[[[真实基址] + 0x10] + 0x40] + 0x0C] + 4 * X坐标] + 0x0C]+ 4 * Y坐标]
取值范围:1~8数字9未开 10旗 11问号12空
雷数据 = [[[[[[[真实基址] + 0x10] + 0x44] + 0x0C] + 4 * X坐标] + 0x0C] + Y坐标]
esi = [[真实基址] + 0x10]
= 雷数
= 行数
= 列数
= 鼠标左键单机次数
第一次单机时:
列号:
行号:
这就是鼠标左键CALL分析了,至于右键CALL提供思路:在第一块数据方格中下硬件写入断点
然后回溯跟踪 直到返回2次后,就是我们要找的CALL
OK...............................分析到这就结束了。
具体实现核心代码:**** Hidden Message *****
附上超级难度下秒杀图:
下载完成后把后缀名改一下,另外我用的是VS2015写的。
非常棒~ 很详细,精华走一走 学习一下{:5_116:} 看看 思路,学习学习 前来学习了 记一次win7下逆向扫雷到DLL(HOOK)注入 思路比较特别,调试还太不太懂 互相学习
共同进步
支持恒大 好好学习天天向上{:5_116:} 这个是win7自带的还是网上下的独立版本的 扫雷游戏