Shou dumpling dance · 2015/08/17 14:53

Note: The framework is risky and should be used with caution.

Cydia Substrate is a code modification platform. It can modify any main process code, whether written in Java or C/C++ (native code). Xposed only supports Java functions in HOOK APP_process, so Cydia Substrate is a powerful and practical HOOK tool.

Website address: http://www.cydiasubstrate.com/

The official tutorial: http://www.cydiasubstrate.com/id/38be592b-bda7-4dd2-b049-cec44ef7a73b

The SDK download address: http://asdk.cydiasubstrate.com/zips/cydia_substrate-r2.zip

0 x00hook Java layer


Xposed before explained the use of why the whole thing, the following simple comparison of the two framework. Want to know before xposed article can look here: drops.wooyun.org/tips/7488

Disadvantage:

  • There is no error warning. It is more troublesome to sort out errors.
  • Need to have a certain understanding of NDK development, relatively Xposed module development cost is higher.
  • There is very little module code available on Github.

Advantage:

  • Native functions can be hooked.
  • Xposed hook principle is not the same, because it is not open source specific principle I do not know. As a result, some anti-hooks may work against Xposed but not Cydia.

Method of use


1. Install the framework app:http://www.cydiasubstrate.com/download/com.saurik.substrate.apk

2. Create an empty Android project. Since the project you create will be loaded as a plug-in, there is no need for an activity. Copy the SUBSTRATE -api.jar from the SDK into the project/libs folder.

3. Configure the Manifest file

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <meta-data android:name="com.saurik.substrate.main"
            android:value=".Main"/>
    </application>
    <uses-permission android:name="cydia.permission.SUBSTRATE"/>
</manifest>
Copy the code

4. Create a class named Main. This class contains a static method initialize that will run when the plug-in is loaded to do the necessary initialization.

#! java import com.saurik.substrate.MS; public class Main { static void initialize() { // ... code to run when extension is loaded } }Copy the code

5.hook imei example

#!java
import com.saurik.substrate.MS;
public class Main {
    static void initialize() {
        MS.hookClassLoad("android.telephony.TelephonyManager",
                new MS.ClassLoadHook() {
                    @SuppressWarnings("unchecked")
                    public void classLoaded(Class<?> arg0) {
                        Method hookimei;
                        try {
                            hookimei = arg0.getMethod("getDeviceId", null);
                        } catch (NoSuchMethodException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                            hookimei = null;
                        }
                        if (hookimei != null) {
                            final MS.MethodPointer old1 = new MS.MethodPointer();
                            MS.hookMethod(arg0, hookimei, new MS.MethodHook() {
                                @Override
                                public Object invoked(Object arg0,
                                        Object... arg1) throws Throwable {
                                    // TODO Auto-generated method stub
                                    System.out.println("hook imei----------->");
                                    String imei = (String) old1.invoke(arg0,
                                            arg1);
                                    System.out.println("imei-------->" + imei);
                                    imei = "999996015409998";
                                    return imei;
                                }
                            }, old1);
                        }
                    }
                });
    }
}
Copy the code

6. In the Cydia APP interface, click Link Substrate Files and restart the phone

7. Use getimei’s applet to verify that the IMEI has been changed

#! java public class MainActivity extends ActionBarActivity { private static final String tag = "MainActivity"; TextView mText ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mText = (TextView) findViewById(R.id.text); TelephonyManager mtelehonyMgr = (TelephonyManager) getSystemService(this.TELEPHONY_SERVICE); Build bd = new Build(); String imei = mtelehonyMgr.getDeviceId(); String imsi = mtelehonyMgr.getSubscriberId(); GetSimSerialNumber () obtains the SIM serial number getLine1Number Obtains the phone number String androidId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); String id = UUID.randomUUID().toString(); String model = bd.MODEL; StringBuilder sb = new StringBuilder(); sb.append("imei = "+ imei); sb.append("\nimsi = " + imsi); sb.append("\nandroid_id = " + androidId); sb.append("\nuuid = " + id); sb.append("\nmodel = " + model); if(imei! =null) mText.setText(sb.toString()); else mText.setText("fail"); }Copy the code

8. Key API introduction

HookClassLoad: This method implementation notifies the specified class when it is loaded. Because a class can be loaded at any time, Substrate provides a method for detecting when a class of interest to the user is being loaded.

The API needs to implement a simple interface, Ms. ClassLoadHook. This interface has only one method classLoaded, which is executed when the class is loaded. The loaded class is passed this method as an argument.

void hookClassLoad(String name, MS.ClassLoadHook hook);
Copy the code
parameter describe
name Package name + class name, using Java. Symbol (full class name to be hooked)
hook An instance of Ms. ClassLoadHook whose classLoaded method is executed when the class is loaded.
#! java MS.hookClassLoad("java.net.HttpURLConnection", new MS.ClassLoadHook() { public void classLoaded(Class<?> _class) { /* do something with _class argument */ } } );Copy the code

Ms. HookMethod: This API allows developers to replace the method with a callback function, which is an object that implements the Ms. MethodHook interface and is typically an anonymous inner class. It contains a invoked function.

#! java void hookMethod(Class _class, Member member, MS.MethodHook hook, MS.MethodPointer old);Copy the code
parameter describe
_class The loaded target class is the class argument passed down from classLoaded
member A method (or constructor) derived by reflection that requires a hook. Note: No hook fields (checked at compile time).
hook An instance of Ms. MethodHook containing the invoked method used instead of the code in member

0 x01hook Native layer


This function xposed can not be implemented.

The whole process is roughly as follows:

  • Create a project to add NDK support
  • Add cydia libraries and header files to the project
  • Modify the AndroidManifest configuration file
  • Modify Android. The md
  • Development module
    • Specify the lib library to hook
    • Keep the original address
    • Substitution function
    • Substrate entry point
      • MSGetImageByName or dlopen
      • MSFindSymbol or dlsym or nlist specifies the method to get the starting address
      • MSHookFunction Replace function

Method of use


** Step 0: Add NDK support and add cydia libraries and headers to the project

The basics of NDK development can be found in this article:The NDK portal

Note that if xxx.cy. CPP, don’t forget.cy

All.cy in module of android.md must be included

LOCAL_MODULE    := DumpDex2.cy
Copy the code

Step 1: Modify the configuration file

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:installLocation="internalOnly"
>
    <application android:hasCode="false">
    </application>

    <uses-permission android:name="cydia.permission.SUBSTRATE"/>
</manifest>
Copy the code

Set android:hasCode false, set Android :installLocation internalOnly”

Step 2: Specify the lib library to hook

#include <substrate.h>

MSConfig(MSFilterExecutable, "/system/bin/app_process")  //MSConfig(MSFilterLibrary, "liblog.so")

// this is a macro that uses __attribute__((__constructor__))
MSInitialize {
    // ... code to run when extension is loaded
}
Copy the code

Set the executable or dynamic library to hook

Step 3: Wait for the class

static void OnResources(JNIEnv *jni, jclass resources, void *data) {
    // ... code to modify the class when loaded
}

MSInitialize {
    MSJavaHookClassLoad(NULL, "android/content/res/Resources", &OnResources);
}
Copy the code

Step 4: Modify the implementation

static jint (*_Resources$getColor)(JNIEnv *jni, jobject _this, ...) ; static jint $Resources$getColor(JNIEnv *jni, jobject _this, jint rid) { jint color = _Resources$getColor(jni, _this, rid); return color & ~0x0000ff00 | 0x00ff0000; } static void OnResources(JNIEnv *jni, jclass resources, void *data) { jmethodID method = jni->GetMethodID(resources, "getColor", "(I)I"); if (method ! = NULL) MSJavaHookMethod(jni, resources, method, &$Resources$getColor, &_Resources$getColor); }Copy the code

The following steps are based on the official website tutorial on some of the white students supplement it.

» file libprocess. So libprocess. So: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not strippedCopy the code

Step 5

Copy libsubstrate- DVM. so(note the choice of ARM and x86 platforms) and substrate. H to the JNI directory. Create the supermathhook.cy.cpp file

Step 6

Configure the Android.mk file

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE:= substrate-dvm LOCAL_SRC_FILES := libsubstrate-dvm.so include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := SuperMathHook.cy LOCAL_SRC_FILES := SuperMathHook.cy.cpp LOCAL_LDLIBS := -llog LOCAL_LDLIBS += -L$(LOCAL_PATH) -lsubstrate-dvm // -l specifies the directory of the library file,-l specifies the filename of the library, and -i specifies the directory of the header file.Copy the code

Join the lib of c

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE:= substrate-dvm
LOCAL_SRC_FILES := libsubstrate-dvm.so
include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_MODULE:= substrate
LOCAL_SRC_FILES := libsubstrate.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE    := CydiaN.cy
LOCAL_SRC_FILES := CydiaN.cy.cpp
LOCAL_LDLIBS := -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH) -lsubstrate-dvm -lsubstrate

include $(BUILD_SHARED_LIBRARY)
Copy the code

Strings look at the function inside.

/data/data/com.jerome.jni/lib # strings libprocess.so                                                               <
/system/bin/linker
__cxa_finalize
__cxa_atexit
Jstring2CStr
malloc
memcpy
__aeabi_unwind_cpp_pr0
Java_com_jerome_jni_JNIProcess_getInfoMD5
....
Copy the code

The mold of the sheller is opened


IDA Dump shucking process is as follows:

  • To/system/lib/libdvm. So method JNI_OnLoad dvmLoadNativeCode/dvmDexFileOpenPartial breakpoint under analysis
  • IDA Add-on App (IDA6.5 and later)
  • Ctrl+ S to view base address + offset
  • IDA analysis looks for dump points
  • F8/F9 is dumped when dex is decrypted to memory

The goal now is to automate this functionality through Cydia’s modules. I’m going to hook dvmDexFileOpenPartial. As for why choose here? This requires an analysis of the Android DEX optimization process


Android optimizes the dex file for each installed application to generate an Odex file. Compared with dex files, Odex files have an Optheader, which relies on library information (local function libraries required by dex files) and auxiliary information (class index information, etc.).

Dex function modules of the optimization process is an independent, located at http://androidxref.com/4.4.3_r1.1/xref/dalvik/dexopt/OptMain.cpp#57 The extractAndProcessZip() function completes the optimization operation.

http://androidxref.com/4.1.1/xref/dalvik/dexopt/OptMain.cpp

The main function in OptMain is the original entry point for loading dex

#!c
int main(int argc, char* const argv[])
{
    set_process_name("dexopt");

    setvbuf(stdout, NULL, _IONBF, 0);

    if (argc > 1) {
        if (strcmp(argv[1], "--zip") == 0)
            return fromZip(argc, argv);
        else if (strcmp(argv[1], "--dex") == 0)
            return fromDex(argc, argv);
        else if (strcmp(argv[1], "--preopt") == 0)
            return preopt(argc, argv);
    }
    ...
    return 1;
}
Copy the code

As you can see, we’ll do different things for each of the three types of files. We’re interested in dex files, so let’s look at the fromDex function:

#! c static int fromDex(int argc, char* const argv[]) { ... if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) ! = 0) { ALOGE("VM init failed"); goto bail; } vmStarted = true; /* do the optimization */ if (! dvmContinueOptimization(fd, offset, length, debugFileName, modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) ! = 0)) { ALOGE("Optimization failed"); goto bail; }... }Copy the code

This function to initialize a virtual machine, and then call dvmContinueOptimization function/dalvik/vm/analysis/DexPrepare CPP, enter this function:

#! c bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, const char* fileName, u4 modWhen, u4 crc, bool isBootstrap) { ... /* * Rewrite the file. Byte reordering, structure realigning, * class verification, and bytecode optimization are all performed * here. * * In theory the file could change size and bits could shift around. * In practice this would be annoying to deal with, so the file * layout is designed so that it can always be rewritten in place. * * This creates the class lookup table as  part of doing the processing. */ success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength, doVerify, doOpt, &pClassLookup, NULL); if (success) { DvmDex* pDvmDex = NULL; u1* dexAddr = ((u1*) mapAddr) + dexOffset; if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) ! = 0) { ALOGE("Unable to create DexFile"); success = false; } else { ... }Copy the code

This function makes some optimizations to the Dex file (such as byte reordering, structure alignment, etc.) and then re-writes the Dex file. If the optimization succeeds, dvmDexFileOpenPartial is called, which calls the actual Dex file. Take a look at the function /dalvik/ VM/dvmdex.cpp in detail

#! c /* * Create a DexFile structure for a "partial" DEX. This is one that is in * the process of being optimized. The optimization header isn't finished * and we won't have any of the auxillary data tables, so we have to do * the initialization slightly differently. * * Returns nonzero on error. */ int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex) { DvmDex* pDvmDex; DexFile* pDexFile; int parseFlags = kDexParseDefault; int result = -1; /* -- file is incomplete, new checksum has not yet been calculated if (gDvm.verifyDexChecksum) parseFlags |= kDexParseVerifyChecksum; */ pDexFile = dexFileParse((u1*)addr, len, parseFlags); if (pDexFile == NULL) { ALOGE("DEX parse failed"); goto bail; } pDvmDex = allocateAuxStructures(pDexFile); if (pDvmDex == NULL) { dexFileFree(pDexFile); goto bail; } pDvmDex->isMappedReadOnly = false; *ppDvmDex = pDvmDex; result = 0; bail: return result; }Copy the code

