所谓逆向的过程就是不断学习新事物,不断积累的过程 可能你第一眼看到一种算法:woc,这是什么玩意?
但当你深思熟虑后会发现:这不是我之前看到过的xx加密的变种吗? 可能你第一眼看到一种新的语言写的程序:woc,xx语言我不会。
但当你深思熟虑后会发现:这语言和之前的xx语言很类似啊
而这些都得靠积累:一是积累逆向工程实力(比如看汇编),二是积累正向工程的能力(比如coding)
希望大家能在逆向中学到如何快速学习新事物,以及如何整理现有的知识应对未知的挑战这次是一个windows专题,里面会涉及到一些和windows相关的知识点,也会涉及到部分coding。enjoy it
broken windows -> open windows -> security windows
Broken Windows
bin下载:http://pan.baidu.com/s/1jIhsNX0 密码:5pqh
先查一下壳,UPX的,拿工具脱一下或者手脱,用一下ESP定律脱也很快,这不是重点。
丢到IDA里面,定位到关键代码:
1 | DWORD __stdcall sub_401020(LPVOID lpThreadParameter) |
1 | DWORD __stdcall sub_401160(LPVOID lpThreadParameter) |
1 | int __usercall sub_401080@<eax>(int pt_input@<edi>) |
dump出的flag_enc:
1 | 0x68, 0x23, 0x51, 0x8d, 0xc8, 0xc9, 0x1f, 0x93, |
我们分析encrypt函数,先大致梳理一遍:
1 | int __usercall encrypt@<eax>(int pt_input@<edi>) |
再精简一下:
1 | int __usercall encrypt@<eax>(int pt_input@<edi>) |
由此逻辑就清楚了,他是4个为一组进行加密(其实还是一位一位加密),写出加密函数:
1 | unsigned char rol(unsigned char des,unsigned char mv){ |
通过加密函数轻松写出解密函数:
1 | void decrypt(unsigned char *pt) { |
完整的解密程序:
1 |
|
最终的flag:hctf{do_you_have_broken_window?}
Open Windows
bin下载:http://pan.baidu.com/s/1midkZCO 密码:b7f0
(这题目前还有一些不明白的地方,先把大佬的wp扔上来)
话不多说,直接上ida代码(已注释)
- DialogProc
这里可以看出一点东西,1.程序对监听了每次按键,来看看msdn上关于WM_KEYUP的解释:https://msdn.microsoft.com/en-us/library/windows/desktop/ms646281(v=vs.85).aspx根据里面可以看出这个wParam是按键对应的virtual-key code
2.对每一次按键,有一个累加器和一个累乘器,同时对按键进行记录(图中sub_4012E0对应c++ vector的push_back,这里没看出来没什么关系)
3.最后在接受到程序退出的信号时,对整个输入进行校验,前提是累加器的值为771, 累乘器的值为0x63A421C737F6FFE0, 同时输入长度应该为10个字符,也就是对应十次按键
接下来来看check函数,动态一调就可以发现check的参数就是对应键盘输入的virtual-key code,还是ida:
好,现在又知道了一些信息:input长度为10,这个前面提到过,每个按键的virtual-key code应该是A-Zhttps://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx这里可以看出A-Z就对应键盘的A-Z,这个没有问题。
先不看下去,其实知道上面这些信息后就可以推flag的每个字节了。
整理一下:
1.len(flag) == 10
2.for x in flag: x in A-Z
3.add all ord(x) in flag == 771
4.mul all ord(x) in flag == 0x63A421C737F6FFE0
这里可以直接爆破10个字节,不过恐怕得跑一天。
一个更好的方法:先分解0x63A421C737F6FFE0
83=0x53 = S
79=O
好,这3个字符就确定了,29(23)×2不对,x5也不对,那只能x3了
又确定两个87=W 69=E
好接下来只剩5个了,爆破走起
1 | mulsum = 0x63A421C737F6FFE0/83/83/87/69/79 |
脚本有重复输出,5个字符get:
[‘D’, ‘F’, ‘K’, ‘L’, ‘Q’]
现在所有字符都拿到了:SSOWEDFKLQ 但不知道顺序
接下去看ida:
check_arr 是一个 16*16的数组,存放的值为0和1,上面必要的代码我都注释了,大概就是flag的每一字节都和该字节后面所有字节做比较得出的结果再去和check_arr里存放的内容比对,即,该数组对应了一种flag
的顺序关系(已知每个flag位和其他flag位的大小关系,大于为1,其他情况为0),虽然check_arr大小为16*16 ,但仔细看可以发现只用到了10*10(才不是我把10写错成了0x10><)
可以看到check_arr第一列都是0,因为自己和自己比较永远是相等,所以check永远是0
那然后我们怎么做才能还原出顺序呢?
我们可以数啊 (
上面字符从小到大排好:
1 | a='SSOWEDFKLQ' |
check_arr 第一行4个1 即 比flag[0]小的有4个,flag[0] = s[4] = ‘L’去掉L, DEFKOQSSW 第二行3个1:flag[1] = s[3] = ‘K’。。。这样下去,最后可以得到flag,嫌麻烦的话写个脚本:
1 | check_count = [4,3,5,3,3,4,1,0,1,0] |
flag就是 LKSOQWEDSF
打开程序手打测试成功
Security Windows
bin下载:http://pan.baidu.com/s/1kUVf7UJ 密码:z2y7
没壳,先OD上跑跑熟悉一下,然后无情丢到IDA里:
定位关键函数(带注释):
1 | BYTE *sub_401200() |
1 | BYTE *sub_401000() |
1 | BYTE *sub_401140() |
dump出的flag_enc:
1 | 0xAF,0xA5,0x92,0x3C,0x0C,0xB1,0x1C,0x33,0x56,0x66,0x3F, |
dump出的byte_4021D0[255]:
1 | 0x07,0x0E,0x15,0x1C,0x23,0x2A,0x31,0x38,0x3F,0x46, |
dump出的asc_402188字符串:“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/”
解密就是加密的反函数,加密的最后一步是调用CryptEncrypt,所以我们调用CryptDecrypt将flag_enc解密第一层,由于我此时并不会使用上面的一系列函数,所以我可以通过OD修改原程序,使他调用CryptDecrypt来解密flag_enc。
查询
https://msdn.microsoft.com/en-us/library/windows/desktop/aa379913(v=vs.85).aspx
和
https://msdn.microsoft.com/en-us/library/windows/desktop/aa379924(v=vs.85).aspx
得:
1 | BOOL WINAPI CryptDecrypt( |
1 | BOOL WINAPI CryptEncrypt( |
我们这样修改程序:
并在合适时机把flag_enc写入0x004033DC:
接着跑完这段代码,flag_enc成功解密(第6层):
flag_enc_5:DHreXw4Zeh0gT9LiVdL1Hd4OHRAZHdJV0skgweIgT8k=
由于之前做过base64相关的re,看的还是比较准的,直接base64解密(第5层):
flag_enc_4:
1 | 0x0c,0x7a,0xde,0x5f,0x0e,0x19,0x7a,0x1d, |
第4层直接用py搞定(第4层):
1 | a=[ |
得到flag_enc_3:
1 | 148,54,178,233,2,223,54,187,224,121,30,142,195,30,35,187,178,2,187,112,223,187,30,195,30,175,224,247,142,224,121,175 |
数组倒序(第3层),继续py:
1 | a=[148,54,178,233,2,223,54,187,224,121,30,142,195,30,35,187,178,2,187,112,223,187,30,195,30,175,224,247,142,224,121,175] |
得到flag_enc_2:
1 | 175,121,224,142,247,224,175,30,195,30,187,223,112,187,2,178,187,35,30,195,142,30,121,224,187,54,223,2,233,178,54,148 |
第2层和第1层由于计算量不大,我直接一位一位用的爆破,py:
1 | a=[ |
得到flag_dec:
1 | hctf{there_is_no_perfect_window} |