看题目名大概是虚拟机(VM)题。先运行一下,提示输入用户名和密码,输错后提示错误退出。看 main
函数,拉到最后:
可见要打印出 flag,需要 v5
和 v8
同时为 0。v5
是一个字符串比较的结果,v8
是一块内存的值。做题时发现 v5
不涉及虚拟机,比较好搞,先看它。
v5
和一串疑似 base64 编码的字符串比较,在 base64_encode
函数中可以确认的确是 base64 算法,但字母表改过:
用这个字母表解码那串 base64,得到 EF468DBAF985B2509C9E200CF3525AB6
。
再前一个函数 convert_to_binascii
只是把数组转换成字符串,如 0x61, 0x62, 0x63 转换成 616263。
前面两个函数看起来是加密算法,搜索其中魔术数 0xA3B1BAC6
可知是 SM4 算法,其密钥在前面的一串赋值语句里。https://github.com/fantapsody/libsms4 这里找了个解密库,自己改一下解密 EF468DBAF985B2509C9E200CF3525AB6
。
得到 6261647265723132
。这应该是输入正确的用户名和密码时,提供给加密函数 sub_4018C4()
的输入,即 v13
的内容,而 v13
由前面一个函数 sub_4066C0()
对用户名进行变换得到,测试了下,这个变换应该是把用户名转成十六进制的 ASCII 码串,比如 abc 转成616263。那么 6261647265723132
对应的用户名就是 badrer12
。
用户名解决了,接着是密码,整个 v5
都没涉及到密码,因此 v8
应该和密码有关,看看密码输入后存到哪:
是存到 ptr
为首地址的一块内存里。上下又有两个函数引起注意:vm_init()
和 vm_execute()
,vm_init()
里是大量的赋值,可能是对某个结构进行初始化,这个结构随后传给 vm_execute()
,vm_execute()
本身只是一个循环里调用另一个函数,进入这个函数 sub_40656D()
可以看到很明显是解释 opcode 的虚拟机:
看到这里,再回去 vm_init()
,这时一些初始化操作的意义就比较清楚了。
像这样的初始化,上面是 opcode,下面是这个 opcode 对应的函数即 handler。大概是虚拟机会对输入的密码进行操作,最后得到一个值,这个值赋给 v8
然后判断是否为 0。直接从 IDA 里把虚拟机部分的代码复制出来,写一个 C 程序模拟这个虚拟机,并根据最终值是否为 0 分别打印 Right 和 Wrong,然后用 angr 符号执行一下就得到密码了。angr 脚本:
运行脚本,等几分钟就有结果。
所以密码是 xyz{|}
。
本地测试一下,成功。
nc
连远程服务器有个坑,密码的结尾不能有换行符,也就是输完密码不能用回车来结束输入… 不然打印不出 flag。所以输完密码立即按 Ctrl+D 输入 EOF
,就可以得到 flag。