首页
论坛
专栏
课程

[原创][原创]Lua以及看雪2017 CTF15题writeup

2017-7-1 16:27 6817

[原创][原创]Lua以及看雪2017 CTF15题writeup

2017-7-1 16:27
6817

1、概述

      有幸参与了看雪2017CTF防御方题目设计,由于各方面的缘故,结果题目设计有点“糙”,惹得各位大侠诸多不满!在此向各位攻击方选手表达深深的歉意,确实由于设计时的准备和平时积累的缘故,题目没有太多的新意,并且设计过程中还出现的各种问题,引得大家的争议,表示一定努力做得更好。在此也同时深深感谢看雪逆向版版主netwind的信任与帮助,初次设计,考虑不周,还望谅解!最后唯有努力不断进步,加强研究,破解更多的题目,交流更多的技术。

       在参加看雪2017ctf比赛中,中间也参与了攻击方,很遗憾中间只完成了第一个题目后由于其他任务便中途中断了,等到自己的题目,被鄙视了一番,深深的歉意,13题时用符号执行,但是由于路径的缘故,符号执行求解失败,最后没有搞定。搞到14题的时候,搞了版本绕过了壳以及调试检测,dump出了内容,但是后面算法部分还是没有完整跟出来(这两天不是周末,投入也不足)。最后看了15题,搞了半天,后面根据题目中的一些字符串,搜索到其使用了lua虚拟机,由于对lua不熟,最后又查询相关的资料,最终搞定。因此准备将自己查找的内容以及相关的理解给总结出来,以便自己以后遇到这类题目能更加顺手的处理。文中的叙述内容参考了其他博客或者网站内容,由于调研的不充分,其中如果有错误的地方请各位大牛指正。

       题目和2016年的看雪CTF的第二题相似,利用了lua的虚拟机,将算法的核心算法隐藏在了lua字节码文件中,然后编译成lua的字节码,通过动态加载并调用相关函数执行加密后再进一步进行相关的异或操作,最后与固定的值进行比较。题目算法很简单,就是两次简单的异或操作,关键将算法代码进行了隐藏,核心就是定位JIT字节码以及最后比较的位置。因此解密的关键就是要对lua虚拟机的源码进行相关的识别,并找到相关的位置,从内存中提取出lua字节码,通过反编译得到相关的算法,由于对lua字节码的也不太熟悉,因此花费了较多的时间。虽然题目算法相对来说比较简单,但是通过分析题目,自己学到了很多东西,对lua的相关知识有了更一步的了解。最后更是坚定自己的信心,只要去研究,认真去搞,就一定能搞干掉它。

2、lua字节码及其反编译