The first two parameters of this function are very important. The first parameter is the starting address of the dex file, and the second parameter is the length of the dex file. With these two parameters, the dex file can be dumped from memory. This function calls dexFileParse() to parse the dex file

So it is also possible to dump at the dexFileParse function. But because the prototype of this function is

DexFile* dexFileParse(const u1* data, size_t length, int flags) 
Copy the code

Struct DexFile {… }, to hook this function, you need to hook the structure from the Android source code or directly modify the image.

Find the name of the dvmDexFileOpenPartial function in libdvm.so

#! Bash » strings libdvm_arm. So | grep dvmDexFileOpenPartial _Z21dvmDexFileOpenPartialPKviPP6DvmDex » strings libdvm_arm.so|grep dexFileParse _Z12dexFileParsePKhjiCopy the code

With this theoretical foundation in hand, you are now ready to formally develop the module. The general process is as follows

  • Specify the lib library to hook
  • Original method template Original method template
  • Modified method is the replacement function
  • Substrate entry point
    • MSGetImageByName or dlopen loads lib to get the image
    • MSFindSymbol or dlsym or nlist specifies the method to get the starting address
    • MSHookFunction Replace function

The complete code

#! c #include "substrate.h" #include <android/log.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <string.h> #define BUFLEN 1024 #define TAG "DEXDUMP" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) //get packagename from pid int getProcessName(char * buffer){ char path_t[256]={0}; pid_t pid=getpid(); char str[15]; sprintf(str, "%d", pid); memset(path_t, 0 , sizeof(path_t)); strcat(path_t, "/proc/"); strcat(path_t, str); strcat(path_t, "/cmdline"); //LOG_ERROR("zhw", "path:%s", path_t); int fd_t = open(path_t, O_RDONLY); if(fd_t>0){ int read_count = read(fd_t, buffer, BUFLEN); if(read_count>0){ int processIndex=0; for(processIndex=0; processIndex<strlen(buffer); processIndex++){ if(buffer[processIndex]==':'){ buffer[processIndex]='_'; } } return 1; } } return 0; } // specify the lib library to hook MSConfig(MSFilterLibrary,"/system/lib/libdvm.so") // keep the original address DexFile* dexFileParse(const u1* data, size_t length, int flags) int (* oldDexFileParse)(const void * addr,int len,int flags); Int myDexFileParse(const void * addr,int len,void ** dvmdex) {LOGD("call my DVM dex!! :%d",getpid()); { //write to file //char buf[200]; Char dexbuffer[64]={0}; char dexbufferNamed[128]={0}; char * bufferProcess=(char*)calloc(256,sizeof(char)); int processStatus= getProcessName(bufferProcess); sprintf(dexbuffer, "_dump_%d", len); strcat(dexbufferNamed,"/sdcard/"); if (processStatus==1) { strcat(dexbufferNamed,bufferProcess); strcat(dexbufferNamed,dexbuffer); }else{ LOGD("FAULT pid not found\n"); } if(bufferProcess! =NULL) { free(bufferProcess); } strcat(dexbufferNamed,".dex"); //sprintf(buf,"/sdcard/dex.%d",len); FILE * f=fopen(dexbufferNamed,"wb"); if(! f) { LOGD(dexbuffer + " : error open sdcard file to write"); } else{ fwrite(addr,1,len,f); fclose(f); Return oldDexFileParse(addr,len,dvmdex); } //Substrate entry point MSInitialize { LOGD("Substrate initialized."); MSImageRef image; // Load lib image = MSGetImageByName("/system/lib/libdvm.so"); if (image ! = NULL) { void * dexload=MSFindSymbol(image,"_Z21dvmDexFileOpenPartialPKviPP6DvmDex"); if(dexload==NULL) { LOGD("error find _Z21dvmDexFileOpenPartialPKviPP6DvmDex "); } else{// replace function //3.MSHookFunction MSHookFunction(dexload,(void*)&myDexFileParse,(void **)&oldDexFileParse); } } else{ LOGD("ERROR FIND LIBDVM"); }}Copy the code

The effect is as follows:

[email protected]:/sdcard $ l |grep dex
app_process_classes_3220.dex
com.ali.tg.testapp_classes_606716.dex
com.chaozh.iReaderFree_classes_4673256.dex
com.secken.app_xg_service_v2_classes_6327832.dex
Copy the code

Sheller mold block improvement

Change the hook point to dexFileParse, as explained above why this is also an option. The dex optimization process is also analyzed. Here, the dex loading process is analyzed.

