吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 15046|回复: 84
收起左侧

[Android CTF] XCTF黑客精神—Frida RPC爆破

  [复制链接]
genliese 发表于 2021-4-9 21:04
本帖最后由 genliese 于 2021-4-21 11:16 编辑

1. 背景

此题比较简单,网上也有各种各样的wp。wp可以分为两类,一类分析算法,另一类是爆破。此题采用的是异或算法,所以分析算法求flag是最快的。而爆破的话,可以重写成等价的代码,如C++、python,或者直接采用主动调用的方式进行爆破,如Frida的主动调用。采用主动调用的好处是不用重写,而我采用Frida RPC进行主动调用的目的是想利用python丰富的库,方便爆破,缺点相对于直接用JS代码进行爆破是太慢了,慢了200多倍。

  • 用到的工具:
    • pixel 3 android 9.0(不能用模拟器,因为被hook的libmyjni.so只有arm架构的)
    • jadx
    • IDA
    • Frida12.8
    • Pycharm
    • VSCode

2. 分析过程

输入注册码,显示如下:

在代码中搜索"您的注册码已保存",相关代码如下:

public class RegActivity extends Activity {
    private Button btn_reg;
    private EditText edit_sn;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_reg);
        this.btn_reg = (Button) findViewById(R.id.button1);
        this.edit_sn = (EditText) findViewById(R.id.editText1);
        this.btn_reg.setOnClickListener(new View.OnClickListener() {
            /* class com.gdufs.xman.RegActivity.AnonymousClass1 */

            public void onClick(View v) {
                String sn = RegActivity.this.edit_sn.getText().toString().trim();
                if (sn == null || sn.length() == 0) {
                    Toast.makeText(RegActivity.this, "您的输入为空", 0).show();
                    return;
                }
                ((MyApp) RegActivity.this.getApplication()).saveSN(sn);
                new AlertDialog.Builder(RegActivity.this).setTitle("回复").setMessage("您的注册码已保存").setPositiveButton("好吧", new DialogInterface.OnClickListener() {
                    /* class com.gdufs.xman.RegActivity.AnonymousClass1.AnonymousClass1 */

                    public void onClick(DialogInterface dialog, int which) {
                        Process.killProcess(Process.myPid());
                    }
                }).show();
            }
        });
    }
}

然后跳转到saveSN函数所在的类MyApp,代码如下:

public class MyApp extends Application {
    public static int m = 0;

    public native void initSN();

    public native void saveSN(String str);

    public native void work();

    static {
        System.loadLibrary("myjni");
    }

    public void onCreate() {
        initSN();
        Log.d("com.gdufs.xman m=", String.valueOf(m));
        super.onCreate();
    }
}

接着分析libmyjni.so文件,在JNI_OnLoad函数中注册了initSNsaveSNwork函数,代码如下:

2.1 分析initSN函数

首先分析initSN函数,其中setValue函数的作用是设置com/gdufs/xman/MyApp类的静态字段m的值,

initSN函数只做了一件事,即如果/sdcard/reg.dat文件的内容是"EoPAoY62@ElRD",com/gdufs/xman/MyApp类的静态字段m的值则设置为1,否则设置为0

通过对com/gdufs/xman/MyApp类的静态字段m交叉引用发现,如果m的值为1,则显示已注册,所以我们怀疑输入的注册码通过一系列的计算后得到的值会保存到/sdcard/reg.dat文件中,如果得到的值为"EoPAoY62@ElRD",则输入的注册码即为flag

2.2 分析 saveSN函数

saveSN也只干了一件事,把输入的注册码经过异或运算之后存到了/sdcard/reg.dat文件中

我们可以试试,直接把/sdcard/reg.dat文件的内容替换成"EoPAoY62@ElRD",会是什么效果,如下:

则我们的flag格式为xman{注册码}

3. 编写脚本

通过上面的分析过程可知,我们可以通过Frida主动调用的方式爆破出flag,首先给出直接用JS代码进行爆破的脚本,再给出RPC爆破的脚本。

3.1 JS代码爆破

var fputs_str = null;

function Hook() {
    Java.perform(function () {
        const imports = Module.enumerateImportsSync("libmyjni.so");
        const imports_len = imports.length;
        var fputs_addr = null;
        for (var i = 0; i < imports_len; i++) {
            if (imports[i].name == "fputs") {
                fputs_addr = imports[i].address;
                break;
            }
        }
        if (fputs_addr != null) {
            Interceptor.attach(fputs_addr, {
                onEnter: function (args) {
                    fputs_str = args[0].readCString();
                },
                onLeave: function (retval) {
                }
            })
        }
    })
}

function Invoke(try_str) {
    Java.perform(function () {
        Java.choose("com.gdufs.xman.MyApp", {
            onMatch: function (instance) {
                instance.saveSN(try_str);
            },
            onComplete: function () {
            }
        })
    })
}

function Main() {
    Hook();
    Java.perform(function () {
        const three_character_array = new Array("EoP", "AoY", "62@", "ElR");
        const last_character = "D";
        const my_dict = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()?_"
        const myapp_class_obj = Java.use("com.gdufs.xman.MyApp");
        const three_character_array_len = three_character_array.length;
        const my_dict_len = my_dict.length
        const myapp = Java.use("com.gdufs.xman.MyApp").$new();
        var flag = "";
        for (var i = 0; i < three_character_array_len; i++) {
            var found = false;
            for (var j = 0; j < my_dict_len; j++) {
                if (found == true) {
                    break;
                }
                for (var k = 0; k < my_dict_len; k++) {
                    if (found == true) {
                        break;
                    }
                    for (var m = 0; m < my_dict_len; m++) {
                        const try_str = my_dict[j] + my_dict[k] + my_dict[m];
                        console.log(`try_str: ${try_str}`);
                        myapp.saveSN(try_str);
                        if (three_character_array[i] == fputs_str) {
                            flag += try_str;
                            console.log(`found: ${try_str}`);
                            found = true;
                            break;
                        }
                    }
                }
            }
        }

        for (var i = 0; i < my_dict_len; i++) {
            const try_str = my_dict[i];
            console.log(`try_str: ${try_str}`);
            Invoke(try_str);
            if (last_character == fputs_str) {
                flag += try_str;
                console.log(`found: ${try_str}`);
                break;
            }
        }
        console.log(`flag: xman{${flag}}`);
    })
}

//不能用setImmediate(Main),会出现Process crashed: SIGSYS SYS_SECCOMP
//需要延迟一下再爆破
setTimeout(Main, 500);

花了大概两分钟爆破出了flag

3.2 RPC爆破

JavaScript代码

var myapp = null;

function Hook() {
    Java.perform(function () {
        myapp = Java.use("com.gdufs.xman.MyApp").$new();
        const imports = Module.enumerateImportsSync("libmyjni.so");
        const imports_len = imports.length;
        var fputs_addr = null;
        for (var i = 0; i < imports_len; i++) {
            if (imports[i].name == "fputs") {
                fputs_addr = imports[i].address;
                break;
            }
        }
        if (fputs_addr != null) {
            Interceptor.attach(fputs_addr, {
                onEnter: function (args) {
                    send(args[0].readCString());
                },
                onLeave: function (retval) {
                }
            })
        }
    })
}

function Invoke(try_str) {
    Java.perform(function () {
        myapp.saveSN(try_str);
    })
}

rpc.exports = {
    hook: Hook,
    invoke: Invoke
}

Python代码

from itertools import permutations
import sys
import time
import frida

result = ""
received = False

def MessageHandler(message, data):
    if message["type"] == "send":
        global result
        global received
        result = message["payload"]
        received = True
    else:
        print(message)

def GeneratePossibilities(dict, count, repetitive=False):
    """

    :param dict: Dictionary
    :param count: Number of characters per group
    :param repetitive: Whether there are duplicate characters in each group
    :return: return permutations(dict, count)
    """
    if repetitive and (count > 1):
        src_dict = dict
        for i in range(count - 1):
            dict += src_dict
    return permutations(dict, count)

device = frida.get_device_manager().add_remote_device("192.168.1.5:8888")
pid = device.spawn("com.gdufs.xman")
session = device.attach(pid)
with open("index.js") as file_descriptor:
    script = session.create_script(file_descriptor.read(), runtime="v8")
    script.on("message", MessageHandler)
    script.load()
    time.sleep(1)
    device.resume(pid)

goal_three_character_array = ["EoP", "AoY", "62@", "ElR"]
goal_three_character_array_length = len(goal_three_character_array)
goal_last_character = "D"
my_dict = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()?_"
flag = ['*'] * 13
possibilities = GeneratePossibilities(my_dict, 3, True)
time.sleep(1)
time_begin = time.time()
script.exports.hook()
first_part_times = 0
try_count = 0
for p in possibilities:
    character1, character2, character3 = p
    try_str = character1 + character2 + character3
    try_count += 1
    sys.stdout.write("[{}]Try_str: {}\r".format(try_count, "".join(try_str)))
    sys.stdout.flush()
    received = False
    script.exports.invoke(try_str)
    while not received:
        pass
    for i in range(goal_three_character_array_length):
        if result == goal_three_character_array[i]:
            if flag[i * 3] == "*":
                first_part_times += 1
            flag[i * 3: i * 3 + 3] = character1, character2, character3
            break
    sys.stdout.write("
  • Flag: xman{{{}}} ".format("".join(flag)))     sys.stdout.flush()     if first_part_times == 4:         break # 清屏 print("\033c") try_count = 0 for try_str in my_dict:     sys.stdout.write("[{}]Try_str: {}\r".format(try_count, "".join(try_str)))     script.exports.invoke(try_str)     if result == goal_last_character:         flag[12] = try_str         break     sys.stdout.write("
  • Flag: xman{{{}}} ".format("".join(flag)))     sys.stdout.flush() # 清屏 print("\033c") print("
  • Flag: xman{{{}}}".format("".join(flag))) time_end = time.time() cost_time = time_end - time_begin print(" cost time: " + str(cost_time // 60) + "min")
  • 采用RPC爆破的方式花了429分钟才爆破出了flag,花的时间是直接使用JS代码进行爆破的214倍

    4. 问题答疑

    4.1 设备问题

    如果出现各种无法解决的问题,尝试换成跟我一样的设备和系统——pixel 3、android 9.0

    4.2 出现global reference table overflow

    错误是全局引用表溢出了,具体意思是Java对象的全局引用表溢出了,你在循环里new Java对象就可能会溢出,因为frida对Java对象的引用就是用的全局引用,不知道什么时候才释放,反正循环的时候没有释放

    4.3 爆破卡住了

    多次调用Java.choose程序就会卡住,好像跟frida版本无关,不知道为什么,所以我直接new一个对象进行主动调用

    4.4 为什么233字符没有被爆破出来?

    排列使用的字典是没有重复字符的,所以3个字符中不可能同时出现两个相同的字符,下面的代码就会出现这种问题

    my_dict = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()?_"
    permutations(dict, 3)

    解决办法是把字典自加多次,代码如下

    def GeneratePossibilities(dict, count, repetitive=False):
        """
    
        :param dict: Dictionary
        :param count: Number of characters per group
        :param repetitive: Whether there are duplicate characters in each group
        :return: return permutations(dict, count)
        """
        if repetitive and (count > 1):
            src_dict = dict
            for i in range(count - 1):
                dict += src_dict
        return permutations(dict, count)
    
    my_dict = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()?_"
    possibilities = GeneratePossibilities(my_dict, 3, True)

    5. 附件

    链接:https://pan.baidu.com/s/1NRPGU_j6CK7bgB363FN33w
    提取码:09wb

    免费评分

    参与人数 33吾爱币 +30 热心值 +29 收起 理由
    suuu7 + 1 热心回复!
    Chenda1 + 1 + 1 我很赞同!
    X38072 + 1 用心讨论,共获提升!
    junjia215 + 1 + 1 热心回复!
    duya1961 + 1 + 1 热心回复!
    jolin7714 + 1 + 1 谢谢@Thanks!
    eway + 1 + 1 谢谢@Thanks!
    xiyue3322 + 1 + 1 用心讨论,共获提升!
    anto_ + 1 + 1 我很赞同!
    chenkeai深蓝 + 1 鼓励转贴优秀软件安全工具和文档!
    Rclear + 1 + 1 太强了大佬
    lookerJ + 1 + 1 用心讨论,共获提升!
    huhong1230 + 1 已经处理,感谢您对吾爱破解论坛的支持!
    azcolf + 1 + 1 热心回复!
    杨辣子 + 1 + 1 谢谢@Thanks!
    不会逆向 + 1 谢谢@Thanks!
    chensweet + 1 + 1 热心回复!
    zq971201 + 1 热心回复!
    qaz007 + 1 + 1 用心讨论,共获提升!
    wrm123 + 1 + 1 我很赞同!
    aaa661179 + 1 + 1 热心回复!
    淡灬看夏丶恋雨 + 1 + 1 我很赞同!
    victos + 1 + 1 谢谢@Thanks!
    wangxiaohong888 + 1 + 1 热心回复!
    _xie + 1 + 1 用心讨论,共获提升!
    Kn1fe + 1 + 1 我很赞同!
    笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
    爱你小吉君 + 1 我很赞同!
    lyl610abc + 1 + 1 臭弟弟表示看不懂
    小熊真麻烦 + 1 + 1 我很赞同!
    为之奈何? + 1 + 1 我很赞同!
    Bluesky10 + 1 + 1 热心回复!
    fanvalen + 1 + 1 不错哦

    查看全部评分

    本帖被以下淘专辑推荐:

    发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

    ghostdough 发表于 2021-4-10 17:16
    大佬今天早上手机接到一条短信,说营业执照要过期要审核资料,我妈点进去按照步骤填写被骗了25万,现在这个网站还在运行,大佬能不能帮帮忙看一下能搞到后台数据,给警方提供线索早点破案吗,账户查到是黑龙江的但是具体账户里有没有钱也不知道,网站地址是http://amssuw.bar/index2.asp
    ,ip是185.251.249.137:80 谢谢大佬,现在这个网站还能访问
    fanvalen 发表于 2021-4-9 21:42
    whats_OvO 发表于 2021-4-9 22:53
    ai474427793 发表于 2021-4-9 21:54
    马克,顺便lznb
    PrincessSnow 发表于 2021-4-9 22:32
    谢谢大佬 学习了!
    尘封_ 发表于 2021-4-10 01:45
    学习学习
    anlovedong 发表于 2021-4-10 05:16
    学习了,谢谢分享
    helanzhu1 发表于 2021-4-10 05:43
    不错,学习了,学习了
    zhlhd 发表于 2021-4-10 08:13
    太强了,学习学习!
    wildfire_810 发表于 2021-4-10 08:19
    好!很有精神!
    您需要登录后才可以回帖 登录 | 注册[Register]

    本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

    快速回复 收藏帖子 返回列表 搜索

    RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

    GMT+8, 2024-4-27 13:14

    Powered by Discuz!

    Copyright © 2001-2020, Tencent Cloud.

    快速回复 返回顶部 返回列表