170928 逆向-Reversing.kr(Direct3D

xiaoxiao2021-02-28  15

1625-5 王子昂 总结《2017年9月28日》 【连续第361天总结】 A. Reversing.kr-Direct3D_FPS B. 这次DX让我有点怂…… 一直对这种大型程序都心有畏惧,可能是因为没写过不了解其原理吧

解压出来可以看到包含一个DX库,一个Data文件夹下放置着各种图片和音效资源,一个exe

运行一打开就把我吓了一跳,差点以为不小心打开的恐怖游戏…… 这囧囧的怪物,还有后面那张好像兵库北的微笑的图片(:з」∠)

惯例拖入IDA查找字符串,这次核心函数就在WinMain中 由于函数太长就不放全部内容了 大体上可以根据游戏画面中左下角显示的10和失败提示找到HP 根据枪声音效找到射击控制部分 根据按键的Switch结构找到移动部分 另外射击的后坐力和音效是有一定的间隔的,这意味着一定有一个计时器(否则将会不间断的连续产生枪声音效,很违和)

试玩了一下,很简陋不过五脏俱全了,基本上开5枪才能杀死一个怪;另外随着距离增加还有随机的散射效果

基本理清整个程序的结构,怪物和自己应该都是对象,在内存中各占132个字节 不过没分析清楚类的成员;只能看出来有存活标识和HP两部分

沿着GameClear!的字符串可以找到胜利检查函数:

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的值不全是可见字符,说明在别的地方还有解密操作,通过交叉引用可以找到 在这里:

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; }

乍一看不一定能识别出来这个函数是做什么的(没有注释的话啦 但是查看交叉引用就能发现这个函数是在下述过程中被调用的:

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脚本打印:

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值

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,

然后脚本处理

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了也没弹窗,再检查一下内存:

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++的类只能算初入门,还欠缺很多…… (IDA的动态调试真难用,但是ODDump内存更不方便啊QAQ)

C. 明日计划 算法学习 Reversing.kr

转载请注明原文地址: https://www.6miu.com/read-450239.html

最新回复(0)