DexClassLoader is widely used by developers for dynamic loading of plug-ins. The PathClassLoader has hardly been seen.

Because PathClassLoader does not provide the directory for optimizing dex but stores Odex in /data/dalvik-cache, it can only load apK files installed in Android system, that is, under /data/app directory The apk.

The parent class of PathClassLoader and DexClassLoader is BaseDexClassLoader

http://androidxref.com/4.4.2_r1/xref/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java

#! c 45 public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {Copy the code

http://androidxref.com/4.4.2_r1/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java

#! c DexPathList(this, dexPath, libraryPath, optimizedDirectory); 260 private static DexFile loadDexFile(File file, File optimizedDirectory)Copy the code

http://androidxref.com/4.4.2_r1/xref/libcore/dalvik/src/main/java/dalvik/system/DexFile.java

#! c 141 static public DexFile loadDex(String sourcePathName, String outputPathName, int flags)Copy the code

Native Private static int openDexFileNative(String sourceName, String outputName, int flags)

#!c
294    private static int openDexFile(String sourceName, String outputName,
295        int flags) throws IOException {
296        return openDexFileNative(new File(sourceName).getCanonicalPath(),
297                                 (outputName == null) ? null : new File(outputName).getCanonicalPath(),
298                                 flags);
299    }
Copy the code

http://androidxref.com/4.4.2_r1/xref/dalvik/vm/native/dalvik_system_DexFile.cpp

#! c 151 static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args, JValue* pResult) //249 static void Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args, JValue* pResult)Copy the code

http://androidxref.com/4.4.2_r1/xref/dalvik/vm/RawDexFile.cpp

#! c 109 int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName, RawDexFile** ppRawDexFile, Bool isBootstrap) // Utility class method opens DEX /Jar fileCopy the code

http://androidxref.com/4.4.4_r1/xref/dalvik/vm/DvmDex.cpp

#! c 93 int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex) // From an open DEX file, map to read-only shared memory and parse the contents //146 int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex) // Open part of the DEX file by address and lengthCopy the code

http://androidxref.com/4.4.4_r1/xref/dalvik/libdex/DexFile.cpp

#! C 289 dexFileParse(const U1 * data, size_t length, int flags) // Parse dex filesCopy the code

The dexFileParse function is called by dvmDexFileOpenFromFd in openDexFile to analyze the name of each class and the index where the code of the class resides in Dex file. Then dexFileParse calls the function dexParseOptData to write the class name into object P DexFile->pClassLookup, of course, also updated index

#! c //Substrate entry point MSInitialize { LOGD("Cydia Init"); MSImageRef image; // Load lib image = MSGetImageByName("/system/lib/libdvm.so"); if (image ! = NULL) { void * dexload=MSFindSymbol(image,"_Z12dexFileParsePKhji"); if(dexload==NULL) { LOGD("error find _Z12dexFileParsePKhji"); } else{// replace function //3.MSHookFunction MSHookFunction(dexload,(void*)&myDexFileParse,(void **)&oldDexFileParse); } } else{ LOGD("ERROR FIND LIBDVM"); }}Copy the code

Sheller mold block improvement two

  • To encode
  • To optimize the output
  • .

The github address is as follows, and there is already a compiled but unsigned APK…

Github.com/WooyunDota/…

If the extract is encode version, need to decode:

base64 -D -i com.ali.tg.testapp_606716.dex.encode.dex -o my.dex
Copy the code

Some error exclusion


NDK Symbol 'NULL' could not be resolved
Copy the code

The NDK environment is not configured, stddef.h is not found

The jni.h header file could not be foundCopy the code

The NDK environment is not configured properly, or the compiler is buggy. If the problem is not resolved, check the NDK environment.

If you encounter some members in both header files, you need to configure include. When I used mkdir, mode_t was ref into NDK and OSX headers, causing the compilation to fail. Add include under solution:

android-ndk-r10d/platforms/android-17/arch-arm/usr/include/sys

Android Studio 1.3 already supports the NDK, and the time to completely abandon Eclipse is coming.

0 x03 reference


www.cnblogs.com/goodhacker/…

www.cnblogs.com/goodhacker/…

www.cnblogs.com/baizx/p/425…

www.gitzx.com/android-cyd…

Trace the Dex loading process from the source code

Github.com/bunnyblue/D…

Android reverse dynamic debugging summary

Dex file optimization analysis and loading

Android ODEX file format parsing

DexClassLoader4.4.2 Dynamic Load Analysis (Disk Load Analysis)

Android4.0 memory Dex data dynamic loading technology