攻防世界illusion暴力破解

6 篇文章 0 订阅
3 篇文章 1 订阅

攻防世界illusion暴力破解

看算法是不可能看算法的,这辈子都不可能看算法的,汇编又不会,出题人又苟得一匹,加密算法就算每一行都看懂了也不知道整体函数是干嘛的,只有暴力破解这种东西才能维持生活。

一、初步分析

先把illusion.apk拖进jadx进行分析,发现java逻辑很简单,就主界面一个按钮,点击就会调用CheckFlag函数进行校验,这个函数传入两个参数,一个是flag,一个是encflag
请添加图片描述其中flag是我们输入的值,encflag是存在文中进行读取的,拿jadx直接打开便可得到
请添加图片描述

二、ida pro进行So文件分析

接着便用ida pro打开so文件进行分析(用Frida写完脚本后死活触发不到,后面看了网上大哥解体思路才发现这是个假函数,根本没有调用,而且对比的字符串也不对,所以hook它根本没有用,此处就不再演示了。 PS: 然额我在这儿死磕了十几个小时,算出来结果不对,一直以为是算法的特性。。。。)
请添加图片描述真正调用的函数需要去Jni_Onload这个函数中找,最终找到的函数为sub_dc8这个函数(Tips:定位到相关代码按F5可以调出伪代码,在伪代码中按TAB键可以跳转到对应的汇编代码)
请添加图片描述然后就需要开始分析伪代码了(咱就是说,虽然不想分析代码,但也不能一点儿代码不看)
请添加图片描述
根据伪代码分析,核心思路就是将输入的flag字符串逐个字符通过运算后,进行储存,然后查看是否和encflag一样。

显然,这儿的伪代码是有问题的,sub_10c0函数的返回值没写明,而且V7变量也没赋值,虽然猜测过函数的值直接赋值给V7然后+32,但结果显然不对。(因为在研究解密函数几小时无果后,果断百度求助大佬,然后某位大佬通过枚举发现,这函数就返回0,1两个值)

并且sub_10c0的核心函数是里面的sub_1028,大概长这样
请添加图片描述
像我这种计算机渣渣,百度了半天就算能差不多搞明白某几步在干嘛,也看不懂整体函数是在干嘛,更整不出decode函数来反向推演。

三、暴力破解

于是乎,咱有了一个大胆的想法,既然正确的flag加密和flag_enc全等,而且flag加密后的位数和加密前的位数相同,那么我可以先输入一个字符,加密后必然等于flag_enc的第一个字符,来暴力破解第一个,破解完第一个后再破解第二个,反正每个字符最多也就127种可能,时间复杂度也不高。

暴力破解的关键点在于修改传入的参数,以及获取每次加密后的字符串,传入的参数可以直接主动调用sub_dc8函数进行,加密后的字符串可以根据最后的判断两个加密串是否全等的函数j_strcmp的入参进行获取。

然而frida的So调用不是很友好(可能是我比较菜),途中经历过无法主动调用sub_dc8函数,尝试模拟点击按钮以触发sub_dc8函数,以上操作调用sub_dc8函数后均未触发j_strcmp函数,导致frida以失败告终。(失败的hook脚本就不放来,太乱了。。。)

在Frida进行无数次尝试后,无计可施的我已经渐渐开始放弃了Frida,偶然想起好像有个叫unidbg个东西,不知道干啥用的。抱着多学门手艺死(马当活马医)的态度,去了解了一下,发现是专门针对So的工具。于是忽,在大量google baidu后,终于写出了unidbg的暴力破解脚本,并成功打出flag,脚本如下:

