攻防世界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赛高!!!!!