2.1 lua脚本以及lu字节码文件

       由于题目中涉及到了lua字节码,这里结合自己做这个题目是查阅的相关资料,对lua脚本语言以及在攻防对抗中的应用进行简单的分析。lua是一种简洁、轻量、可扩展的脚本语言,源自于葡萄牙语中月亮的意思。lua设计的目标是使其成为一种能够很容易嵌入到其他语言中的脚本语言,其使用ANSI C语言的方式编写并且以源代码的形式开放,因此可以很方便的嵌入到其他脚本语言中,目前Lua语言经常被嵌入到C语言中,其以简洁以及性能高效而被广泛使用,尤其在游戏外挂中,由于其简单快捷,更是被各种游戏外挂作者使用。

     上面对Lua脚本语言进行了简单的概述,下面接着介绍Lua脚本、Lua字节码以及Lua虚拟机。由于Lua是一种脚本语言,因此其需要加载到Lua虚拟机中进行执行。Lua脚本是一种利用Lua语法编写的脚本语言,其以后缀.lua结束,并以明文的形式存在,利用记事本等工具便可以直接打开查看。下是本题目中提取得到的Lua脚本:

       从图中可以看到Lua也是也函数为单位,函数之间可以被相互调用。而Lua字节码文件则是将lua脚本进行编译后得到的字节码文件,其编译后得到的字节码能够被lua虚拟机解释执行(实际上lua脚本在实际使用时也会被编译成字节码形式再被虚拟机解释执行)。字节码是指在虚拟机内部被定义为具有特定含义的功能字节,虚拟机能翻译这些特定的字节码到相应的语言信息并执行与之语意信息相符号的汇编指令。目前以字节码形式脚本主要以两种形式存在,分别是luac和luaJIT。其中luac是原始的版本。而LuaJIT则是lua的另外一个实现脚本,其JIT代表的是just-in-time,也就是解释执行。LuaJIT相对于luac来说更加高效一些,lua脚本文件编译成luac和luajit字节码文件后都带有一定的格式,下图分别是luac和luajit两种字节码文件的格式开头:

       上面分别是luac和luajit两种字节码文件的开始标志。上图是luac格式文件的开始,一般以固定标志\x1B\x4C\x75开始,而第五位一般是版本信息,这里表示是luc 5.1,在一些题目中,经常会将开始的字节码进行修改,以让一些反编译工具进行反编译时失败,这时一般需要分析程序,看程序中的提示符来决定使用的是哪个版本的lua虚拟机。下图是luajit的格式文件,其开始固定为\x1B\x4C\4A。