package com.test;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.arm.HookStatus;
import com.github.unidbg.hook.HookContext;
import com.github.unidbg.hook.ReplaceCallback;
import com.github.unidbg.hook.hookzz.HookZz;
import com.github.unidbg.linux.android.AndroidARMEmulator;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.sun.jna.Pointer;
import unicorn.Arm64Const;
import unicorn.ArmConst;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class illusion extends AbstractJni {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    private DvmClass cNative;
    private static String r0;

    private illusion () {
        // 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("monkeylord.illusion").build();
        // 获取模拟器的内存操作接口
        final Memory memory = emulator.getMemory();
        // 设置系统类库解析
        memory.setLibraryResolver(new AndroidResolver(23));
        // 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作
//        vm =  emulator.createDalvikVM(new File("unidbg-android/src/test/java/com/test/llusion.apk"));
        vm =  emulator.createDalvikVM();
        // 加载目标SO
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/test/libnative-lib.so"), true); // 加载so到虚拟内存
        //获取本SO模块的句柄,后续需要用它
        module = dm.getModule();
        vm.setJni(this); // 设置JNI
        vm.setVerbose(false); // 打印日志

        dm.callJNI_OnLoad(emulator); // 调用JNI OnLoad
    }

    public static void main(String[] args) {
        illusion test = new illusion();
        test.hook();
        String flag_enc = "Ku@'G_V9v(yGS";

        String flag = "";

        for (int index = 0; index < flag_enc.length(); index++) {
            for (int i = 33; i < 127; i++) {
                test.CheckFlag(flag + String.valueOf((char) i), flag_enc);  // 进行判断
                if ((illusion.r0.length() > index)  && (flag_enc.charAt(index) == illusion.r0.charAt(index))) {
                    flag += String.valueOf((char) i);
                    break;
                }
            }
            System.out.println("Flag[0:"+index+"]: "+flag);
        }

        System.out.println("Completely Flag is: "+flag);

    }

    private void CheckFlag(String flag, String flag_enc) {
        //创建jobject对象
        DvmObject<?> thiz = vm.resolveClass("monkeylord.illusion").newObject(null);
        List<Object> list = new ArrayList<>(10);
        list.add(vm.getJNIEnv());
        list.add(0);  
        list.add(vm.addLocalObject(new StringObject(vm, flag)));   // arg 3
        list.add(vm.addLocalObject(new StringObject(vm, flag_enc)));   // arg 4

        Number number =  module.callFunction(emulator, 0xdc9, list.toArray());
//        System.out.print("result:");
//        DvmObject<?> object = vm.getObject(number.intValue());
//        System.out.print(object.getValue().toString());
    }

    public void hook() {
        HookZz hook = HookZz.getInstance(emulator);
        hook.replace(module.base + 0x00000E64+1, new ReplaceCallback() {
            @Override
            public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
//                System.out.println(context.getPointerArg(0).getString(0));  // 入参1 R0寄存器
                illusion.r0 = context.getPointerArg(0).getString(0);
//                System.out.println(context.getPointerArg(1).getString(0));  // 入参2 R1寄存器
                return super.onCall(emulator, context,originFunction);
            }

        }, true);
    }
}

运行结果如下:
请添加图片描述Flag->CISCN{GJ5728}

最后,我只想说,unidbg赛高!!!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Light Illusion的.3dl文件是一种文本文件,因此使用C++解析它是可行的。您可以使用C++中的文件输入/输出(fstream)类来打开并读取文件内容。然后,您可以使用字符串分割和转换函数来解析文件中的数据,并将其存储在适当的数据结构中。 首先,您需要打开文件并读取其内容,可以像下面这样使用fstream类: ```c++ #include <fstream> #include <string> std::ifstream file("example.3dl"); std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); ``` 上述代码将文件example.3dl的内容读入到字符串变量content中。接下来,您可以使用字符串分割函数(例如split())将content字符串分割为行,并使用字符串流(stringstream)将每个行分割为单独的数据。以下是一个示例代码,该代码假设.3dl文件中的每一行都包含两个浮点数值: ```c++ #include <sstream> #include <vector> std::vector<std::pair<float, float>> data; std::istringstream ss(content); std::string line; while (std::getline(ss, line, '\n')) { std::istringstream line_ss(line); float val1, val2; line_ss >> val1 >> val2; data.push_back({val1, val2}); } ``` 上述代码将文件内容分割为行,然后将每一行分割为两个浮点数值,并将每个值存储在一个pair中。pair然后添加到vector中,表示文件中的所有数据。您可以使用类似的代码来解析其他数据类型或更复杂的数据结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值