From 06d40c194895bd88cba30d9aad8fefef634d4f41 Mon Sep 17 00:00:00 2001 From: luoyesiqiu Date: Sat, 3 Aug 2024 20:14:58 +0800 Subject: [PATCH] Encrypt so files --- dpt/src/main/java/com/luoye/dpt/Const.java | 2 + .../main/java/com/luoye/dpt/builder/Apk.java | 51 +- .../main/java/com/luoye/dpt/elf/ReadElf.java | 457 ++++++++++++++++++ .../java/com/luoye/dpt/util/HexUtils.java | 72 +++ .../main/java/com/luoye/dpt/util/IoUtils.java | 36 ++ .../java/com/luoye/dpt/util/RC4Utils.java | 20 + shell/src/main/cpp/CMakeLists.txt | 1 + shell/src/main/cpp/common/dpt_macro.h | 12 +- shell/src/main/cpp/dpt.cpp | 40 +- shell/src/main/cpp/dpt.h | 3 + shell/src/main/cpp/dpt_hook.cpp | 14 +- shell/src/main/cpp/dpt_risk.cpp | 1 - shell/src/main/cpp/dpt_util.cpp | 63 ++- shell/src/main/cpp/dpt_util.h | 5 + shell/src/main/cpp/rc4/rc4.c | 101 ++++ shell/src/main/cpp/rc4/rc4.h | 62 +++ .../java/com/luoyesiqiu/shell/JniBridge.java | 2 +- 17 files changed, 925 insertions(+), 17 deletions(-) create mode 100644 dpt/src/main/java/com/luoye/dpt/elf/ReadElf.java create mode 100644 dpt/src/main/java/com/luoye/dpt/util/RC4Utils.java create mode 100644 shell/src/main/cpp/rc4/rc4.c create mode 100644 shell/src/main/cpp/rc4/rc4.h diff --git a/dpt/src/main/java/com/luoye/dpt/Const.java b/dpt/src/main/java/com/luoye/dpt/Const.java index 5ecc3132..3c72e912 100644 --- a/dpt/src/main/java/com/luoye/dpt/Const.java +++ b/dpt/src/main/java/com/luoye/dpt/Const.java @@ -26,4 +26,6 @@ public class Const { public static final String PROXY_APPLICATION_NAME = "com.luoyesiqiu.shell.ProxyApplication"; public static final String PROXY_COMPONENT_FACTORY = "com.luoyesiqiu.shell.ProxyComponentFactory"; public static final short MULTI_DEX_CODE_VERSION = 1; + + public static final String DEFAULT_RC4_KEY = "ncWK&S5wbqU%IX6j"; } diff --git a/dpt/src/main/java/com/luoye/dpt/builder/Apk.java b/dpt/src/main/java/com/luoye/dpt/builder/Apk.java index 135d3807..fdec371b 100644 --- a/dpt/src/main/java/com/luoye/dpt/builder/Apk.java +++ b/dpt/src/main/java/com/luoye/dpt/builder/Apk.java @@ -3,6 +3,7 @@ import com.android.apksigner.ApkSignerTool; import com.iyxan23.zipalignjava.ZipAlign; import com.luoye.dpt.Const; +import com.luoye.dpt.elf.ReadElf; import com.luoye.dpt.model.Instruction; import com.luoye.dpt.model.MultiDexCode; import com.luoye.dpt.task.ThreadPool; @@ -12,6 +13,7 @@ import com.luoye.dpt.util.LogUtils; import com.luoye.dpt.util.ManifestUtils; import com.luoye.dpt.util.MultiDexCodeUtils; +import com.luoye.dpt.util.RC4Utils; import com.luoye.dpt.util.ZipUtils; import com.wind.meditor.core.FileProcesser; import com.wind.meditor.property.AttributeItem; @@ -22,6 +24,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -135,6 +138,7 @@ private static void process(Apk apk){ apk.addProxyDex(apkMainProcessPath); apk.copyNativeLibs(apkMainProcessPath); + apk.encryptSoFiles(apkMainProcessPath); apk.buildApk(apkFile.getAbsolutePath(),apkMainProcessPath, FileUtils.getExecutablePath()); @@ -246,7 +250,7 @@ protected void addJunkCodeDex(String apkDir) { } private void compressDexFiles(String apkDir){ - ZipUtils.compress(getDexFiles(apkDir),getOutAssetsDir(apkDir).getAbsolutePath()+File.separator + "i11111i111.zip"); + ZipUtils.compress(getDexFiles(apkDir),getOutAssetsDir(apkDir).getAbsolutePath() + File.separator + "i11111i111.zip"); } private void copyNativeLibs(String apkDir){ @@ -254,6 +258,51 @@ private void copyNativeLibs(String apkDir){ FileUtils.copy(file.getAbsolutePath(),getOutAssetsDir(apkDir).getAbsolutePath() + File.separator + "vwwwwwvwww"); } + private void encryptSoFiles(String apkDir){ + + File obfDir = new File(getOutAssetsDir(apkDir).getAbsolutePath() + File.separator, "vwwwwwvwww"); + File[] soAbiDirs = obfDir.listFiles(); + if(soAbiDirs != null) { + for (File soAbiDir : soAbiDirs) { + File[] soFiles = soAbiDir.listFiles(); + if(soFiles != null) { + for (File soFile : soFiles) { + if(!soFile.getAbsolutePath().endsWith(".so")) { + continue; + } + try { + ReadElf readElf = new ReadElf(soFile); + List sectionHeaders = readElf.getSectionHeaders(); + readElf.close(); + for (ReadElf.SectionHeader sectionHeader : sectionHeaders) { + + if(".bitcode".equals(sectionHeader.getName())) { + + LogUtils.info("start encrypt %s section: %s,offset: %s,size: %s", + soFile.getAbsolutePath(), + sectionHeader.getName(), + Long.toHexString(sectionHeader.getOffset()), + Long.toHexString(sectionHeader.getSize()) + ); + + byte[] bitcode = IoUtils.readFile(soFile.getAbsolutePath(),sectionHeader.getOffset(),(int)sectionHeader.getSize()); + + byte[] enc = RC4Utils.crypt(Const.DEFAULT_RC4_KEY.getBytes(StandardCharsets.UTF_8),bitcode); + + IoUtils.writeFile(soFile.getAbsolutePath(),enc,sectionHeader.getOffset()); + } + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + + } + private void deleteAllDexFiles(String dir){ List dexFiles = getDexFiles(dir); for (File dexFile : dexFiles) { diff --git a/dpt/src/main/java/com/luoye/dpt/elf/ReadElf.java b/dpt/src/main/java/com/luoye/dpt/elf/ReadElf.java new file mode 100644 index 00000000..5f42170f --- /dev/null +++ b/dpt/src/main/java/com/luoye/dpt/elf/ReadElf.java @@ -0,0 +1,457 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.luoye.dpt.elf; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +/** + * A poor man's implementation of the readelf command. This program is designed + * to parse ELF (Executable and Linkable Format) files. + */ +public class ReadElf implements AutoCloseable { + /** The magic values for the ELF identification. */ + private static final byte[] ELFMAG = { + (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F', }; + private static final int EI_NIDENT = 16; + private static final int EI_CLASS = 4; + private static final int EI_DATA = 5; + private static final int EM_386 = 3; + private static final int EM_MIPS = 8; + private static final int EM_ARM = 40; + private static final int EM_X86_64 = 62; + // http://en.wikipedia.org/wiki/Qualcomm_Hexagon + private static final int EM_QDSP6 = 164; + private static final int EM_AARCH64 = 183; + private static final int ELFCLASS32 = 1; + private static final int ELFCLASS64 = 2; + private static final int ELFDATA2LSB = 1; + private static final int ELFDATA2MSB = 2; + private static final int EV_CURRENT = 1; + private static final long PT_LOAD = 1; + private static final int SHT_SYMTAB = 2; + private static final int SHT_STRTAB = 3; + private static final int SHT_DYNAMIC = 6; + private static final int SHT_DYNSYM = 11; + public static class Symbol { + public static final int STB_LOCAL = 0; + public static final int STB_GLOBAL = 1; + public static final int STB_WEAK = 2; + public static final int STB_LOPROC = 13; + public static final int STB_HIPROC = 15; + public static final int STT_NOTYPE = 0; + public static final int STT_OBJECT = 1; + public static final int STT_FUNC = 2; + public static final int STT_SECTION = 3; + public static final int STT_FILE = 4; + public static final int STT_COMMON = 5; + public static final int STT_TLS = 6; + public final String name; + public final int bind; + public final int type; + Symbol(String name, int st_info) { + this.name = name; + this.bind = (st_info >> 4) & 0x0F; + this.type = st_info & 0x0F; + } + @Override + public String toString() { + return "Symbol[" + name + "," + toBind() + "," + toType() + "]"; + } + private String toBind() { + switch (bind) { + case STB_LOCAL: + return "LOCAL"; + case STB_GLOBAL: + return "GLOBAL"; + case STB_WEAK: + return "WEAK"; + } + return "STB_??? (" + bind + ")"; + } + private String toType() { + switch (type) { + case STT_NOTYPE: + return "NOTYPE"; + case STT_OBJECT: + return "OBJECT"; + case STT_FUNC: + return "FUNC"; + case STT_SECTION: + return "SECTION"; + case STT_FILE: + return "FILE"; + case STT_COMMON: + return "COMMON"; + case STT_TLS: + return "TLS"; + } + return "STT_??? (" + type + ")"; + } + } + public static class SectionHeader { + + public SectionHeader(String sh_name, long sh_type, long sh_flags, long sh_addr, long sh_offset, long sh_size) { + this.sh_name = sh_name; + this.sh_type = sh_type; + this.sh_flags = sh_flags; + this.sh_addr = sh_addr; + this.sh_offset = sh_offset; + this.sh_size = sh_size; + } + + public String getName() { + return sh_name; + } + + public long getType() { + return sh_type; + } + + public long getFlags() { + return sh_flags; + } + + public long getAddr() { + return sh_addr; + } + + public long getOffset() { + return sh_offset; + } + + public long getSize() { + return sh_size; + } + private String sh_name; + private long sh_type; + private long sh_flags; + private long sh_addr; + private long sh_offset; + private long sh_size; + + + } + private final String mPath; + private final RandomAccessFile mFile; + private final byte[] mBuffer = new byte[512]; + private int mEndian; + private boolean mIsDynamic; + private boolean mIsPIE; + private int mType; + private int mAddrSize; + /** Symbol Table offset */ + private long mSymTabOffset; + /** Symbol Table size */ + private long mSymTabSize; + /** Dynamic Symbol Table offset */ + private long mDynSymOffset; + /** Dynamic Symbol Table size */ + private long mDynSymSize; + /** Section Header String Table offset */ + private long mShStrTabOffset; + /** Section Header String Table size */ + private long mShStrTabSize; + /** String Table offset */ + private long mStrTabOffset; + /** String Table size */ + private long mStrTabSize; + /** Dynamic String Table offset */ + private long mDynStrOffset; + /** Dynamic String Table size */ + private long mDynStrSize; + /** Symbol Table symbol names */ + private Map mSymbols; + /** Dynamic Symbol Table symbol names */ + private Map mDynamicSymbols; + private List mSectionHeaderList; + + public static ReadElf read(File file) throws IOException { + return new ReadElf(file); + } + public boolean isDynamic() { + return mIsDynamic; + } + public int getType() { + return mType; + } + public boolean isPIE() { + return mIsPIE; + } + public ReadElf(File file) throws IOException { + mPath = file.getPath(); + mFile = new RandomAccessFile(file, "r"); + if (mFile.length() < EI_NIDENT) { + throw new IllegalArgumentException("Too small to be an ELF file: " + file); + } + readHeader(); + } + + @Override + public void close() { + try { + mFile.close(); + } catch (IOException ignored) { + } + } + + private void readHeader() throws IOException { + mFile.seek(0); + mFile.readFully(mBuffer, 0, EI_NIDENT); + if (mBuffer[0] != ELFMAG[0] || mBuffer[1] != ELFMAG[1] || + mBuffer[2] != ELFMAG[2] || mBuffer[3] != ELFMAG[3]) { + throw new IllegalArgumentException("Invalid ELF file: " + mPath); + } + int elfClass = mBuffer[EI_CLASS]; + if (elfClass == ELFCLASS32) { + mAddrSize = 4; + } else if (elfClass == ELFCLASS64) { + mAddrSize = 8; + } else { + throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath); + } + mEndian = mBuffer[EI_DATA]; + if (mEndian == ELFDATA2LSB) { + } else if (mEndian == ELFDATA2MSB) { + throw new IOException("Unsupported ELFDATA2MSB file: " + mPath); + } else { + throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath); + } + mType = readHalf(); + int e_machine = readHalf(); + if (e_machine != EM_386 && e_machine != EM_X86_64 && + e_machine != EM_AARCH64 && e_machine != EM_ARM && + e_machine != EM_MIPS && + e_machine != EM_QDSP6) { + throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath); + } + // AbiTest relies on us rejecting any unsupported combinations. + if ((e_machine == EM_386 && elfClass != ELFCLASS32) || + (e_machine == EM_X86_64 && elfClass != ELFCLASS64) || + (e_machine == EM_AARCH64 && elfClass != ELFCLASS64) || + (e_machine == EM_ARM && elfClass != ELFCLASS32) || + (e_machine == EM_QDSP6 && elfClass != ELFCLASS32)) { + throw new IOException("Invalid e_machine/EI_CLASS ELF combination: " + + e_machine + "/" + elfClass + ": " + mPath); + } + long e_version = readWord(); + if (e_version != EV_CURRENT) { + throw new IOException("Invalid e_version: " + e_version + ": " + mPath); + } + long e_entry = readAddr(); + long ph_off = readOff(); + long sh_off = readOff(); + long e_flags = readWord(); + int e_ehsize = readHalf(); + int e_phentsize = readHalf(); + int e_phnum = readHalf(); + int e_shentsize = readHalf(); + int e_shnum = readHalf(); + int e_shstrndx = readHalf(); + readSectionHeaders(sh_off, e_shnum, e_shentsize, e_shstrndx); + readProgramHeaders(ph_off, e_phnum, e_phentsize); + } + private void readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx) + throws IOException { + // Read the Section Header String Table offset first. + if(mSectionHeaderList == null) { + mSectionHeaderList = new ArrayList<>(); + } + + { + mFile.seek(sh_off + e_shstrndx * e_shentsize); + long sh_name = readWord(); + long sh_type = readWord(); + long sh_flags = readX(mAddrSize); + long sh_addr = readAddr(); + long sh_offset = readOff(); + long sh_size = readX(mAddrSize); + // ... + if (sh_type == SHT_STRTAB) { + mShStrTabOffset = sh_offset; + mShStrTabSize = sh_size; + } + } + for (int i = 0; i < e_shnum; ++i) { + // Don't bother to re-read the Section Header StrTab. + if (i == e_shstrndx) { + continue; + } + mFile.seek(sh_off + i * e_shentsize); + long sh_name = readWord(); + long sh_type = readWord(); + long sh_flags = readX(mAddrSize); + long sh_addr = readAddr(); + long sh_offset = readOff(); + long sh_size = readX(mAddrSize); + final String shName = readShStrTabEntry(sh_name); + + mSectionHeaderList.add(new SectionHeader(shName,sh_type,sh_flags,sh_addr,sh_offset,sh_size)); + + if (sh_type == SHT_SYMTAB || sh_type == SHT_DYNSYM) { + if (".symtab".equals(shName)) { + mSymTabOffset = sh_offset; + mSymTabSize = sh_size; + } else if (".dynsym".equals(shName)) { + mDynSymOffset = sh_offset; + mDynSymSize = sh_size; + } + } else if (sh_type == SHT_STRTAB) { + if (".strtab".equals(shName)) { + mStrTabOffset = sh_offset; + mStrTabSize = sh_size; + } else if (".dynstr".equals(shName)) { + mDynStrOffset = sh_offset; + mDynStrSize = sh_size; + } + } else if (sh_type == SHT_DYNAMIC) { + mIsDynamic = true; + } + } + } + private void readProgramHeaders(long ph_off, int e_phnum, int e_phentsize) throws IOException { + for (int i = 0; i < e_phnum; ++i) { + mFile.seek(ph_off + i * e_phentsize); + long p_type = readWord(); + if (p_type == PT_LOAD) { + if (mAddrSize == 8) { + // Only in Elf64_phdr; in Elf32_phdr p_flags is at the end. + long p_flags = readWord(); + } + long p_offset = readOff(); + long p_vaddr = readAddr(); + // ... + if (p_vaddr == 0) { + mIsPIE = true; + } + } + } + } + private HashMap readSymbolTable(long symStrOffset, long symStrSize, + long tableOffset, long tableSize) throws IOException { + HashMap result = new HashMap(); + mFile.seek(tableOffset); + while (mFile.getFilePointer() < tableOffset + tableSize) { + long st_name = readWord(); + int st_info; + if (mAddrSize == 8) { + st_info = readByte(); + int st_other = readByte(); + int st_shndx = readHalf(); + long st_value = readAddr(); + long st_size = readX(mAddrSize); + } else { + long st_value = readAddr(); + long st_size = readWord(); + st_info = readByte(); + int st_other = readByte(); + int st_shndx = readHalf(); + } + if (st_name == 0) { + continue; + } + final String symName = readStrTabEntry(symStrOffset, symStrSize, st_name); + if (symName != null) { + Symbol s = new Symbol(symName, st_info); + result.put(symName, s); + } + } + return result; + } + private String readShStrTabEntry(long strOffset) throws IOException { + if (mShStrTabOffset == 0 || strOffset < 0 || strOffset >= mShStrTabSize) { + return null; + } + return readString(mShStrTabOffset + strOffset); + } + private String readStrTabEntry(long tableOffset, long tableSize, long strOffset) + throws IOException { + if (tableOffset == 0 || strOffset < 0 || strOffset >= tableSize) { + return null; + } + return readString(tableOffset + strOffset); + } + private int readHalf() throws IOException { + return (int) readX(2); + } + private long readWord() throws IOException { + return readX(4); + } + private long readOff() throws IOException { + return readX(mAddrSize); + } + private long readAddr() throws IOException { + return readX(mAddrSize); + } + private long readX(int byteCount) throws IOException { + mFile.readFully(mBuffer, 0, byteCount); + int answer = 0; + if (mEndian == ELFDATA2LSB) { + for (int i = byteCount - 1; i >= 0; i--) { + answer = (answer << 8) | (mBuffer[i] & 0xff); + } + } else { + final int N = byteCount - 1; + for (int i = 0; i <= N; ++i) { + answer = (answer << 8) | (mBuffer[i] & 0xff); + } + } + return answer; + } + private String readString(long offset) throws IOException { + long originalOffset = mFile.getFilePointer(); + mFile.seek(offset); + mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset)); + mFile.seek(originalOffset); + for (int i = 0; i < mBuffer.length; ++i) { + if (mBuffer[i] == 0) { + return new String(mBuffer, 0, i); + } + } + return null; + } + private int readByte() throws IOException { + return mFile.read() & 0xff; + } + public Symbol getSymbol(String name) { + if (mSymbols == null) { + try { + mSymbols = readSymbolTable(mStrTabOffset, mStrTabSize, mSymTabOffset, mSymTabSize); + } catch (IOException e) { + return null; + } + } + return mSymbols.get(name); + } + public Symbol getDynamicSymbol(String name) { + if (mDynamicSymbols == null) { + try { + mDynamicSymbols = readSymbolTable( + mDynStrOffset, mDynStrSize, mDynSymOffset, mDynSymSize); + } catch (IOException e) { + return null; + } + } + return mDynamicSymbols.get(name); + } + + public List getSectionHeaders() { + return mSectionHeaderList; + } +} \ No newline at end of file diff --git a/dpt/src/main/java/com/luoye/dpt/util/HexUtils.java b/dpt/src/main/java/com/luoye/dpt/util/HexUtils.java index be4a66a7..4921f5ef 100644 --- a/dpt/src/main/java/com/luoye/dpt/util/HexUtils.java +++ b/dpt/src/main/java/com/luoye/dpt/util/HexUtils.java @@ -1,6 +1,7 @@ package com.luoye.dpt.util; import java.util.Arrays; +import java.util.List; import java.util.Locale; public class HexUtils { @@ -15,4 +16,75 @@ public static String toHexArray(byte[] data){ } return Arrays.toString(array); } + + public static String toHexString(long l) { + return "0x" + Long.toHexString(l); + } + + public static String toHexString(byte[] array) { + Byte[] bytes = new Byte[array.length]; + for (int i = 0; i < array.length; i++) { + bytes[i] = array[i]; + } + return toHexString(bytes); + } + + public static String toHexString(T[] array) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("["); + int len = 0; + if(array instanceof Byte[]) { + len = 2; + } + else if(array instanceof Short[]) { + len = 4; + } + else if(array instanceof Integer[]) { + len = 8; + } + else if(array instanceof Long[]) { + len = 16; + } + else { + throw new IllegalArgumentException("unsupport type: " + array.getClass()); + } + + for (int i = 0; i < array.length; i++) { + stringBuilder.append(String.format(Locale.US,"%0" + len + "x",array[i])); + if(i < array.length - 1) { + stringBuilder.append(" "); + } + } + stringBuilder.append("]"); + + return stringBuilder.toString(); + } + + public static String toHexString(List list) { + if(list == null) { + return ""; + } + T t = list.get(0); + if(t instanceof Byte) { + Byte[] array = new Byte[list.size()]; + return toHexString(list.toArray(array)); + + } + else if(t instanceof Short) { + Short[] array = new Short[list.size()]; + return toHexString(list.toArray(array)); + } + else if(t instanceof Integer) { + Integer[] array = new Integer[list.size()]; + return toHexString(list.toArray(array)); + } + else if(t instanceof Long) { + Long[] array = new Long[list.size()]; + return toHexString(list.toArray(array)); + } + else { + throw new IllegalArgumentException("unsupport type: " + list.getClass()); + } + + } } diff --git a/dpt/src/main/java/com/luoye/dpt/util/IoUtils.java b/dpt/src/main/java/com/luoye/dpt/util/IoUtils.java index d60a24c6..9d00933b 100644 --- a/dpt/src/main/java/com/luoye/dpt/util/IoUtils.java +++ b/dpt/src/main/java/com/luoye/dpt/util/IoUtils.java @@ -6,6 +6,42 @@ import java.io.*; public class IoUtils { + + public static byte[] readFile(String file, long offset ,int len){ + FileInputStream fileInputStream = null; + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + fileInputStream = new FileInputStream(file); + byte[] buf = new byte[len]; + fileInputStream.skip(offset); + fileInputStream.read(buf); + byteArrayOutputStream.write(buf); + } + catch (Exception e){ + e.printStackTrace(); + } + finally { + close(fileInputStream); + close(byteArrayOutputStream); + } + return byteArrayOutputStream.toByteArray(); + } + + public static void writeFile(String dest ,byte[] data ,long offset){ + RandomAccessFile randomAccessFile = null; + try{ + randomAccessFile = new RandomAccessFile(new File(dest),"rw"); + randomAccessFile.seek(offset); + randomAccessFile.write(data); + } + catch (IOException e){ + e.printStackTrace(); + } + finally { + close(randomAccessFile); + } + } + public static byte[] readFile(String file){ FileInputStream fileInputStream = null; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); diff --git a/dpt/src/main/java/com/luoye/dpt/util/RC4Utils.java b/dpt/src/main/java/com/luoye/dpt/util/RC4Utils.java new file mode 100644 index 00000000..bdce06a9 --- /dev/null +++ b/dpt/src/main/java/com/luoye/dpt/util/RC4Utils.java @@ -0,0 +1,20 @@ +package com.luoye.dpt.util; + + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +public class RC4Utils { + public static final String transform = "RC4"; + public static byte[] crypt(byte[] key,byte[] in) { + try { + Cipher cipher = Cipher.getInstance(transform); + SecretKeySpec spec = new SecretKeySpec(key,transform); + cipher.init(Cipher.ENCRYPT_MODE,spec); + return cipher.doFinal(in); + } catch (Exception e) { + } + + return null; + } +} \ No newline at end of file diff --git a/shell/src/main/cpp/CMakeLists.txt b/shell/src/main/cpp/CMakeLists.txt index 679abc9e..273c2e9f 100644 --- a/shell/src/main/cpp/CMakeLists.txt +++ b/shell/src/main/cpp/CMakeLists.txt @@ -8,6 +8,7 @@ set(DPT_NATIVE_SOURCE dpt_jni.cpp dpt_util.cpp dpt_risk.cpp + rc4/rc4.c dex/dex_file.cpp dex/MultiDexCode.cpp dex/CodeItem.cpp diff --git a/shell/src/main/cpp/common/dpt_macro.h b/shell/src/main/cpp/common/dpt_macro.h index b762e72a..f5ca3a3c 100644 --- a/shell/src/main/cpp/common/dpt_macro.h +++ b/shell/src/main/cpp/common/dpt_macro.h @@ -7,11 +7,16 @@ #include "banned.h" +#define SECTION_NAME_BITCODE ".bitcode" + +#define DEFAULT_RC4_KEY "ncWK&S5wbqU%IX6j" + #define SECTION(name) __attribute__ ((section(name))) #define KEEP_SYMBOL __attribute__((visibility("default"))) #define INIT_ARRAY_SECTION __attribute__ ((constructor)) #define ALWAYS_INLINE static inline __attribute__((always_inline)) #define SYS_INLINE ALWAYS_INLINE +#define DPT_ENCRYPT SECTION(SECTION_NAME_BITCODE) #define DEXES_ZIP_NAME "i11111i111.zip" #define CACHE_DIR "code_cache" @@ -23,8 +28,7 @@ #define CODE_ITEM_NAME_IN_ASSETS "OoooooOooo" - -#define PAGE_START(addr) ((addr) & PAGE_MASK) +#define PAGE_START(addr) ((addr) & (uintptr_t)PAGE_MASK) #ifdef __LP64__ #define LIB_DIR "lib64" @@ -48,11 +52,15 @@ #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Off Elf64_Off +#define Elf_Word Elf64_Word +#define Elf_Half Elf64_Half #else #define Elf_Ehdr Elf32_Ehdr #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Off Elf32_Off +#define Elf_Word Elf32_Word +#define Elf_Half Elf32_Half #endif #ifndef LIKELY diff --git a/shell/src/main/cpp/dpt.cpp b/shell/src/main/cpp/dpt.cpp index e83629c8..c72d5441 100644 --- a/shell/src/main/cpp/dpt.cpp +++ b/shell/src/main/cpp/dpt.cpp @@ -239,8 +239,42 @@ void createAntiRiskProcess() { } } +void decrypt_bitcode() { + Dl_info info; + dladdr((const void *)decrypt_bitcode,&info); + Elf_Shdr shdr; + + get_elf_section(&shdr,info.dli_fname,SECTION_NAME_BITCODE); + Elf_Off offset = shdr.sh_offset; + Elf_Word size = shdr.sh_size; + + void *target = (u_char *)info.dli_fbase + offset; + + int rwe_prot = PROT_READ | PROT_WRITE | PROT_EXEC; + int ret = dpt_mprotect(target,(void *)((uint8_t *)target + size),rwe_prot); + if(ret == -1) { + abort(); + } + + u_char *bitcode = (u_char *)malloc(size); + struct rc4_state dec_state; + rc4_init(&dec_state, reinterpret_cast(DEFAULT_RC4_KEY), strlen(DEFAULT_RC4_KEY)); + rc4_crypt(&dec_state, reinterpret_cast(target), + reinterpret_cast(bitcode), + size); + DLOGD("%s offset: %x size: %x",__FUNCTION__ ,(int)offset,(int)size); + + memcpy(target,bitcode,size); + free(bitcode); + + int re_prot = PROT_READ | PROT_EXEC; + + dpt_mprotect(target,(void *)((uint8_t *)target + size),re_prot); +} + void init_dpt() { DLOGI("init_dpt call!"); + decrypt_bitcode(); dpt_hook(); createAntiRiskProcess(); } @@ -407,7 +441,7 @@ void init_app(JNIEnv *env, jclass __unused) { printTime("read apk data took =" , start); } -void readCodeItem(uint8_t *data,size_t data_len) { +DPT_ENCRYPT void readCodeItem(uint8_t *data,size_t data_len) { if (data != nullptr && data_len >= 0) { @@ -442,7 +476,7 @@ void readCodeItem(uint8_t *data,size_t data_len) { JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *__unused) { JNIEnv *env = nullptr; - if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) { + if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; } @@ -451,7 +485,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *__unused) { } DLOGI("JNI_OnLoad called!"); - return JNI_VERSION_1_4; + return JNI_VERSION_1_6; } JNIEXPORT void JNI_OnUnload(__unused JavaVM* vm,__unused void* reserved) { diff --git a/shell/src/main/cpp/dpt.h b/shell/src/main/cpp/dpt.h index b9d9ad7c..d7b7935b 100644 --- a/shell/src/main/cpp/dpt.h +++ b/shell/src/main/cpp/dpt.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,8 @@ #include "dpt_util.h" #include "dpt_risk.h" #include "common/dpt_log.h" +#include "common/dpt_macro.h" +#include "rc4/rc4.h" #include "dpt_hook.h" #include "dex/MultiDexCode.h" #include "dex/CodeItem.h" diff --git a/shell/src/main/cpp/dpt_hook.cpp b/shell/src/main/cpp/dpt_hook.cpp index 92cd7e95..822de7e4 100644 --- a/shell/src/main/cpp/dpt_hook.cpp +++ b/shell/src/main/cpp/dpt_hook.cpp @@ -68,7 +68,7 @@ void change_dex_protective(uint8_t * begin,int dexSize,int dexIndex){ } } -void patchMethod(uint8_t *begin,__unused const char *location,uint32_t dexSize,int dexIndex,uint32_t methodIdx,uint32_t codeOff){ +DPT_ENCRYPT void patchMethod(uint8_t *begin,__unused const char *location,uint32_t dexSize,int dexIndex,uint32_t methodIdx,uint32_t codeOff){ if(codeOff == 0){ NLOG("[*] patchMethod dex: %d methodIndex: %d no need patch!",dexIndex,methodIdx); return; @@ -107,7 +107,7 @@ void patchMethod(uint8_t *begin,__unused const char *location,uint32_t dexSize,i } } -void patchClass(__unused const char* descriptor, +DPT_ENCRYPT void patchClass(__unused const char* descriptor, const void* dex_file, const void* dex_class_def) { @@ -198,7 +198,7 @@ void patchClass(__unused const char* descriptor, } -void *DefineClassV22(void* thiz,void* self, +DPT_ENCRYPT void *DefineClassV22(void* thiz,void* self, const char* descriptor, size_t hash, void* class_loader, @@ -215,7 +215,7 @@ void *DefineClassV22(void* thiz,void* self, return nullptr; } -void *DefineClassV21(void* thiz, +DPT_ENCRYPT void *DefineClassV21(void* thiz, const char* descriptor, void* class_loader, const void* dex_file, @@ -229,7 +229,7 @@ void *DefineClassV21(void* thiz, return nullptr; } -void hook_DefineClass(){ +DPT_ENCRYPT void hook_DefineClass(){ void* defineClassAddress = DobbySymbolResolver(GetClassLinkerDefineClassLibPath(),getClassLinkerDefineClassSymbol()); if(g_sdkLevel >= __ANDROID_API_L_MR1__) { DobbyHook(defineClassAddress, (void *) DefineClassV22, (void **) &g_originDefineClassV22); @@ -247,7 +247,7 @@ const char *getArtLibName() { return "libart.so"; } -void* fake_mmap(void* __addr, size_t __size, int __prot, int __flags, int __fd, off_t __offset){ +DPT_ENCRYPT void* fake_mmap(void* __addr, size_t __size, int __prot, int __flags, int __fd, off_t __offset){ BYTEHOOK_STACK_SCOPE(); int prot = __prot; @@ -280,7 +280,7 @@ void* fake_mmap(void* __addr, size_t __size, int __prot, int __flags, int __fd, return addr; } -void hook_mmap(){ +DPT_ENCRYPT void hook_mmap(){ bytehook_stub_t stub = bytehook_hook_single( getArtLibName(), "libc.so", diff --git a/shell/src/main/cpp/dpt_risk.cpp b/shell/src/main/cpp/dpt_risk.cpp index cfb9757e..ba891078 100644 --- a/shell/src/main/cpp/dpt_risk.cpp +++ b/shell/src/main/cpp/dpt_risk.cpp @@ -48,7 +48,6 @@ void junkCodeDexProtect(JNIEnv *env) { DLOGD("detectFridaOnThread found frida threads"); crash(); } - DLOGD("detectFridaOnThread pass"); sleep(10); } } diff --git a/shell/src/main/cpp/dpt_util.cpp b/shell/src/main/cpp/dpt_util.cpp index 2208b494..d8e674ed 100644 --- a/shell/src/main/cpp/dpt_util.cpp +++ b/shell/src/main/cpp/dpt_util.cpp @@ -7,12 +7,26 @@ #include #include #include +#include #include "dpt_util.h" #include "common/dpt_log.h" #include "minizip-ng/mz_strm.h" using namespace dpt; + +int dpt_mprotect(void *start,void *end,int prot) { + uintptr_t start_addr = PAGE_START((uintptr_t)start); + uintptr_t end_addr = PAGE_START((uintptr_t)end - 1) + PAGE_SIZE; + size_t size = end_addr - start_addr; + + DLOGD("%s begin: %p, size: %zu",__FUNCTION__,(void *)start_addr,size); + + + if (0 != mprotect((void *)start_addr, size, prot)) return -1; + return 0; +} + static int separate_dex_number(std::string *str) { int sum = 0; int mul = 1; @@ -321,6 +335,52 @@ bool read_zip_file_entry(void* zip_addr,off_t zip_size,const char* entry_name,vo } } +void get_elf_section(Elf_Shdr *target,const char *elf_path,const char *sh_name) { + if(elf_path == NULL) { + return; + } + DLOGD("%s path: %s",__FUNCTION__,elf_path); + + FILE *elf_fp = fopen(elf_path, "r"); + if(!elf_fp) { + return; + } + + fseek(elf_fp, 0L, SEEK_END); + size_t lib_size = ftell(elf_fp); + fseek(elf_fp, 0L, SEEK_SET); + + uint8_t *data = (uint8_t *) calloc(lib_size, 1); + fread(data, 1, lib_size, elf_fp); + + uint8_t *elf_bytes_data = data; + + Elf_Ehdr *ehdr = (Elf_Ehdr *) elf_bytes_data; + Elf_Shdr *shdr = (Elf_Shdr *) (((uint8_t *) elf_bytes_data) + ehdr->e_shoff); + DLOGD("%s section header table addr = %p, offset = %x", __FUNCTION__, shdr, + (int) ehdr->e_shoff); + Elf_Half shstrndx = ehdr->e_shstrndx; + + //seek to .shstr + Elf_Shdr *shstr_shdr = (Elf_Shdr *)((uint8_t *) shdr + sizeof(Elf_Shdr) * shstrndx); + + uint8_t *shstr_addr = elf_bytes_data + shstr_shdr->sh_offset; + + DLOGD("%s shstr addr: %p", __FUNCTION__, shstr_addr); + + for (int i = 0; i < ehdr->e_shnum; i++) { + const char *section_name = reinterpret_cast((char *)shstr_addr + shdr->sh_name); + if (strcmp(section_name, sh_name) == 0) { + DLOGD("%s find section %s,offset: %x", __FUNCTION__,sh_name,(int)shdr->sh_offset); + + memcpy(target,shdr,sizeof(Elf_Shdr)); + } + shdr++; + } + free(data); + fclose(elf_fp); +} + const char* find_symbol_in_elf_file(const char *elf_file,int keyword_count,...) { FILE *elf_fp = fopen(elf_file, "r"); if(elf_fp) { @@ -467,7 +527,7 @@ int find_in_maps(int count,...) { return found; } -int find_in_threads_list(int count,...) { +DPT_ENCRYPT int find_in_threads_list(int count,...) { char task_path[128] = {0}; pid_t pid = getpid(); snprintf(task_path, ARRAY_LENGTH(task_path), "/proc/%d/task",pid); @@ -484,7 +544,6 @@ int find_in_threads_list(int count,...) { if(isdigit(de->d_name[0])) { int tid = atoi(de->d_name); if(tid == pid) { - DLOGW("list thread self: %d",pid); continue; } char stat_path[256] = {0}; diff --git a/shell/src/main/cpp/dpt_util.h b/shell/src/main/cpp/dpt_util.h index ceb3454b..93e79022 100644 --- a/shell/src/main/cpp/dpt_util.h +++ b/shell/src/main/cpp/dpt_util.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -54,6 +56,9 @@ bool read_zip_file_entry(void* zip_addr,off_t zip_size,const char* entry_name, v int find_in_maps(int count,...); int find_in_threads_list(int count,...); const char* find_symbol_in_elf_file(const char *elf_file,int keyword_count,...); +void get_elf_section(Elf_Shdr *target,const char *elf_path,const char *sh_name); + +int dpt_mprotect(void *start,void *end,int prot); void getClassName(JNIEnv *env,jobject obj,char *destClassName,size_t max_len); void parseClassName(const char *src, char *dest); diff --git a/shell/src/main/cpp/rc4/rc4.c b/shell/src/main/cpp/rc4/rc4.c new file mode 100644 index 00000000..fbca427f --- /dev/null +++ b/shell/src/main/cpp/rc4/rc4.c @@ -0,0 +1,101 @@ +/* + * rc4.c + * + * Copyright (c) 1996-2000 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/crypto/rc4/rc4.c,v 1.2.2.1 2000/04/18 04:48:31 archie Exp $ + */ + +#include "rc4.h" + +static __inline void +swap_bytes(u_char *a, u_char *b) +{ + u_char temp; + + temp = *a; + *a = *b; + *b = temp; +} + +/* + * Initialize an RC4 state buffer using the supplied key, + * which can have arbitrary length. + */ +void +rc4_init(struct rc4_state *const state, const u_char *key, int keylen) +{ + u_char j; + int i; + + /* Initialize state with identity permutation */ + for (i = 0; i < 256; i++) + state->perm[i] = (u_char)i; + state->index1 = 0; + state->index2 = 0; + + /* Randomize the permutation using key data */ + for (j = i = 0; i < 256; i++) { + j += state->perm[i] + key[i % keylen]; + swap_bytes(&state->perm[i], &state->perm[j]); + } +} + +/* + * Encrypt some data using the supplied RC4 state buffer. + * The input and output buffers may be the same buffer. + * Since RC4 is a stream cypher, this function is used + * for both encryption and decryption. + */ +void +rc4_crypt(struct rc4_state *const state, + const u_char *inbuf, u_char *outbuf, int buflen) +{ + int i; + u_char j; + + for (i = 0; i < buflen; i++) { + + /* Update modification indicies */ + state->index1++; + state->index2 += state->perm[state->index1]; + + /* Modify permutation */ + swap_bytes(&state->perm[state->index1], + &state->perm[state->index2]); + + /* Encrypt/decrypt next byte */ + j = state->perm[state->index1] + state->perm[state->index2]; + outbuf[i] = inbuf[i] ^ state->perm[j]; + } +} diff --git a/shell/src/main/cpp/rc4/rc4.h b/shell/src/main/cpp/rc4/rc4.h new file mode 100644 index 00000000..e0185b5e --- /dev/null +++ b/shell/src/main/cpp/rc4/rc4.h @@ -0,0 +1,62 @@ + +/* + * rc4.h + * + * Copyright (c) 1996-2000 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/crypto/rc4/rc4.h,v 1.2.2.1 2000/04/18 04:48:32 archie Exp $ + */ + +#ifndef _SYS_CRYPTO_RC4_RC4_H_ +#define _SYS_CRYPTO_RC4_RC4_H_ + +#include + +struct rc4_state { + u_char perm[256]; + u_char index1; + u_char index2; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rc4_init(struct rc4_state *state, const u_char *key, int keylen); +extern void rc4_crypt(struct rc4_state *state, + const u_char *inbuf, u_char *outbuf, int buflen); +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/shell/src/main/java/com/luoyesiqiu/shell/JniBridge.java b/shell/src/main/java/com/luoyesiqiu/shell/JniBridge.java index 21eb257a..c3e0bd58 100644 --- a/shell/src/main/java/com/luoyesiqiu/shell/JniBridge.java +++ b/shell/src/main/java/com/luoyesiqiu/shell/JniBridge.java @@ -14,7 +14,7 @@ */ @Keep public class JniBridge { - private static final String TAG = JniBridge.class.getSimpleName(); + private static final String TAG = "dpt_" + JniBridge.class.getSimpleName(); public static native void craoc(String applicationClassName); public static native void craa(Context context, String applicationClassName); public static native void ia();