com.github.unidbg.linux.android.ArmLD Maven / Gradle / Ivy
package com.github.unidbg.linux.android;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.Svc;
import com.github.unidbg.Symbol;
import com.github.unidbg.arm.ArmHook;
import com.github.unidbg.arm.ArmSvc;
import com.github.unidbg.arm.HookStatus;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.context.RegisterContext;
import com.github.unidbg.linux.LinuxModule;
import com.github.unidbg.linux.struct.dl_phdr_info32;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.MemoryBlock;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.github.unidbg.pointer.UnidbgStructure;
import com.github.unidbg.spi.Dlfcn;
import com.github.unidbg.spi.InitFunction;
import com.github.unidbg.unix.struct.DlInfo32;
import com.sun.jna.Pointer;
import keystone.Keystone;
import keystone.KeystoneArchitecture;
import keystone.KeystoneEncoded;
import keystone.KeystoneMode;
import net.fornwall.jelf.ElfDynamicStructure;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import unicorn.ArmConst;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class ArmLD extends Dlfcn {
private static final Log log = LogFactory.getLog(ArmLD.class);
private final Backend backend;
ArmLD(Backend backend, SvcMemory svcMemory) {
super(svcMemory);
this.backend = backend;
}
@Override
public long hook(final SvcMemory svcMemory, String libraryName, String symbolName, long old) {
if ("libdl.so".equals(libraryName)) {
if (log.isDebugEnabled()) {
log.debug("link " + symbolName + ", old=0x" + Long.toHexString(old));
}
switch (symbolName) {
case "dl_iterate_phdr":
return svcMemory.registerSvc(new ArmSvc() {
private MemoryBlock block;
@Override
public UnidbgPointer onRegister(SvcMemory svcMemory, int svcNumber) {
try (Keystone keystone = new Keystone(KeystoneArchitecture.Arm, KeystoneMode.Arm)) {
KeystoneEncoded encoded = keystone.assemble(Arrays.asList(
"push {r4-r7, lr}",
"svc #0x" + Integer.toHexString(svcNumber),
"pop {r7}",
"cmp r7, #0",
"beq 0x34",
"pop {r0-r2}",
"blx r7",
"cmp r0, #0",
"beq 0x8",
"pop {r7}",
"cmp r7, #0",
"popne {r4-r6}",
"bne 0x24",
"mov r7, #0",
"mov r5, #0x" + Integer.toHexString(Svc.POST_CALLBACK_SYSCALL_NUMBER),
"mov r4, #0x" + Integer.toHexString(svcNumber),
"svc #0",
"pop {r4-r7, pc}"));
byte[] code = encoded.getMachineCode();
UnidbgPointer pointer = svcMemory.allocate(code.length, "dl_iterate_phdr");
pointer.write(0, code, 0, code.length);
if (log.isDebugEnabled()) {
log.debug("dl_iterate_phdr: pointer=" + pointer);
}
return pointer;
}
}
@Override
public long handle(Emulator> emulator) {
if (block != null) {
throw new IllegalStateException();
}
RegisterContext context = emulator.getContext();
UnidbgPointer cb = context.getPointerArg(0);
UnidbgPointer data = context.getPointerArg(1);
Collection modules = emulator.getMemory().getLoadedModules();
List list = new ArrayList<>();
for (Module module : modules) {
LinuxModule lm = (LinuxModule) module;
if (lm.elfFile != null) {
list.add(lm);
}
}
Collections.reverse(list);
final int size = UnidbgStructure.calculateSize(dl_phdr_info32.class);
block = emulator.getMemory().malloc(size * list.size(), true);
UnidbgPointer ptr = block.getPointer();
Backend backend = emulator.getBackend();
UnidbgPointer sp = UnidbgPointer.register(emulator, ArmConst.UC_ARM_REG_SP);
if (log.isDebugEnabled()) {
log.debug("dl_iterate_phdr cb=" + cb + ", data=" + data + ", size=" + list.size() + ", sp=" + sp);
}
try {
sp = sp.share(-4, 0);
sp.setInt(0, 0); // NULL-terminated
for (LinuxModule module : list) {
dl_phdr_info32 info = new dl_phdr_info32(ptr);
UnidbgPointer dlpi_addr = UnidbgPointer.pointer(emulator, module.base);
assert dlpi_addr != null;
info.dlpi_addr = (int) dlpi_addr.toUIntPeer();
ElfDynamicStructure dynamicStructure = module.dynamicStructure;
if (dynamicStructure != null && dynamicStructure.soName > 0 && dynamicStructure.dt_strtab_offset > 0) {
info.dlpi_name = (int) (UnidbgPointer.nativeValue(dlpi_addr.share(dynamicStructure.dt_strtab_offset + dynamicStructure.soName)));
} else {
info.dlpi_name = (int) (UnidbgPointer.nativeValue(module.createPathMemory(svcMemory)));
}
info.dlpi_phdr = (int) (UnidbgPointer.nativeValue(dlpi_addr.share(module.elfFile.ph_offset)));
info.dlpi_phnum = module.elfFile.num_ph;
info.pack();
sp = sp.share(-4, 0);
sp.setPointer(0, data); // data
sp = sp.share(-4, 0);
sp.setInt(0, size); // size
sp = sp.share(-4, 0);
sp.setPointer(0, ptr); // dl_phdr_info
sp = sp.share(-4, 0);
sp.setPointer(0, cb); // callback
ptr = ptr.share(size, 0);
}
return context.getLongArg(0);
} finally {
backend.reg_write(ArmConst.UC_ARM_REG_SP, sp.peer);
}
}
@Override
public void handlePostCallback(Emulator> emulator) {
super.handlePostCallback(emulator);
if (block == null) {
throw new IllegalStateException();
}
block.free();
block = null;
}
}).peer;
case "dlerror":
return svcMemory.registerSvc(new ArmSvc() {
@Override
public long handle(Emulator> emulator) {
return error.peer;
}
}).peer;
case "dlclose":
return svcMemory.registerSvc(new ArmSvc() {
@Override
public long handle(Emulator> emulator) {
RegisterContext context = emulator.getContext();
int handle = context.getIntArg(0);
if (log.isDebugEnabled()) {
log.debug("dlclose handle=0x" + Long.toHexString(handle));
}
return dlclose(emulator.getMemory(), handle);
}
}).peer;
case "dlopen":
return svcMemory.registerSvc(new ArmSvc() {
@Override
public UnidbgPointer onRegister(SvcMemory svcMemory, int svcNumber) {
ByteBuffer buffer = ByteBuffer.allocate(28);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(0xe92d40f0); // push {r4-r7, lr}
buffer.putInt(ArmSvc.assembleSvc(svcNumber)); // svc #svcNumber
buffer.putInt(0xe49d7004); // pop {r7}
buffer.putInt(0xe3570000); // cmp r7, #0
buffer.putInt(0x124fe010); // subne lr, pc, #16
buffer.putInt(0x112fff17); // bxne r7
buffer.putInt(0xe8bd80f1); // pop {r0, r4-r7, pc} with return address
byte[] code = buffer.array();
UnidbgPointer pointer = svcMemory.allocate(code.length, "dlopen");
pointer.write(code);
return pointer;
}
@Override
public long handle(Emulator> emulator) {
RegisterContext context = emulator.getContext();
Pointer fileNamePointer = context.getPointerArg(0);
int flags = context.getIntArg(1);
String filename;
if (fileNamePointer == null) {
Module module = emulator.getMemory().findModuleByAddress(context.getLR());
if (module == null) {
throw new UnsupportedOperationException();
}
filename = module.name;
} else {
filename = fileNamePointer.getString(0);
}
if (log.isDebugEnabled()) {
log.debug("dlopen filename=" + filename + ", flags=" + flags + ", LR=" + context.getLRPointer());
}
return dlopen(emulator.getMemory(), filename, emulator);
}
}).peer;
case "dladdr":
return svcMemory.registerSvc(new ArmSvc() {
@Override
public long handle(Emulator> emulator) {
RegisterContext context = emulator.getContext();
int addr = context.getIntArg(0);
Pointer info = context.getPointerArg(1);
if (log.isDebugEnabled()) {
log.debug("dladdr addr=0x" + Long.toHexString(addr) + ", info=" + info + ", LR=" + context.getLRPointer());
}
Module module = emulator.getMemory().findModuleByAddress(addr);
if (module == null) {
return 0;
}
Symbol symbol = module.findClosestSymbolByAddress(addr, true);
DlInfo32 dlInfo = new DlInfo32(info);
dlInfo.dli_fname = (int) UnidbgPointer.nativeValue(module.createPathMemory(svcMemory));
dlInfo.dli_fbase = (int) module.base;
if (symbol != null) {
dlInfo.dli_sname = (int) UnidbgPointer.nativeValue(symbol.createNameMemory(svcMemory));
dlInfo.dli_saddr = (int) symbol.getAddress();
}
dlInfo.pack();
return 1;
}
}).peer;
case "dlsym":
return svcMemory.registerSvc(new ArmSvc() {
@Override
public long handle(Emulator> emulator) {
RegisterContext context = emulator.getContext();
int handle = context.getIntArg(0);
Pointer symbol = context.getPointerArg(1);
if (log.isDebugEnabled()) {
log.debug("dlsym handle=0x" + Long.toHexString(handle) + ", symbol=" + symbol.getString(0) + ", LR=" + context.getLRPointer());
}
return dlsym(emulator, (handle & 0xffffffffL), symbol.getString(0));
}
}).peer;
case "dl_unwind_find_exidx":
return svcMemory.registerSvc(new ArmSvc() {
@Override
public long handle(Emulator> emulator) {
RegisterContext context = emulator.getContext();
Pointer pc = context.getPointerArg(0);
Pointer pcount = context.getPointerArg(1);
log.info("dl_unwind_find_exidx pc" + pc + ", pcount=" + pcount);
return 0;
}
}).peer;
case "android_get_application_target_sdk_version":
return svcMemory.registerSvc(new ArmHook() {
@Override
protected HookStatus hook(Emulator> emulator) {
return HookStatus.LR(emulator, 0);
}
}).peer;
}
}
return 0;
}
private long dlopen(Memory memory, String filename, Emulator> emulator) {
UnidbgPointer pointer = UnidbgPointer.register(emulator, ArmConst.UC_ARM_REG_SP);
try {
Module module = memory.dlopen(filename, false);
pointer = pointer.share(-4, 0); // return value
if (module == null) {
pointer.setInt(0, 0);
pointer = pointer.share(-4, 0); // NULL-terminated
pointer.setInt(0, 0);
if (!"libnetd_client.so".equals(filename)) {
log.info("dlopen failed: " + filename + ", LR=" + emulator.getContext().getLRPointer());
} else if(log.isDebugEnabled()) {
log.debug("dlopen failed: " + filename);
}
this.error.setString(0, "Resolve library " + filename + " failed");
return 0;
} else {
pointer.setInt(0, (int) module.base);
pointer = pointer.share(-4, 0); // NULL-terminated
pointer.setInt(0, 0);
LinuxModule m = (LinuxModule) module;
if (m.getUnresolvedSymbol().isEmpty()) {
for (InitFunction initFunction : m.initFunctionList) {
long address = initFunction.getAddress();
if (address == 0) {
continue;
}
if (log.isDebugEnabled()) {
log.debug("[" + m.name + "]PushInitFunction: 0x" + Long.toHexString(address));
}
pointer = pointer.share(-4, 0); // init array
pointer.setInt(0, (int) address);
}
m.initFunctionList.clear();
}
return module.base;
}
} finally {
backend.reg_write(ArmConst.UC_ARM_REG_SP, pointer.peer);
}
}
private int dlclose(Memory memory, long handle) {
if (memory.dlclose(handle)) {
return 0;
} else {
this.error.setString(0, "dlclose 0x" + Long.toHexString(handle) + " failed");
return -1;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy