0x00 日常查壳
无壳64位
0x01 分析主函数
首先简单看下程序行为,就是去找到日期和符来歌
于是拖进ida一看
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
0x02 SetDate
简单分析一下SetDate函数
1 | __int64 __fastcall SetDate(__int64 dates, unsigned int *date) |
异或等式
关键还是异或那 写个等式
1 | k[5] = k[0] ^ CalcRoundKey( k[3] ^ k[2] ^ k[1] ^ CK[0] ) |
那么现在我们有了CK常量和加密后的k[35] k[32] k[33] k[34](也就是dates[28] [29] [30] [31])
不用在意CalcRoundKey怎么操作,只用关心这只是个值
那么逆回去的就是
1 | k[31] = k[35] ^ CalcRoundKey( k[34] ^ k[33] ^ k[32] ^ CK[31] ) //拿回了k[31] |
于是可以写个等式
k[i] = k[i + 4] ^ CalcRoundKey( k[i + 3] ^ k[i + 2] ^ k[i + 1] ^ CK[i] )
CalcRoundKey
就是数本身异或左移13位和右移9位(右移9位也可以理解成左移23位)
1 | __int64 __fastcall CalcRoundKey(int a1) |
GetDate!
于是只要写好函数直接开逆
1 |
|
GetPart!
0x03 SingFlag
拿到了正确的日期 于是可以去找符来歌
从这里是可以得知我们要的符来歌是24位
异或等式
1 | void __fastcall SingFlag(__int64 flags, __int64 flag) |
其实还是一个异或等式
1 | flags[6] = flags[0] ^ T( flags[5], 0 ) |
那么到最后是有flags[60] flags[61] flags[62] flags[63] flags[64] flags[65]
同理写出等式
1 | i != 6: flags[i] = flags[i + 6] ^ flags[i + 5] |
(j 为轮次 第一次为0 第二次为1以此类推)
T运算
实现相应操作即可构成数值
1 | __int64 __fastcall T(unsigned int a1, int a2) |
0x04 DFS
已经知道上面怎么逆了,现在就是拿回这六个数值(flags[60] flags[61] flags[62] flags[63] flags[64] flags[65])
这边就是把六个值顺序放回到t数组
例如 flags[60] = 0x12345678
t[0] = 78, t[1] = 56, t[2] = 34, t[3] = 12
1 | for ( i = 0; i <= 5; ++i ) |
于是就这是个破坏性的异或,我们无法直接靠爆破逆回去,因为值分叉了,需要穷举所有异或的可能性解决
(最后一个值t[23]是没有变的,突破口就在这)
1 | void DFS(unsigned char * flag, int deep) |
通过输入正确的日期 我们可以拿到真正的密文串(我把恢复数值放到PartSuccessful里面)
0x05 GetFlag
于是与逆符来歌合并一下
1 |
|
Get符来歌
结束了?还没有,我们继续输入得到这串符来歌
1 | Flag: VNCTF{TimeFl20211205ightMachine} |
About this Post
This post is written by P.Z, licensed under CC BY-NC 4.0.