-
-
[推荐]看雪·安恒 2020 KCTF 春季赛 | 第三题设计思路及解析
-
发表于: 2020-4-20 17:20 7526
-
第三题《寻踪觅源》历时2天,已于19号中午12点关闭攻击通道。此题共有3188人围观,最终共有12支战队攻破。
接下来让我们一起来看一下这道题的设计思路和详细解析吧。
本题出题战队 HU1 :
lelfei,业余的crack爱好者。学生时代对电脑产生了浓厚的兴趣,经历了很长时间的游戏沉迷后,开始慢慢转向学习技术,工作后自学了ASM,VB,VC,HTML,ASP,Python等语言的入门。工作原因上网较少,对单机的逆向分析、算法比较感兴趣,但是由于缺少系统的学习,水平处于“入行较早层次较低知识较杂”的阶段。
预设用户名:KCTF
预设SN:40017535dad01714402635730122
设计说明:发现一个Fabrice Bellard大牛写的开源js引擎quickjs,想学习一下源码,自己动手编译了一个WIN版本,发现可以直接把js脚本编译成EXE,并且生成的文件大小不到1M,用js写crackme是一个多么有意思的事!于是就有了这个CM,也帮大牛推广一下quickjs。膜拜作者Fabrice Bellard,知名开源项目FFMPEG和QEMU都出自他手,项目地址:https://bellard.org/quickjs/ 。
CM设计思路很简单,详见src.js。编译过程比较麻烦,需要先修改Makefile启用win编译环境支持,用MSYS编译出编译引擎,再修改qjsc.c源码启用win下把js编译成exe的功能,用qjsc.exe从js脚本生成C文件,最后用gcc编译出crackme.exe。修改过的Makefile和qjsc.c也提供了,可以对比原始文件看看改动的位置。
编译qjsc.exe命令:运行msys中的mingw32.exe并切换到工作目录,运行mingw32-make
使用qjsc.exe编译js文件:在msys环境中运行:./qjsc -fno-date -fno-eval -fno-string-normalize -fno-regexp -fno-json -fno-proxy -fno-map -fno-typedarray -fno-promise -o crackme src.js
修改C源码文件后重新编译命令:gcc -O2 -flto -D _GNU_SOURCE -I . -o crackme out9492.c ./libquickjs.lto.a -lm -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic
破解建议:想要分析出quickjs引擎的运行原理难度太大,应该比一般的VM引擎还要复杂,不过CM中并没有抹除quickjs的相关信息,敏感的人应该很快发现关键字,然后上网搜索到quickjs源码,从源码中分析出字节码的定义,反编译出js脚本,然后就简单了。目前这个项目还比较小众,估计没有现成的反编译工具,不过有源码参考,自己动手吧。
文件说明:KCTF2020Q1_by_lelfei.rar 参赛文件
源码文件:quickjs-2020-01-19.tar.xz 官方源码Makefile 修改后的Makefile文件,可以在win下用gcc编译qjsc.c 修改后的qjsc.c,可以在win下把js编译成exesrc.js js脚本源码out9492.c 包含js字节码的参赛文件源码kctf2020q1.py js脚本的python实现以及keygenCM设计说明.txt 本说明文件(附件下载链接:https://bbs.pediy.com/thread-258890.htm)
本题解题思路由金箍棒配色的泰迪熊战队Riatre提供:
说明:本文分析的是修正多解 bug 前的 binary,但相关方法应当是通用的。
题目给出了一个 Win32 控制台应用程序 lelfei.exe,简单看下会发现它是由 GCC: (i686-posix-dwarf-rev2, Built by MinGW-W64 project) 6.3.0 构建的,且带有调试符号,IDA Pro 可正常识别。 这些像是 JS_NewObjectFromShape 的符号非常扎眼,搜索一下会发现它是 QuickJS 中的。下载 QuickJS,构建出来(直接打 make 就可以)之后试一试,可以发现它有一个 qjsc,可以将一段 JavaScript 脚本编译为字节码后,和一个生成出的 stub 及 QuickJS 本身一起链接成一个可执行程序。使用 qjsc -e 可以让它输出嵌入字节码后,编译链接前的 C 代码。随便写一个 Hello World,这么做一下,观察得到的程序结构:
跟 lelfei.exe 基本是一样的。lelfei.exe 的 main 函数显著大,是因为构建时开了 LTO,自动 inline 了很多函数进来。
简单阅读一下可以发现,lelfei.exe 在 qjsc 本身生成的逻辑上做了一点改动,加入了读入用户名、序列号的部分,读入的用户名和序列号会直接 patch 进程序内嵌的字节码。
简单看下代码可以发现程序中嵌入的字节码在 0x458040 处,长度为 946 字节。
我们将它提取出来,照猫画虎换进一个像是上面那样的 .c 中,然后编译并和 QuickJS 链接起来,看看能不能执行:
呃……好像不可以。挂上调试器调一下(因为是源码
建的,可以源码调试 QuickJS),会发现看起来读入的字节码从中间某块往后就不太正确了。考虑到并不是全都不对,先不考虑是作者将它打乱了,而是考虑是否是版本不匹配。下载 QuickJS 的历史版本逐一尝试,尝试到 2020-01-19 版的时候:
执行成功了。得到了在 Windows 下直接执行这个程序并输入错误序列号一样的反应。
我们手动将作者给出的用户名和序列号 patch 进字节码里重新编译,可以发现确实能输出 Success!。至此,原始的 lelfei.exe 已经没有了任何作用,可以抛在一边了。 事情进展到这种地步之后,基本的思路自然是在 QuickJS 中加入代码,将读取回来的字节码输出出来,然后逆向字节码。
但稍微读一下 QuickJS 的代码就会发现,它本身就带有这个功能,修改相关宏定义启用即可;另外,当函数对象是读入而非编译出来的时,并没有打印字节码,修改 JS_ReadObjectRec 函数在 BC_TAG_FUNCTION_BYTECODE 这个 case 最后加一行输出即可。
总之,对 quickjs.c 文件应用下面这个 patch:
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!