├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── reinhard │ │ └── elftag │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── reinhard │ │ │ └── binutils │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── reinhard │ └── elftag │ └── ExampleUnitTest.java ├── build.gradle ├── elftag ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── reinhard │ │ └── elftag │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ └── cpp │ │ ├── CMakeLists.txt │ │ └── elftag.c │ └── test │ └── java │ └── com │ └── reinhard │ └── elftag │ └── ExampleUnitTest.java ├── fixelfsection ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── reinhard │ │ └── fixelfsection │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ └── cpp │ │ ├── CMakeLists.txt │ │ ├── fix.c │ │ └── fix.h │ └── test │ └── java │ └── com │ └── reinhard │ └── fixelfsection │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── sofixer ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── reinhard │ └── sofixer │ └── ExampleInstrumentedTest.java ├── main ├── AndroidManifest.xml └── cpp │ ├── CMakeLists.txt │ ├── ElfReader.cpp │ ├── ElfReader.h │ ├── ElfRebuilder.cpp │ ├── ElfRebuilder.h │ ├── FDebug.h │ ├── elf.h │ ├── exelf.h │ └── main.cpp └── test └── java └── com └── reinhard └── sofixer └── ExampleUnitTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 说明 / intro 3 | 4 | * 该工程下的工具主要运行在安卓手机上,请将工具用 adb push 到手机后使用 5 | 6 | * These bin utils are work only on android devices, please push to your devices first (use adb push cmd) 7 | 8 | * 发布的版本只提供了 armeabi-v7a 架构的版本,如果需要其他版本,请自行编译 9 | 10 | * Release versions only contain armeabi-v7a, if you need other arch, please compile by yourself 11 | 12 | # elftag 13 | 14 | * modify app_process32(64) to load third so lib 15 | 16 | ## reference 17 | 18 | * [一种简单的Android全局注入方案](https://bbs.pediy.com/thread-224191.htm) 19 | 20 | * [修改android app_process elf (实现rrrfff大神 第一步)](https://bbs.pediy.com/thread-224297.htm) 21 | 22 | ## usage 23 | 24 | ``` 25 | usage: elftag [option] 26 | modify dynamic section tag of DEBUG to NEEDED and set its value to android_runtime.so 27 | Options are: 28 | -r Revert modification to DEBUG tag 29 | -h Display this information 30 | ``` 31 | 32 | # FixElfSection 33 | 34 | * 用于dump elf文件后的section修复,修复后可以在IDA中直接查看 35 | 36 | * [FixElfSection](https://github.com/WangYinuo/FixElfSection) 的 AndroidStudio 实现 37 | 38 | ## reference 39 | 40 | * [ELF文件格式学习,section修复](https://blog.csdn.net/yi_nuo_wang/article/details/72626846) 41 | 42 | * [Android so库文件的区节section修复代码分析](https://blog.csdn.net/qq1084283172/article/details/78818917) 43 | 44 | ## usage 45 | 46 | ```shell 47 | usage: FixElfSection 48 | fix.so will be created in the same directory 49 | ``` 50 | 51 | # SoFixer 52 | 53 | * so修复相关 54 | 55 | * [SoFixer](https://github.com/F8LEFT/SoFixer) 的 AndroidStudio 实现 56 | 57 | ## reference 58 | 59 | * [简单粗暴的so加解密实现](https://bbs.pediy.com/thread-191649.htm) 60 | 61 | * [ELF section修复的一些思考](https://bbs.pediy.com/thread-192874.htm=%3E) 62 | 63 | ## usage 64 | 65 | ```shell 66 | SoFixer v0.2 author F8LEFT(currwin) 67 | Useage: SoFixer -s sourcefile -o generatefile 68 | try rebuild shdr with phdr 69 | Options are: 70 | -d --debug Show debug info 71 | -m --memso memBaseAddr(16bit format) Source file is dump from memory from address x 72 | -s --source sourceFilePath Source file path 73 | -o --output generateFilePath Generate file path 74 | -h --help Display this information 75 | ``` -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.reinhard.binutils" 7 | minSdkVersion 14 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation project(path: ':elftag') 24 | implementation project(path: ':fixelfsection') 25 | implementation project(path: ':sofixer') 26 | implementation 'androidx.appcompat:appcompat:1.1.0' 27 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 28 | testImplementation 'junit:junit:4.12' 29 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 30 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 31 | } 32 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/reinhard/elftag/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.reinhard.elftag; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.reinhard.elftag", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/reinhard/binutils/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.reinhard.binutils; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | public class MainActivity extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ElfTag 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/reinhard/elftag/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.reinhard.elftag; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.6.3' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /elftag/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /elftag/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion "29.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 28 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles 'consumer-rules.pro' 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | externalNativeBuild { 25 | cmake { 26 | path file('src/main/cpp/CMakeLists.txt') 27 | } 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: 'libs', include: ['*.jar']) 33 | 34 | implementation 'androidx.appcompat:appcompat:1.1.0' 35 | testImplementation 'junit:junit:4.12' 36 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 37 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 38 | } 39 | -------------------------------------------------------------------------------- /elftag/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/elftag/consumer-rules.pro -------------------------------------------------------------------------------- /elftag/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /elftag/src/androidTest/java/com/reinhard/elftag/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.reinhard.elftag; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | import androidx.test.platform.app.InstrumentationRegistry; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.reinhard.elftag.test", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /elftag/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /elftag/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Sets the minimum version of CMake required to build your native library. 2 | # This ensures that a certain set of CMake features is available to 3 | # your build. 4 | 5 | cmake_minimum_required(VERSION 3.4.1) 6 | 7 | # Specifies a library name, specifies whether the library is STATIC or 8 | # SHARED, and provides relative paths to the source code. You can 9 | # define multiple libraries by adding multiple add.library() commands, 10 | # and CMake builds them for you. When you build your app, Gradle 11 | # automatically packages shared libraries with your APK. 12 | 13 | add_executable(elftag elftag.c) -------------------------------------------------------------------------------- /elftag/src/main/cpp/elftag.c: -------------------------------------------------------------------------------- 1 | /** 2 | * elf tag modifier, update dynamic section tag of DEBUG to NEEDED and set its value to SO_NAME_DST. 3 | * 4 | * @author Reinhard(李剑波) 5 | * @date 2019/02/22 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /** 16 | * character count limit of a dynstr 17 | */ 18 | #define DYNSTR_LEN 32 19 | /** 20 | * so name to use (add string to dynstr table is difficult, so we just use string that already exist) 21 | */ 22 | #define SO_NAME_SRC "libandroid_runtime.so" 23 | /** 24 | * new so name (simply truncate string) 25 | */ 26 | #define SO_NAME_DST "android_runtime.so" 27 | /** 28 | * character count that are truncated 29 | */ 30 | #define SO_NAME_OFFSET 3 31 | 32 | /** 33 | * whether elf file is 32 bit or not 34 | */ 35 | static int isElf32; 36 | /** 37 | * elf header (32 bit) 38 | */ 39 | static Elf32_Ehdr ehdr32; 40 | /** 41 | * elf header (64 bit) 42 | */ 43 | static Elf64_Ehdr ehdr64; 44 | /** 45 | * section header of .dynamic (32 bit) 46 | */ 47 | static Elf32_Shdr shdrDynamic32; 48 | /** 49 | * section header of .dynamic (64 bit) 50 | */ 51 | static Elf64_Shdr shdrDynamic64; 52 | /** 53 | * section header of .dynstr (32 bit) 54 | */ 55 | static Elf32_Shdr shdrDynstr32; 56 | /** 57 | * section header of .dynstr (64 bit) 58 | */ 59 | static Elf64_Shdr shdrDynstr64; 60 | /** 61 | * dynamic table offset of tag DEBUG 62 | */ 63 | static int64_t dynTableOffset = -1; 64 | /** 65 | * dynstr table offset of SO_NAME_SRC 66 | */ 67 | static int64_t dynstrTableOffset = -1; 68 | 69 | /** 70 | * option - whether revert modification to DEBUG tag or not 71 | */ 72 | static int do_revert = JNI_FALSE; 73 | 74 | static void usage(FILE *stream) { 75 | fprintf(stream, "usage: elftag [option] \n"); 76 | fprintf(stream, 77 | " modify dynamic section tag of DEBUG to NEEDED and set its value to %s\n", 78 | SO_NAME_DST); 79 | fprintf(stream, " Options are:\n"); 80 | fprintf(stream, " -r Revert modification to DEBUG tag\n"); 81 | fprintf(stream, " -h Display this information\n"); 82 | } 83 | 84 | #pragma clang diagnostic push 85 | #pragma clang diagnostic ignored "-Wint-conversion" 86 | 87 | /** 88 | * get elf header 89 | * @param handle file handle 90 | * @return 1 on success,0 otherwise 91 | */ 92 | static int get_file_header(FILE *handle) { 93 | unsigned char e_ident[EI_NIDENT]; 94 | /* Read in the identity array. */ 95 | if (fread(e_ident, EI_NIDENT, 1, handle) != 1) { 96 | fprintf(stdout, "Fail to read elf header's e_ident field\n"); 97 | return JNI_FALSE; 98 | } 99 | 100 | /* Determine how to read the header. */ 101 | switch (e_ident[EI_CLASS]) { 102 | case ELFCLASS32: 103 | fprintf(stdout, "The elf file is 32 bit\n"); 104 | isElf32 = JNI_TRUE; 105 | fseek(handle, 0, SEEK_SET); 106 | if (fread(ehdr32.e_ident, sizeof(ehdr32), 1, handle) != 1) { 107 | fprintf(stdout, "Fail to read elf header\n"); 108 | return JNI_FALSE; 109 | } 110 | break; 111 | case ELFCLASS64: 112 | fprintf(stdout, "The elf file is 64 bit\n"); 113 | isElf32 = JNI_FALSE; 114 | fseek(handle, 0, SEEK_SET); 115 | if (fread(ehdr64.e_ident, sizeof(ehdr64), 1, handle) != 1) { 116 | fprintf(stdout, "Fail to read elf header\n"); 117 | return JNI_FALSE; 118 | } 119 | break; 120 | default: 121 | fprintf(stdout, "Unknown elf file\n"); 122 | return JNI_FALSE; 123 | } 124 | return JNI_TRUE; 125 | } 126 | 127 | #pragma clang diagnostic pop 128 | 129 | /** 130 | * get section header of .dynamic and .dynstr 131 | * @param handle file handle 132 | * @return 1 on success, 0 otherwise 133 | */ 134 | static int get_32bit_section_headers(FILE *handle) { 135 | Elf32_Shdr shdr32; 136 | unsigned int size = ehdr32.e_shentsize; 137 | unsigned int num = ehdr32.e_shnum; 138 | int getDynamic = JNI_FALSE; 139 | int getDynstr = JNI_FALSE; 140 | 141 | if (size == 0 || num == 0) { 142 | fprintf(stdout, "Cope with unexpected section header sizes.\n"); 143 | return JNI_FALSE; 144 | } 145 | if (size < sizeof shdr32) { 146 | fprintf(stdout, 147 | "The e_shentsize field in the ELF header is less than the size of an ELF section header\n"); 148 | return JNI_FALSE; 149 | } 150 | if (size > sizeof shdr32) { 151 | fprintf(stdout, 152 | "The e_shentsize field in the ELF header is larger than the size of an ELF section header\n"); 153 | return JNI_FALSE; 154 | } 155 | fseek(handle, ehdr32.e_shoff, SEEK_SET); 156 | for (int i = 0; i < ehdr32.e_shnum; i++) { 157 | if (fread(&shdr32, 1, size, handle) != size) { 158 | fprintf(stdout, "Read section header failed for entry %d\n", i); 159 | return JNI_FALSE; 160 | } 161 | switch (shdr32.sh_type) { 162 | case SHT_DYNAMIC: 163 | memcpy(&shdrDynamic32, &shdr32, sizeof(shdr32)); 164 | getDynamic = JNI_TRUE; 165 | break; 166 | case SHT_STRTAB: 167 | if (shdr32.sh_flags == SHF_ALLOC) { 168 | memcpy(&shdrDynstr32, &shdr32, sizeof(shdr32)); 169 | getDynstr = JNI_TRUE; 170 | } 171 | break; 172 | default: 173 | break; 174 | } 175 | } 176 | if (!getDynamic) { 177 | fprintf(stdout, "Cannot get section header of .dynamic\n"); 178 | return JNI_FALSE; 179 | } else if (!getDynstr) { 180 | fprintf(stdout, "Cannot get section header of .dynstr\n"); 181 | return JNI_FALSE; 182 | } else { 183 | return JNI_TRUE; 184 | } 185 | } 186 | 187 | /** 188 | * get section header of .dynamic and .dynstr 189 | * @param handle file handle 190 | * @return 1 on success, 0 otherwise 191 | */ 192 | static int get_64bit_section_headers(FILE *handle) { 193 | Elf64_Shdr shdr64; 194 | unsigned int size = ehdr64.e_shentsize; 195 | unsigned int num = ehdr64.e_shnum; 196 | int getDynamic = JNI_FALSE; 197 | int getDynstr = JNI_FALSE; 198 | 199 | if (size == 0 || num == 0) { 200 | fprintf(stdout, "Cope with unexpected section header sizes.\n"); 201 | return JNI_FALSE; 202 | } 203 | if (size < sizeof shdr64) { 204 | fprintf(stdout, 205 | "The e_shentsize field in the ELF header is less than the size of an ELF section header\n"); 206 | return JNI_FALSE; 207 | } 208 | if (size > sizeof shdr64) { 209 | fprintf(stdout, 210 | "The e_shentsize field in the ELF header is larger than the size of an ELF section header\n"); 211 | return JNI_FALSE; 212 | } 213 | fseek(handle, ehdr64.e_shoff, SEEK_SET); 214 | for (int i = 0; i < ehdr64.e_shnum; i++) { 215 | if (fread(&shdr64, 1, size, handle) != size) { 216 | fprintf(stdout, "Read section header failed for entry %d\n", i); 217 | return JNI_FALSE; 218 | } 219 | switch (shdr64.sh_type) { 220 | case SHT_DYNAMIC: 221 | memcpy(&shdrDynamic64, &shdr64, sizeof(shdr64)); 222 | getDynamic = JNI_TRUE; 223 | break; 224 | case SHT_STRTAB: 225 | if (shdr64.sh_flags == SHF_ALLOC) { 226 | memcpy(&shdrDynstr64, &shdr64, sizeof(shdr64)); 227 | getDynstr = JNI_TRUE; 228 | } 229 | break; 230 | default: 231 | break; 232 | } 233 | } 234 | if (!getDynamic) { 235 | fprintf(stdout, "Cannot get section header of .dynamic\n"); 236 | return JNI_FALSE; 237 | } else if (!getDynstr) { 238 | fprintf(stdout, "Cannot get section header of .dynstr\n"); 239 | return JNI_FALSE; 240 | } else { 241 | return JNI_TRUE; 242 | } 243 | } 244 | 245 | /** 246 | * get dynamic section string for dynamic table entry dyn 247 | * @param file_name file name (full path) 248 | * @param dyn dynamic table entry 249 | * @param dynstr placeholder to store result dynamic section string 250 | */ 251 | static void get_32bit_dynamic_section_string(const char *file_name, Elf32_Dyn dyn, char *dynstr) { 252 | FILE *handle = fopen(file_name, "r"); 253 | fseek(handle, shdrDynstr32.sh_offset + dyn.d_un.d_val, SEEK_SET); 254 | fgets(dynstr, DYNSTR_LEN, handle); 255 | fclose(handle); 256 | } 257 | 258 | /** 259 | * get dynamic section string for dynamic table entry dyn 260 | * @param file_name file name (full path) 261 | * @param dyn dynamic table entry 262 | * @param dynstr placeholder to store result dynamic section string 263 | */ 264 | static void get_64bit_dynamic_section_string(const char *file_name, Elf64_Dyn dyn, char *dynstr) { 265 | FILE *handle = fopen(file_name, "r"); 266 | fseek(handle, shdrDynstr64.sh_offset + dyn.d_un.d_val, SEEK_SET); 267 | fgets(dynstr, DYNSTR_LEN, handle); 268 | fclose(handle); 269 | } 270 | 271 | /** 272 | * get dynamic table offset of tag DEBUG and dynstr table offset of tag NEEDED with SO_NAME_SRC 273 | * @param file_name file name (full path) 274 | * @param handle file handle 275 | * @return 1 on success, 0 otherwise 276 | */ 277 | static int get_32bit_dynamic_sections(const char *file_name, FILE *handle) { 278 | Elf32_Dyn dyn; 279 | char dynstr[DYNSTR_LEN]; 280 | fseek(handle, shdrDynamic32.sh_offset, SEEK_SET); 281 | for (int i = 0; i < shdrDynamic32.sh_size; i += shdrDynamic32.sh_entsize) { 282 | fread(&dyn, 1, sizeof(dyn), handle); 283 | switch (dyn.d_tag) { 284 | case DT_NEEDED: 285 | // get so name 286 | get_32bit_dynamic_section_string(file_name, dyn, dynstr); 287 | if (!strcmp(dynstr, SO_NAME_SRC)) { 288 | dynstrTableOffset = dyn.d_un.d_val; 289 | } else if (!strcmp(dynstr, SO_NAME_DST)) { 290 | if (do_revert) { 291 | dynTableOffset = i; 292 | } else { 293 | fprintf(stdout, "Already processed!\n"); 294 | return JNI_FALSE; 295 | } 296 | } 297 | break; 298 | case DT_DEBUG: 299 | if (do_revert) { 300 | fprintf(stdout, "No need to revert!\n"); 301 | return JNI_FALSE; 302 | } else { 303 | dynTableOffset = i; 304 | } 305 | break; 306 | default: 307 | break; 308 | } 309 | } 310 | if (dynTableOffset < 0) { 311 | fprintf(stdout, "Cannot get tag DEBUG!\n"); 312 | return JNI_FALSE; 313 | } else if (dynstrTableOffset < 0) { 314 | fprintf(stdout, "Cannot get tag NEEDED with %s!\n", SO_NAME_SRC); 315 | return JNI_FALSE; 316 | } else { 317 | return JNI_TRUE; 318 | } 319 | } 320 | 321 | /** 322 | * get dynamic table offset of tag DEBUG and dynstr table offset of tag NEEDED with SO_NAME_SRC 323 | * @param file_name file name (full path) 324 | * @param handle file handle 325 | * @return 1 on success, 0 otherwise 326 | */ 327 | static int get_64bit_dynamic_sections(const char *file_name, FILE *handle) { 328 | Elf64_Dyn dyn; 329 | char dynstr[DYNSTR_LEN]; 330 | fseek(handle, shdrDynamic64.sh_offset, SEEK_SET); 331 | for (int i = 0; i < shdrDynamic64.sh_size; i += shdrDynamic64.sh_entsize) { 332 | fread(&dyn, 1, sizeof(dyn), handle); 333 | switch (dyn.d_tag) { 334 | case DT_NEEDED: 335 | // get so name 336 | get_64bit_dynamic_section_string(file_name, dyn, dynstr); 337 | if (!strcmp(dynstr, SO_NAME_SRC)) { 338 | dynstrTableOffset = dyn.d_un.d_val; 339 | } else if (!strcmp(dynstr, SO_NAME_DST)) { 340 | if (do_revert) { 341 | dynTableOffset = i; 342 | } else { 343 | fprintf(stdout, "Already processed!\n"); 344 | return JNI_FALSE; 345 | } 346 | } 347 | break; 348 | case DT_DEBUG: 349 | if (do_revert) { 350 | fprintf(stdout, "No need to revert!\n"); 351 | return JNI_FALSE; 352 | } else { 353 | dynTableOffset = i; 354 | } 355 | break; 356 | default: 357 | break; 358 | } 359 | } 360 | if (dynTableOffset < 0) { 361 | fprintf(stdout, "Cannot get tag DEBUG!\n"); 362 | return JNI_FALSE; 363 | } else if (dynstrTableOffset < 0) { 364 | fprintf(stdout, "Cannot get tag NEEDED with %s!\n", SO_NAME_SRC); 365 | return JNI_FALSE; 366 | } else { 367 | return JNI_TRUE; 368 | } 369 | } 370 | 371 | /** 372 | * update dynamic section tag of DEBUG to NEEDED with SO_NAME_DST 373 | * @param file_name file name (full path) 374 | * @return 1 on success, 0 otherwise 375 | */ 376 | static int process_32bit_dyn_tag(const char *file_name) { 377 | int ret = JNI_TRUE; 378 | FILE *handle = fopen(file_name, "r+"); 379 | fseek(handle, shdrDynamic32.sh_offset + dynTableOffset, SEEK_SET); 380 | size_t byteCount = shdrDynamic32.sh_entsize / 2; 381 | int buf = do_revert ? DT_DEBUG : DT_NEEDED; 382 | // write d_tag 383 | if (fwrite(&buf, 1, byteCount, handle) != byteCount) { 384 | fprintf(stdout, "Fail to write d_tag!\n"); 385 | ret = JNI_FALSE; 386 | } else { 387 | buf = do_revert ? 0 : dynstrTableOffset + SO_NAME_OFFSET; 388 | // write d_val 389 | if (fwrite(&buf, 1, byteCount, handle) != byteCount) { 390 | fprintf(stdout, "Fail to write d_val!\n"); 391 | ret = JNI_FALSE; 392 | } 393 | } 394 | fclose(handle); 395 | return ret; 396 | } 397 | 398 | /** 399 | * update dynamic section tag of DEBUG to NEEDED with SO_NAME_DST 400 | * @param file_name file name (full path) 401 | * @return 1 on success, 0 otherwise 402 | */ 403 | static int process_64bit_dyn_tag(const char *file_name) { 404 | int ret = JNI_TRUE; 405 | FILE *handle = fopen(file_name, "r+"); 406 | fseek(handle, shdrDynamic64.sh_offset + dynTableOffset, SEEK_SET); 407 | size_t byteCount = shdrDynamic64.sh_entsize / 2; 408 | int64_t buf = do_revert ? DT_DEBUG : DT_NEEDED; 409 | // write d_tag 410 | if (fwrite(&buf, 1, byteCount, handle) != byteCount) { 411 | fprintf(stdout, "Fail to write d_tag!\n"); 412 | ret = JNI_FALSE; 413 | } else { 414 | buf = do_revert ? 0 : dynstrTableOffset + SO_NAME_OFFSET; 415 | // write d_val 416 | if (fwrite(&buf, 1, byteCount, handle) != byteCount) { 417 | fprintf(stdout, "Fail to write d_val!\n"); 418 | ret = JNI_FALSE; 419 | } 420 | } 421 | fclose(handle); 422 | return ret; 423 | } 424 | 425 | #pragma clang diagnostic push 426 | #pragma ide diagnostic ignored "android-cloexec-fopen" 427 | 428 | static int process_file(const char *file_name) { 429 | int ret = JNI_FALSE; 430 | FILE *handle = fopen(file_name, "r"); 431 | // get elf header 432 | if (get_file_header(handle)) { 433 | if (isElf32) { 434 | // get section header of .dynamic and .dynstr 435 | if (get_32bit_section_headers(handle)) { 436 | // get dynamic table offset of tag DEBUG and dynstr table offset of tag NEEDED with SO_NAME_SRC 437 | if (get_32bit_dynamic_sections(file_name, handle)) { 438 | // update dynamic section tag of DEBUG 439 | ret = process_32bit_dyn_tag(file_name); 440 | } 441 | } 442 | } else { 443 | // get section header of .dynamic and .dynstr 444 | if (get_64bit_section_headers(handle)) { 445 | // get dynamic table offset of tag DEBUG and dynstr table offset of tag NEEDED with SO_NAME_SRC 446 | if (get_64bit_dynamic_sections(file_name, handle)) { 447 | // update dynamic section tag of DEBUG 448 | ret = process_64bit_dyn_tag(file_name); 449 | } 450 | } 451 | } 452 | } 453 | fclose(handle); 454 | return ret; 455 | } 456 | 457 | #pragma clang diagnostic pop 458 | 459 | static int parse_args(int argc, char **argv) { 460 | if (argc < 2 || (*argv[argc - 1] == '-')) { 461 | return JNI_FALSE; 462 | } 463 | int ch; 464 | while ((ch = getopt(argc, argv, "rh")) != EOF) { 465 | switch (ch) { 466 | case 'r': 467 | do_revert = JNI_TRUE; 468 | fprintf(stdout, "Revert modification to DEBUG tag\n"); 469 | break; 470 | case 'h': 471 | return JNI_FALSE; 472 | default: 473 | break; 474 | } 475 | } 476 | return JNI_TRUE; 477 | } 478 | 479 | int main(int argc, char **argv) { 480 | int ret = JNI_ERR; 481 | if (parse_args(argc, argv)) { 482 | if (process_file(argv[argc - 1])) { 483 | fprintf(stdout, "Success!\n"); 484 | ret = JNI_OK; 485 | } 486 | } else { 487 | usage(stdout); 488 | } 489 | return ret; 490 | } 491 | -------------------------------------------------------------------------------- /elftag/src/test/java/com/reinhard/elftag/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.reinhard.elftag; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /fixelfsection/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /fixelfsection/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion "29.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 28 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles 'consumer-rules.pro' 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | externalNativeBuild { 25 | cmake { 26 | path file('src/main/cpp/CMakeLists.txt') 27 | } 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: 'libs', include: ['*.jar']) 33 | 34 | implementation 'androidx.appcompat:appcompat:1.1.0' 35 | testImplementation 'junit:junit:4.12' 36 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 37 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 38 | } 39 | -------------------------------------------------------------------------------- /fixelfsection/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/fixelfsection/consumer-rules.pro -------------------------------------------------------------------------------- /fixelfsection/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /fixelfsection/src/androidTest/java/com/reinhard/fixelfsection/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.reinhard.fixelfsection; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.reinhard.fixelfsection.test", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /fixelfsection/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /fixelfsection/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Sets the minimum version of CMake required to build your native library. 2 | # This ensures that a certain set of CMake features is available to 3 | # your build. 4 | 5 | cmake_minimum_required(VERSION 3.4.1) 6 | 7 | # Specifies a library name, specifies whether the library is STATIC or 8 | # SHARED, and provides relative paths to the source code. You can 9 | # define multiple libraries by adding multiple add.library() commands, 10 | # and CMake builds them for you. When you build your app, Gradle 11 | # automatically packages shared libraries with your APK. 12 | 13 | add_executable(FixElfSection fix.c) -------------------------------------------------------------------------------- /fixelfsection/src/main/cpp/fix.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include 3 | #include 4 | #include "fix.h" 5 | 6 | char* str = "..dynsym..dynstr..hash..rel.dyn..rel.plt..plt..text@.ARM.extab..ARM.exidx..fini_array..init_array..dynamic..got..data..bss..shstrtab\0"; 7 | char* str1 = "\0.dynsym\0.dynstr\0.hash\0.rel.dyn\0.rel.plt\0.plt\0.text@.ARM.extab\0.ARM.exidx\0.fini_array\0.init_array\0.dynamic\0.got\0.data\0.bss\0.shstrtab\0"; 8 | Elf32_Shdr shdr[SHDRS] = { 0 }; 9 | 10 | void get_elf_header(char* buffer, Elf32_Ehdr** pehdr) { 11 | int header_len = sizeof(Elf32_Ehdr); 12 | memset(*pehdr, 0, header_len); 13 | memcpy(*pehdr, (void*)buffer, header_len); 14 | } 15 | 16 | void get_program_table(Elf32_Ehdr ehdr, char* buffer, Elf32_Phdr** pphdr) { 17 | int ph_size = ehdr.e_phentsize; 18 | int ph_num = ehdr.e_phnum; 19 | memset(*pphdr, 0, ph_size * ph_num); 20 | memcpy(*pphdr, buffer + ehdr.e_phoff, ph_size * ph_num); 21 | } 22 | 23 | long get_file_len(FILE* p) { 24 | fseek (p, 0, SEEK_END); 25 | long fsize = ftell (p); 26 | rewind (p); 27 | return fsize; 28 | } 29 | 30 | void fix_section_table(Elf32_Phdr* phdr, Elf32_Ehdr* pehdr, char* buffer) { 31 | Elf32_Dyn* dyn = NULL; 32 | Elf32_Dyn* d = NULL; 33 | Elf32_Phdr load = {0}; 34 | 35 | int ph_num = pehdr->e_phnum; 36 | int dyn_size = 0; 37 | int dyn_off = 0; 38 | int nbucket = 0; 39 | int nchain = 0; 40 | int flag = 0; 41 | 42 | int i = 0; 43 | for(; i < ph_num; i++) { 44 | if (phdr[i].p_type == PT_LOAD) { 45 | if (phdr[i].p_vaddr > 0x0) { 46 | printf("get PT_LOAD\n"); 47 | load = phdr[i]; 48 | shdr[BSS].sh_name = strstr(str, ".bss") - str; 49 | shdr[BSS].sh_type = SHT_NOBITS; 50 | shdr[BSS].sh_flags = SHF_WRITE | SHF_ALLOC; 51 | shdr[BSS].sh_addr = phdr[i].p_vaddr + phdr[i].p_filesz; 52 | shdr[BSS].sh_offset = shdr[BSS].sh_addr - 0x1000; 53 | shdr[BSS].sh_addralign = 1; 54 | continue; 55 | } 56 | } 57 | 58 | if(phdr[i].p_type == PT_DYNAMIC) { 59 | printf("get PT_DYNAMIC\n"); 60 | shdr[DYNAMIC].sh_name = strstr(str, ".dynamic") - str; 61 | shdr[DYNAMIC].sh_type = SHT_DYNAMIC; 62 | shdr[DYNAMIC].sh_flags = SHF_WRITE | SHF_ALLOC; 63 | shdr[DYNAMIC].sh_addr = phdr[i].p_vaddr; 64 | shdr[DYNAMIC].sh_offset = phdr[i].p_offset + 0x1000; 65 | shdr[DYNAMIC].sh_size = phdr[i].p_filesz; 66 | shdr[DYNAMIC].sh_link = 2; 67 | shdr[DYNAMIC].sh_info = 0; 68 | shdr[DYNAMIC].sh_addralign = 4; 69 | shdr[DYNAMIC].sh_entsize = 8; 70 | dyn_size = phdr[i].p_filesz; 71 | // 之前使用 phdr[i].p_offset 字段获取 dyn_off ,是有问题的 72 | // 从内存中dump下来的数据 应使用vaddr字段 73 | dyn_off = phdr[i].p_vaddr; 74 | 75 | continue; 76 | } 77 | 78 | if(phdr[i].p_type == PT_LOPROC || phdr[i].p_type == PT_LOPROC + 1) { 79 | printf("get PT_LOPROC\n"); 80 | shdr[ARMEXIDX].sh_name = strstr(str, ".ARM.exidx") - str; 81 | shdr[ARMEXIDX].sh_type = SHT_LOPROC; 82 | shdr[ARMEXIDX].sh_flags = SHF_ALLOC; 83 | shdr[ARMEXIDX].sh_addr = phdr[i].p_vaddr; 84 | shdr[ARMEXIDX].sh_offset = phdr[i].p_offset; 85 | shdr[ARMEXIDX].sh_size = phdr[i].p_filesz; 86 | shdr[ARMEXIDX].sh_link = 7; 87 | shdr[ARMEXIDX].sh_info = 0; 88 | shdr[ARMEXIDX].sh_addralign = 4; 89 | shdr[ARMEXIDX].sh_entsize = 8; 90 | continue; 91 | } 92 | } 93 | dyn = (Elf32_Dyn*)malloc(dyn_size); 94 | memcpy(dyn, buffer + dyn_off, dyn_size); 95 | i = 0; 96 | for (; i < dyn_size / sizeof(Elf32_Dyn); i++) { 97 | switch(dyn[i].d_tag) { 98 | case DT_SYMTAB: 99 | printf("get DT_SYMTAB\n"); 100 | shdr[DYNSYM].sh_name = strstr(str, ".dynsym") - str; 101 | shdr[DYNSYM].sh_type = SHT_DYNSYM; 102 | shdr[DYNSYM].sh_flags = SHF_ALLOC; 103 | shdr[DYNSYM].sh_addr = dyn[i].d_un.d_ptr; 104 | shdr[DYNSYM].sh_offset = dyn[i].d_un.d_ptr; 105 | shdr[DYNSYM].sh_link = 2; 106 | shdr[DYNSYM].sh_info = 1; 107 | shdr[DYNSYM].sh_addralign = 4; 108 | shdr[DYNSYM].sh_entsize = 16; 109 | break; 110 | 111 | case DT_STRTAB: 112 | printf("get DT_STRTAB\n"); 113 | shdr[DYNSTR].sh_name = strstr(str, ".dynstr") - str; 114 | shdr[DYNSTR].sh_type = SHT_STRTAB; 115 | shdr[DYNSTR].sh_flags = SHF_ALLOC; 116 | shdr[DYNSTR].sh_offset = dyn[i].d_un.d_ptr; 117 | shdr[DYNSTR].sh_addr = dyn[i].d_un.d_ptr; 118 | shdr[DYNSTR].sh_addralign = 1; 119 | shdr[DYNSTR].sh_entsize = 0; 120 | break; 121 | 122 | case DT_HASH: 123 | printf("get DT_HASH\n"); 124 | shdr[HASH].sh_name = strstr(str, ".hash") - str; 125 | shdr[HASH].sh_type = SHT_HASH; 126 | shdr[HASH].sh_flags = SHF_ALLOC; 127 | shdr[HASH].sh_addr = dyn[i].d_un.d_ptr; 128 | shdr[HASH].sh_offset = dyn[i].d_un.d_ptr; 129 | memcpy(&nbucket, buffer + shdr[HASH].sh_offset, 4); 130 | memcpy(&nchain, buffer + shdr[HASH].sh_offset + 4, 4); 131 | shdr[HASH].sh_size = (nbucket + nchain + 2) * sizeof(int); 132 | shdr[HASH].sh_link = 4; 133 | shdr[HASH].sh_info = 1; 134 | shdr[HASH].sh_addralign = 4; 135 | shdr[HASH].sh_entsize = 4; 136 | break; 137 | 138 | case DT_REL: 139 | printf("get DT_REL\n"); 140 | shdr[RELDYN].sh_name = strstr(str, ".rel.dyn") - str; 141 | shdr[RELDYN].sh_type = SHT_REL; 142 | shdr[RELDYN].sh_flags = SHF_ALLOC; 143 | shdr[RELDYN].sh_addr = dyn[i].d_un.d_ptr; 144 | shdr[RELDYN].sh_offset = dyn[i].d_un.d_ptr; 145 | shdr[RELDYN].sh_link = 4; 146 | shdr[RELDYN].sh_info = 0; 147 | shdr[RELDYN].sh_addralign = 4; 148 | shdr[RELDYN].sh_entsize = 8; 149 | break; 150 | 151 | case DT_JMPREL: 152 | printf("get DT_JMPREL\n"); 153 | shdr[RELPLT].sh_name = strstr(str, ".rel.plt") - str; 154 | shdr[RELPLT].sh_type = SHT_REL; 155 | shdr[RELPLT].sh_flags = SHF_ALLOC; 156 | shdr[RELPLT].sh_addr = dyn[i].d_un.d_ptr; 157 | shdr[RELPLT].sh_offset = dyn[i].d_un.d_ptr; 158 | shdr[RELPLT].sh_link = 1; 159 | shdr[RELPLT].sh_info = 6; 160 | shdr[RELPLT].sh_addralign = 4; 161 | shdr[RELPLT].sh_entsize = 8; 162 | break; 163 | 164 | case DT_PLTRELSZ: 165 | printf("get DT_PLTRELSZ\n"); 166 | shdr[RELPLT].sh_size = dyn[i].d_un.d_val; 167 | break; 168 | 169 | case DT_FINI: 170 | printf("get DT_FINI\n"); 171 | shdr[FINIARRAY].sh_name = strstr(str, ".fini_array") - str; 172 | shdr[FINIARRAY].sh_type = 15; 173 | shdr[FINIARRAY].sh_flags = SHF_WRITE | SHF_ALLOC; 174 | shdr[FINIARRAY].sh_offset = dyn[i].d_un.d_ptr - 0x1000; 175 | shdr[FINIARRAY].sh_addr = dyn[i].d_un.d_ptr; 176 | shdr[FINIARRAY].sh_addralign = 4; 177 | shdr[FINIARRAY].sh_entsize = 0; 178 | break; 179 | 180 | case DT_INIT: 181 | printf("get DT_INIT\n"); 182 | shdr[INITARRAY].sh_name = strstr(str, ".init_array") - str; 183 | shdr[INITARRAY].sh_type = 14; 184 | shdr[INITARRAY].sh_flags = SHF_WRITE | SHF_ALLOC; 185 | shdr[INITARRAY].sh_offset = dyn[i].d_un.d_ptr - 0x1000; 186 | shdr[INITARRAY].sh_addr = dyn[i].d_un.d_ptr; 187 | shdr[INITARRAY].sh_addralign = 4; 188 | shdr[INITARRAY].sh_entsize = 0; 189 | break; 190 | 191 | case DT_RELSZ: 192 | printf("get DT_RELSZ\n"); 193 | shdr[RELDYN].sh_size = dyn[i].d_un.d_val; 194 | break; 195 | 196 | case DT_STRSZ: 197 | shdr[DYNSTR].sh_size = dyn[i].d_un.d_val; 198 | break; 199 | 200 | case DT_PLTGOT: 201 | printf("get DT_PLTGOT\n"); 202 | shdr[GOT].sh_name = strstr(str, ".got") - str; 203 | shdr[GOT].sh_type = SHT_PROGBITS; 204 | shdr[GOT].sh_flags = SHF_WRITE | SHF_ALLOC; 205 | shdr[GOT].sh_addr = shdr[DYNAMIC].sh_addr + shdr[DYNAMIC].sh_size; 206 | shdr[GOT].sh_offset = shdr[GOT].sh_addr - 0x1000; 207 | shdr[GOT].sh_size = dyn[i].d_un.d_ptr; 208 | shdr[GOT].sh_addralign = 4; 209 | break; 210 | } 211 | } 212 | shdr[GOT].sh_size = shdr[GOT].sh_size + 4 * (shdr[RELPLT].sh_size) / sizeof(Elf32_Rel) + 3 * sizeof(int) - shdr[GOT].sh_addr; 213 | 214 | //STRTAB地址 - SYMTAB地址 = SYMTAB大小 215 | shdr[DYNSYM].sh_size = shdr[DYNSTR].sh_addr - shdr[DYNSYM].sh_addr; 216 | 217 | shdr[FINIARRAY].sh_size = shdr[INITARRAY].sh_addr - shdr[FINIARRAY].sh_addr; 218 | shdr[INITARRAY].sh_size = shdr[DYNAMIC].sh_addr - shdr[INITARRAY].sh_addr; 219 | 220 | shdr[PLT].sh_name = strstr(str, ".plt") - str; 221 | shdr[PLT].sh_type = SHT_PROGBITS; 222 | shdr[PLT].sh_flags = SHF_ALLOC | SHF_EXECINSTR; 223 | shdr[PLT].sh_addr = shdr[RELPLT].sh_addr + shdr[RELPLT].sh_size; 224 | shdr[PLT].sh_offset = shdr[PLT].sh_addr; 225 | shdr[PLT].sh_size = (20 + 12 * (shdr[RELPLT].sh_size) / sizeof(Elf32_Rel)); 226 | shdr[PLT].sh_addralign = 4; 227 | 228 | shdr[TEXT].sh_name = strstr(str, ".text") - str; 229 | shdr[TEXT].sh_type = SHT_PROGBITS; 230 | shdr[TEXT].sh_flags = SHF_ALLOC | SHF_EXECINSTR; 231 | shdr[TEXT].sh_addr = shdr[PLT].sh_addr + shdr[PLT].sh_size; 232 | shdr[TEXT].sh_offset = shdr[TEXT].sh_addr; 233 | shdr[TEXT].sh_size = shdr[ARMEXIDX].sh_addr - shdr[TEXT].sh_addr; 234 | 235 | shdr[DATA].sh_name = strstr(str, ".data") - str; 236 | shdr[DATA].sh_type = SHT_PROGBITS; 237 | shdr[DATA].sh_flags = SHF_WRITE | SHF_ALLOC; 238 | shdr[DATA].sh_addr = shdr[GOT].sh_addr + shdr[GOT].sh_size; 239 | shdr[DATA].sh_offset = shdr[DATA].sh_addr - 0x1000; 240 | shdr[DATA].sh_size = load.p_vaddr + load.p_filesz - shdr[DATA].sh_addr; 241 | shdr[DATA].sh_addralign = 4; 242 | shdr[GOT].sh_size = shdr[DATA].sh_offset - shdr[GOT].sh_offset; 243 | 244 | shdr[STRTAB].sh_name = strstr(str, ".shstrtab") - str; 245 | shdr[STRTAB].sh_type = SHT_STRTAB; 246 | shdr[STRTAB].sh_flags = SHT_NULL; 247 | shdr[STRTAB].sh_addr = 0; 248 | shdr[STRTAB].sh_offset = shdr[BSS].sh_addr - 0x1000; 249 | shdr[STRTAB].sh_size = strlen(str) + 1; 250 | shdr[STRTAB].sh_addralign = 1; 251 | } 252 | 253 | int main(int argc, char const* argv[]) { 254 | FILE* fr = NULL; 255 | FILE* fw = NULL; 256 | long flen = 0,result = 0; 257 | char* buffer = NULL; 258 | Elf32_Ehdr* pehdr = NULL; 259 | Elf32_Phdr* pphdr = NULL; 260 | 261 | if (argc < 2) { 262 | printf("less args\n"); 263 | return JNI_ERR; 264 | } 265 | 266 | fr = fopen(argv[1],"rb"); 267 | if(fr == NULL) { 268 | printf("Open failed: \n"); 269 | goto error; 270 | } 271 | 272 | flen = get_file_len(fr); 273 | 274 | buffer = (char*)malloc(sizeof(char)*flen); 275 | memset(buffer, 0, flen); 276 | if (buffer == NULL) { 277 | printf("Malloc error\n"); 278 | goto error; 279 | } 280 | 281 | result = fread (buffer,1,flen,fr); 282 | if (result != flen) { 283 | printf("Reading error\n"); 284 | goto error; 285 | } 286 | 287 | fw = fopen("fix.so","wb"); 288 | if(fw == NULL) { 289 | printf("Open failed: fix.so\n"); 290 | goto error; 291 | } 292 | 293 | printf("----------[get_elf_header]----------\n"); 294 | pehdr = (Elf32_Ehdr*)malloc(sizeof(Elf32_Ehdr)); 295 | get_elf_header(buffer, &pehdr); 296 | printf("ehdr->e_type=\t\t%x\n", pehdr->e_type); 297 | printf("ehdr->e_machine=\t%x\n", pehdr->e_machine); 298 | printf("ehdr->e_version=\t%x\n", pehdr->e_version); 299 | printf("ehdr->e_entry=\t\t%x\n", pehdr->e_entry); 300 | printf("ehdr->e_phoff=\t\t%x\n", pehdr->e_phoff); 301 | printf("ehdr->e_shoff=\t\t%x\n", pehdr->e_shoff); 302 | printf("ehdr->e_flags=\t\t%x\n", pehdr->e_flags); 303 | printf("ehdr->e_ehsize=\t\t%x\n", pehdr->e_ehsize); 304 | printf("ehdr->e_phentsize=\t%x\n", pehdr->e_phentsize); 305 | printf("ehdr->e_phnum=\t\t%x\n", pehdr->e_phnum); 306 | printf("ehdr->e_shentsize=\t%x\n", pehdr->e_shentsize); 307 | printf("ehdr->e_shnum=\t\t%x\n", pehdr->e_shnum); 308 | printf("ehdr->e_shstrndx=\t%x\n", pehdr->e_shstrndx); 309 | printf("\n"); 310 | 311 | printf("----------[get_program_table]----------\n"); 312 | pphdr = (Elf32_Phdr*)malloc(pehdr->e_phentsize * pehdr->e_phnum); 313 | get_program_table(*pehdr, buffer, &pphdr); 314 | printf("phdr->e_type=\t%x\n", pphdr->p_type); 315 | printf("phdr->p_offset=\t%x\n", pphdr->p_offset); 316 | printf("phdr->p_vaddr=\t%x\n", pphdr->p_vaddr); 317 | printf("phdr->p_paddr=\t%x\n", pphdr->p_paddr); 318 | printf("phdr->p_filesz=\t%x\n", pphdr->p_filesz); 319 | printf("phdr->p_memsz=\t%x\n", pphdr->p_memsz); 320 | printf("phdr->p_flags=\t%x\n", pphdr->p_flags); 321 | printf("\n"); 322 | 323 | printf("----------[fix_section_table]----------\n"); 324 | fix_section_table(pphdr, pehdr, buffer); 325 | 326 | pehdr->e_shnum = SHDRS; 327 | pehdr->e_shstrndx = SHDRS - 1; 328 | pehdr->e_shoff = shdr[STRTAB].sh_offset + strlen(str) + 1; 329 | 330 | printf("----------[Create Fixed ELF]----------\n"); 331 | memcpy(buffer, pehdr, sizeof(Elf32_Ehdr)); 332 | memcpy(buffer + shdr[GOT].sh_offset, buffer + shdr[GOT].sh_offset + 0x1000, shdr[GOT].sh_size); 333 | //memset(buffer + shdr[DATA].sh_offset, 0, shdr[DATA].sh_offset); 334 | memcpy(buffer + shdr[STRTAB].sh_offset, str1, strlen(str) + 1); 335 | memcpy(buffer + pehdr->e_shoff, shdr, pehdr->e_shentsize * pehdr->e_shnum); 336 | flen = shdr[STRTAB].sh_offset + strlen(str) + 1 + SHDRS * sizeof(Elf32_Shdr); 337 | fwrite(buffer, sizeof(char) * flen, 1, fw); 338 | printf("----------[Done]----------"); 339 | error: 340 | if(fw != NULL) 341 | fclose(fw); 342 | if(fr != NULL) 343 | fclose(fr); 344 | if(buffer != NULL) 345 | free(buffer); 346 | return 0; 347 | } -------------------------------------------------------------------------------- /fixelfsection/src/main/cpp/fix.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define SHDRS 16 7 | /* 8 | .dynsym .dynstr .hash .rel.dyn .rel.plt 9 | .plt .text .ARM.extab .ARM.exidx .fini_array 10 | .init_array .dynamic .got .data 11 | */ 12 | #define NONE 0 13 | #define DYNSYM 1 14 | #define DYNSTR 2 15 | #define HASH 3 16 | #define RELDYN 4 17 | #define RELPLT 5 18 | #define PLT 6 19 | #define TEXT 7 20 | #define ARMEXIDX 8 21 | #define FINIARRAY 9 22 | #define INITARRAY 10 23 | #define DYNAMIC 11 24 | #define GOT 12 25 | #define DATA 13 26 | #define BSS 14 27 | #define STRTAB 15 28 | // 29 | -------------------------------------------------------------------------------- /fixelfsection/src/test/java/com/reinhard/fixelfsection/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.reinhard.fixelfsection; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | android.useAndroidX=true 15 | # Automatically convert third-party libraries to use AndroidX 16 | android.enableJetifier=true 17 | 18 | 19 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Apr 24 14:58:24 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':elftag' 3 | include ':fixelfsection' 4 | include ':sofixer' 5 | -------------------------------------------------------------------------------- /sofixer/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sofixer/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion "29.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 28 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles 'consumer-rules.pro' 15 | 16 | externalNativeBuild { 17 | cmake { 18 | cppFlags "-std=c++11 -Werror" 19 | } 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | 30 | externalNativeBuild { 31 | cmake { 32 | path file('src/main/cpp/CMakeLists.txt') 33 | } 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation fileTree(dir: 'libs', include: ['*.jar']) 39 | 40 | implementation 'androidx.appcompat:appcompat:1.1.0' 41 | testImplementation 'junit:junit:4.12' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 44 | } 45 | -------------------------------------------------------------------------------- /sofixer/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuFengXue/AndroidBinUtils/a54b2b58ff52a4bf6166e7400d0a45a505e5e757/sofixer/consumer-rules.pro -------------------------------------------------------------------------------- /sofixer/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /sofixer/src/androidTest/java/com/reinhard/sofixer/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.reinhard.sofixer; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.reinhard.sofixer.test", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sofixer/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /sofixer/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Sets the minimum version of CMake required to build your native library. 2 | # This ensures that a certain set of CMake features is available to 3 | # your build. 4 | 5 | cmake_minimum_required(VERSION 3.4.1) 6 | 7 | project(SoFixer) 8 | 9 | # Specifies a library name, specifies whether the library is STATIC or 10 | # SHARED, and provides relative paths to the source code. You can 11 | # define multiple libraries by adding multiple add.library() commands, 12 | # and CMake builds them for you. When you build your app, Gradle 13 | # automatically packages shared libraries with your APK. 14 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Werror") 15 | # enable __SO64__ for 64bit so.. 16 | #add_definitions("-D__SO64__") 17 | 18 | aux_source_directory(. ROOT_SRC) 19 | 20 | #if(SLibrary) 21 | # add_library(SoFixer STATIC ${ROOT_SRC}) 22 | #else() 23 | add_executable(SoFixer ${ROOT_SRC}) 24 | #endif() -------------------------------------------------------------------------------- /sofixer/src/main/cpp/ElfReader.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // Created by F8LEFT on 2017/6/3. 4 | // Copyright (c) 2017. All rights reserved. 5 | //===----------------------------------------------------------------------===// 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #include "ElfReader.h" 10 | #include "elf.h" 11 | #include "FDebug.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | /** 21 | TECHNICAL NOTE ON ELF LOADING. 22 | 23 | An ELF file's program header table contains one or more PT_LOAD 24 | segments, which corresponds to portions of the file that need to 25 | be mapped into the process' address space. 26 | 27 | Each loadable segment has the following important properties: 28 | 29 | p_offset -> segment file offset 30 | p_filesz -> segment file size 31 | p_memsz -> segment memory size (always >= p_filesz) 32 | p_vaddr -> segment's virtual address 33 | p_flags -> segment flags (e.g. readable, writable, executable) 34 | 35 | We will ignore the p_paddr and p_align fields of Elf32_Phdr for now. 36 | 37 | The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz) 38 | ranges of virtual addresses. A few rules apply: 39 | 40 | - the virtual address ranges should not overlap. 41 | 42 | - if a segment's p_filesz is smaller than its p_memsz, the extra bytes 43 | between them should always be initialized to 0. 44 | 45 | - ranges do not necessarily start or end at page boundaries. Two distinct 46 | segments can have their start and end on the same page. In this case, the 47 | page inherits the mapping flags of the latter segment. 48 | 49 | Finally, the real load addrs of each segment is not p_vaddr. Instead the 50 | loader decides where to load the first segment, then will load all others 51 | relative to the first one to respect the initial range layout. 52 | 53 | For example, consider the following list: 54 | 55 | [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ], 56 | [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ], 57 | 58 | This corresponds to two segments that cover these virtual address ranges: 59 | 60 | 0x30000...0x34000 61 | 0x40000...0x48000 62 | 63 | If the loader decides to load the first segment at address 0xa0000000 64 | then the segments' load address ranges will be: 65 | 66 | 0xa0030000...0xa0034000 67 | 0xa0040000...0xa0048000 68 | 69 | In other words, all segments must be loaded at an address that has the same 70 | constant offset from their p_vaddr value. This offset is computed as the 71 | difference between the first segment's load address, and its p_vaddr value. 72 | 73 | However, in practice, segments do _not_ start at page boundaries. Since we 74 | can only memory-map at page boundaries, this means that the bias is 75 | computed as: 76 | 77 | load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr) 78 | 79 | (NOTE: The value must be used as a 32-bit unsigned integer, to deal with 80 | possible wrap around UINT32_MAX for possible large p_vaddr values). 81 | 82 | And that the phdr0_load_address must start at a page boundary, with 83 | the segment's real content starting at: 84 | 85 | phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr) 86 | 87 | Note that ELF requires the following condition to make the mmap()-ing work: 88 | 89 | PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset) 90 | 91 | The load_bias must be added to any p_vaddr value read from the ELF file to 92 | determine the corresponding memory address. 93 | 94 | **/ 95 | 96 | 97 | #define MAYBE_MAP_FLAG(x,from,to) (((x) & (from)) ? (to) : 0) 98 | #define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \ 99 | MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ 100 | MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) 101 | ElfReader::ElfReader() 102 | : source_(nullptr), name_(nullptr), fd_(-1), 103 | phdr_num_(0), phdr_mmap_(NULL), phdr_table_(NULL), phdr_size_(0), 104 | load_start_(NULL), load_size_(0), load_bias_(0), 105 | loaded_phdr_(NULL) { 106 | } 107 | 108 | ElfReader::~ElfReader() { 109 | if (phdr_mmap_ != NULL) { 110 | delete [](uint8_t*)phdr_mmap_; 111 | } 112 | if(load_start_ != nullptr) { 113 | delete [](uint8_t*)load_start_; 114 | } 115 | } 116 | 117 | bool ElfReader::Load() { 118 | // try open 119 | return ReadElfHeader() && 120 | VerifyElfHeader() && 121 | ReadProgramHeader() && 122 | ReserveAddressSpace() && 123 | LoadSegments() && 124 | FindPhdr() && 125 | PatchPhdr(); 126 | } 127 | 128 | bool ElfReader::ReadElfHeader() { 129 | ssize_t rc = read(fd_, &header_, sizeof(header_)); 130 | if (rc < 0) { 131 | FLOGE("can't read file \"%s\": %s", name_, strerror(errno)); 132 | return false; 133 | } 134 | if (rc != sizeof(header_)) { 135 | FLOGE("\"%s\" is too small to be an ELF executable", name_); 136 | return false; 137 | } 138 | return true; 139 | } 140 | 141 | bool ElfReader::VerifyElfHeader() { 142 | if (header_.e_ident[EI_MAG0] != ELFMAG0 || 143 | header_.e_ident[EI_MAG1] != ELFMAG1 || 144 | header_.e_ident[EI_MAG2] != ELFMAG2 || 145 | header_.e_ident[EI_MAG3] != ELFMAG3) { 146 | FLOGE("\"%s\" has bad ELF magic", name_); 147 | return false; 148 | } 149 | #ifndef __SO64__ 150 | if (header_.e_ident[EI_CLASS] != ELFCLASS32) { 151 | FLOGE("\"%s\" not 32-bit: %d", name_, header_.e_ident[EI_CLASS]); 152 | return false; 153 | } 154 | #else 155 | if (header_.e_ident[EI_CLASS] != ELFCLASS64) { 156 | FLOGE("\"%s\" not 64-bit: %d", name_, header_.e_ident[EI_CLASS]); 157 | return false; 158 | } 159 | #endif 160 | 161 | if (header_.e_ident[EI_DATA] != ELFDATA2LSB) { 162 | FLOGE("\"%s\" not little-endian: %d", name_, header_.e_ident[EI_DATA]); 163 | return false; 164 | } 165 | 166 | if (header_.e_type != ET_DYN) { 167 | FLOGE("\"%s\" has unexpected e_type: %d", name_, header_.e_type); 168 | return false; 169 | } 170 | 171 | if (header_.e_version != EV_CURRENT) { 172 | FLOGE("\"%s\" has unexpected e_version: %d", name_, header_.e_version); 173 | return false; 174 | } 175 | 176 | return true; 177 | } 178 | 179 | // Loads the program header table from an ELF file into a read-only private 180 | // anonymous mmap-ed block. 181 | bool ElfReader::ReadProgramHeader() { 182 | phdr_num_ = header_.e_phnum; 183 | 184 | // Like the kernel, we only accept program header tables that 185 | // are smaller than 64KiB. 186 | if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(Elf_Phdr)) { 187 | FLOGE("\"%s\" has invalid e_phnum: %zu", name_, phdr_num_); 188 | return false; 189 | } 190 | 191 | phdr_size_ = phdr_num_ * sizeof(Elf_Phdr); 192 | void* mmap_result = new uint8_t[phdr_size_]; 193 | if(!LoadFileData(mmap_result, phdr_size_, header_.e_phoff)) { 194 | FLOGE("\"%s\" has no valid phdr data", name_); 195 | return false; 196 | } 197 | 198 | phdr_mmap_ = mmap_result; 199 | phdr_table_ = reinterpret_cast(reinterpret_cast(mmap_result)); 200 | 201 | if(dump_so_file_) { 202 | auto phdr = phdr_table_; 203 | for(auto i = 0; i < phdr_num_; i++) { 204 | phdr->p_filesz = phdr->p_memsz; // expend filesize to memsiz 205 | phdr->p_paddr = phdr->p_vaddr; 206 | phdr->p_offset = phdr->p_vaddr; // elf has been loaded. 207 | phdr++; 208 | } 209 | // fix phdr, just load all data 210 | std::vector loaded_phdrs; 211 | for (auto i = 0; i < phdr_num_; i++) { 212 | auto phdr = &phdr_table_[i]; 213 | if(phdr->p_type != PT_LOAD) continue; 214 | loaded_phdrs.push_back(phdr); 215 | } 216 | if (!loaded_phdrs.empty()) { 217 | for (unsigned long i = 0, total = loaded_phdrs.size(); i < total; i++) { 218 | auto phdr = loaded_phdrs[i]; 219 | if (i != total - 1) { 220 | // to next loaded segament 221 | auto nphdr = loaded_phdrs[i+1]; 222 | phdr->p_memsz = nphdr->p_vaddr - phdr->p_vaddr; 223 | } else { 224 | // to the file end 225 | phdr->p_memsz = file_size - phdr->p_vaddr; 226 | } 227 | phdr->p_filesz = phdr->p_memsz; 228 | } 229 | } 230 | } 231 | return true; 232 | } 233 | 234 | /* Returns the size of the extent of all the possibly non-contiguous 235 | * loadable segments in an ELF program header table. This corresponds 236 | * to the page-aligned size in bytes that needs to be reserved in the 237 | * process' address space. If there are no loadable segments, 0 is 238 | * returned. 239 | * 240 | * If out_min_vaddr or out_max_vaddr are non-NULL, they will be 241 | * set to the minimum and maximum addresses of pages to be reserved, 242 | * or 0 if there is nothing to load. 243 | */ 244 | size_t phdr_table_get_load_size(const Elf_Phdr* phdr_table, 245 | size_t phdr_count, 246 | Elf_Addr* out_min_vaddr, 247 | Elf_Addr* out_max_vaddr) 248 | { 249 | Elf_Addr min_vaddr = 0xFFFFFFFFU; 250 | Elf_Addr max_vaddr = 0x00000000U; 251 | 252 | bool found_pt_load = false; 253 | for (size_t i = 0; i < phdr_count; ++i) { 254 | const Elf_Phdr* phdr = &phdr_table[i]; 255 | 256 | if (phdr->p_type != PT_LOAD) { 257 | continue; 258 | } 259 | found_pt_load = true; 260 | 261 | if (phdr->p_vaddr < min_vaddr) { 262 | min_vaddr = phdr->p_vaddr; 263 | } 264 | 265 | if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) { 266 | max_vaddr = phdr->p_vaddr + phdr->p_memsz; 267 | } 268 | } 269 | if (!found_pt_load) { 270 | min_vaddr = 0x00000000U; 271 | } 272 | 273 | min_vaddr = PAGE_START(min_vaddr); 274 | max_vaddr = PAGE_END(max_vaddr); 275 | 276 | if (out_min_vaddr != NULL) { 277 | *out_min_vaddr = min_vaddr; 278 | } 279 | if (out_max_vaddr != NULL) { 280 | *out_max_vaddr = max_vaddr; 281 | } 282 | return max_vaddr - min_vaddr; 283 | } 284 | 285 | // Reserve a virtual address range big enough to hold all loadable 286 | // segments of a program header table. This is done by creating a 287 | // private anonymous mmap() with PROT_NONE. 288 | bool ElfReader::ReserveAddressSpace() { 289 | Elf_Addr min_vaddr; 290 | load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr); 291 | if (load_size_ == 0) { 292 | FLOGE("\"%s\" has no loadable segments", name_); 293 | return false; 294 | } 295 | 296 | uint8_t* addr = reinterpret_cast(min_vaddr); 297 | // alloc map data, and load in addr 298 | uint8_t * start = new uint8_t[load_size_]; 299 | 300 | load_start_ = start; 301 | load_bias_ = reinterpret_cast(reinterpret_cast(start) 302 | - reinterpret_cast(addr)); 303 | return true; 304 | } 305 | 306 | // Map all loadable segments in process' address space. 307 | // This assumes you already called phdr_table_reserve_memory to 308 | // reserve the address space range for the library. 309 | // TODO: assert assumption. 310 | bool ElfReader::LoadSegments() { 311 | // TODO fix file dada load error, file data between LOAD seg should be loaded 312 | for (size_t i = 0; i < phdr_num_; ++i) { 313 | const Elf_Phdr* phdr = &phdr_table_[i]; 314 | 315 | if (phdr->p_type != PT_LOAD) { 316 | continue; 317 | } 318 | 319 | // Segment addresses in memory. 320 | Elf_Addr seg_start = phdr->p_vaddr; 321 | Elf_Addr seg_end = seg_start + phdr->p_memsz; 322 | 323 | Elf_Addr seg_page_start = PAGE_START(seg_start); 324 | Elf_Addr seg_page_end = PAGE_END(seg_end); 325 | 326 | Elf_Addr seg_file_end = seg_start + phdr->p_filesz; 327 | 328 | // File offsets. 329 | Elf_Addr file_start = phdr->p_offset; 330 | Elf_Addr file_end = file_start + phdr->p_filesz; 331 | 332 | Elf_Addr file_page_start = PAGE_START(file_start); 333 | Elf_Addr file_length = file_end - file_page_start; 334 | 335 | 336 | if (file_length != 0) { 337 | // memory data loading 338 | void* load_point = seg_page_start + reinterpret_cast(load_bias_); 339 | if(!LoadFileData(load_point, file_length, file_page_start)) { 340 | FLOGE("couldn't map \"%s\" segment %zu: %s", name_, i, strerror(errno)); 341 | return false; 342 | } 343 | 344 | } 345 | 346 | // if the segment is writable, and does not end on a page boundary, 347 | // zero-fill it until the page limit. 348 | if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) { 349 | memset(seg_file_end + reinterpret_cast(load_bias_), 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end)); 350 | } 351 | 352 | seg_file_end = PAGE_END(seg_file_end); 353 | 354 | // seg_file_end is now the first page address after the file 355 | // content. If seg_end is larger, we need to zero anything 356 | // between them. This is done by using a private anonymous 357 | // map for all extra pages. 358 | if (seg_page_end > seg_file_end) { 359 | void* load_point = (uint8_t*)load_bias_ + seg_file_end; 360 | memset(load_point, 0, seg_page_end - seg_file_end); 361 | } 362 | } 363 | return true; 364 | } 365 | 366 | /* Used internally. Used to set the protection bits of all loaded segments 367 | * with optional extra flags (i.e. really PROT_WRITE). Used by 368 | * phdr_table_protect_segments and phdr_table_unprotect_segments. 369 | */ 370 | static int 371 | _phdr_table_set_load_prot(const Elf_Phdr* phdr_table, 372 | int phdr_count, 373 | uint8_t *load_bias, 374 | int extra_prot_flags) 375 | { 376 | const Elf_Phdr* phdr = phdr_table; 377 | const Elf_Phdr* phdr_limit = phdr + phdr_count; 378 | 379 | for (; phdr < phdr_limit; phdr++) { 380 | if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) 381 | continue; 382 | 383 | auto seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 384 | auto seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 385 | 386 | auto ret = 0; 387 | 388 | // int ret = mprotect((void*)seg_page_start, 389 | // seg_page_end - seg_page_start, 390 | // PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags); 391 | // if (ret < 0) { 392 | // return -1; 393 | // } 394 | } 395 | return 0; 396 | } 397 | 398 | /* Restore the original protection modes for all loadable segments. 399 | * You should only call this after phdr_table_unprotect_segments and 400 | * applying all relocations. 401 | * 402 | * Input: 403 | * phdr_table -> program header table 404 | * phdr_count -> number of entries in tables 405 | * load_bias -> load bias 406 | * Return: 407 | * 0 on error, -1 on failure (error code in errno). 408 | */ 409 | int 410 | phdr_table_protect_segments(const Elf_Phdr* phdr_table, 411 | int phdr_count, 412 | uint8_t *load_bias) 413 | { 414 | return _phdr_table_set_load_prot(phdr_table, phdr_count, 415 | load_bias, 0); 416 | } 417 | 418 | /* Change the protection of all loaded segments in memory to writable. 419 | * This is useful before performing relocations. Once completed, you 420 | * will have to call phdr_table_protect_segments to restore the original 421 | * protection flags on all segments. 422 | * 423 | * Note that some writable segments can also have their content turned 424 | * to read-only by calling phdr_table_protect_gnu_relro. This is no 425 | * performed here. 426 | * 427 | * Input: 428 | * phdr_table -> program header table 429 | * phdr_count -> number of entries in tables 430 | * load_bias -> load bias 431 | * Return: 432 | * 0 on error, -1 on failure (error code in errno). 433 | */ 434 | int 435 | phdr_table_unprotect_segments(const Elf_Phdr* phdr_table, 436 | int phdr_count, 437 | uint8_t *load_bias) 438 | { 439 | return _phdr_table_set_load_prot(phdr_table, phdr_count, 440 | load_bias, /*PROT_WRITE*/0); 441 | } 442 | 443 | /* Used internally by phdr_table_protect_gnu_relro and 444 | * phdr_table_unprotect_gnu_relro. 445 | */ 446 | static int 447 | _phdr_table_set_gnu_relro_prot(const Elf_Phdr* phdr_table, 448 | int phdr_count, 449 | uint8_t *load_bias, 450 | int prot_flags) 451 | { 452 | const Elf_Phdr* phdr = phdr_table; 453 | const Elf_Phdr* phdr_limit = phdr + phdr_count; 454 | 455 | for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 456 | // if (phdr->p_type != PT_GNU_RELRO) 457 | // continue; 458 | 459 | /* Tricky: what happens when the relro segment does not start 460 | * or end at page boundaries?. We're going to be over-protective 461 | * here and put every page touched by the segment as read-only. 462 | * 463 | * This seems to match Ian Lance Taylor's description of the 464 | * feature at http://www.airs.com/blog/archives/189. 465 | * 466 | * Extract: 467 | * Note that the current dynamic linker code will only work 468 | * correctly if the PT_GNU_RELRO segment starts on a page 469 | * boundary. This is because the dynamic linker rounds the 470 | * p_vaddr field down to the previous page boundary. If 471 | * there is anything on the page which should not be read-only, 472 | * the program is likely to fail at runtime. So in effect the 473 | * linker must only emit a PT_GNU_RELRO segment if it ensures 474 | * that it starts on a page boundary. 475 | */ 476 | auto seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 477 | auto seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 478 | 479 | auto ret = 0; 480 | // int ret = mprotect((void*)seg_page_start, 481 | // seg_page_end - seg_page_start, 482 | // prot_flags); 483 | // if (ret < 0) { 484 | // return -1; 485 | // } 486 | } 487 | return 0; 488 | } 489 | 490 | /* Apply GNU relro protection if specified by the program header. This will 491 | * turn some of the pages of a writable PT_LOAD segment to read-only, as 492 | * specified by one or more PT_GNU_RELRO segments. This must be always 493 | * performed after relocations. 494 | * 495 | * The areas typically covered are .got and .data.rel.ro, these are 496 | * read-only from the program's POV, but contain absolute addresses 497 | * that need to be relocated before use. 498 | * 499 | * Input: 500 | * phdr_table -> program header table 501 | * phdr_count -> number of entries in tables 502 | * load_bias -> load bias 503 | * Return: 504 | * 0 on error, -1 on failure (error code in errno). 505 | */ 506 | int 507 | phdr_table_protect_gnu_relro(const Elf_Phdr* phdr_table, 508 | int phdr_count, 509 | uint8_t *load_bias) 510 | { 511 | return _phdr_table_set_gnu_relro_prot(phdr_table, 512 | phdr_count, 513 | load_bias, 514 | /*PROT_READ*/0); 515 | } 516 | 517 | 518 | # ifndef PT_ARM_EXIDX 519 | # define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ 520 | # endif 521 | 522 | /* Return the address and size of the .ARM.exidx section in memory, 523 | * if present. 524 | * 525 | * Input: 526 | * phdr_table -> program header table 527 | * phdr_count -> number of entries in tables 528 | * load_bias -> load bias 529 | * Output: 530 | * arm_exidx -> address of table in memory (NULL on failure). 531 | * arm_exidx_count -> number of items in table (0 on failure). 532 | * Return: 533 | * 0 on error, -1 on failure (_no_ error code in errno) 534 | */ 535 | int 536 | phdr_table_get_arm_exidx(const Elf_Phdr* phdr_table, 537 | int phdr_count, 538 | uint8_t * load_bias, 539 | Elf_Addr** arm_exidx, 540 | unsigned* arm_exidx_count) 541 | { 542 | const Elf_Phdr* phdr = phdr_table; 543 | const Elf_Phdr* phdr_limit = phdr + phdr_count; 544 | 545 | for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 546 | if (phdr->p_type != PT_ARM_EXIDX) 547 | continue; 548 | 549 | *arm_exidx = (Elf_Addr*)((uint8_t *)load_bias + phdr->p_vaddr); 550 | *arm_exidx_count = (unsigned)(phdr->p_memsz / sizeof(Elf_Addr)); 551 | return 0; 552 | } 553 | *arm_exidx = NULL; 554 | *arm_exidx_count = 0; 555 | return -1; 556 | } 557 | 558 | /* Return the address and size of the ELF file's .dynamic section in memory, 559 | * or NULL if missing. 560 | * 561 | * Input: 562 | * phdr_table -> program header table 563 | * phdr_count -> number of entries in tables 564 | * load_bias -> load bias 565 | * Output: 566 | * dynamic -> address of table in memory (NULL on failure). 567 | * dynamic_count -> number of items in table (0 on failure). 568 | * dynamic_flags -> protection flags for section (unset on failure) 569 | * Return: 570 | * void 571 | */ 572 | void 573 | phdr_table_get_dynamic_section(const Elf_Phdr* phdr_table, 574 | int phdr_count, 575 | uint8_t *load_bias, 576 | Elf_Dyn** dynamic, 577 | size_t* dynamic_count, 578 | Elf_Word* dynamic_flags) 579 | { 580 | const Elf_Phdr* phdr = phdr_table; 581 | const Elf_Phdr* phdr_limit = phdr + phdr_count; 582 | 583 | for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 584 | if (phdr->p_type != PT_DYNAMIC) { 585 | continue; 586 | } 587 | 588 | *dynamic = reinterpret_cast(load_bias + phdr->p_vaddr); 589 | if (dynamic_count) { 590 | *dynamic_count = (unsigned)(phdr->p_memsz / sizeof(Elf_Dyn)); 591 | } 592 | if (dynamic_flags) { 593 | *dynamic_flags = phdr->p_flags; 594 | } 595 | return; 596 | } 597 | *dynamic = NULL; 598 | if (dynamic_count) { 599 | *dynamic_count = 0; 600 | } 601 | } 602 | 603 | // Returns the address of the program header table as it appears in the loaded 604 | // segments in memory. This is in contrast with 'phdr_table_' which 605 | // is temporary and will be released before the library is relocated. 606 | bool ElfReader::FindPhdr() { 607 | const Elf_Phdr* phdr_limit = phdr_table_ + phdr_num_; 608 | 609 | // If there is a PT_PHDR, use it directly. 610 | for (const Elf_Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { 611 | if (phdr->p_type == PT_PHDR) { 612 | return CheckPhdr((uint8_t*)load_bias_ + phdr->p_vaddr); 613 | } 614 | } 615 | 616 | // Otherwise, check the first loadable segment. If its file offset 617 | // is 0, it starts with the ELF header, and we can trivially find the 618 | // loaded program header from it. 619 | for (const Elf_Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { 620 | if (phdr->p_type == PT_LOAD) { 621 | if (phdr->p_offset == 0) { 622 | uint8_t *elf_addr = (uint8_t*)load_bias_ + phdr->p_vaddr; 623 | const Elf_Ehdr* ehdr = (const Elf_Ehdr*)(void*)elf_addr; 624 | Elf_Addr offset = ehdr->e_phoff; 625 | return CheckPhdr((uint8_t*)ehdr + offset); 626 | } 627 | break; 628 | } 629 | } 630 | 631 | FLOGE("can't find loaded phdr for \"%s\"", name_); 632 | return false; 633 | } 634 | 635 | bool ElfReader::PatchPhdr() { 636 | const Elf_Phdr* phdr_limit = phdr_table_ + phdr_num_; 637 | memcpy((void*)loaded_phdr_, (void*)phdr_table_, (uintptr_t)phdr_limit - (uintptr_t)phdr_table_ ); 638 | return true; 639 | } 640 | 641 | // Ensures that our program header is actually within a loadable 642 | // segment. This should help catch badly-formed ELF files that 643 | // would cause the linker to crash later when trying to access it. 644 | bool ElfReader::CheckPhdr(uint8_t * loaded) { 645 | const Elf_Phdr* phdr_limit = phdr_table_ + phdr_num_; 646 | auto loaded_end = loaded + (phdr_num_ * sizeof(Elf_Phdr)); 647 | for (Elf_Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { 648 | if (phdr->p_type != PT_LOAD) { 649 | continue; 650 | } 651 | auto seg_start = phdr->p_vaddr + (uint8_t*)load_bias_; 652 | auto seg_end = phdr->p_filesz + seg_start; 653 | if (seg_start <= loaded && loaded_end <= seg_end) { 654 | loaded_phdr_ = reinterpret_cast(loaded); 655 | return true; 656 | } 657 | } 658 | FLOGE("\"%s\" loaded phdr %p not in loadable segment", name_, loaded); 659 | return false; 660 | } 661 | 662 | bool ElfReader::LoadFileData(void *addr, size_t len, int offset) { 663 | lseek(fd_, offset, SEEK_SET); 664 | auto rc = read(fd_, addr, len); 665 | 666 | if (rc < 0) { 667 | FLOGE("can't read file \"%s\": %s", name_, strerror(errno)); 668 | return false; 669 | } 670 | if (rc != len) { 671 | FLOGE("\"%s\" has no enough data at %x:%zx, not a valid file or you need to dump more data", name_, offset, len); 672 | return false; 673 | } 674 | return true; 675 | } 676 | 677 | void ElfReader::setSource(const char *source, int fd) { 678 | name_ = source; 679 | fd_ = fd; 680 | file_size = lseek(fd_, 0L, SEEK_END); 681 | lseek(fd_, 0L, SEEK_SET); 682 | } 683 | -------------------------------------------------------------------------------- /sofixer/src/main/cpp/ElfReader.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // Created by F8LEFT on 2017/6/3. 4 | // Copyright (c) 2017. All rights reserved. 5 | //===----------------------------------------------------------------------===// 6 | // Parse and read elf file. 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef SOFIXER_ELFREADER_H 10 | #define SOFIXER_ELFREADER_H 11 | 12 | #import "exelf.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | class ElfRebuilder; 19 | 20 | class ElfReader { 21 | public: 22 | ElfReader(); 23 | ~ElfReader(); 24 | 25 | bool Load(); 26 | void setSource(const char* source, int fd); 27 | 28 | size_t phdr_count() { return phdr_num_; } 29 | uint8_t * load_start() { return load_start_; } 30 | Elf_Addr load_size() { return load_size_; } 31 | uint8_t * load_bias() { return load_bias_; } 32 | const Elf_Phdr* loaded_phdr() { return loaded_phdr_; } 33 | 34 | const Elf_Ehdr* record_ehdr() { return &header_; } 35 | private: 36 | bool ReadElfHeader(); 37 | bool VerifyElfHeader(); 38 | bool ReadProgramHeader(); 39 | bool ReserveAddressSpace(); 40 | bool LoadSegments(); 41 | bool FindPhdr(); 42 | bool CheckPhdr(uint8_t *); 43 | bool LoadFileData(void* addr, size_t len, int offset); 44 | 45 | bool PatchPhdr(); 46 | 47 | const char* name_; 48 | const char* source_; 49 | 50 | int fd_; 51 | 52 | Elf_Ehdr header_; 53 | size_t phdr_num_; 54 | 55 | void* phdr_mmap_; 56 | Elf_Phdr* phdr_table_; 57 | Elf_Addr phdr_size_; 58 | 59 | // First page of reserved address space. 60 | uint8_t * load_start_; 61 | // Size in bytes of reserved address space. 62 | Elf_Addr load_size_; 63 | size_t file_size; 64 | // Load bias. 65 | uint8_t * load_bias_; 66 | 67 | // Loaded phdr. 68 | const Elf_Phdr* loaded_phdr_; 69 | 70 | // feature 71 | public: 72 | void setDumpSoFile(bool b) { dump_so_file_ = b; } 73 | void setDumpSoBaseAddr(Elf_Addr base) { dump_so_base_ = base; } 74 | 75 | private: 76 | bool dump_so_file_ = false; 77 | Elf_Addr dump_so_base_ = 0; 78 | 79 | friend class ElfRebuilder; 80 | }; 81 | 82 | 83 | 84 | size_t 85 | phdr_table_get_load_size(const Elf_Phdr* phdr_table, 86 | size_t phdr_count, 87 | Elf_Addr* min_vaddr = NULL, 88 | Elf_Addr* max_vaddr = NULL); 89 | 90 | int 91 | phdr_table_protect_segments(const Elf_Phdr* phdr_table, 92 | int phdr_count, 93 | uint8_t * load_bias); 94 | 95 | int 96 | phdr_table_unprotect_segments(const Elf_Phdr* phdr_table, 97 | int phdr_count, 98 | uint8_t * load_bias); 99 | 100 | int 101 | phdr_table_protect_gnu_relro(const Elf_Phdr* phdr_table, 102 | int phdr_count, 103 | uint8_t *load_bias); 104 | 105 | 106 | int phdr_table_get_arm_exidx(const Elf_Phdr* phdr_table, 107 | int phdr_count, 108 | uint8_t * load_bias, 109 | Elf_Addr** arm_exidx, 110 | unsigned* arm_exidix_count); 111 | 112 | void 113 | phdr_table_get_dynamic_section(const Elf_Phdr* phdr_table, 114 | int phdr_count, 115 | uint8_t * load_bias, 116 | Elf_Dyn** dynamic, 117 | size_t* dynamic_count, 118 | Elf_Word* dynamic_flags); 119 | 120 | 121 | #endif //SOFIXER_ELFREADER_H 122 | -------------------------------------------------------------------------------- /sofixer/src/main/cpp/ElfRebuilder.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // Created by F8LEFT on 2017/6/4. 4 | // Copyright (c) 2017. All rights reserved. 5 | //===----------------------------------------------------------------------===// 6 | // 7 | //===----------------------------------------------------------------------===// 8 | #include 9 | #include "ElfRebuilder.h" 10 | #include "elf.h" 11 | #include "FDebug.h" 12 | 13 | 14 | ElfRebuilder::ElfRebuilder(ElfReader *elf_reader) { 15 | elf_reader_ = elf_reader; 16 | } 17 | 18 | bool ElfRebuilder::RebuildPhdr() { 19 | FLOGD("=======================RebuildPhdr========================="); 20 | auto phdr = (Elf_Phdr*)elf_reader_->loaded_phdr(); 21 | for(auto i = 0; i < elf_reader_->phdr_count(); i++) { 22 | phdr->p_filesz = phdr->p_memsz; // expend filesize to memsiz 23 | // p_paddr and p_align is not used in load, just ignore it. 24 | // fix file offset. 25 | phdr->p_paddr = phdr->p_vaddr; 26 | phdr->p_offset = phdr->p_vaddr; // elf has been loaded. 27 | phdr++; 28 | } 29 | FLOGD("=====================RebuildPhdr End======================"); 30 | return true; 31 | } 32 | 33 | bool ElfRebuilder::RebuildShdr() { 34 | FLOGD("=======================RebuildShdr========================="); 35 | // rebuilding shdr, link information 36 | auto base = si.load_bias; 37 | shstrtab.push_back('\0'); 38 | 39 | // empty shdr 40 | if(true) { 41 | Elf_Shdr shdr = {0}; 42 | shdrs.push_back(shdr); 43 | } 44 | 45 | // gen .dynsym 46 | if(si.symtab != nullptr) { 47 | sDYNSYM = shdrs.size(); 48 | 49 | Elf_Shdr shdr; 50 | shdr.sh_name = shstrtab.length(); 51 | shstrtab.append(".dynsym"); 52 | shstrtab.push_back('\0'); 53 | 54 | shdr.sh_type = SHT_DYNSYM; 55 | shdr.sh_flags = SHF_ALLOC; 56 | shdr.sh_addr = (uintptr_t)si.symtab - (uintptr_t)base; 57 | shdr.sh_offset = shdr.sh_addr; 58 | shdr.sh_size = 0; // calc sh_size later(pad to next shdr) 59 | shdr.sh_link = 0; // link to dynstr later 60 | // shdr.sh_info = 1; 61 | shdr.sh_info = 0; 62 | shdr.sh_addralign = 4; 63 | shdr.sh_entsize = 0x10; 64 | 65 | shdrs.push_back(shdr); 66 | } 67 | 68 | // gen .dynstr 69 | if(si.strtab != nullptr) { 70 | sDYNSTR = shdrs.size(); 71 | 72 | Elf_Shdr shdr; 73 | shdr.sh_name = shstrtab.length(); 74 | shstrtab.append(".dynstr"); 75 | shstrtab.push_back('\0'); 76 | 77 | shdr.sh_type = SHT_STRTAB; 78 | shdr.sh_flags = SHF_ALLOC; 79 | shdr.sh_addr = (uintptr_t)si.strtab - (uintptr_t)base; 80 | shdr.sh_offset = shdr.sh_addr; 81 | shdr.sh_size = si.strtabsize; 82 | shdr.sh_link = 0; 83 | shdr.sh_info = 0; 84 | shdr.sh_addralign = 1; 85 | shdr.sh_entsize = 0x0; 86 | 87 | shdrs.push_back(shdr); 88 | } 89 | 90 | // gen .hash 91 | if(si.hash != 0) { 92 | sHASH = shdrs.size(); 93 | 94 | Elf_Shdr shdr; 95 | shdr.sh_name = shstrtab.length(); 96 | shstrtab.append(".hash"); 97 | shstrtab.push_back('\0'); 98 | 99 | shdr.sh_type = SHT_HASH; 100 | shdr.sh_flags = SHF_ALLOC; 101 | shdr.sh_addr = si.hash - base; 102 | shdr.sh_offset = shdr.sh_addr; 103 | // TODO 32bit, 64bit? 104 | shdr.sh_size = (si.nbucket + si.nchain) * sizeof(Elf_Addr) + 2 * sizeof(Elf_Addr); 105 | shdr.sh_link = sDYNSYM; 106 | shdr.sh_info = 0; 107 | shdr.sh_addralign = 4; 108 | shdr.sh_entsize = 0x4; 109 | 110 | shdrs.push_back(shdr); 111 | } 112 | 113 | // gen .rel.dyn 114 | if(si.rel != nullptr) { 115 | sRELDYN = shdrs.size(); 116 | 117 | Elf_Shdr shdr; 118 | shdr.sh_name = shstrtab.length(); 119 | shstrtab.append(".rel.dyn"); 120 | shstrtab.push_back('\0'); 121 | 122 | shdr.sh_type = SHT_REL; 123 | shdr.sh_flags = SHF_ALLOC; 124 | shdr.sh_addr = (uintptr_t)si.rel - (uintptr_t)base; 125 | shdr.sh_offset = shdr.sh_addr; 126 | shdr.sh_size = si.rel_count * sizeof(Elf_Rel); 127 | shdr.sh_link = sDYNSYM; 128 | shdr.sh_info = 0; 129 | shdr.sh_addralign = 4; 130 | shdr.sh_entsize = 0x8; 131 | 132 | shdrs.push_back(shdr); 133 | } 134 | 135 | // gen .rel.plt 136 | if(si.plt_rel != nullptr) { 137 | sRELPLT = shdrs.size(); 138 | 139 | Elf_Shdr shdr; 140 | shdr.sh_name = shstrtab.length(); 141 | shstrtab.append(".rel.plt"); 142 | shstrtab.push_back('\0'); 143 | 144 | shdr.sh_type = SHT_REL; 145 | shdr.sh_flags = SHF_ALLOC; 146 | shdr.sh_addr = (uintptr_t)si.plt_rel - (uintptr_t)base; 147 | shdr.sh_offset = shdr.sh_addr; 148 | shdr.sh_size = si.plt_rel_count * sizeof(Elf_Rel); 149 | shdr.sh_link = sDYNSYM; 150 | shdr.sh_info = 0; 151 | shdr.sh_addralign = 4; 152 | shdr.sh_entsize = 0x8; 153 | 154 | shdrs.push_back(shdr); 155 | } 156 | 157 | // gen.plt with .rel.plt 158 | if(si.plt_rel != nullptr) { 159 | sPLT = shdrs.size(); 160 | 161 | Elf_Shdr shdr; 162 | shdr.sh_name = shstrtab.length(); 163 | shstrtab.append(".plt"); 164 | shstrtab.push_back('\0'); 165 | 166 | shdr.sh_type = SHT_PROGBITS; 167 | shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; 168 | shdr.sh_addr = shdrs[sRELPLT].sh_addr + shdrs[sRELPLT].sh_size; 169 | shdr.sh_offset = shdr.sh_addr; 170 | // TODO fix size 32bit 64bit? 171 | shdr.sh_size = 20/*Pure code*/ + 12 * si.plt_rel_count; 172 | shdr.sh_link = 0; 173 | shdr.sh_info = 0; 174 | shdr.sh_addralign = 4; 175 | shdr.sh_entsize = 0x0; 176 | 177 | shdrs.push_back(shdr); 178 | } 179 | 180 | // gen.text&ARM.extab 181 | if(si.plt_rel != nullptr) { 182 | sTEXTTAB = shdrs.size(); 183 | 184 | Elf_Shdr shdr; 185 | shdr.sh_name = shstrtab.length(); 186 | shstrtab.append(".text&ARM.extab"); 187 | shstrtab.push_back('\0'); 188 | 189 | shdr.sh_type = SHT_PROGBITS; 190 | shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; 191 | shdr.sh_addr = shdrs[sPLT].sh_addr + shdrs[sPLT].sh_size; 192 | // Align 8 193 | while (shdr.sh_addr & 0x7) { 194 | shdr.sh_addr ++; 195 | } 196 | 197 | shdr.sh_offset = shdr.sh_addr; 198 | shdr.sh_size = 0; // calc later 199 | shdr.sh_link = 0; 200 | shdr.sh_info = 0; 201 | shdr.sh_addralign = 8; 202 | shdr.sh_entsize = 0x0; 203 | 204 | shdrs.push_back(shdr); 205 | } 206 | 207 | // gen ARM.exidx 208 | if(si.ARM_exidx != nullptr) { 209 | sARMEXIDX = shdrs.size(); 210 | 211 | Elf_Shdr shdr; 212 | shdr.sh_name = shstrtab.length(); 213 | shstrtab.append(".ARM.exidx"); 214 | shstrtab.push_back('\0'); 215 | 216 | shdr.sh_type = SHT_ARMEXIDX; 217 | shdr.sh_flags = SHF_ALLOC | SHF_LINK_ORDER; 218 | shdr.sh_addr = (uintptr_t)si.ARM_exidx - (uintptr_t)base; 219 | shdr.sh_offset = shdr.sh_addr; 220 | shdr.sh_size = si.ARM_exidx_count * sizeof(Elf_Addr); 221 | shdr.sh_link = sTEXTTAB; 222 | shdr.sh_info = 0; 223 | shdr.sh_addralign = 4; 224 | shdr.sh_entsize = 0x8; 225 | 226 | shdrs.push_back(shdr); 227 | } 228 | // gen .fini_array 229 | if(si.fini_array != nullptr) { 230 | sRELPLT = shdrs.size(); 231 | 232 | Elf_Shdr shdr; 233 | shdr.sh_name = shstrtab.length(); 234 | shstrtab.append(".fini_array"); 235 | shstrtab.push_back('\0'); 236 | 237 | shdr.sh_type = SHT_FINI_ARRAY; 238 | shdr.sh_flags = SHF_ALLOC | SHF_WRITE; 239 | shdr.sh_addr = (uintptr_t)si.fini_array - (uintptr_t)base; 240 | shdr.sh_offset = shdr.sh_addr; 241 | shdr.sh_size = si.fini_array_count * sizeof(Elf_Addr); 242 | shdr.sh_link = 0; 243 | shdr.sh_info = 0; 244 | shdr.sh_addralign = 4; 245 | shdr.sh_entsize = 0x0; 246 | 247 | shdrs.push_back(shdr); 248 | } 249 | 250 | // gen .init_array 251 | if(si.init_array != nullptr) { 252 | sRELPLT = shdrs.size(); 253 | 254 | Elf_Shdr shdr; 255 | shdr.sh_name = shstrtab.length(); 256 | shstrtab.append(".init_array"); 257 | shstrtab.push_back('\0'); 258 | 259 | shdr.sh_type = SHT_INIT_ARRAY; 260 | shdr.sh_flags = SHF_ALLOC | SHF_WRITE; 261 | shdr.sh_addr = (uintptr_t)si.init_array - (uintptr_t)base; 262 | shdr.sh_offset = shdr.sh_addr; 263 | shdr.sh_size = si.init_array_count * sizeof(Elf_Addr); 264 | shdr.sh_link = 0; 265 | shdr.sh_info = 0; 266 | shdr.sh_addralign = 4; 267 | shdr.sh_entsize = 0x0; 268 | 269 | shdrs.push_back(shdr); 270 | } 271 | 272 | // gen .dynamic 273 | if(si.dynamic != nullptr) { 274 | sDYNAMIC = shdrs.size(); 275 | 276 | Elf_Shdr shdr; 277 | shdr.sh_name = shstrtab.length(); 278 | shstrtab.append(".dynamic"); 279 | shstrtab.push_back('\0'); 280 | 281 | shdr.sh_type = SHT_DYNAMIC; 282 | shdr.sh_flags = SHF_ALLOC | SHF_WRITE; 283 | shdr.sh_addr = (uintptr_t)si.dynamic - (uintptr_t)base; 284 | shdr.sh_offset = shdr.sh_addr; 285 | shdr.sh_size = si.dynamic_count * sizeof(Elf_Dyn); 286 | shdr.sh_link = sDYNSTR; 287 | shdr.sh_info = 0; 288 | shdr.sh_addralign = 4; 289 | shdr.sh_entsize = 0x8; 290 | 291 | shdrs.push_back(shdr); 292 | } 293 | 294 | // get .got 295 | if(si.plt_got != nullptr) { 296 | // global_offset_table 297 | sGOT = shdrs.size(); 298 | auto sLast = sGOT - 1; 299 | 300 | Elf_Shdr shdr; 301 | shdr.sh_name = shstrtab.length(); 302 | shstrtab.append(".got"); 303 | shstrtab.push_back('\0'); 304 | 305 | shdr.sh_type = SHT_PROGBITS; 306 | shdr.sh_flags = SHF_ALLOC | SHF_WRITE; 307 | shdr.sh_addr = shdrs[sLast].sh_addr + shdrs[sLast].sh_size; 308 | // Align8?? 309 | while (shdr.sh_addr & 0x7) { 310 | shdr.sh_addr ++; 311 | } 312 | 313 | shdr.sh_offset = shdr.sh_addr; 314 | shdr.sh_size = (uintptr_t)(si.plt_got + si.plt_rel_count) - shdr.sh_addr - (uintptr_t)base + 3 * sizeof(Elf_Addr); 315 | shdr.sh_link = 0; 316 | shdr.sh_info = 0; 317 | shdr.sh_addralign = 4; 318 | shdr.sh_entsize = 0x0; 319 | 320 | shdrs.push_back(shdr); 321 | } 322 | 323 | // gen .data 324 | if(true) { 325 | sDATA = shdrs.size(); 326 | auto sLast = sDATA - 1; 327 | 328 | Elf_Shdr shdr; 329 | shdr.sh_name = shstrtab.length(); 330 | shstrtab.append(".data"); 331 | shstrtab.push_back('\0'); 332 | 333 | shdr.sh_type = SHT_PROGBITS; 334 | shdr.sh_flags = SHF_ALLOC | SHF_WRITE; 335 | shdr.sh_addr = shdrs[sLast].sh_addr + shdrs[sLast].sh_size; 336 | shdr.sh_offset = shdr.sh_addr; 337 | shdr.sh_size = si.max_load - shdr.sh_addr; 338 | shdr.sh_link = 0; 339 | shdr.sh_info = 0; 340 | shdr.sh_addralign = 4; 341 | shdr.sh_entsize = 0x0; 342 | 343 | shdrs.push_back(shdr); 344 | } 345 | 346 | // gen .bss 347 | // if(true) { 348 | // sBSS = shdrs.size(); 349 | // 350 | // Elf_Shdr shdr; 351 | // shdr.sh_name = shstrtab.length(); 352 | // shstrtab.append(".bss"); 353 | // shstrtab.push_back('\0'); 354 | // 355 | // shdr.sh_type = SHT_NOBITS; 356 | // shdr.sh_flags = SHF_ALLOC | SHF_WRITE; 357 | // shdr.sh_addr = si.max_load; 358 | // shdr.sh_offset = shdr.sh_addr; 359 | // shdr.sh_size = 0; // not used 360 | // shdr.sh_link = 0; 361 | // shdr.sh_info = 0; 362 | // shdr.sh_addralign = 8; 363 | // shdr.sh_entsize = 0x0; 364 | // 365 | // shdrs.push_back(shdr); 366 | // } 367 | 368 | // gen .shstrtab, pad into last data 369 | if(true) { 370 | sSHSTRTAB = shdrs.size(); 371 | 372 | Elf_Shdr shdr; 373 | shdr.sh_name = shstrtab.length(); 374 | shstrtab.append(".shstrtab"); 375 | shstrtab.push_back('\0'); 376 | 377 | shdr.sh_type = SHT_STRTAB; 378 | shdr.sh_flags = 0; 379 | shdr.sh_addr = si.max_load; 380 | shdr.sh_offset = shdr.sh_addr; 381 | shdr.sh_size = shstrtab.length(); 382 | shdr.sh_link = 0; 383 | shdr.sh_info = 0; 384 | shdr.sh_addralign = 1; 385 | shdr.sh_entsize = 0x0; 386 | 387 | shdrs.push_back(shdr); 388 | } 389 | 390 | // link section data 391 | if(sDYNSYM != 0) { 392 | shdrs[sDYNSYM].sh_link = sDYNSTR; 393 | } 394 | 395 | // sort shdr and recalc size 396 | for(auto i = 1; i < shdrs.size(); i++) { 397 | for(auto j = i + 1; j < shdrs.size(); j++) { 398 | if(shdrs[i].sh_addr > shdrs[j].sh_addr) { 399 | // exchange i, j 400 | auto tmp = shdrs[i]; 401 | shdrs[i] = shdrs[j]; 402 | shdrs[j] = tmp; 403 | 404 | // exchange index 405 | auto chgIdx = [i, j](Elf_Word &t) { 406 | if(t == i) { 407 | t = j; 408 | } else if(t == j) { 409 | t = i; 410 | } 411 | }; 412 | chgIdx(sDYNSYM); 413 | chgIdx(sDYNSTR); 414 | chgIdx(sHASH); 415 | chgIdx(sRELDYN); 416 | chgIdx(sRELPLT); 417 | chgIdx(sPLT); 418 | chgIdx(sTEXTTAB); 419 | chgIdx(sARMEXIDX); 420 | chgIdx(sFINIARRAY); 421 | chgIdx(sINITARRAY); 422 | chgIdx(sDYNAMIC); 423 | chgIdx(sGOT); 424 | chgIdx(sDATA); 425 | chgIdx(sBSS); 426 | chgIdx(sSHSTRTAB); 427 | } 428 | } 429 | } 430 | 431 | if(sDYNSYM != 0) { 432 | auto sNext = sDYNSYM + 1; 433 | shdrs[sDYNSYM].sh_size = shdrs[sNext].sh_addr - shdrs[sDYNSYM].sh_addr; 434 | } 435 | 436 | if(sTEXTTAB != 0) { 437 | auto sNext = sTEXTTAB + 1; 438 | shdrs[sTEXTTAB].sh_size = shdrs[sNext].sh_addr - shdrs[sTEXTTAB].sh_addr; 439 | } 440 | 441 | // fix for size 442 | for(auto i = 2; i < shdrs.size(); i++) { 443 | if(shdrs[i].sh_offset - shdrs[i-1].sh_offset < shdrs[i-1].sh_size) { 444 | shdrs[i-1].sh_size = shdrs[i].sh_offset - shdrs[i-1].sh_offset; 445 | } 446 | } 447 | 448 | FLOGD("=====================RebuildShdr End======================"); 449 | return true; 450 | } 451 | 452 | bool ElfRebuilder::Rebuild() { 453 | return RebuildPhdr() && ReadSoInfo() && RebuildShdr() && RebuildRelocs() && RebuildFin(); 454 | } 455 | 456 | bool ElfRebuilder::ReadSoInfo() { 457 | FLOGD("=======================ReadSoInfo========================="); 458 | si.base = si.load_bias = elf_reader_->load_bias(); 459 | si.phdr = elf_reader_->loaded_phdr(); 460 | si.phnum = elf_reader_->phdr_count(); 461 | auto base = si.load_bias; 462 | phdr_table_get_load_size(si.phdr, si.phnum, &si.min_load, &si.max_load); 463 | 464 | /* Extract dynamic section */ 465 | phdr_table_get_dynamic_section(si.phdr, si.phnum, si.base, &si.dynamic, 466 | &si.dynamic_count, &si.dynamic_flags); 467 | if(si.dynamic == nullptr) { 468 | FLOGE("No valid dynamic phdr data"); 469 | return false; 470 | } 471 | 472 | phdr_table_get_arm_exidx(si.phdr, si.phnum, si.base, 473 | &si.ARM_exidx, (unsigned*)&si.ARM_exidx_count); 474 | 475 | // Extract useful information from dynamic section. 476 | uint32_t needed_count = 0; 477 | for (Elf_Dyn* d = si.dynamic; d->d_tag != DT_NULL; ++d) { 478 | switch(d->d_tag){ 479 | case DT_HASH: 480 | si.hash = d->d_un.d_ptr + (uint8_t*)base; 481 | si.nbucket = ((unsigned *) (base + d->d_un.d_ptr))[0]; 482 | si.nchain = ((unsigned *) (base + d->d_un.d_ptr))[1]; 483 | si.bucket = (unsigned *) (base + d->d_un.d_ptr + 8); 484 | si.chain = (unsigned *) (base + d->d_un.d_ptr + 8 + si.nbucket * 4); 485 | break; 486 | case DT_STRTAB: 487 | si.strtab = (const char *) (base + d->d_un.d_ptr); 488 | FLOGD("string table found at %x", d->d_un.d_ptr); 489 | break; 490 | case DT_SYMTAB: 491 | si.symtab = (Elf_Sym *) (base + d->d_un.d_ptr); 492 | FLOGD("symbol table found at %x", d->d_un.d_ptr); 493 | break; 494 | case DT_PLTREL: 495 | if (d->d_un.d_val != DT_REL) { 496 | FLOGE("unsupported DT_RELA in \"%s\"", si.name); 497 | return false; 498 | } 499 | break; 500 | case DT_JMPREL: 501 | si.plt_rel = (Elf_Rel*) (base + d->d_un.d_ptr); 502 | FLOGD("%s plt_rel (DT_JMPREL) found at %x", si.name, d->d_un.d_ptr); 503 | break; 504 | case DT_PLTRELSZ: 505 | si.plt_rel_count = d->d_un.d_val / sizeof(Elf_Rel); 506 | FLOGD("%s plt_rel_count (DT_PLTRELSZ) %zu", si.name, si.plt_rel_count); 507 | break; 508 | case DT_REL: 509 | si.rel = (Elf_Rel*) (base + d->d_un.d_ptr); 510 | FLOGD("%s rel (DT_REL) found at %x", si.name, d->d_un.d_ptr); 511 | break; 512 | case DT_RELSZ: 513 | si.rel_count = d->d_un.d_val / sizeof(Elf_Rel); 514 | FLOGD("%s rel_size (DT_RELSZ) %zu", si.name, si.rel_count); 515 | break; 516 | case DT_PLTGOT: 517 | /* Save this in case we decide to do lazy binding. We don't yet. */ 518 | si.plt_got = (Elf_Addr *)(base + d->d_un.d_ptr); 519 | break; 520 | case DT_DEBUG: 521 | // Set the DT_DEBUG entry to the address of _r_debug for GDB 522 | // if the dynamic table is writable 523 | break; 524 | case DT_RELA: 525 | FLOGE("unsupported DT_RELA in \"%s\"", si.name); 526 | return false; 527 | case DT_INIT: 528 | si.init_func = reinterpret_cast(base + d->d_un.d_ptr); 529 | FLOGD("%s constructors (DT_INIT) found at %x", si.name, d->d_un.d_ptr); 530 | break; 531 | case DT_FINI: 532 | si.fini_func = reinterpret_cast(base + d->d_un.d_ptr); 533 | FLOGD("%s destructors (DT_FINI) found at %x", si.name, d->d_un.d_ptr); 534 | break; 535 | case DT_INIT_ARRAY: 536 | si.init_array = reinterpret_cast(base + d->d_un.d_ptr); 537 | FLOGD("%s constructors (DT_INIT_ARRAY) found at %x", si.name, d->d_un.d_ptr); 538 | break; 539 | case DT_INIT_ARRAYSZ: 540 | si.init_array_count = ((unsigned)d->d_un.d_val) / sizeof(Elf_Addr); 541 | FLOGD("%s constructors (DT_INIT_ARRAYSZ) %zu", si.name, si.init_array_count); 542 | break; 543 | case DT_FINI_ARRAY: 544 | si.fini_array = reinterpret_cast(base + d->d_un.d_ptr); 545 | FLOGD("%s destructors (DT_FINI_ARRAY) found at %x", si.name, d->d_un.d_ptr); 546 | break; 547 | case DT_FINI_ARRAYSZ: 548 | si.fini_array_count = ((unsigned)d->d_un.d_val) / sizeof(Elf_Addr); 549 | FLOGD("%s destructors (DT_FINI_ARRAYSZ) %zu", si.name, si.fini_array_count); 550 | break; 551 | case DT_PREINIT_ARRAY: 552 | si.preinit_array = reinterpret_cast(base + d->d_un.d_ptr); 553 | FLOGD("%s constructors (DT_PREINIT_ARRAY) found at %d", si.name, d->d_un.d_ptr); 554 | break; 555 | case DT_PREINIT_ARRAYSZ: 556 | si.preinit_array_count = ((unsigned)d->d_un.d_val) / sizeof(Elf_Addr); 557 | FLOGD("%s constructors (DT_PREINIT_ARRAYSZ) %zu", si.name, si.preinit_array_count); 558 | break; 559 | case DT_TEXTREL: 560 | si.has_text_relocations = true; 561 | break; 562 | case DT_SYMBOLIC: 563 | si.has_DT_SYMBOLIC = true; 564 | break; 565 | case DT_NEEDED: 566 | ++needed_count; 567 | break; 568 | case DT_FLAGS: 569 | if (d->d_un.d_val & DF_TEXTREL) { 570 | si.has_text_relocations = true; 571 | } 572 | if (d->d_un.d_val & DF_SYMBOLIC) { 573 | si.has_DT_SYMBOLIC = true; 574 | } 575 | break; 576 | case DT_STRSZ: 577 | si.strtabsize = d->d_un.d_val; 578 | break; 579 | case DT_SYMENT: 580 | case DT_RELENT: 581 | break; 582 | case DT_MIPS_RLD_MAP: 583 | // Set the DT_MIPS_RLD_MAP entry to the address of _r_debug for GDB. 584 | break; 585 | case DT_MIPS_RLD_VERSION: 586 | case DT_MIPS_FLAGS: 587 | case DT_MIPS_BASE_ADDRESS: 588 | case DT_MIPS_UNREFEXTNO: 589 | break; 590 | 591 | case DT_MIPS_SYMTABNO: 592 | si.mips_symtabno = d->d_un.d_val; 593 | break; 594 | 595 | case DT_MIPS_LOCAL_GOTNO: 596 | si.mips_local_gotno = d->d_un.d_val; 597 | break; 598 | 599 | case DT_MIPS_GOTSYM: 600 | si.mips_gotsym = d->d_un.d_val; 601 | break; 602 | case DT_SONAME: 603 | si.name = (const char *) (base + d->d_un.d_ptr); 604 | FLOGD("soname %s", si.name); 605 | break; 606 | default: 607 | FLOGD("Unused DT entry: type 0x%08x arg 0x%08x", d->d_tag, d->d_un.d_val); 608 | break; 609 | } 610 | } 611 | FLOGD("=======================ReadSoInfo End========================="); 612 | return true; 613 | } 614 | 615 | // Finally, generate rebuild_data 616 | bool ElfRebuilder::RebuildFin() { 617 | FLOGD("=======================try to finish file========================="); 618 | auto load_size = si.max_load - si.min_load; 619 | rebuild_size = load_size + shstrtab.length() + 620 | shdrs.size() * sizeof(Elf_Shdr); 621 | rebuild_data = new uint8_t[rebuild_size]; 622 | memcpy(rebuild_data, (void*)si.load_bias, load_size); 623 | // pad with shstrtab 624 | memcpy(rebuild_data + load_size, shstrtab.c_str(), shstrtab.length()); 625 | // pad with shdrs 626 | auto shdr_off = load_size + shstrtab.length(); 627 | memcpy(rebuild_data + (int)shdr_off, (void*)&shdrs[0], 628 | shdrs.size() * sizeof(Elf_Shdr)); 629 | auto ehdr = *elf_reader_->record_ehdr(); 630 | ehdr.e_shnum = shdrs.size(); 631 | ehdr.e_shoff = (Elf_Addr)shdr_off; 632 | ehdr.e_shstrndx = sSHSTRTAB; 633 | memcpy(rebuild_data, &ehdr, sizeof(Elf_Ehdr)); 634 | 635 | FLOGD("=======================End========================="); 636 | return true; 637 | } 638 | 639 | bool ElfRebuilder::RebuildRelocs() { 640 | FLOGD("=======================RebuildRelocs========================="); 641 | if(!elf_reader_->dump_so_file_) return true; 642 | auto relocate = [](uint8_t * base, Elf_Rel* rel, size_t count, Elf_Addr dump_base) { 643 | if(rel == nullptr || count == 0) return false; 644 | for(auto idx = 0; idx < count; idx++, rel++) { 645 | #ifndef __SO64__ 646 | auto type = ELF32_R_TYPE(rel->r_info); 647 | auto sym = ELF32_R_SYM(rel->r_info); 648 | #else 649 | auto type = ELF64_R_TYPE(rel->r_info); 650 | auto sym = ELF64_R_SYM(rel->r_info); 651 | #endif 652 | auto prel = reinterpret_cast(base + rel->r_offset); 653 | if(type == 0) { // R_*_NONE 654 | continue; 655 | } 656 | switch (type) { 657 | // I don't known other so info, if i want to fix it, I must dump other so file 658 | case R_386_RELATIVE: 659 | case R_ARM_RELATIVE: 660 | *prel = *prel - dump_base; 661 | break; 662 | default: 663 | break; 664 | } 665 | } 666 | 667 | return true; 668 | }; 669 | relocate(si.load_bias, si.plt_rel, si.plt_rel_count, elf_reader_->dump_so_base_); 670 | relocate(si.load_bias, si.rel, si.rel_count, elf_reader_->dump_so_base_); 671 | FLOGD("=======================RebuildRelocs End======================="); 672 | return true; 673 | } 674 | 675 | 676 | 677 | 678 | -------------------------------------------------------------------------------- /sofixer/src/main/cpp/ElfRebuilder.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // Created by F8LEFT on 2017/6/4. 4 | // Copyright (c) 2017. All rights reserved. 5 | //===----------------------------------------------------------------------===// 6 | // Rebuild elf file with ElfReader 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef SOFIXER_ELFREBUILDER_H 10 | #define SOFIXER_ELFREBUILDER_H 11 | 12 | #include 13 | #include "ElfReader.h" 14 | #include 15 | #include 16 | 17 | 18 | #define SOINFO_NAME_LEN 128 19 | struct soinfo { 20 | public: 21 | const char* name = "name"; 22 | const Elf_Phdr* phdr = nullptr; 23 | size_t phnum = 0; 24 | Elf_Addr entry = 0; 25 | uint8_t * base = 0; 26 | unsigned size = 0; 27 | 28 | Elf_Addr min_load; 29 | Elf_Addr max_load; 30 | 31 | uint32_t unused1 = 0; // DO NOT USE, maintained for compatibility. 32 | 33 | Elf_Dyn* dynamic = nullptr; 34 | size_t dynamic_count = 0; 35 | Elf_Word dynamic_flags = 0; 36 | 37 | uint32_t unused2 = 0; // DO NOT USE, maintained for compatibility 38 | uint32_t unused3 = 0; // DO NOT USE, maintained for compatibility 39 | 40 | unsigned flags = 0; 41 | 42 | const char* strtab = nullptr; 43 | Elf_Sym* symtab = nullptr; 44 | 45 | uint8_t * hash = 0; 46 | size_t strtabsize = 0; 47 | size_t nbucket = 0; 48 | size_t nchain = 0; 49 | unsigned* bucket = nullptr; 50 | unsigned* chain = nullptr; 51 | 52 | Elf_Addr * plt_got = nullptr; 53 | 54 | Elf_Rel* plt_rel = nullptr; 55 | size_t plt_rel_count = 0; 56 | 57 | Elf_Rel* rel = nullptr; 58 | size_t rel_count = 0; 59 | 60 | void* preinit_array = nullptr; 61 | size_t preinit_array_count = 0; 62 | 63 | void** init_array = nullptr; 64 | size_t init_array_count = 0; 65 | void** fini_array = nullptr; 66 | size_t fini_array_count = 0; 67 | 68 | void* init_func = nullptr; 69 | void* fini_func = nullptr; 70 | 71 | // ARM EABI section used for stack unwinding. 72 | Elf_Addr * ARM_exidx = nullptr; 73 | size_t ARM_exidx_count = 0; 74 | unsigned mips_symtabno = 0; 75 | unsigned mips_local_gotno = 0; 76 | unsigned mips_gotsym = 0; 77 | 78 | // When you read a virtual address from the ELF file, add this 79 | // value to get the corresponding address in the process' address space. 80 | uint8_t * load_bias = nullptr; 81 | 82 | bool has_text_relocations = false; 83 | bool has_DT_SYMBOLIC = false; 84 | }; 85 | 86 | 87 | class ElfRebuilder { 88 | public: 89 | ElfRebuilder(ElfReader* elf_reader); 90 | ~ElfRebuilder() { if(rebuild_data != nullptr) delete []rebuild_data; } 91 | bool Rebuild(); 92 | 93 | void* getRebuildData() { return rebuild_data; } 94 | size_t getRebuildSize() { return rebuild_size; } 95 | private: 96 | bool RebuildPhdr(); 97 | bool RebuildShdr(); 98 | bool ReadSoInfo(); 99 | bool RebuildRelocs(); 100 | bool RebuildFin(); 101 | 102 | ElfReader* elf_reader_; 103 | soinfo si; 104 | 105 | int rebuild_size = 0; 106 | uint8_t * rebuild_data = nullptr; 107 | 108 | Elf_Word sDYNSYM = 0; 109 | Elf_Word sDYNSTR = 0; 110 | Elf_Word sHASH = 0; 111 | Elf_Word sRELDYN = 0; 112 | Elf_Word sRELPLT = 0; 113 | Elf_Word sPLT = 0; 114 | Elf_Word sTEXTTAB = 0; 115 | Elf_Word sARMEXIDX = 0; 116 | Elf_Word sFINIARRAY = 0; 117 | Elf_Word sINITARRAY = 0; 118 | Elf_Word sDYNAMIC = 0; 119 | Elf_Word sGOT = 0; 120 | Elf_Word sDATA = 0; 121 | Elf_Word sBSS = 0; 122 | Elf_Word sSHSTRTAB = 0; 123 | 124 | std::vector shdrs; 125 | std::string shstrtab; 126 | 127 | private: 128 | bool isPatchInit = false; 129 | public: 130 | void setPatchInit(bool b) { isPatchInit = b; } 131 | }; 132 | 133 | 134 | #endif //SOFIXER_ELFREBUILDER_H 135 | -------------------------------------------------------------------------------- /sofixer/src/main/cpp/FDebug.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // Created by F8LEFT on 2018/7/4. 4 | // Copyright (c) 2018. All rights reserved. 5 | //===----------------------------------------------------------------------===// 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef ANDDBG_ALOG_H 10 | #define ANDDBG_ALOG_H 11 | 12 | #if !defined(NDEBUG) 13 | 14 | #define TOSTR(fmt) #fmt 15 | #define FLFMT TOSTR([%s:%d]) 16 | #define FNLINE TOSTR(\n) 17 | 18 | #define FLOGE(fmt, ...) printf(FLFMT fmt FNLINE, __FUNCTION__, __LINE__, ##__VA_ARGS__) 19 | #define FLOGD(fmt, ...) printf(FLFMT fmt FNLINE, __FUNCTION__, __LINE__, ##__VA_ARGS__) 20 | #define FLOGW(fmt, ...) printf(FLFMT fmt FNLINE, __FUNCTION__, __LINE__, ##__VA_ARGS__) 21 | #define FLOGI(fmt, ...) printf(FLFMT fmt FNLINE, __FUNCTION__, __LINE__, ##__VA_ARGS__) 22 | #define FLOGV(fmt, ...) printf(FLFMT fmt FNLINE, __FUNCTION__, __LINE__, ##__VA_ARGS__) 23 | #else 24 | #define FLOGE(fmt, ...) 25 | #define FLOGD(fmt, ...) 26 | #define FLOGW(fmt, ...) 27 | #define FLOGI(fmt, ...) 28 | #define FLOGV(fmt, ...) 29 | #endif 30 | 31 | #endif //ANDDBG_ALOG_H 32 | -------------------------------------------------------------------------------- /sofixer/src/main/cpp/exelf.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // Created by F8LEFT on 2017/6/28. 4 | // Copyright (c) 2017. All rights reserved. 5 | //===----------------------------------------------------------------------===// 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef FAOATDUMP_EXELF_H 10 | #define FAOATDUMP_EXELF_H 11 | 12 | #include "elf.h" 13 | 14 | #ifndef __SO64__ 15 | typedef Elf32_Ehdr Elf_Ehdr; 16 | typedef Elf32_Phdr Elf_Phdr; 17 | typedef Elf32_Shdr Elf_Shdr; 18 | typedef Elf32_Sym Elf_Sym; 19 | typedef Elf32_Dyn Elf_Dym; 20 | typedef Elf32_Rel Elf_Rel; 21 | typedef Elf32_Rela Elf_Rela; 22 | typedef Elf32_Addr Elf_Addr; 23 | typedef Elf32_Dyn Elf_Dyn; 24 | typedef Elf32_Word Elf_Word; 25 | #else 26 | typedef Elf64_Ehdr Elf_Ehdr; 27 | typedef Elf64_Phdr Elf_Phdr; 28 | typedef Elf64_Shdr Elf_Shdr; 29 | typedef Elf64_Sym Elf_Sym; 30 | typedef Elf64_Dyn Elf_Dym; 31 | typedef Elf64_Rel Elf_Rel; 32 | typedef Elf64_Rela Elf_Rela; 33 | typedef Elf64_Addr Elf_Addr; 34 | typedef Elf64_Dyn Elf_Dyn; 35 | typedef Elf64_Word Elf_Word; 36 | #endif 37 | 38 | #ifndef PAGE_SIZE 39 | #define PAGE_SIZE 0x1000 40 | 41 | #define PAGE_MASK (~(PAGE_SIZE-1)) 42 | // Returns the address of the page containing address 'x'. 43 | #define PAGE_START(x) ((x) & PAGE_MASK) 44 | 45 | // Returns the offset of address 'x' in its page. 46 | #define PAGE_OFFSET(x) ((x) & ~PAGE_MASK) 47 | 48 | // Returns the address of the next page after address 'x', unless 'x' is 49 | // itself at the start of a page. 50 | #define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE-1)) 51 | #endif 52 | 53 | #endif //FAOATDUMP_EXELF_H 54 | -------------------------------------------------------------------------------- /sofixer/src/main/cpp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ElfReader.h" 3 | #include "ElfRebuilder.h" 4 | #include "FDebug.h" 5 | #include 6 | 7 | 8 | const char* short_options = "hdm:s:o:"; 9 | const struct option long_options[] = { 10 | {"help", 0, NULL, 'h'}, 11 | {"debug", 0, NULL, 'd'}, 12 | {"memso", 1, NULL, 'm'}, 13 | {"source", 1, NULL, 's'}, 14 | {"output", 1, NULL, 'o'}, 15 | {nullptr, 0, nullptr, 0} 16 | }; 17 | void useage(); 18 | 19 | int main(int argc, char* argv[]) { 20 | int c; 21 | 22 | ElfReader elf_reader; 23 | 24 | std::string source, output; 25 | bool isValidArg = true; 26 | while((c = getopt_long(argc, argv, short_options, long_options, nullptr)) != -1) { 27 | switch (c) { 28 | case 'd': 29 | printf("Use debug mode\n"); 30 | break; 31 | case 's': 32 | source = optarg; 33 | break; 34 | case 'o': 35 | output = optarg; 36 | break; 37 | case 'm': { 38 | auto is16Bit = [](const char* c) { 39 | auto len = strlen(c); 40 | if(len > 2) { 41 | if(c[0] == '0' & c[1] == 'x') return true; 42 | } 43 | bool is10bit = true; 44 | for(auto i = 0; i < len; i++) { 45 | if((c[i] > 'a' && c[i] < 'f') || 46 | (c[i] > 'A' && c[i] < 'F')) { 47 | is10bit = false; 48 | } 49 | } 50 | return !is10bit; 51 | }; 52 | #ifndef __SO64__ 53 | auto base = strtoul(optarg, 0, is16Bit(optarg) ? 16: 10); 54 | #else 55 | auto base = strtoull(optarg, 0, is16Bit(optarg) ? 16: 10); 56 | #endif 57 | elf_reader.setDumpSoFile(true); 58 | elf_reader.setDumpSoBaseAddr(base); 59 | } 60 | break; 61 | default: 62 | isValidArg = false; 63 | break; 64 | } 65 | } 66 | if(!isValidArg) { 67 | useage(); 68 | return -1; 69 | } 70 | 71 | auto file = fopen(source.c_str(), "rb"); 72 | if(nullptr == file) { 73 | printf("source so file cannot found!!!\n"); 74 | return -1; 75 | } 76 | auto fd = fileno(file); 77 | 78 | printf("start to rebuild elf file\n"); 79 | elf_reader.setSource(source.c_str(), fd); 80 | 81 | if(!elf_reader.Load()) { 82 | printf("source so file is invalid\n"); 83 | return -1; 84 | } 85 | 86 | ElfRebuilder elf_rebuilder(&elf_reader); 87 | if(!elf_rebuilder.Rebuild()) { 88 | printf("error occured in rebuilding elf file\n"); 89 | return -1; 90 | } 91 | fclose(file); 92 | 93 | if (!output.empty()) { 94 | file = fopen(output.c_str(), "wb+"); 95 | if(nullptr == file) { 96 | printf("output so file cannot write !!!\n"); 97 | return -1; 98 | } 99 | fwrite(elf_rebuilder.getRebuildData(), elf_rebuilder.getRebuildSize(), 1, file); 100 | fclose(file); 101 | } 102 | 103 | printf("Done!!!\n"); 104 | return 0; 105 | } 106 | 107 | void useage() { 108 | printf("SoFixer v0.2 author F8LEFT(currwin)\n"); 109 | printf("Useage: SoFixer -s sourcefile -o generatefile\n"); 110 | printf(" try rebuild shdr with phdr\n"); 111 | printf(" Options are:\n"); 112 | 113 | printf(" -d --debug Show debug info\n"); 114 | printf(" -m --memso memBaseAddr(16bit format) Source file is dump from memory from address x\n"); 115 | printf(" -s --source sourceFilePath Source file path\n"); 116 | printf(" -o --output generateFilePath Generate file path\n"); 117 | printf(" -h --help Display this information\n"); 118 | 119 | } 120 | -------------------------------------------------------------------------------- /sofixer/src/test/java/com/reinhard/sofixer/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.reinhard.sofixer; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } --------------------------------------------------------------------------------