[SUCTF 2025]SU_Harmony
本文最后更新于7 天前,其中的信息可能已经过时,如有错误可以直接在文章下留言

最近第一届OpenHarmony CTF专题赛要开始了,报名了看看题。其实去年开始就陆续出现鸿蒙应用逆向的题目,第一次出现是在2024的京麟杯吧,而且题目非常难。现在就零时抱佛脚一下,复现一下之前其实就挺想复现的鸿蒙逆向的题目。

题目描述

//太好了,是数学大师,我们有救啦
//Great! It’s the Math Master, we are saved!

附件给了一个entry-default-unsigned.hap文件

应用程序包基础知识-开发基础知识-基础入门 – 华为HarmonyOS开发者

看看官方介绍

HAP(Harmony Ability Package)是应用安装和运行的基本单元。HAP包是由代码、资源、第三方库、配置文件等打包生成的模块包,其主要分为两种类型:entry和feature。

  • entry:应用的主模块,作为应用的入口,提供了应用的基础功能。
  • feature:应用的动态特性模块,作为应用能力的扩展,可以根据用户的需求和设备类型进行选择性安装。

应用程序包可以只包含一个基础的entry包,也可以包含一个基础的entry包和多个功能性的feature包。

和apk文件一样,其实是个zip,解压可以看到里面的文件,本体的结构如下

可以看到这里的libs目录下的so文件肯定要关注,应该和分析安卓so文件的流程差不多。

然后是关注这里的ets/modules.abc,这里的abc文件,官方介绍如下

方舟字节码(ArkCompiler Bytecode)文件,是ArkCompiler的编译工具链以源代码作为输入编译生成的产物,其文件后缀名为.abc。在发布态,abc文件会被打包到HAP中。

不知道全称的以为华为命名文件这么草率😅,鸿蒙应用的开发看官网是使用自己研发的ArkTS语言,应该是对应安卓开发的Java/Kotlin语言,所以这里的abc文件也对应安卓的dex文件。

abc字节码文件的反编译工具有abc-decompiler,这里Java版本太低打开会报错

ohos-decompiler/abc-decompiler

然后搜了一下最近刚好出了在线的反编译器.abcD

HarmonyOS NEXT鸿蒙应用反编译器 .abcD 发布试用 – 知乎

.abcD

abc-decompiler反编译的内容很多这种奇奇怪怪的字符,看起来不太舒服,估计还是基于Jadx的兼容不能做到完美

.abcD反编译出来的代码就比较干净,而且已经不像是Java代码了,abc-decompiler这边还是全是public属性的函数,当然我也不知道ArkTS语言是什么样的,不知道谁反编译的效果更好一些。

分析过程中一般需要关注的是源代码中main包的pages目录,这里包含了该程序中的所有的页面,这里只有一个Index页面。

