-
-
Save SeeFlowerX/94b7c52e8bc3d6ddb9108ac5003c6570 to your computer and use it in GitHub Desktop.
dump上下文模拟执行doCommandNative方法
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.kuaishou.android.security.internal.dispatch; | |
import com.alibaba.fastjson.JSONArray; | |
import com.alibaba.fastjson.JSONObject; | |
import com.github.unidbg.AndroidEmulator; | |
import com.github.unidbg.Module; | |
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.linux.android.AndroidEmulatorBuilder; | |
import com.github.unidbg.linux.android.AndroidResolver; | |
import com.github.unidbg.linux.android.dvm.*; | |
import com.github.unidbg.linux.android.dvm.array.ArrayObject; | |
import com.github.unidbg.linux.android.dvm.wrapper.DvmBoolean; | |
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger; | |
import com.github.unidbg.memory.Memory; | |
import com.github.unidbg.memory.MemoryBlock; | |
import com.github.unidbg.pointer.UnidbgPointer; | |
import org.apache.commons.io.IOUtils; | |
import unicorn.ArmConst; | |
import unicorn.UnicornConst; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.nio.ByteBuffer; | |
import java.nio.ByteOrder; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.zip.DataFormatException; | |
import java.util.zip.Inflater; | |
public class JNICLibrary extends AbstractJni { | |
private final AndroidEmulator emulator; | |
private final VM vm; | |
JNICLibrary() { | |
emulator = AndroidEmulatorBuilder.for32Bit() | |
.setProcessName("com.smile.gifmaker") | |
.addBackendFactory(new Unicorn2Factory(true)) | |
.build(); | |
emulator.getSyscallHandler().setEnableThreadDispatcher(false); | |
final Memory memory = emulator.getMemory(); | |
memory.setLibraryResolver(new AndroidResolver(23)); | |
vm = emulator.createDalvikVM(); | |
vm.setJni(this); | |
vm.setVerbose(true); | |
} | |
public static void main(String[] args) throws Exception { | |
JNICLibrary mJNICLibrary = new JNICLibrary(); | |
mJNICLibrary.load_context("unidbg-android/src/test/resources/DumpContext_20220808_000250"); | |
mJNICLibrary.hook_libc(); | |
mJNICLibrary.doCommandNative(); | |
} | |
private void hook_libc() { | |
long libc_gettimeofday_addr = 3964436480L + 0x33534; | |
emulator.getBackend().hook_add_new(new CodeHook() { | |
@Override | |
public void hook(Backend backend, long address, int size, Object user) { | |
UnidbgPointer tv_ptr = emulator.getContext().getPointerArg(0); | |
ByteBuffer tv = ByteBuffer.allocate(8); | |
tv.order(ByteOrder.LITTLE_ENDIAN); | |
tv.putInt(0,1659890020); | |
tv.putInt(4, 2891054); | |
byte[] data = tv.array(); | |
tv_ptr.write(0,data,0,8); | |
backend.reg_write(ArmConst.UC_ARM_REG_PC, backend.reg_read(ArmConst.UC_ARM_REG_LR).longValue()); | |
} | |
@Override | |
public void onAttach(UnHook unHook) { | |
} | |
@Override | |
public void detach() { | |
} | |
}, libc_gettimeofday_addr, libc_gettimeofday_addr, null); | |
long libc_pthread_mutex_lock_addr = 3964436480L + 0x812EC; | |
emulator.getBackend().hook_add_new(new CodeHook() { | |
@Override | |
public void hook(Backend backend, long address, int size, Object user) { | |
backend.reg_write(ArmConst.UC_ARM_REG_R0, 0L); | |
backend.reg_write(ArmConst.UC_ARM_REG_PC, backend.reg_read(ArmConst.UC_ARM_REG_LR).longValue()); | |
} | |
@Override | |
public void onAttach(UnHook unHook) { | |
} | |
@Override | |
public void detach() { | |
} | |
}, libc_pthread_mutex_lock_addr, libc_pthread_mutex_lock_addr, null); | |
long libc_pthread_mutex_unlock_addr = 3964436480L + 0x8166C; | |
emulator.getBackend().hook_add_new(new CodeHook() { | |
@Override | |
public void hook(Backend backend, long address, int size, Object user) { | |
backend.reg_write(ArmConst.UC_ARM_REG_R0, 0L); | |
backend.reg_write(ArmConst.UC_ARM_REG_PC, backend.reg_read(ArmConst.UC_ARM_REG_LR).longValue()); | |
} | |
@Override | |
public void onAttach(UnHook unHook) { | |
} | |
@Override | |
public void detach() { | |
} | |
}, libc_pthread_mutex_unlock_addr, libc_pthread_mutex_unlock_addr, null); | |
long libc_malloc_addr = 3964436480L + 0x294F8; | |
emulator.getBackend().hook_add_new(new CodeHook() { | |
@Override | |
public void hook(Backend backend, long address, int size, Object user) { | |
int msize = backend.reg_read(ArmConst.UC_ARM_REG_R0).intValue(); | |
MemoryBlock block = emulator.getMemory().malloc(msize, true); | |
backend.reg_write(ArmConst.UC_ARM_REG_R0, block.getPointer().toUIntPeer()); | |
backend.reg_write(ArmConst.UC_ARM_REG_PC, backend.reg_read(ArmConst.UC_ARM_REG_LR).longValue()); | |
} | |
@Override | |
public void onAttach(UnHook unHook) { | |
} | |
@Override | |
public void detach() { | |
} | |
}, libc_malloc_addr, libc_malloc_addr, null); | |
long libc_caloc_addr = 3964436480L + 0x2943C; | |
emulator.getBackend().hook_add_new(new CodeHook() { | |
@Override | |
public void hook(Backend backend, long address, int size, Object user) { | |
int num = backend.reg_read(ArmConst.UC_ARM_REG_R0).intValue(); | |
int msize = backend.reg_read(ArmConst.UC_ARM_REG_R1).intValue(); | |
MemoryBlock block = emulator.getMemory().malloc(num * msize, true); | |
backend.reg_write(ArmConst.UC_ARM_REG_R0, block.getPointer().toUIntPeer()); | |
backend.reg_write(ArmConst.UC_ARM_REG_PC, backend.reg_read(ArmConst.UC_ARM_REG_LR).longValue()); | |
} | |
@Override | |
public void onAttach(UnHook unHook) { | |
} | |
@Override | |
public void detach() { | |
} | |
}, libc_caloc_addr, libc_caloc_addr, null); | |
long libc_free_addr = 3964436480L + 0x29490; | |
emulator.getBackend().hook_add_new(new CodeHook() { | |
@Override | |
public void hook(Backend backend, long address, int size, Object user) { | |
backend.reg_write(ArmConst.UC_ARM_REG_PC, backend.reg_read(ArmConst.UC_ARM_REG_LR).longValue()); | |
} | |
@Override | |
public void onAttach(UnHook unHook) { | |
} | |
@Override | |
public void detach() { | |
} | |
}, libc_free_addr, libc_free_addr, null); | |
long libcpp_patch_addr = 3620732928L + 0x87BB0; | |
emulator.getBackend().hook_add_new(new CodeHook() { | |
@Override | |
public void hook(Backend backend, long address, int size, Object user) { | |
backend.reg_write(ArmConst.UC_ARM_REG_PC, backend.reg_read(ArmConst.UC_ARM_REG_LR).longValue()); | |
} | |
@Override | |
public void onAttach(UnHook unHook) { | |
} | |
@Override | |
public void detach() { | |
} | |
}, libcpp_patch_addr, libcpp_patch_addr, null); | |
@Override | |
public boolean callBooleanMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) { | |
switch (signature){ | |
case "java/lang/Boolean->booleanValue()Z":{ | |
return (boolean) dvmObject.getValue(); | |
// return false; | |
} | |
} | |
return super.callBooleanMethodV(vm, dvmObject, signature, vaList); | |
} | |
int UNICORN_PAGE_SIZE = 0x1000; | |
private long align_page_down(long x){ | |
return x & ~(UNICORN_PAGE_SIZE - 1); | |
} | |
private long align_page_up(long x){ | |
return (x + UNICORN_PAGE_SIZE - 1) & ~(UNICORN_PAGE_SIZE - 1); | |
} | |
private void map_segment(long address, long size, int perms){ | |
long mem_start = address; | |
long mem_end = address + size; | |
long mem_start_aligned = align_page_down(mem_start); | |
long mem_end_aligned = align_page_up(mem_end); | |
if (mem_start_aligned < mem_end_aligned){ | |
emulator.getBackend().mem_map(mem_start_aligned, mem_end_aligned - mem_start_aligned, perms); | |
} | |
} | |
private void load_context(String dump_dir) throws IOException, DataFormatException, IOException { | |
Backend backend = emulator.getBackend(); | |
Memory memory = emulator.getMemory(); | |
String context_file = dump_dir + "\\" + "_index.json"; | |
InputStream is = new FileInputStream(context_file); | |
String jsonTxt = IOUtils.toString(is, "UTF-8"); | |
JSONObject context = JSONObject.parseObject(jsonTxt); | |
JSONObject regs = context.getJSONObject("regs"); | |
backend.reg_write(ArmConst.UC_ARM_REG_R0, Long.decode(regs.getString("r0"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R1, Long.decode(regs.getString("r1"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R2, Long.decode(regs.getString("r2"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R3, Long.decode(regs.getString("r3"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R4, Long.decode(regs.getString("r4"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R5, Long.decode(regs.getString("r5"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R6, Long.decode(regs.getString("r6"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R7, Long.decode(regs.getString("r7"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R8, Long.decode(regs.getString("r8"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R9, Long.decode(regs.getString("r9"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R10, Long.decode(regs.getString("r10"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R11, Long.decode(regs.getString("r11"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_R12, Long.decode(regs.getString("r12"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_SP, Long.decode(regs.getString("sp"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_LR, Long.decode(regs.getString("lr"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_PC, Long.decode(regs.getString("pc"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_CPSR, Long.decode(regs.getString("cpsr"))); | |
backend.reg_write(ArmConst.UC_ARM_REG_FPSCR, Long.decode(regs.getString("fpscr"))); | |
// 好像不设置这个也不会有什么影响 | |
memory.setStackPoint(Long.decode(regs.getString("sp"))); | |
backend.enableVFP(); | |
JSONArray segments = context.getJSONArray("segments"); | |
for (int i = 0; i < segments.size(); i++) { | |
JSONObject segment = segments.getJSONObject(i); | |
String path = segment.getString("name"); | |
long start = segment.getLong("start"); | |
long end = segment.getLong("end"); | |
String content_file = segment.getString("content_file"); | |
JSONObject permissions = segment.getJSONObject("permissions"); | |
int perms = 0; | |
if (permissions.getBoolean("r")){ | |
perms |= UnicornConst.UC_PROT_READ; | |
} | |
if (permissions.getBoolean("w")){ | |
perms |= UnicornConst.UC_PROT_WRITE; | |
} | |
if (permissions.getBoolean("x")){ | |
perms |= UnicornConst.UC_PROT_EXEC; | |
} | |
String[] paths = path.split("/"); | |
String module_name = paths[paths.length - 1]; | |
List<String> white_list = Arrays.asList(new String[]{"libkwsgmain.so", "libc.so", "[anon:stack_and_tls:19161]", "[anon:.bss]", "[vdso]", "[vvar]", "[anon:scudo:primary]", "libc++_shared.so", "[anon:scudo:secondary]"}); | |
if (white_list.contains(module_name)){ | |
int size = (int)(end - start); | |
map_segment(start, size, perms); | |
String content_file_path = dump_dir + "\\" + content_file; | |
File content_file_f = new File(content_file_path); | |
if (content_file_f.exists()){ | |
InputStream content_file_is = new FileInputStream(content_file_path); | |
byte[] content_file_buf = IOUtils.toByteArray(content_file_is); | |
// 解压 | |
Inflater decompresser = new Inflater(); | |
decompresser.setInput(content_file_buf, 0, content_file_buf.length); | |
byte[] result = new byte[size]; | |
int resultLength = decompresser.inflate(result); | |
decompresser.end(); | |
backend.mem_write(start, result); | |
} | |
else { | |
System.out.println("not exists path=" + path); | |
byte[] fill_mem = new byte[size]; | |
Arrays.fill( fill_mem, (byte) 0 ); | |
backend.mem_write(start, fill_mem); | |
} | |
} | |
} | |
} | |
private void doCommandNative() { | |
// emulator.traceCode(); | |
List<Object> list = new ArrayList<>(10); | |
list.add(vm.getJNIEnv()); // 第一个参数是env | |
DvmObject<?> thiz = vm.resolveClass("com/kuaishou/android/security/internal/dispatch/JNICLibrary").newObject(null); | |
list.add(vm.addLocalObject(thiz)); // 第二个参数,实例方法是jobject,静态方法是jclass,直接填0,一般用不到。 | |
DvmObject<?> context = vm.resolveClass("com/yxcorp/gifshow/App", vm.resolveClass("android/app/Application")).newObject(null); // context | |
vm.addLocalObject(context); | |
list.add(10418); //参数1 | |
StringObject urlObj = new StringObject(vm, "/rest/n/encode/androidffdb4a81067a6f7e20afbc3ec76652b3"); | |
vm.addLocalObject(urlObj); | |
ArrayObject arrayObject = new ArrayObject(urlObj); | |
vm.addLocalObject(arrayObject); | |
StringObject appkey = new StringObject(vm,"d7b7d042-d4f2-4012-be60-d97ff2429c17"); | |
vm.addLocalObject(appkey); | |
DvmInteger intergetobj = DvmInteger.valueOf(vm, -1); | |
vm.addLocalObject(intergetobj); | |
DvmBoolean boolobj = DvmBoolean.valueOf(vm, false); | |
vm.addLocalObject(boolobj); | |
StringObject whitestr = new StringObject(vm,""); | |
vm.addLocalObject(whitestr); | |
ArrayObject objlist = new ArrayObject(arrayObject, appkey, intergetobj, boolobj, context, null, boolobj, whitestr); | |
list.add(vm.addLocalObject(objlist)); | |
// 这里获取 dump 时的 pc 地址作为模拟执行起始地址 | |
long ctx_addr = emulator.getBackend().reg_read(ArmConst.UC_ARM_REG_PC).longValue(); | |
// 开始模拟执行 | |
Number result = Module.emulateFunction(emulator, ctx_addr + 1, list.toArray()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment