好友
阅读权限30
听众
最后登录1970-1-1
|
本帖最后由 whklhh 于 2017-11-14 15:14 编辑
Reversing.kr是韩国的一个逆向题目网站
有分国度的排行榜,还是挺有意思的
本题即来自于第七关Direct3D_FPS
http://reversing.kr/challenge.php可以下载到文件
这次DX让我有点怂……
一直对这种大型程序都心有畏惧,可能是因为没写过不了解其原理吧
解压出来可以看到包含一个DX库,一个Data文件夹下放置着各种图片和音效资源,一个exe
运行一打开就把我吓了一跳,差点以为不小心打开的恐怖游戏……
这囧囧的怪物,还有后面那张好像兵库北的微笑的图片
(:з」∠)惯例拖入IDA查找字符串,这次核心函数就在WinMain中
由于函数太长就不放全部内容了
大体上可以根据游戏画面中左下角显示的10和失败提示找到HP
根据枪声音效找到射击控制部分
根据按键的Switch结构找到移动部分
另外射击的后坐力和音效是有一定的间隔的,这意味着一定有一个计时器(否则将会不间断的连续产生枪声音效,很违和)
试玩了一下,很简陋不过五脏俱全了,基本上开5枪才能杀死一个怪;另外随着距离增加还有随机的散射效果
基本理清整个程序的结构,怪物和自己应该都是对象,在内存中各占132个字节
不过没分析清楚类的成员;只能看出来有存活标识和HP两部分
沿着GameClear!的字符串可以找到胜利检查函数:
[C++] 纯文本查看 复制代码 int *Is_win()
{
int *result; // eax@1
result = dword_E49194;//怪物[0]地址
while ( *result != 1 )//遍历,发现有存活就跳出
{
result += 132;//指针指向下一个怪物
if ( (signed int)result >= (signed int)&unk_E4F8B4 )//遍历完全
{
MessageBoxA(hWnd, "CkfkbuliLE\\E_ZF\x1C\a%%)p\x1749\x01\x16IL \x15\v\x0F麟褒爰籼跓躔栉皓", "Game Clear!", 0x40u);//显示flag
return (int *)SendMessageA(hWnd, 2u, 0, 0);
}
}
return result;
}
从遍历完全的e4f8b4和开始的e49194作差,除以单位长度可以得知一共是50个单位
很明显flag的值不全是可见字符,说明在别的地方还有解密操作,通过交叉引用可以找到
在这里:
[C++] 纯文本查看 复制代码 int __thiscall hit(void *this)
{
int result; // eax@1
int v2; // ecx@2
int v3; // edx@2
result = sub_E43440(this);//得到被击中的怪物指针
if ( result != -1 )
{
v2 = 132 * result;
v3 = dword_E49190[132 * result];//该怪物的血量,OD中查到是100
if ( v3 > 0 )
{
dword_E49190[v2] = v3 - 2;//每单位时间hp-2,实际上一枪经过了10单位时间(枪声间隔),即一枪是20hp
}
else
{
dword_E49194[v2] = 0;
flag[result] ^= byte_E49184[v2 * 4];//flag的第n个字节与第n个怪物对象的某个数据进行异或
}
}
return result;
}
乍一看不一定能识别出来这个函数是做什么的(没有注释的话啦
但是查看交叉引用就能发现这个函数是在下述过程中被调用的:
[C++] 纯文本查看 复制代码 if ( dword_E47BD4 )
{
hit(v12);
if ( dword_E47BD8 < 5 )//也许是后坐力导致的镜头偏移?但是从游戏观察来看镜头上抬是和音效同步的,即一次枪声上抬一次;不明白这里的gap的作用
sub_E41880((int)&flt_E47BE0, dword_E47D70, (int)&v21);
else
dword_E47BD8 = 0;
++dword_E47BD8;
if ( shoot_gap >= 10 ) // 每10单位时间播放一次音效
{
shoot_gap = 0;
PlaySoundA("data\\Shoot.wav", 0, 1u);
}
++shoot_gap;
}
很明显,是射击函数
由此可以推断hit函数中进行的是运算等逻辑部分
我直接用IDC打印了一下对象的值,发现是空的;
说明还有动态申请的过程
在IDA中直接运行游戏,然后IDC脚本打印:
[C++] 纯文本查看 复制代码 IDC>auto i;for(i=0;i<50;i++)Message(“%d “, Byte(0xe49184 + i*132*4));
0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 112 116 120 124 128 132 136 140 144 148 152 156 160 164 168 172 176 180 184 188 192 196
还好,就是i*4,很好处理
直接python脚本跑一下就能得到:
先Dump字符串的ASCII值
[C++] 纯文本查看 复制代码 IDC>auto i;for(i=0;i<50;i++)Message(“%d, “, Byte(0xe47028 + i));
67, 107, 102, 107, 98, 117, 108, 105, 76, 69, 92, 69, 95, 90, 70, 28, 7, 37, 37, 41, 112, 23, 52, 57, 1, 22, 73, 76, 32, 21, 11, 15, 247, 235, 250, 232, 176, 253, 235, 188, 244, 204, 218, 159, 245, 240, 232, 206, 240, 169,
然后脚本处理
[Python] 纯文本查看 复制代码 s = [67, 107, 102, 107, 98, 117, 108, 105, 76, 69, 92, 69, 95, 90, 70, 28, 7, 37, 37, 41, 112, 23, 52, 57, 1, 22, 73, 76, 32, 21, 11, 15, 247, 235, 250, 232, 176, 253, 235, 188, 244, 204, 218, 159, 245, 240, 232, 206, 240, 169]
for i in range(50):
print(chr(s[i] ^ i*4), end='')
Congratulation~ Game Clear! Password is Thr3EDPr0m
这题中i*4的值是很简单的,如果很困难怎么办呢?
其实还可以写外挂,增伤锁血之类的
将hit函数中的hp-2改为-100(实践来看,远距离的散射效果似乎是按照每单位时间进行的,也就是说如果离得远很有可能一枪的1/10没打中啥的……奇妙的判定)
这样可以快速将怪打完,剩最后一只了合影留个念
但是最后clear了也没弹窗,再检查一下内存:
[C++] 纯文本查看 复制代码 IDC>auto i;for(i=0;i<50;i++)Message(“%d “, Byte(0xe49194 + i*132*4));
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
最后有2个单位还没死……想了一下,应该一个是中间那个大圆球,一个是自己吧
自杀理论上来说跟最后一只怪倒是说不定有可能;但是中间那个大圆球被命中的时候不会触发hit的击中判定(下断未触发),所以也就不会被扣血量,更不可能杀死……╮(╯_╰)╭没辙,正常游玩下是不可能得到flag的
有点好奇会不会有人五枪一只、慢慢打死48只怪兽,然后发现啥都没有呢……太恶意了OTZ
整体来说,逆向C++的类只能算初入门,还欠缺很多中间很多函数仍然不明所以,虽然找到flag了但是对整个程序的把握还差得远……
(IDA的动态调试真难用,但是ODDump内存更不方便啊QAQ)
PS:由于Data+DX的大小堪堪超过了1MB,只得分成两个包╮(╯_╰)╭PPS:其实可以直接去reversing.kr网站上下载的呀……_(:з」∠)_ |
免费评分
-
查看全部评分
|