Web – Triangle – WriteUp

最近看了一道 JavaScript 逆向 题目 , 这是我第一次遇到JS逆向 , 对我来说难度很大 , 没有做出来

并且最初网上各种 WriteUp 也没有看懂 , 经过学习整理后在下面记录自己的思路与解题过程


Triangle

  • 题目界面是这样的

    png

    查看源码后发现页面加载了三个JS文件并找到了login()函数 , 除此之外还根据不同浏览器分别定义了三个变量 o1 , o2 , o3

    png

    引入的三个JS文件肯定比较关键 , 之后再看

    png

    通过这个login()函数可以看到存在三个关键函数 , enc_pw() , get_pw() , test_pw()

    并且知道当 test_pw(enc_pw(document.getElementById('password').value) , get_pw()) == 1 时 , 可以成功拿到Flag

    png

    这些变量定义的有点吓人 , 先不去管他 , 之后的代码应该会使用这些变量

  • Util.js

    查看 util.js 文件 , 发现里面定义了三个函数

    png

    经过反混淆后 , 三个函数定义如下

    png

     charCodeAt() : 返回指定位置的字符的 Unicode 编码
    
     fromCharCode() : 接受一个指定的 Unicode 编码 , 返回一个字符串
    

    这些函数将会被调用 , 它们的意思都不难理解

  • Unicorn.js

    Unicorn.js 是一个 Javascript 实现的开源CPU模拟器 , 在 JavaScript中文网 中是这么介绍它的

    png

    尽管它的官方文档现在无法访问 , 但可以从官方的demo中了解如何使用 Unicorn.js

    png

  • Secret.js

    该文件是本题的核心 , 其中定义了 enc_pw() , get_pw() , test_pw() 三个函数

    png

    经过反混淆后 , 三个函数的定义如下

    • enc_pw(e)

    png

    • test_pw(e,_)

    png

    • get_pw()

    png

  • 现在从每一个函数入手 , 看一下它们到底有什么作用

    • enc_pw(e)

      分析代码后 , 发现该函数是利用ARM架构的模拟器来实现加密函数 , 通过下面这个循环 , 将 o2[a] 数组中的内容加密( 变为 x[o2[a]] )后写入内存

      png

      为了更好的理解这个加密函数的流程 , 可以选择将 R.mem_write() 函数写入内存的数据( 也就是x[o2[a]] 数组中的值) 取出并转换为ARM代码来分析 , 因此可以构造如下代码

      png

      通过上面代码 , 将 x[o2[a]] 中每一个值都传递给 output[a] 数组 . 输出 output[] 数组内容 , 就得到了加密过程.

      但这并不是结束 , 我们需要将这个加密过程转换成可读的ARM代码( 也就是汇编语言 ) , 而 getARM1() 返回的是十进制整数 . 并且我只找到了 HEX -> ARM 的工具 . 为了方便可以将十进制数据转换为十六进制数据

      JavaScript一个经典的十六进制转换函数

      png

      然后运行 toHexString(getARM1()) 可以得到加密过程的十六进制字符串

      png

      再利用找到的 在线HextoArm转换器 , 将十六进制字符串转换为汇编代码

      png

      注意这里要选择 ARMv7 ARM , 将汇编代码保存下来

    • get_pw()

      该函数没有参数 , 直接运行就可以的到一个字符串 , 这个字符串在后面 test_pw() 函数中会用到 , 保存下来

      png

    • test_pw(e,_)

      前面已经知道本题的目标是 : test_pw(enc_pw(document.getElementById('password').value) , get_pw()) == 1

      所以这里 e 就是用户输入的内容通过enc_pw()函数后返回的内容 , 而 _ 则是 get_pw()函数的返回值 , 并且是已知的 , 为 "XYzaSAAX_PBssisodjsal_sSUVWZYYYb"

      test_pw()enc_pw() 两个函数结构非常类似 , 所以这里同样尝试读取 a.mem_write() 函数写入内存中的值( t[o1[o]] ) , 并将返回数据转换为ARM代码

      png

      png

      将汇编代码保存 , 稍后查看

  • 汇编代码分析

    问过的大佬都说使用 IDA PRO 来分析这些汇编代码 , 但我以前没接触过逆向 , 所以不会用 IDA PRO ... 但本题代码比较简单 , 可以自己手动分析

    下面是我自己分析的结果 , 注释写在一旁

    • enc_pw(e)

      png

      代码的逻辑是这样的

      png

      说几个我认为的重点

      1. R5 什么时候为 1 ?

        寄存器 R5 值的变化与 AND R5, R4, #1 这句代码相关 , 将 R4 与 立即数1 进行逻辑与操作 , 将结果赋值给 R5 . 看下图

        png

        可知 : 当 R4 为奇数时 , R5 == 1

      2. R3 什么时候大于 R2 ?

        当 R3 < R2 时 , 程序会跳转到开头部分( 也就是 LDRB R4, [R0] ) , 然后重新开始比较 .

        寄存器 R3 的初值为 0 , 每个循环会递增+1 ( ADD R3, R3, #1 ) . 因此这里 R3 可以看做是一个递增的计数器

        寄存器 R2 的值仅在程序开始时被定义( MOV R2, SL ) , 这里 SL == R10 , 而在JS代码中 , R10 被定义为 e.length ( R.reg_write_i32(uc.ARM_REG_R10,e.length) ) . 因此这里 R2 的值为 e.length , 也就是用户输入字符串的长度

    • test_pw(e,_)

      png

      代码逻辑是这样的

      png

      说几个我认为的重点

      1. 我们需要什么 ?

        注意JS中的代码 : a.reg_read_i32(uc.ARM_REG_R5) , 最后我们需要读取寄存器 R5 的值 , 所以这里需要程序执行到 MOV R5, #1 . 如何满足这个条件 ? 至少需要 R2 = R6 且 R4 >= R3

      2. R2 什么时候大于等于 R6 ?

        直接看这个流程图

        png

      3. 什么时候 R4 >= R3 ?

        R4 初值为 0 , 根据 ADD R4, R4, #1 , 可以把R4 看成一个从 0 开始的递增计数器

        MOV R3, R8 , 根据 a.reg_write_i32(uc.ARM_REG_R8,_.length) 可以看出 R8 = _.length . 别忘了我们真正需要的是test_pw(enc_pw(document.getElementById('password').value) , get_pw()) == 1 , 因此这里 _ 也就是 get_pw() 的返回值

        get_pw() 这个函数没有参数 , 直接运行可以的到返回值

        png

  • 逆向构造 JavaScript 函数

    在搞明白这些汇编代码的流程后 , 需要逆向构造 JS 代码 , 得到ARM加密前的数据信息

    • Testrev()

      png

      可以把 i 看成 r4 , 把 a 看成 r6 , 这样比较容易理解

    • Encrev()

      png

      可以把 i 看成 R3 , sub 看成 R6 , x 看成 R4 , 这样比较容易理解

  • 拿到 Flag

    现在已经构造出了 test_pw() 的逆向函数 Testrev()enc_pw() 的逆向函数 Encrev() , 在配合已有的 htos() 函数 , 可以得到一个字符串 , 这个字符串就是加密前的内容 , 即为 flag !

    png

    Flag 即为 : flag:{MPmVH94PTH7hhafgYahYaVfKJNLRNQLZ}


总结

这是我第一次接触到 JavaScript 逆向的题目 , 做的很吃力 . 但是CTF本身就是一个学习的过程 , 这题让我收获了很多很多!

但题目中依旧有一些点我还不太明白 . 在彻底搞透本题后 , 我会完善这篇文章 , 本文仅供参考 , 如果您有什么更好的见解 , 欢迎留言~

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