2.2 lua虚拟机

      一般每种语言都有自己opcode(operation code,“操作码”),相当于每种语言都有了自己的“汇编语言”,虚拟机就是可以理解这些opcode并将其转化为各个平台处理器所能够识别的机器指令。 lua虚拟机用于解释执行lua字节码,将字节码翻译成处理器能够识别的机器指令并执行,搭建了字节码到处理器指令集之间的桥梁。下图是脚本语言的运行流程:

       其中parser用于对外部输入进行词法和语法分析,然后再转换为opcode字节码,最后虚拟机对opcode字节码进行解释执行。对于lua语言,目前虚拟机主要有两种,一种是原生的虚拟机,还有一种是luajit虚拟机。在前面解释了luajit虚拟机所识别的字节码文件和luac的不同。lua虚拟机是开源的,由巴西里约热内卢天主教大学所设计的。它于2003年发布最初版本lua1.0。目前最新的版本是5.3,而release版本是Lua 5.3.4,目前在lua的官方网站上可以下载到lua虚拟机源文件的各个版本https://www.lua.org/ftp/。由于lua虚拟机版本不同,其生成的luac字节码文件也不同,在2.1节中描述了luac字节码文件的第五个字节表示了lua虚拟机版本。去年看雪CTF2016比赛的第二个题目http://bbs.pediy.com/thread-213712.htm中,其使用了lua5.3.3版本的虚拟机,但是却将luac字节码文件的开头修成了ls 1.1,并且修改了虚拟机源代码,导致被修改的虚拟机能够识别别修改后的字节码文件,而一般的反编译工具却无法准确识别因而导致反编译失败。这种情况下一般是需要攻击者通过分析程序的代码,由于将luac嵌入到c代码中执行时,创作者一般都需要将lua虚拟机编译到目标程序中,由于lua虚拟机调用函数时会有很多的字符串,因此可以分析程序通过查看相关的字符串来获取其真正所使用的lua虚拟机版本,然后将错误的字节码开头修改成正确的后方可进行反编译。

       另外一种广泛使用的lua虚拟机是luaJIT,其优势是性能优越,同时支持ffi,能方便的集成到C语言中实现。LuaJIT虚拟机字节码文件的开始是以"\x1B\x4C\x4A"作为开始的,在2.1节中已经描述。目前最新的luajit虚拟机版本是LuaJIT 2.1.0-beta3 ,在The LuaJIT Project这个网站上(http://luajit.org/)可以下载到luaJIT虚拟机的各个版本。在今年的题目中其使用的就是LuaJIT 2.1.0-beta3的虚拟机版本,在这里作者并没有修改Lua虚拟机,使得题目相对比较容易做出来。

        该节对Lua虚拟机进行了简单的介绍,关于Lua虚拟机源码解读的文章,网上有很多,CSDN上有个专栏是探索Lua5.2的内部实现(http://blog.csdn.net/yuanlin2008/article/category/1307277),自己还没完整读过。关于luaJIT的源码阅读,这里找到一个简单的LuaJIT源码分析的Pdf(https://www.gitbook.com/book/gaoxiaojun/luajit-source-code-analysis/details),但是其没有叙述完整,只描述了bytecode部分内容。

2.3 lua字节码文件反编译

       在进行程序分析中,大部分程序不是直接将lua脚本嵌入到程序中,而是将脚本文件编译成字节码文件,然后通过lua虚拟机的luaL_loadbuffer 来进行加载,而且很多程序都会将lua字节码文件进行加密或者部分opcode的修改,导致常规工具解密失败或者无法解密。因此在实际分析时一般是定位到lualL_loadbuffer函数(该函数用于动态加载lua字节码文件),然后从内存中直接dump出lua字节码文件,再利用相应的工具进行反编译最后得到lua脚本文件,这部分可以参考看雪CTF2016第二题http://www.10tiao.com/html/523/201611/2458280475/1.html在实际应用中一些较复杂的时可能会修改lua虚拟机的源代码,从而使得提取lua字节码文件比较困难,常见的是很多外挂会将lua字节码文件进行加密后存放在文件中,在使用时动态解密后再加载,这时可以在luaL_loadbuffer时对内存进行dump从而获取相应的字节码文件。更加复杂的情况是外挂作者会对lua虚拟机的源码进行修改,从而使得直接dump会失败,比如对luaL_loadbuffer的源码进行修改,在luaL_loadbuffer函数里面进行解密后再进行加载,这时不能直接在函数外层进行dump,而应该进入到luaL_loaderbuffer函数进行等解密完成后再进行dump。一些还会对虚拟机源码中的关于字节码处理的代码进行修改,使得某个关键字节码对应的语言信息发生改变,从而使得常规的反编译工具反编译失败,这种情况下也得分析lua虚拟机的源码,找到作者修改的位置,并在字节码文件中将相应的字节码替换为原来的正确字节码,从而使得普通反编译工具能够反编译成功。关于如何获取luaL字节码,可以查看看雪论坛的另外这篇文章“浅析android手游lua脚本的加密与解密http://bbs.pediy.com/thread-216969.htm,其详细进行了描述,也分享了案例,这里有很多内容可以参考。下面简单叙述下常规情况下如何对lua字节码文件进行反编译。目前对原生luac字节码文件进行反编译的工具一般是使用luacdec(http://luaforge.net/projects/luadec51/)这个工具,,该工具用于对lua5.1的bytecode进行反编译。其一般命令如下:

luacdec.exe -d filename
注:luacdec -o [编译后脚本名称] [脚本名]

       

       而针对luaJIT生成的字节码文件,一般是利用luajit的命令行工具,将字节码转换成可读的模式。在实际使用中对luaJIT字节码文件进行反编译一般是使用luajit-decomp这个工具来对luaJIT字节文件进行反编译,该工程项目文件github地址为如下:https://github.com/bobsayshilol/luajit-decomp。该工程可以对luac和luajit等字节码文件都进行反编译,但是该工程默认的lua版本是5.1,而luajit的版本为2.0.2,因此在实际使用中,如果需要反编译不同版本的lua字节码文件,则需要下载相应版本的lua和luajit的源码,进行编译后替换掉原文件夹中的lua51.dll和luajit.exe。

    

      上图是一个修改过的对luajit-decomp进行修改过的工程,用于对lua字节码进行反编译,并且已经编译了用于解密2.0.2,2.0.3和2.1.0三个版本的字节码dll和exe,分别放在2.0.2,2.0.3,2.1.0三个文件夹下,如果反编译时需要用到哪个版本的luajit.exe 进行反编译,则直接在对对应的目录下将相应的luaJit.exe和lua51.dll拷贝并替换到主目录下的文件即可。工具被放在了附件中,具体下载见附件,使用时在data文件夹中包含三个文件,其中在luajit中存放luajit字节码文件:

       放置完成后,直接运行decoder.exe,则会进行批量反编译,将luaJit下面的所有的字节码文件进行反编译,最后在data文件夹的out目录中生成相应反编译后的文件。如果出现解密失败,那么就必须要考虑上面提到的问题,是否是版本不匹配,作者是否是修改了luajit虚拟机的源代码,导致字节码文件中的一些字节被修改或者替换,从而需要进行修正后在进行解密。该工具引用自看雪文章“浅析android手游lua脚本的加密与解密”作者所提供。在附件中也会提供相应的下载,解密本题目时也是使用了该工具。

3、题目分析和解题思路

3.1 题目分析

       现在该轮到题目分析了,这个题目放出来两个小时就被id为“风间仁”的选手搞定,如本次比赛组织者“netwind”所说“已经到了在用意念做题”的地步,膜拜一下。我想攻击者这么牛,也是经过很多磨炼和无数的努力才能达到此登峰造极的地步。所谓“台上一分钟,台下十年功”,做任何事情都不容易,但只要努力就肯定能战胜。

       下面开始分享一下自己的解题过程,拿到题目直接运行,随便输入序列号,提示Wrong!

       于是按照常规思路开始上调试器,拖到IDA里面静态分析,发现被加壳,开始跟踪分析用普通的OllyICE被提示检测到调试器:

       于是换上HawkOD,以及strongOD等插件,反调试被搞定,于是脱壳,试了所有能够尝试的各种脱壳手段,发现不行,于是单步跟着走,发现调用VirtualAlloc申请空间,然后利用拷贝数据到申请空间中,最后有利用VitualProtect改变代码段的权限,将数据写入到.txt代码段中,跟踪到0x00401000等位置处被解密出来,以为字节能搞定,跟着程序继续走,但是到后面又跑丢了或者程序异常,搞了半天也没搞定,于是放弃纠缠。换思路,不再纠缠于于脱壳,直接将程序运行起来,然后附加上HawkOD,这时一切正常,看了一下0x00401000等位置处的相关代码,发现也和之前分析到的解密后代码相同,初步判定解密完成。为了方便了解程序流程,还是从0x00401000出开始进行了内存dump,拖到ida中,发现还是很大部分函数能够解析,但后面分析发现这些dump出来的内容对分析程序帮助不大,这种方法在对付一些已经过了反调试,但是无法进行完整脱壳时还有很有用的,在实际分析中,经常会遇到很多壳无法完整的跟踪或者脱掉,那么就等程序运行起来后,在附加上调试器,然后直接从内存中dump出代码,拖到ida里面查看,很多时候我们只需要分析到程序的核心跳转流程就行了,并并不需要完整的脱壳。

        于是开始在调试器里面分析,大体思路就是利用ALT+M在,然后ctrl+B在内存中搜索相关的字符串,比如输入的字符串,或者提示Wrong!等字符,然后设置硬件内存断点,查看程序访问的位置,经过多次调试分析,总共设置了20多个断点,大概分析到一些关键代码位置,但是刚开始分析了很久还是不明白,由于刚开始不对Lua不熟悉,经常跟踪到lua的虚拟机中,然后半天被绕晕,不知所云。下面大概截取一些在分析过程中通过设置断点定位到的关键代码位置:

     位置1:0x40103d,写入luaJIT字节码文件,在这里向esp+0x36位置写入字节码问文件内容,esp+0x30中存放的是字节码文件开始的六个字节,其开始为\x1B\x4C\x4A

       写入完成后总共有0x275个字节的字节码文件,写入完成后内存中的字节码文件内容如下所示:


       但是由于不熟悉LuaJIT字节码文件,依然没看明白是啥东西,尴尬!然后接着分析:

在0x4021CF

          在写入字节码文件中,后面的函数分别是完成了LuaJIT虚拟机初始化以及Loal_loadbuffer加载luajit字节码文件等,入上图所示。其中在0x004021CF处调用函数sub_00412270是加载了luaJIT的相关库,并完成了初始化等工作。开始不熟悉,跟进了该函数,走了半天,也正是该函数里面的内容给予了自己的提示,因此遇到不明白的就多跟着程序走,多走几次,总会看出点东西来,程序使用了混淆技术,走了很久终于发现了一些有意思的东西,在0x42a088的Call EDI会被调用多次,里面进去后会出现很多有意思的字符串。后面经过和源码确认是此处循环多次执行是加载了多个lua虚拟机的核心的库。。

         第一次进入后出现了一些关键字符串,如下所示

          有Lua 5.1 coroutine,这些信息本来可以作为提示了其可能使用了Lua 虚拟机,但是由于不熟悉了,还没是没有识别出来,但是后面经过分析其使用的是Luajit beta 2.1.0的虚拟机,这个函数多次被调用,第二次调用相关信息如下:


       有大量的lua字符串出现,这次开始觉得有点可疑,应该是使用了什么工程的源代码。这个给一个自己以前分析的经验,一般在程序中看到有很多有意义的字符串,但是又不像是作者自己写的,那多半是用了什么开源库,之前自己分析一个加密软件时,发现其使用了很多crypt先关的字符串,利用字符串在google进行搜索,确认了是使用了openssl库,一般确认了使用什么开源库时,分析就简单了,这是只需要找到源码,然后对应分析知道其调用的函数,最后分析就方便很多了。同样的思路,直接上谷歌,关键词crackme 加lua,一搜索,出现的居然是看雪CTF2016第二题的信息,初略看了下,知道了lua在crackme中的应用,但是自己去年没做啊。


         继续搜索,发现了HighHand大神关于关于第二题的解题思路(http://bbs.pediy.com/thread-213697.htm),里面提到标准的luac字节码文件开始为0x1B,0x6C,0x73,0x11,只是在那个题目中作者将其修改成了0x1B,0x6C,0x73,0x11,0x00;


          但是和本题目的开始的几个字节也对不上啊,于是继续分析,在后面0x42A088处有多次调用了call edi这个函数,后来经过确认是调用的luaL_openlibs这个函数里面的,分别加载了相关的模块,下面是调用luaopen_table:


       调用luaopen_io:


         调用luaopen_os:

          调用luaopen_string:


调用luaopen_math

调用luaopen_debug


调用luaopen_bit:

           上述过程后来经过下载luajit beta 2.1,0的源代码比对确认后确认实际上执行luaL_openlibs,这里面通过调用lj_lib_load这个函数,分别执行了上述功能。


        在最后一个执行luaopen_jit时出现了LuaJIT 2.1.0-beta3字符串,这里才提醒了自己,于是上google查选,发现LuaJIT 2.1.0-beta3是lua虚拟机的另外一个版本,而且起luaJIt脚本文件开始确实也是0x1B,0x4C,0x4A,越是下载了luaJIT 2.1.0-beta3的源码,经过字符串对比分析后才还原确认上前面分析的函数。、

       下面就是调用最后一个luaopen_jit函数。

       这是在源码该函数,可以和上述匹配起来。

        由于确认了上述信息,通过了解可知,其肯定会调用luaL_loadbuffer动态加载字节码文件,于是进一步确认了0x004021e0执行的是:luaL_loadbuffer函数。


         于是从内存中luajit字节码文件提取出来并进行反编译,使用的是luajit-decomp来对文件进行反编译,放在了附件中可以下载后使用,具体请查看2.3节的使用。解密最后得到lua脚本如下:

-- BYTECODE -- lua.bytes:0-0
function someFunc2()
var_2_4 = INPUT_VAR_0_
var_2_5 = INPUT_VAR_1_
var_2_6 = INPUT_VAR_1_
var_2_3 = string.sub(var_2_4, var_2_5, var_2_6) --var_2_3 REPLACE-REPLACE
string.byte(var_2_3)
end
-- BYTECODE -- lua.bytes:0-0
function someFunc3()
var_3_2 = INPUT_VAR_0_
var_3_1 = string.len(var_3_2)
if var_3_1 ~= 0 then
--jump to 0009 (if previous if statement is false) --0009 JMP-JMP
var_3_1 = 0 --var_3_1 NUMBER-NUMBER
return var_3_1
--location 0009--0009 LOCATION-LOCATION
var_3_3 = INPUT_VAR_0_
var_3_4 = 1 --var_3_4 NUMBER-NUMBER
var_3_2 = by(var_3_3, var_3_4)
var_3_3 = 112 --var_3_3 NUMBER-NUMBER
var_3_1 = bit.bxor(var_3_2, var_3_3)
var_3_4 = INPUT_VAR_0_
var_3_5 = 2 --var_3_5 NUMBER-NUMBER
var_3_3 = by(var_3_4, var_3_5)
var_3_4 = 101 --var_3_4 NUMBER-NUMBER
var_3_2 = bit.bxor(var_3_3, var_3_4)
var_3_5 = INPUT_VAR_0_
var_3_6 = 3 --var_3_6 NUMBER-NUMBER
var_3_4 = by(var_3_5, var_3_6)
var_3_5 = 100 --var_3_5 NUMBER-NUMBER
var_3_3 = bit.bxor(var_3_4, var_3_5)
var_3_6 = INPUT_VAR_0_
var_3_7 = 4 --var_3_7 NUMBER-NUMBER
var_3_5 = by(var_3_6, var_3_7)
until false or (previous if statement is true) --location 0039
var_3_6 = 105 --var_3_6 NUMBER-NUMBER
var_3_4 = bit.bxor(var_3_5, var_3_6)
var_3_7 = INPUT_VAR_0_
var_3_8 = 5 --var_3_8 NUMBER-NUMBER
var_3_6 = by(var_3_7, var_3_8)
var_3_7 = 121 --var_3_7 NUMBER-NUMBER
var_3_5 = bit.bxor(var_3_6, var_3_7)
var_3_8 = INPUT_VAR_0_
var_3_9 = 6 --var_3_9 NUMBER-NUMBER
var_3_7 = by(var_3_8, var_3_9)
var_3_8 = 49 --var_3_8 NUMBER-NUMBER
var_3_6 = bit.bxor(var_3_7, var_3_8)
var_3_9 = INPUT_VAR_0_
var_3_10 = 7 --var_3_10 NUMBER-NUMBER
var_3_8 = by(var_3_9, var_3_10)
var_3_9 = 50 --var_3_9 NUMBER-NUMBER
until false or (previous if statement is true) --location 0064
var_3_7 = bit.bxor(var_3_8, var_3_9)
var_3_10 = INPUT_VAR_0_
var_3_11 = 8 --var_3_11 NUMBER-NUMBER
var_3_9 = by(var_3_10, var_3_11)
var_3_10 = 51 --var_3_10 NUMBER-NUMBER
var_3_8 = bit.bxor(var_3_9, var_3_10)
var_3_11 = INPUT_VAR_0_
var_3_12 = 9 --var_3_12 NUMBER-NUMBER
var_3_10 = by(var_3_11, var_3_12)
var_3_11 = 52 --var_3_11 NUMBER-NUMBER
var_3_9 = bit.bxor(var_3_10, var_3_11)
var_3_12 = INPUT_VAR_0_
var_3_13 = 10 --var_3_13 NUMBER-NUMBER
var_3_11 = by(var_3_12, var_3_13)
var_3_12 = 53 --var_3_12 NUMBER-NUMBER
var_3_10 = bit.bxor(var_3_11, var_3_12)
var_3_13 = INPUT_VAR_0_
var_3_14 = 11 --var_3_14 NUMBER-NUMBER
var_3_12 = by(var_3_13, var_3_14)
var_3_13 = 54 --var_3_13 NUMBER-NUMBER
var_3_11 = bit.bxor(var_3_12, var_3_13)
var_3_14 = INPUT_VAR_0_
var_3_15 = 12 --var_3_15 NUMBER-NUMBER
var_3_13 = by(var_3_14, var_3_15)
var_3_14 = 55 --var_3_14 NUMBER-NUMBER
var_3_12 = bit.bxor(var_3_13, var_3_14)
var_3_13 = var_3_1
var_3_14 = var_3_2
var_3_15 = var_3_3
var_3_16 = var_3_4
var_3_17 = var_3_5
var_3_18 = var_3_6
var_3_19 = var_3_7
var_3_20 = var_3_8
var_3_21 = var_3_9
var_3_22 = var_3_10
var_3_23 = var_3_11
var_3_24 = var_3_12
return var_3_13, var_3_14, var_3_15, var_3_16, var_3_17, var_3_18, var_3_19, var_3_20, var_3_21, var_3_22, var_3_23, var_3_24
end
-- BYTECODE -- lua.bytes:0-0
function someFunc4()
var_4_1 = "bit" --var_4_1 STRING-STRING
require(var_4_1)
local randomFunction43 = function() end -- starts at  lua.bytes:0
by = randomFunction43
local randomFunction44 = function() end -- starts at  lua.bytes:0
main = randomFunction44
return
end

         脚本比较简单,核心是someFunc3函数,将输入字符串中每次取出特定的字符来进行异或,最后返回所有字符串,这里需要by函数,该函数等于function(),而function函数又等于someFunc4,主但是是someFunc4这个函数里面并没有声明,但是根据提示function开始于lua.bytes,因此确认somFunc4就是lua开始位置的函数someFunc2()


        由于函数比较简单,这里不做分析。继续回到C代码中,往后面起多次调用了sub_00412ce0这个函数,这里进去后会调用lua脚本文件中的sumFunc3这个函数,每次在字符串中进去截取特定字符的偏移,脚本中其处理使用了sub函数来取出特定位置的字符,这里从字符串末尾往前计算位置,因此传入字符串的偏移从-12到-1,实际上也是从第一个开始取到最后一个,总共调用了12次。

           每次计算完成有得到的结果又和特定的字符再进行了异或操作。最后在0x4022f3出开始依次进行比较。

3.2 题目解密过程

        分析玩题目过程后,由于起算法比较简单,只是进行两次异或加密后最后进行比较,因此得到如下解密代码:

#encoding:utf-8
comparebytes= [0x18,0x16,0x1E,0x2F,0x48,0x11,0x21,0x37,0x33,0x86,0x52,0x94]
def decrypt(comparebytes):
    flag = ''
    flag+=chr(comparebytes[0]^0x05^112)
    flag+=chr(comparebytes[1]^0x12^101)
    flag+=chr(comparebytes[2]^0x0A^100)
    flag+=chr(comparebytes[3]^0x29^105)
    flag+=chr(comparebytes[4]^0x42^121)
    flag+=chr(comparebytes[5]^0x41^49)
    flag+=chr(comparebytes[6]^0x75^50)
    flag+=chr(comparebytes[7]^0x61^51)
    flag+=chr(comparebytes[8]^0x35^52)
    flag+=chr(comparebytes[9]^0x83^53)
    flag+=chr(comparebytes[10]^0x55^54)
    flag+=chr(comparebytes[11]^0x94^55)
    return flag
print decrypt(comparebytes)

        运行脚本得到flag为:maposafe2017,提交答案正确。

4. 总结 

       总的来说,最后一题题目简单,算法比较容易,如比赛组织者说最后一题就是让大家愉快的结束比赛,但是由于自己对Lua的不熟悉,加上去年没有及时关注这类型题目,还是花费了大量的时间。因此给予自己的要想提高CTF比赛能力,一定要多看,多动手实践,平时更多的是注重积累,对以前的题目都拿出来分析一下,都自己提高能力有很大的帮助。看雪平台是个很好的平台,多去拜读大牛的作品和精华帖子,看雪组织这样的比赛,也给大家提供了交流学习的平台,祝看雪越办越好。加油!




[招聘]欢迎市场人员加入看雪学院团队!

上传的附件:
打赏 + 5.00
打赏次数 1 金额 + 5.00
收起 
赞赏  CCkicker   +5.00 2017/07/12
最新回复 (21)
kanxue 8 2017-7-1 16:43
2
0
文章很详细!赞
liuluping 2 2017-7-1 16:50
3
0
谢谢,能否帮我放到看雪CTF2017的writeUp板块区域啊,一定更加努力!
爱琴海 13 2017-7-1 21:57
4
0
学术味道
netwind 13 2017-7-1 22:19
5
0
很认真和谦逊!赞
Angelxf 6 2017-7-2 06:18
6
0
叼逼!膜拜一下
Angelxf 6 2017-7-2 06:22
7
0
爱琴海 学术味道
不知道爱琴海大神是什么意思?
wsc 2017-7-2 08:03
8
0
大神膜拜
维一零 4 2017-7-2 08:53
9
0
写得真好,这篇文章将会成为很多初学者学习逆向Lua的参考,所以希望作者再花点时间修正一下文中的一些错别字哈。另外,文章后面提到代码调用了12次somefunc3,这个地方好像不对,可以在Ida里交叉引用看到另外一个库函数调用了该函数,所以该函数应该也是一个库函数,我识别出来应该是把Lua栈指定位置的数据转成整数的那个函数。
liuluping 2 2017-7-2 11:02
10
0
好的,文中有好多错别字,那个地方可能是没太对,更正一下,谢谢指正,更加努力!
爱琴海 13 2017-7-2 12:01
11
0
Angelxf 不知道爱琴海大神是什么意思?
就说他写的是文章,很端正的学术风格,值得大家学习
liuluping 2 2017-7-2 12:12
12
0
爱琴海 就说他写的是文章,很端正的学术风格,值得大家学习
谢谢鼓励,继续加油!
Angelxf 6 2017-7-2 12:22
13
0
爱琴海 就说他写的是文章,很端正的学术风格,值得大家学习
Rookietp 2017-7-2 19:37
14
0
我记得luajit  还可以用luajit  -  什么的命令来解压.
布拉德皮蛋 2017-7-2 21:12
15
0
太厉害了,666
cqzhou 2017-7-3 10:52
16
0
新手学习  Mark
wanglisong 2017-7-4 09:06
17
0
刘师兄  就是棒
koflfy 1 2017-7-9 17:14
18
0
这分析,厉害了。。mark之。
fanggame 2017-7-25 22:49
19
0
---------------------------
AutoIt  Error
---------------------------
Line  791    (File  "F:\luajit-decomp\decoder.exe"):
Error:  Variable  must  be  of  type  "Object".
---------------------------
确定     
---------------------------
问下这个怎么回事呢????
泪落晨曦 2018-3-28 15:12
20
0
请教下楼主,我使用这个luajit  dec这个反编译工具的时候也遇到了这个问题,请问下要怎么解决呢
---------------------------
AutoIt    Error
---------------------------
Line    791        (File    "F:\luajit-decomp\decoder.exe"):
Error:    Variable    must    be    of    type    "Object".
---------------------------
确定           
---------------------------
wodexinren 2018-4-4 14:24
21
0
学习,感谢分享
陈jack 2018-10-13 17:24
22
0
很好的文章
游客
登录 | 注册 方可回帖
返回