package jingqi; import capstone.Capstone; import capstone.api.Instruction; import capstone.api.RegsAccess; import com.alibaba.fastjson.util.IOUtils; import com.alibaba.fastjson.util.ModuleUtil; import com.github.unidbg.*; import com.github.unidbg.arm.backend.Backend; import com.github.unidbg.arm.backend.CodeHook; import com.github.unidbg.arm.backend.UnHook; import com.github.unidbg.arm.backend.Unicorn2Factory; import com.github.unidbg.arm.context.Arm32RegisterContext; import com.github.unidbg.arm.context.RegisterContext; import com.github.unidbg.debugger.BreakPointCallback; import com.github.unidbg.debugger.Debugger; 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.memory.Memory; import com.sun.jna.Pointer; import keystone.Keystone; import keystone.KeystoneArchitecture; import keystone.KeystoneMode; import sample.sample; import unicorn.*;
import javax.sound.midi.Patch; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.lang.annotation.Native; import java.nio.charset.StandardCharsets; import java.util.*;
public class jingqi extends AbstractJni {
public static final java.util.Map<String, Integer> REG_MAP = new java.util.HashMap<>(); public static final Map<Long, Integer> haspatched = new HashMap<>();
static { REG_MAP.put("x0", Arm64Const.UC_ARM64_REG_X0); REG_MAP.put("x1", Arm64Const.UC_ARM64_REG_X1); REG_MAP.put("x2", Arm64Const.UC_ARM64_REG_X2); REG_MAP.put("x3", Arm64Const.UC_ARM64_REG_X3); REG_MAP.put("x4", Arm64Const.UC_ARM64_REG_X4); REG_MAP.put("x5", Arm64Const.UC_ARM64_REG_X5); REG_MAP.put("x6", Arm64Const.UC_ARM64_REG_X6); REG_MAP.put("x7", Arm64Const.UC_ARM64_REG_X7); REG_MAP.put("x8", Arm64Const.UC_ARM64_REG_X8); REG_MAP.put("x9", Arm64Const.UC_ARM64_REG_X9); REG_MAP.put("x10", Arm64Const.UC_ARM64_REG_X10); REG_MAP.put("x11", Arm64Const.UC_ARM64_REG_X11); REG_MAP.put("x12", Arm64Const.UC_ARM64_REG_X12); REG_MAP.put("x13", Arm64Const.UC_ARM64_REG_X13); REG_MAP.put("x14", Arm64Const.UC_ARM64_REG_X14); REG_MAP.put("x15", Arm64Const.UC_ARM64_REG_X15); REG_MAP.put("x16", Arm64Const.UC_ARM64_REG_X16); REG_MAP.put("x17", Arm64Const.UC_ARM64_REG_X17); REG_MAP.put("x18", Arm64Const.UC_ARM64_REG_X18); REG_MAP.put("x19", Arm64Const.UC_ARM64_REG_X19); REG_MAP.put("x20", Arm64Const.UC_ARM64_REG_X20); REG_MAP.put("x21", Arm64Const.UC_ARM64_REG_X21); REG_MAP.put("x22", Arm64Const.UC_ARM64_REG_X22); REG_MAP.put("x23", Arm64Const.UC_ARM64_REG_X23); REG_MAP.put("x24", Arm64Const.UC_ARM64_REG_X24); REG_MAP.put("x25", Arm64Const.UC_ARM64_REG_X25); REG_MAP.put("x26", Arm64Const.UC_ARM64_REG_X26); REG_MAP.put("x27", Arm64Const.UC_ARM64_REG_X27); REG_MAP.put("x28", Arm64Const.UC_ARM64_REG_X28); REG_MAP.put("x29", Arm64Const.UC_ARM64_REG_X29); REG_MAP.put("x30", Arm64Const.UC_ARM64_REG_X30);
REG_MAP.put("w0", Arm64Const.UC_ARM64_REG_W0); REG_MAP.put("w1", Arm64Const.UC_ARM64_REG_W1); REG_MAP.put("w2", Arm64Const.UC_ARM64_REG_W2); REG_MAP.put("w3", Arm64Const.UC_ARM64_REG_W3); REG_MAP.put("w4", Arm64Const.UC_ARM64_REG_W4); REG_MAP.put("w5", Arm64Const.UC_ARM64_REG_W5); REG_MAP.put("w6", Arm64Const.UC_ARM64_REG_W6); REG_MAP.put("w7", Arm64Const.UC_ARM64_REG_W7); REG_MAP.put("w8", Arm64Const.UC_ARM64_REG_W8); REG_MAP.put("w9", Arm64Const.UC_ARM64_REG_W9); REG_MAP.put("w10", Arm64Const.UC_ARM64_REG_W10); REG_MAP.put("w11", Arm64Const.UC_ARM64_REG_W11); REG_MAP.put("w12", Arm64Const.UC_ARM64_REG_W12); REG_MAP.put("w13", Arm64Const.UC_ARM64_REG_W13); REG_MAP.put("w14", Arm64Const.UC_ARM64_REG_W14); REG_MAP.put("w15", Arm64Const.UC_ARM64_REG_W15); REG_MAP.put("w16", Arm64Const.UC_ARM64_REG_W16); REG_MAP.put("w17", Arm64Const.UC_ARM64_REG_W17); REG_MAP.put("w18", Arm64Const.UC_ARM64_REG_W18); REG_MAP.put("w19", Arm64Const.UC_ARM64_REG_W19); REG_MAP.put("w20", Arm64Const.UC_ARM64_REG_W20); REG_MAP.put("w21", Arm64Const.UC_ARM64_REG_W21); REG_MAP.put("w22", Arm64Const.UC_ARM64_REG_W22); REG_MAP.put("w23", Arm64Const.UC_ARM64_REG_W23); REG_MAP.put("w24", Arm64Const.UC_ARM64_REG_W24); REG_MAP.put("w25", Arm64Const.UC_ARM64_REG_W25); REG_MAP.put("w26", Arm64Const.UC_ARM64_REG_W26); REG_MAP.put("w27", Arm64Const.UC_ARM64_REG_W27); REG_MAP.put("w28", Arm64Const.UC_ARM64_REG_W28); REG_MAP.put("w29", Arm64Const.UC_ARM64_REG_W29); REG_MAP.put("w30", Arm64Const.UC_ARM64_REG_W30);
REG_MAP.put("sp", Arm64Const.UC_ARM64_REG_SP); REG_MAP.put("wsp", Arm64Const.UC_ARM64_REG_WSP); REG_MAP.put("xzr", Arm64Const.UC_ARM64_REG_XZR); REG_MAP.put("wzr", Arm64Const.UC_ARM64_REG_WZR); }
public static volatile boolean ISEMU=false; public int flagcsel=0; public int cselflag=0; Stack<instructAndReg> st = new Stack<instructAndReg>(); private final AndroidEmulator emulator; private final DvmClass NativeApi; public Module module; private final VM vm; public jingqi(){ emulator=AndroidEmulatorBuilder.for64Bit() .addBackendFactory(new Unicorn2Factory(true)) .setProcessName("com.primite.drillbeam") .build();
Memory memory =emulator.getMemory(); memory.setLibraryResolver(new AndroidResolver(23)); vm= emulator.createDalvikVM(new File("D:\\下载\\ctf题目\\2025京麒杯\\drillbeam.apk")); vm.setJni(this); vm.setVerbose(true); NativeApi =vm.resolveClass("com.primite.drillbeam.Check"); memory.setCallInitFunction(true); memory.addModuleListener(new ModuleListener() { @Override public void onLoaded(Emulator<?> emulator, Module module) { if(module.name.contains("re0")){ String tracefile="D:\\下载\\ctf题目\\2025京麒杯\\drillbeam\\lib\\arm64-v8a\\trace.txt"; PrintStream pp= null; try { pp = new PrintStream(new FileOutputStream(tracefile),true); } catch (FileNotFoundException e) { throw new RuntimeException(e); } brbr(module); } } }); DalvikModule dm= vm.loadLibrary("re0",true); module=dm.getModule();
dm.callJNI_OnLoad(emulator); } public String calls(){ String arg1="aaaaaaaaaa"; String ret = NativeApi.newObject(null).callJniMethodObject(emulator, "calc(Ljava/lang/String;)Ljava/lang/String;",arg1).getValue().toString(); return ret; }
public static void main(String []args){ jingqi xv=new jingqi(); String result=xv.calls(); System.out.println(result); xv.dumpSo("D:\\下载\\ctf题目\\2025京麒杯\\drillbeam\\lib\\arm64-v8a\\libre0_brbr.so","D:\\下载\\ctf题目\\2025京麒杯\\drillbeam\\lib\\arm64-v8a\\111_path.so"); }
public void dumpSo(String origSoPath, String outSoPath) { try { java.nio.file.Files.copy( java.nio.file.Paths.get(origSoPath), java.nio.file.Paths.get(outSoPath), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
Backend backend = emulator.getBackend(); long base = module.base;
try (java.io.RandomAccessFile raf = new java.io.RandomAccessFile(outSoPath, "rw")) { for (Long addr : jingqi.haspatched.keySet()) {
long fileOffset = addr - base;
byte[] b = backend.mem_read(addr, 4);
raf.seek(fileOffset); raf.write(b); } }
System.out.printf("[overlay dump] base=0x%x, patched bytes=%d -> %s%n", base, jingqi.haspatched.size(), outSoPath);
} catch (Exception e) { e.printStackTrace(); } }
public void brbr(Module module){ emulator.getBackend().hook_add_new(new CodeHook() { @Override public void hook(Backend backend, long address, int size, Object user) { if(jingqi.haspatched.get(address)!=null ){ return; }
Capstone capstone; capstone=new Capstone(Capstone.CS_ARCH_ARM64,Capstone.CS_MODE_ARM);
byte[] bytes=emulator.getBackend().mem_read(address,4); Instruction[] disasm=capstone.disasm(bytes,0);
if(flagcsel==0&&(!disasm[0].getMnemonic().toLowerCase().equals("cmp"))){ return ; }
if(flagcsel==1&&(disasm[0].getMnemonic().toLowerCase().equals("br"))){ Regs myReg=new Regs(address-module.base,emulator.getBackend()); instructAndReg ins=new instructAndReg(address-module.base,disasm,myReg); System.out.println("push " + Long.toHexString(ins.address)+" "+disasm[0].getMnemonic() +"---- "+disasm[0].getOpStr()); st.add(ins);
String bropstr=disasm[0].getOpStr(); caluateClass clz=new caluateClass(st); List <Integer> result=clz.caluate(emulator,module,bropstr);
flagcsel=0; cselflag=0; } if(disasm[0].getMnemonic().toLowerCase().equals("cmp")){
int aaa=(int)(address-module.base);
int cnt=4; while(cnt!=28) { if(address-module.base==0x024F4||address-module.base==0x2BBC||address- module.base==0x2838){ flagcsel=1; break; } long add=address+cnt; long ss=add-module.base; byte[] mach=emulator.getBackend().mem_read(add,4); Instruction [] ddd=capstone.disasm(mach,0); if (ddd == null || ddd.length == 0) { break; } if (ddd[0].getMnemonic().equals("br") && cselflag == 0) {
break; } if(ddd[0].getMnemonic().equals("b")){ break; }
if(ddd[0].getMnemonic().equals("csel")||ddd[0].getMnemonic().equals("csinc")||ddd[0].getMnemonic().equals("cinc")||ddd[0].getMnemonic().equals("cset")){ cselflag=1; } if(cselflag==1&&ddd[0].getMnemonic().equals("br")){ flagcsel=1; }
cnt+=4; } if(flagcsel==0){ cselflag=0; } } if(flagcsel==1){
Regs myReg=new Regs(address-module.base,emulator.getBackend());
instructAndReg ins=new instructAndReg(address-module.base,disasm,myReg);
System.out.println("push " + Long.toHexString(ins.address)+" "+disasm[0].getMnemonic() +"---- "+disasm[0].getOpStr()); st.push(ins); }
} @Override public void onAttach(UnHook unHook) {
} @Override public void detach() {
} },module.base,module.base+module.size,null); } } class Regs{ public long RegAddress; public List<Number> reg; public List<byte[]> vec; public Regs(long address,Backend bk){ this.RegAddress=address; List<Number> nb = new ArrayList<>(); for(int i=0;i<29;i++) { nb.add(bk.reg_read(i+ Arm64Const.UC_ARM64_REG_X0)); } nb.add(bk.reg_read(Arm64Const.UC_ARM64_REG_FP)); nb.add(bk.reg_read(Arm64Const.UC_ARM64_REG_LR)); nb.add(bk.reg_read(Arm64Const.UC_ARM64_REG_NZCV)); this.reg=nb;
}
public List<Number> getReg(){ return this.reg; }
}
class instructAndReg{ public long address; public Instruction[] instructions; public Regs regs; public instructAndReg(long address,Instruction[] instructions,Regs regs){ this.address=address; this.instructions=instructions; this.regs=regs; } }
class caluateClass{ public Stack<instructAndReg> stk; public List<Integer> toAddress; public long bAddress=0; public long ltAddress=0; public long brAddress=0;
public caluateClass(Stack<instructAndReg> stk){ this.stk=stk; } public boolean evalCond(String cond, long nzcv) { int N = (int) ((nzcv >> 31) & 1); int Z = (int) ((nzcv >> 30) & 1); int C = (int) ((nzcv >> 29) & 1); int V = (int) ((nzcv >> 28) & 1);
cond = cond.toLowerCase(); switch (cond) { case "eq": return Z == 1; case "ne": return Z == 0;
case "cs": case "hs": return C == 1; case "cc": case "lo": return C == 0;
case "mi": return N == 1; case "pl": return N == 0;
case "vs": return V == 1; case "vc": return V == 0;
case "hi": return C == 1 && Z == 0; case "ls": return C == 0 || Z == 1;
case "ge": return N == V; case "lt": return N != V;
case "gt": return Z == 0 && N == V; case "le": return Z == 1 || N != V;
case "al": return true;
default: throw new IllegalArgumentException("unknown cond: " + cond); } }
public List<Integer> caluate(AndroidEmulator emulator,Module module,String bropstr){ instructAndReg insReg=stk.pop(); Regs regreg; String cond; Instruction [] ins =insReg.instructions; int number=Integer.parseInt(ins[0].getOpStr().substring(1)); brAddress=((long)insReg.regs.reg.get(number)-module.base); long startAddress; long lastAddress=insReg.address; while(true){ instructAndReg qiu=stk.pop(); if(qiu.instructions[0].getMnemonic().equals("csel")||qiu.instructions[0].getMnemonic().equals("csinc")||qiu.instructions[0].getMnemonic().equals("cinc")||qiu.instructions[0].getMnemonic().equals("cset")){ long cselAddress=qiu.address+module.base; long nzcv=qiu.regs.reg.get(31).longValue(); String [] parts=qiu.instructions[0].getOpStr().split(", "); if(qiu.instructions[0].getMnemonic().equals("cinc")){ cond=parts[2].trim(); }else if(qiu.instructions[0].getMnemonic().equals("cset")){ cond=parts[1].trim(); } else { cond = parts[3].trim(); } boolean taken=evalCond(cond,nzcv); if(taken==true){ ltAddress=brAddress; }else{ bAddress=brAddress; } while(true){ instructAndReg cmp=stk.pop(); if(cmp.instructions[0].getMnemonic().equals("cmp")){
regreg=cmp.regs; startAddress=cmp.address; break; } }
byte[] bytes=emulator.getBackend().mem_read(startAddress+ module.base,(lastAddress-startAddress)+4);
twice twoMi = new twice( emulator, bytes, ltAddress, bAddress, regreg, startAddress + module.base, lastAddress + module.base, bropstr, module, cond );
if(cond.equals("lt")||cond.equals("eq")||cond.equals("ne")) { Patch1 patch = new Patch1( emulator, bytes, twoMi.ltAddress, twoMi.bAddress, startAddress + module.base, lastAddress + module.base, module, cselAddress, cond ); } for(long i=startAddress+module.base;i<=lastAddress+module.base;i++) { jingqi.haspatched.put(i, 1); } System.out.println("taken = " + taken); break; } }
return this.toAddress; } }
class twice { long ltAddress, bAddress; long startAddress, endAddress;
public twice(AndroidEmulator emulator, byte[] bytes, long ltAddress, long bAddress, Regs reg, long startAddress, long endAddress, String bropstr, Module module, String cond ) {
this.bAddress = bAddress; this.ltAddress = ltAddress; this.startAddress = startAddress; this.endAddress = endAddress;
Backend mainBackend = emulator.getBackend(); Capstone capstone = new Capstone(Capstone.CS_ARCH_ARM64, Capstone.CS_MODE_ARM); Instruction[] ins = capstone.disasm(bytes, 0);
for (Instruction i : ins) { System.out.println(" " + i.getMnemonic() + " " + i.getOpStr()); } System.out.println("baddress->" + Long.toHexString(bAddress) + " b.ltaddress->" + Long.toHexString(ltAddress));
if (ins.length == 0 || !"cmp".equalsIgnoreCase(ins[0].getMnemonic())) { return; }
long modBase = module.base; long modSize = module.size;
long mapStart = modBase & ~0xfffL; long offset = modBase - mapStart; long rawSize = offset + modSize; long mapSize = (rawSize + 0xfffL) & ~0xfffL;
byte[] snapshot = mainBackend.mem_read(modBase, (int) modSize);
byte[] patched = Arrays.copyOf(bytes, bytes.length);
for (int i = 0; i < ins.length; i++) { String m = ins[i].getMnemonic().toLowerCase(); int off = i * 4;
if (i == 0 && m.equals("cmp")) { patched[off] = 0x1f; patched[off + 1] = 0x20; patched[off + 2] = 0x03; patched[off + 3] = (byte) 0xd5; System.out.println("[twice] patch cmp -> nop"); continue; }
if (m.startsWith("str") || m.startsWith("stp") || m.startsWith("stur")) { patched[off] = 0x1f; patched[off + 1] = 0x20; patched[off + 2] = 0x03; patched[off + 3] = (byte) 0xd5; System.out.println("[twice] patch store -> nop at ins#" + i + " (" + m + ")"); } }
int lastIndex = ins.length - 1; if (lastIndex >= 0) { String lastM = ins[lastIndex].getMnemonic().toLowerCase(); if (lastM.equals("br")) { int off = lastIndex * 4; patched[off] = 0x1f; patched[off + 1] = 0x20; patched[off + 2] = 0x03; patched[off + 3] = (byte) 0xd5; System.out.println("[twice] patch br -> nop at ins#" + lastIndex); } }
Unicorn uc = new Unicorn(UnicornConst.UC_ARCH_ARM64, UnicornConst.UC_MODE_ARM);
try { uc.mem_map(mapStart, mapSize, UnicornConst.UC_PROT_ALL); uc.mem_write(modBase, snapshot);
uc.mem_write(startAddress, patched);
for (int i = 0; i < 29; i++) { long v = reg.reg.get(i).longValue(); uc.reg_write(Arm64Const.UC_ARM64_REG_X0 + i, v); } long fp = reg.reg.get(29).longValue(); long lr = reg.reg.get(30).longValue(); uc.reg_write(Arm64Const.UC_ARM64_REG_FP, fp); uc.reg_write(Arm64Const.UC_ARM64_REG_LR, lr);
boolean realTaken = (this.ltAddress != 0); boolean wantTaken = !realTaken;
long nzcvFake = buildNzcvFor(cond.toLowerCase(), wantTaken); uc.reg_write(Arm64Const.UC_ARM64_REG_NZCV, nzcvFake);
uc.reg_write(Arm64Const.UC_ARM64_REG_PC, startAddress);
uc.emu_start(startAddress, endAddress, 0, 50);
String brRegName = bropstr.trim(); Integer brRegId = jingqi.REG_MAP.get(brRegName); if (brRegId == null) { System.out.println("[twice] unknown br reg: " + brRegName); return; } long aim = ((Number) uc.reg_read(brRegId)).longValue(); long aa = aim - module.base;
System.out.printf("[twice] alt branch target reg=%s -> 0x%x (off=0x%x)%n", brRegName, aim, aa);
if (this.bAddress == 0) { this.bAddress = aa; } else if (this.ltAddress == 0) { this.ltAddress = aa; } } catch (Exception e) { System.out.println("[twice] emu error: " + e); } finally { try { uc.emu_stop(); } catch (Exception ignored) {} try { uc.mem_unmap(mapStart, mapSize); } catch (Exception ignored) {} try { uc.closeAll(); } catch (Exception ignored) {} } }
private static long buildNzcvFor(String cond, boolean wantTrue) { int N = 0, Z = 0, C = 0, V = 0;
switch (cond) { case "eq": Z = wantTrue ? 1 : 0; break; case "ne": Z = wantTrue ? 0 : 1; break; case "lt": if (wantTrue) { N = 1; V = 0; } else { N = 0; V = 0; } break; case "ge": if (wantTrue) { N = 0; V = 0; } else { N = 1; V = 0; } break; case "gt": if (wantTrue) { Z = 0; N = 0; V = 0; } else { Z = 1; N = 0; V = 0; } break; case "le": if (wantTrue) { Z = 1; } else { Z = 0; N = 0; V = 0; } break; case "cs": case "hs": C = wantTrue ? 1 : 0; break; case "cc": case "lo": C = wantTrue ? 0 : 1; break; case "mi": N = wantTrue ? 1 : 0; break; case "pl": N = wantTrue ? 0 : 1; break; case "vs": V = wantTrue ? 1 : 0; break; case "vc": V = wantTrue ? 0 : 1; break; case "hi": if (wantTrue) { C = 1; Z = 0; } else { C = 0; Z = 0; } break; case "ls": if (wantTrue) { C = 0; } else { C = 1; Z = 0; } break; case "al": default: Z = wantTrue ? 0 : 1; break; }
return ((long) N << 31) | ((long) Z << 30) | ((long) C << 29) | ((long) V << 28); }
private static int parseRegIndex(String reg) { reg = reg.trim(); if (reg.startsWith("w") || reg.startsWith("x")) { try { return Integer.parseInt(reg.substring(1)); } catch (NumberFormatException e) { return -1; } } return -1; } }
class Patch1 { public long ltaddress, bAddress; public AndroidEmulator emulator; public long startAddress; public long endAddress;
public Patch1(AndroidEmulator emulator, byte[] bytes, long ltaddress, long bAddress, long startAddress, long endAddress, Module module, long cselAddress ,String cond) {
this.emulator = emulator; this.ltaddress = ltaddress; this.bAddress = bAddress; this.startAddress = startAddress; this.endAddress = endAddress;
Backend backend = emulator.getBackend(); Keystone keystone = new Keystone( KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian );
long ltAbs = module.base + ltaddress; long bAbs = module.base + bAddress;
byte[] nop = keystone.assemble("nop", 0).getMachineCode(); backend.mem_write(endAddress, nop); System.out.printf("[Patch1] NOPed BR instruction at 0x%x%n", endAddress);
String op1; if(cond.equals("eq")){ op1 = String.format("b.eq 0x%x", ltAbs); }else if(cond.equals("lt")){ op1 = String.format("b.lt 0x%x", ltAbs); }else { op1 = String.format("b.ne 0x%x", ltAbs); } long ldraddress=endAddress-8;
byte[] bltCode = keystone.assemble(op1, (int) ldraddress).getMachineCode();
long bPc = ldraddress + bltCode.length; String op2 = String.format("b 0x%x", bAbs); byte[] bCode = keystone.assemble(op2, (int) bPc).getMachineCode();
backend.mem_write(ldraddress, bltCode); backend.mem_write(bPc, bCode); byte[] cur = backend.mem_read(endAddress, 4);
}
}
|