abc-decompiler这边反编译了出入口函数func_main_0,用于进行初始化。.abcD网站则没用反编译出这样的函数名。

    public Object func_main_0(Object functionObject, Object newTarget, Index this) {
        newlexenvwithname([3, "Index", 0, "4newTarget", 1, "this", 2], 3);
        _lexenv_0_1_ = newTarget;
        _lexenv_0_2_ = this;
        if (isIn("finalizeConstruction", ViewPU.prototype) == false) {
            Reflect.set(ViewPU.prototype, "finalizeConstruction", #*#);
        }
        obj = ViewPU.#~@0=#Index(Object2, Object3, ViewPU, ["setInitiallyProvidedValue", "&entry/src/main/ets/pages/Index&.#~@0>#setInitiallyProvidedValue", 1, "updateStateVars", "&entry/src/main/ets/pages/Index&.#~@0>#updateStateVars", 1, "purgeVariableDependenciesOnElmtId", "&entry/src/main/ets/pages/Index&.#~@0>#purgeVariableDependenciesOnElmtId", 1, "aboutToBeDeleted", "&entry/src/main/ets/pages/Index&.#~@0>#aboutToBeDeleted", 0, 4]);
        obj2 = obj.prototype;
        obj2["message"].getter = obj2.#~@0>#message;
        obj2["message"].setter = obj2.#~@0>#message^1;
        obj2["inputBackgroundColor"].getter = obj2.#~@0>#inputBackgroundColor;
        obj2["inputBackgroundColor"].setter = obj2.#~@0>#inputBackgroundColor^1;
        obj2.handleInput = obj2.#~@0>#handleInput;
        obj2.initialRender = obj2.#~@0>#initialRender;
        obj2.rerender = obj2.#~@0>#rerender;
        obj.getEntryName = obj.#~@0<#getEntryName;
        _lexenv_0_0_ = obj;
        registerNamedRoute(#*#^1, "", createobjectwithbuffer(["bundleName", "com.swdd.suapp2", "moduleName", "entry", "pagePath", "pages/Index", "pageFullPath", "entry/src/main/ets/pages/Index", "integratedHsp", "false", "moduleType", "followWithHap"]));
        return null;
    }

这里有一个handleInput ,我们追踪一下找到关键的逻辑

这里应该就是调用了libentry.so文件中的check函数来检查输入,要求函数的返回值为right。

再提到ArkTS的一些关键部分

TextInput是鸿蒙 ArkUI 中的一个重要组件,用于创建用户可交互的输入框。它通常用于响应用户的输入操作,那这里提交之后应该触发onSubmit事件,这里在abc-decompiler中就不知道这个事件触发后执行哪里的代码。.abcD可以看到,这里就直接跳转到handleInput函数,所以说反编译器果然都是各有优点

这里有个onClick事件也会触发handleInput函数,没有鸿蒙真机或者模拟器,就无法知道其应用布局,也无法动态分析,这也是鸿蒙逆向出题人和做题人都面对的困境,出题人出不了需要动态分析的,因为我们根本做不了,反正目前鸿蒙逆向仅靠静态分析是可以做的。

那我们就看so文件。

这是一个入口模块的注册函数,点击unk_F390,引用entry字符串可以找到

再追踪

这里的sub_3750函数应该就是abc层当中调用的check函数。或者

也能找到sub_3750函数,这里sub_3750函数的代码量很长,看了很多文章都能看出是对代码进行了混淆,如果是当时的我来做估计就看不出,然后被这一大坨代码恶心到,然后放弃了😢。

代码很复杂,不过可以通过一些打印语句提取关键信息

flag的长度是32,再跟踪这里的input变量

这里复制了元素,再跟踪copy

这里应该是通过两层for循环把输入分为4组,每次将8个字节传入了函数sub_57B0,同时还传入了一个数组,这些数组都是一些大数。前面元素的复制也是两层for循环,分为4组,和这里一样的操作。

这里的sub_57B0应该就是我们要进行解密的一个逻辑

可以发现对我们的输入进行处理,然后和大数数组的元素进行对比,这里的函数参数也是逐渐传递,所以s1很明显就是对输入处理之后的结果。

这几个函数依旧有混淆,但是可以喂给AI,可以加快分析

分析完之后大概就是这样

所以就是解这样的一个方程

参考其他战队的解题脚本可以直接解

import math
from Crypto.Util.number import long_to_bytes

s = [
    999272289930604998,
    1332475531266467542,
    1074388003071116830,
    1419324015697459326,
    978270870200633520,
    369789474534896558,
    344214162681978048,
    2213954953857181622,
]
def decrypt(enc):
    e = enc * 2
    d = e + 3
    a = -1 + math.sqrt(1 + d)
    a = int(a)
    return a
for i in s:
    a = decrypt(i)
    print(long_to_bytes(a)[::-1].decode(), end="")
# SUCTF{Ma7h_WorldIs_S0_B3aut1ful}

或者z3解

from z3 import *
dst = [999272289930604998, 1332475531266467542, 1074388003071116830, 1419324015697459326, 978270870200633520, 369789474534896558, 344214162681978048, 2213954953857181622]
flag = b''
solver = Solver()
x = [Int(f"x{i}") for i in range(8)]
for i in range(8):
    solver.add(x[i] > 0)
    expr = (x[i]**2 + 2*x[i] - 3) / 2 - dst[i] # 整除
    solver.add(0 <= expr)
    solver.add(expr < 1)
if solver.check() == sat:
    m = solver.model()
    ans = [m[x[i]].as_long() for i in range(8)]
    flag = b''.join([y.to_bytes(4, 'little') for y in ans])
print(flag)
# b'SUCTF{Ma7h_WorldIs_S0_B3aut1ful}'

所以flag就是SUCTF{Ma7h_WorldIs_S0_B3aut1ful}

文末附加内容
暂无评论

发送评论 编辑评论


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