commit 8b2d63fcaa3ef202c46079aecd96ddd4dcb7a96f
Author: L-JINBIN <921558445@qq.com>
Date: Sun Dec 25 16:49:43 2022 +0800
First commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..fb7f4a8
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..1b4ef23
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..bdd9278
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.MD b/README.MD
new file mode 100644
index 0000000..0ef6243
--- /dev/null
+++ b/README.MD
@@ -0,0 +1,3 @@
+# ApkSignatureKillerEx
+
+本项目演示了 MT 去除签名校验功能的原理,以及它的一种对抗方式。
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
new file mode 100644
index 0000000..69fc1e3
--- /dev/null
+++ b/app/CMakeLists.txt
@@ -0,0 +1,30 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html
+
+# Sets the minimum version of CMake required to build the native library.
+
+cmake_minimum_required(VERSION 3.4.1)
+
+add_library( # Sets the name of the library.
+ test
+
+ # Sets the library as a shared library.
+ SHARED
+
+ # Provides a relative path to your source file(s).
+ src/main/c/openat.c
+ )
+
+find_library( # Sets the name of the path variable.
+ log-lib
+
+ # Specifies the name of the NDK library that
+ # you want CMake to locate.
+ log)
+
+target_link_libraries( # Specifies the target library.
+ test
+
+ # Links the target library to the log library
+ # included in the NDK.
+ ${log-lib})
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..ed70581
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,53 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ namespace 'bin.mt.test'
+ compileSdk 32
+ ndkVersion '24.0.8215888'
+
+ defaultConfig {
+ applicationId "bin.mt.signature"
+ minSdk 21
+ //noinspection OldTargetApi
+ targetSdk 32
+ versionCode 1
+ versionName "1.0"
+ externalNativeBuild {
+ cmake {
+ cppFlags ""
+ abiFilters = ["armeabi-v7a", "x86", "arm64-v8a", "x86_64"]
+ }
+ }
+ ndk {
+ abiFilters = ["armeabi-v7a", "x86", "arm64-v8a", "x86_64"]
+ }
+ }
+
+ signingConfigs {
+ debug {
+ storeFile file("../fake.jks")
+ storePassword "123456"
+ keyAlias "key0"
+ keyPassword "123456"
+ v1SigningEnabled true
+ v2SigningEnabled true
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ implementation project(path: ':killer')
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b43536e
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/c/openat.c b/app/src/main/c/openat.c
new file mode 100644
index 0000000..17d0538
--- /dev/null
+++ b/app/src/main/c/openat.c
@@ -0,0 +1,66 @@
+//
+// Created by Thom on 2019/3/30.
+//
+
+#include
+#include
+#include "openat.h"
+#include
+#include
+
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+
+intptr_t openAt(intptr_t fd, const char *path, intptr_t flag) {
+#if defined(__arm__)
+ intptr_t r;
+ asm volatile(
+#ifndef OPTIMIZE_ASM
+ "mov r0, %1\n\t"
+ "mov r1, %2\n\t"
+ "mov r2, %3\n\t"
+#endif
+
+ "mov ip, r7\n\t"
+ ".cfi_register r7, ip\n\t"
+ "mov r7, #" STR(__NR_openat) "\n\t"
+ "svc #0\n\t"
+ "mov r7, ip\n\t"
+ ".cfi_restore r7\n\t"
+
+#ifndef OPTIMIZE_ASM
+ "mov %0, r0\n\t"
+#endif
+ : "=r" (r)
+ : "r" (fd), "r" (path), "r" (flag));
+ return r;
+#elif defined(__aarch64__)
+ intptr_t r;
+ asm volatile(
+#ifndef OPTIMIZE_ASM
+ "mov x0, %1\n\t"
+ "mov x1, %2\n\t"
+ "mov x2, %3\n\t"
+#endif
+
+ "mov x8, #" STR(__NR_openat) "\n\t"
+ "svc #0\n\t"
+
+#ifndef OPTIMIZE_ASM
+ "mov %0, x0\n\t"
+#endif
+ : "=r" (r)
+ : "r" (fd), "r" (path), "r" (flag));
+ return r;
+#else
+ return (intptr_t) syscall(__NR_openat, fd, path, flag);
+#endif
+}
+
+JNIEXPORT jint JNICALL
+Java_bin_mt_test_MainActivity_openAt(JNIEnv *env, __attribute__((unused)) jclass clazz, jstring path) {
+ const char* p = (*env)->GetStringUTFChars(env, path, 0);
+ __android_log_print(ANDROID_LOG_INFO, "openAt", "path=%s", p);
+ intptr_t fd = openAt(AT_FDCWD, p, O_RDONLY);
+ return fd;
+}
\ No newline at end of file
diff --git a/app/src/main/c/openat.h b/app/src/main/c/openat.h
new file mode 100644
index 0000000..21dbcb6
--- /dev/null
+++ b/app/src/main/c/openat.h
@@ -0,0 +1,27 @@
+//
+// Created by Thom on 2019/3/30.
+//
+
+#ifndef BREVENT_OPENAT_H
+#define BREVENT_OPENAT_H
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __ANDROID__
+
+intptr_t openAt(intptr_t fd, const char *path, intptr_t flag);
+
+#else
+#define openAt openat
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //BREVENT_OPENAT_H
diff --git a/app/src/main/java/bin/mt/test/MainActivity.java b/app/src/main/java/bin/mt/test/MainActivity.java
new file mode 100644
index 0000000..4f35cc0
--- /dev/null
+++ b/app/src/main/java/bin/mt/test/MainActivity.java
@@ -0,0 +1,145 @@
+package bin.mt.test;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.widget.TextView;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+public class MainActivity extends Activity {
+
+ public static class App extends Application {
+ @Override
+ protected void attachBaseContext(Context base) {
+ new bin.mt.signature.KillerApplication(); // 注释掉这句即可关闭过签
+ super.attachBaseContext(base);
+ }
+ }
+
+ static {
+ System.loadLibrary("test");
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ TextView msg = findViewById(R.id.msg);
+
+ // 以下演示了三种获取签名MD5的方式
+
+ String signatureExpected = "3bf8931788824c6a1f2c6f6ff80f6b21";
+ String signatureFromAPI = md5(signatureFromAPI());
+ String signatureFromAPK = md5(signatureFromAPK());
+ String signatureFromSVC = md5(signatureFromSVC());
+
+ // 开启过签后,API与APK方式会获取到虚假的签名MD5
+
+ // 而SVC方式总是能获取到真实的签名MD5
+
+ SpannableStringBuilder sb = new SpannableStringBuilder();
+ append(sb, "Expected: ", signatureExpected, Color.BLACK);
+ append(sb, "From API: ", signatureFromAPI, signatureExpected.equals(signatureFromAPI) ? Color.BLUE : Color.RED);
+ append(sb, "From APK: ", signatureFromAPK, signatureExpected.equals(signatureFromAPK) ? Color.BLUE : Color.RED);
+ append(sb, "From SVC: ", signatureFromSVC, signatureExpected.equals(signatureFromSVC) ? Color.BLUE : Color.RED);
+
+ // 当然SVC并非绝对安全,只是相对而言更加可靠,实际运用还需结合更多的手段
+
+ msg.setText(sb);
+ }
+
+ private static void append(SpannableStringBuilder sb, String header, String value, int color) {
+ int start = sb.length();
+ sb.append(header).append(value).append("\n");
+ int end = sb.length();
+ sb.setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ private byte[] signatureFromAPI() {
+ try {
+ @SuppressLint("PackageManagerGetSignatures")
+ PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
+ return info.signatures[0].toByteArray();
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private byte[] signatureFromAPK() {
+ try (ZipFile zipFile = new ZipFile(getPackageResourcePath())) {
+ Enumeration extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (entry.getName().matches("(META-INF/.*)\\.(RSA|DSA|EC)")) {
+ InputStream is = zipFile.getInputStream(entry);
+ CertificateFactory certFactory = CertificateFactory.getInstance("X509");
+ X509Certificate x509Cert = (X509Certificate) certFactory.generateCertificate(is);
+ return x509Cert.getEncoded();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private byte[] signatureFromSVC() {
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(openAt(getPackageResourcePath()));
+ ZipInputStream zis = new ZipInputStream(new FileInputStream(fd.getFileDescriptor()))) {
+ ZipEntry entry;
+ while ((entry = zis.getNextEntry()) != null) {
+ if (entry.getName().matches("(META-INF/.*)\\.(RSA|DSA|EC)")) {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X509");
+ X509Certificate x509Cert = (X509Certificate) certFactory.generateCertificate(zis);
+ return x509Cert.getEncoded();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ private String md5(byte[] bytes) {
+ if (bytes == null) {
+ return "null";
+ }
+ try {
+ byte[] digest = MessageDigest.getInstance("MD5").digest(bytes);
+ String hexDigits = "0123456789abcdef";
+ char[] str = new char[digest.length * 2];
+ int k = 0;
+ for (byte b : digest) {
+ str[k++] = hexDigits.charAt(b >>> 4 & 0xf);
+ str[k++] = hexDigits.charAt(b & 0xf);
+ }
+ return new String(str);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static native int openAt(String path);
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..2b9e9ce
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..90f9008
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '7.3.0' apply false
+ id 'com.android.library' version '7.3.0' apply false
+}
\ No newline at end of file
diff --git a/fake.jks b/fake.jks
new file mode 100644
index 0000000..cb4233b
Binary files /dev/null and b/fake.jks differ
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..3e927b1
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a62c752
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Dec 23 22:08:17 CST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/key.jks b/key.jks
new file mode 100644
index 0000000..2d89017
Binary files /dev/null and b/key.jks differ
diff --git a/killer/.gitignore b/killer/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/killer/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/killer/CMakeLists.txt b/killer/CMakeLists.txt
new file mode 100644
index 0000000..954bf96
--- /dev/null
+++ b/killer/CMakeLists.txt
@@ -0,0 +1,37 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html
+
+# Sets the minimum version of CMake required to build the native library.
+
+cmake_minimum_required(VERSION 3.4.1)
+
+set(CMAKE_C_FLAGS "-Wall -Wextra -Werror -fvisibility=hidden")
+
+add_library( # Sets the name of the library.
+ SignatureKiller
+
+ # Sets the library as a shared library.
+ SHARED
+
+ # Provides a relative path to your source file(s).
+ src/main/c/mt_jni.c
+ src/main/c/xhook.c
+ src/main/c/xh_core.c
+ src/main/c/xh_elf.c
+ src/main/c/xh_log.c
+ src/main/c/xh_util.c
+ src/main/c/xh_version.c)
+
+find_library( # Sets the name of the path variable.
+ log-lib
+
+ # Specifies the name of the NDK library that
+ # you want CMake to locate.
+ log)
+
+target_link_libraries( # Specifies the target library.
+ SignatureKiller
+
+ # Links the target library to the log library
+ # included in the NDK.
+ ${log-lib})
diff --git a/killer/build.gradle b/killer/build.gradle
new file mode 100644
index 0000000..bcd4b0f
--- /dev/null
+++ b/killer/build.gradle
@@ -0,0 +1,39 @@
+plugins {
+ id 'com.android.library'
+}
+
+android {
+ namespace 'bin.mt.signature'
+ compileSdk 32
+ ndkVersion '24.0.8215888'
+
+ defaultConfig {
+ minSdk 21
+ //noinspection OldTargetApi
+ targetSdk 32
+ externalNativeBuild {
+ cmake {
+ cppFlags ""
+ abiFilters = ["armeabi-v7a", "x86", "arm64-v8a", "x86_64"]
+ }
+ }
+ ndk {
+ abiFilters = ["armeabi-v7a", "x86", "arm64-v8a", "x86_64"]
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ implementation 'org.lsposed.hiddenapibypass:hiddenapibypass:4.3'
+}
\ No newline at end of file
diff --git a/killer/src/main/AndroidManifest.xml b/killer/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..74b7379
--- /dev/null
+++ b/killer/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/killer/src/main/assets/SignatureKiller/origin.apk b/killer/src/main/assets/SignatureKiller/origin.apk
new file mode 100644
index 0000000..3987103
Binary files /dev/null and b/killer/src/main/assets/SignatureKiller/origin.apk differ
diff --git a/killer/src/main/c/mt_jni.c b/killer/src/main/c/mt_jni.c
new file mode 100644
index 0000000..40cfa79
--- /dev/null
+++ b/killer/src/main/c/mt_jni.c
@@ -0,0 +1,67 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "xhook.h"
+#include "xh_log.h"
+
+const char *apkPath__;
+const char *repPath__;
+
+int (*old_open)(const char *, int, mode_t);
+static int openImpl(const char *pathname, int flags, mode_t mode) {
+ //XH_LOG_ERROR("open: %s", pathname);
+ if (strcmp(pathname, apkPath__) == 0){
+ //XH_LOG_ERROR("replace -> %s", repPath__);
+ return old_open(repPath__, flags, mode);
+ }
+ return old_open(pathname, flags, mode);
+}
+
+int (*old_open64)(const char *, int, mode_t);
+static int open64Impl(const char *pathname, int flags, mode_t mode) {
+ //XH_LOG_ERROR("open64: %s", pathname);
+ if (strcmp(pathname, apkPath__) == 0){
+ //XH_LOG_ERROR("replace -> %s", repPath__);
+ return old_open64(repPath__, flags, mode);
+ }
+ return old_open64(pathname, flags, mode);
+}
+
+int (*old_openat)(int, const char*, int, mode_t);
+static int openatImpl(int fd, const char *pathname, int flags, mode_t mode) {
+ //XH_LOG_ERROR("openat: %s", pathname);
+ if (strcmp(pathname, apkPath__) == 0){
+ //XH_LOG_ERROR("replace -> %s", repPath__);
+ return old_openat(fd, repPath__, flags, mode);
+ }
+ return old_openat(fd, pathname, flags, mode);
+}
+
+int (*old_openat64)(int, const char*, int, mode_t);
+static int openat64Impl(int fd, const char *pathname, int flags, mode_t mode) {
+ //XH_LOG_ERROR("openat64: %s", pathname);
+ if (strcmp(pathname, apkPath__) == 0){
+ //XH_LOG_ERROR("replace -> %s", repPath__);
+ return old_openat64(fd, repPath__, flags, mode);
+ }
+ return old_openat64(fd, pathname, flags, mode);
+}
+
+JNIEXPORT void JNICALL
+Java_bin_mt_signature_KillerApplication_hookApkPath(JNIEnv *env, __attribute__((unused)) jclass clazz, jstring apkPath, jstring repPath) {
+ apkPath__ = (*env)->GetStringUTFChars(env, apkPath, 0);
+ repPath__ = (*env)->GetStringUTFChars(env, repPath, 0);
+
+ xhook_register(".*\\.so$", "openat64", openat64Impl, (void **) &old_openat64);
+ xhook_register(".*\\.so$", "openat", openatImpl, (void **) &old_openat);
+ xhook_register(".*\\.so$", "open64", open64Impl, (void **) &old_open64);
+ xhook_register(".*\\.so$", "open", openImpl, (void **) &old_open);
+
+ xhook_refresh(0);
+}
diff --git a/killer/src/main/c/queue.h b/killer/src/main/c/queue.h
new file mode 100755
index 0000000..c2443be
--- /dev/null
+++ b/killer/src/main/c/queue.h
@@ -0,0 +1,554 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: stable/9/sys/sys/queue.h 252365 2013-06-29 04:25:40Z lstewart $
+ */
+
+#ifndef QUEUE_H
+#define QUEUE_H
+
+/* #include */
+#define __containerof(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field)))
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may be traversed in either direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - + - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_FROM + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_FROM_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_FROM - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _FOREACH_REVERSE_FROM_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT - - + +
+ * _REMOVE_AFTER + - + -
+ * _REMOVE_HEAD + - + -
+ * _REMOVE + + + +
+ * _SWAP + + + +
+ *
+ */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type, qual) \
+ struct name { \
+ struct type *qual slh_first; /* first element */ \
+ }
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type, qual) \
+ struct { \
+ struct type *qual sle_next; /* next element */ \
+ }
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+ } while (0)
+
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+ } while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+ } while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do { \
+ SLIST_NEXT(elm, field) = \
+ SLIST_NEXT(SLIST_NEXT(elm, field), field); \
+ } while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+ } while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_REMOVE_AFTER(curelm, field); \
+ } \
+ } while (0)
+
+#define SLIST_SWAP(head1, head2, type) do { \
+ struct type *swap_first = SLIST_FIRST(head1); \
+ SLIST_FIRST(head1) = SLIST_FIRST(head2); \
+ SLIST_FIRST(head2) = swap_first; \
+ } while (0)
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type, qual) \
+ struct name { \
+ struct type *qual lh_first; /* first element */ \
+ }
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type, qual) \
+ struct { \
+ struct type *qual le_next; /* next element */ \
+ struct type *qual *le_prev; /* address of previous next element */ \
+ }
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+ } while (0)
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_PREV(elm, head, type, field) \
+ ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
+ __containerof((elm)->field.le_prev, struct type, field.le_next))
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+ } while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+ } while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+ } while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+ } while (0)
+
+#define LIST_SWAP(head1, head2, type, field) do { \
+ struct type *swap_tmp = LIST_FIRST((head1)); \
+ LIST_FIRST((head1)) = LIST_FIRST((head2)); \
+ LIST_FIRST((head2)) = swap_tmp; \
+ if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
+ if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
+ } while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type, qual) \
+ struct name { \
+ struct type *qual stqh_first;/* first element */ \
+ struct type *qual *stqh_last;/* addr of last next element */ \
+ }
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type, qual) \
+ struct { \
+ struct type *qual stqe_next; /* next element */ \
+ }
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+ } while (0)
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? NULL : \
+ __containerof((head)->stqh_last, struct type, field.stqe_next))
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+ } while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+ } while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+ } while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
+ if ((STAILQ_NEXT(elm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+ } while (0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ STAILQ_REMOVE_AFTER(head, curelm, field); \
+ } \
+ } while (0)
+
+#define STAILQ_SWAP(head1, head2, type) do { \
+ struct type *swap_first = STAILQ_FIRST(head1); \
+ struct type **swap_last = (head1)->stqh_last; \
+ STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_FIRST(head2) = swap_first; \
+ (head2)->stqh_last = swap_last; \
+ if (STAILQ_EMPTY(head1)) \
+ (head1)->stqh_last = &STAILQ_FIRST(head1); \
+ if (STAILQ_EMPTY(head2)) \
+ (head2)->stqh_last = &STAILQ_FIRST(head2); \
+ } while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type, qual) \
+ struct name { \
+ struct type *qual tqh_first; /* first element */ \
+ struct type *qual *tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type, qual) \
+ struct { \
+ struct type *qual tqe_next; /* next element */ \
+ struct type *qual *tqe_prev; /* address of previous next element */ \
+ }
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ } while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ } while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ } while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ } \
+ } while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define TAILQ_SWAP(head1, head2, type, field) do { \
+ struct type *swap_first = (head1)->tqh_first; \
+ struct type **swap_last = (head1)->tqh_last; \
+ (head1)->tqh_first = (head2)->tqh_first; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ (head2)->tqh_first = swap_first; \
+ (head2)->tqh_last = swap_last; \
+ if ((swap_first = (head1)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head1)->tqh_first; \
+ else \
+ (head1)->tqh_last = &(head1)->tqh_first; \
+ if ((swap_first = (head2)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head2)->tqh_first; \
+ else \
+ (head2)->tqh_last = &(head2)->tqh_first; \
+ } while (0)
+
+#endif
diff --git a/killer/src/main/c/tree.h b/killer/src/main/c/tree.h
new file mode 100755
index 0000000..dc938ae
--- /dev/null
+++ b/killer/src/main/c/tree.h
@@ -0,0 +1,768 @@
+/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
+/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
+/* $FreeBSD: stable/9/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */
+
+/*-
+ * Copyright 2002 Niels Provos
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TREE_H
+#define TREE_H
+
+/* #include */
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure. Every operation
+ * on the tree causes a splay to happen. The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree. On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n). The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute. It fulfills a set of conditions:
+ * - every search path from the root to a leaf consists of the
+ * same number of black nodes,
+ * - each red node (except for the root) has a black parent,
+ * - each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type) \
+struct name { \
+ struct type *sph_root; /* root of the tree */ \
+}
+
+#define SPLAY_INITIALIZER(root) \
+ { NULL }
+
+#define SPLAY_INIT(root) do { \
+ (root)->sph_root = NULL; \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ENTRY(type) \
+struct { \
+ struct type *spe_left; /* left element */ \
+ struct type *spe_right; /* right element */ \
+}
+
+#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
+#define SPLAY_ROOT(head) (head)->sph_root
+#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do { \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do { \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
+ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
+ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp) \
+void name##_SPLAY(struct name *, struct type *); \
+void name##_SPLAY_MINMAX(struct name *, int); \
+struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
+ \
+/* Finds the node with the same key as elm */ \
+static __inline struct type * \
+name##_SPLAY_FIND(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) \
+ return(NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) \
+ return (head->sph_root); \
+ return (NULL); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_NEXT(struct name *head, struct type *elm) \
+{ \
+ name##_SPLAY(head, elm); \
+ if (SPLAY_RIGHT(elm, field) != NULL) { \
+ elm = SPLAY_RIGHT(elm, field); \
+ while (SPLAY_LEFT(elm, field) != NULL) { \
+ elm = SPLAY_LEFT(elm, field); \
+ } \
+ } else \
+ elm = NULL; \
+ return (elm); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_MIN_MAX(struct name *head, int val) \
+{ \
+ name##_SPLAY_MINMAX(head, val); \
+ return (SPLAY_ROOT(head)); \
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp) \
+struct type * \
+name##_SPLAY_INSERT(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) { \
+ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
+ } else { \
+ int __comp; \
+ name##_SPLAY(head, elm); \
+ __comp = (cmp)(elm, (head)->sph_root); \
+ if(__comp < 0) { \
+ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+ SPLAY_RIGHT(elm, field) = (head)->sph_root; \
+ SPLAY_LEFT((head)->sph_root, field) = NULL; \
+ } else if (__comp > 0) { \
+ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT(elm, field) = (head)->sph_root; \
+ SPLAY_RIGHT((head)->sph_root, field) = NULL; \
+ } else \
+ return ((head)->sph_root); \
+ } \
+ (head)->sph_root = (elm); \
+ return (NULL); \
+} \
+ \
+struct type * \
+name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *__tmp; \
+ if (SPLAY_EMPTY(head)) \
+ return (NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) { \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+ } else { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+ name##_SPLAY(head, elm); \
+ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
+ } \
+ return (elm); \
+ } \
+ return (NULL); \
+} \
+ \
+void \
+name##_SPLAY(struct name *head, struct type *elm) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+ int __comp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) > 0){ \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+} \
+ \
+/* Splay with either the minimum or the maximum element \
+ * Used to find minimum or maximum element in tree. \
+ */ \
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while (1) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp > 0) { \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+}
+
+#define SPLAY_NEGINF -1
+#define SPLAY_INF 1
+
+#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head) \
+ for ((x) = SPLAY_MIN(name, head); \
+ (x) != NULL; \
+ (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type) \
+struct name { \
+ struct type *rbh_root; /* root of the tree */ \
+}
+
+#define RB_INITIALIZER(root) \
+ { NULL }
+
+#define RB_INIT(root) do { \
+ (root)->rbh_root = NULL; \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_BLACK 0
+#define RB_RED 1
+#define RB_ENTRY(type) \
+struct { \
+ struct type *rbe_left; /* left element */ \
+ struct type *rbe_right; /* right element */ \
+ struct type *rbe_parent; /* parent element */ \
+ int rbe_color; /* node color */ \
+}
+
+#define RB_LEFT(elm, field) (elm)->field.rbe_left
+#define RB_RIGHT(elm, field) (elm)->field.rbe_right
+#define RB_PARENT(elm, field) (elm)->field.rbe_parent
+#define RB_COLOR(elm, field) (elm)->field.rbe_color
+#define RB_ROOT(head) (head)->rbh_root
+#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do { \
+ RB_PARENT(elm, field) = parent; \
+ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
+ RB_COLOR(elm, field) = RB_RED; \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_SET_BLACKRED(black, red, field) do { \
+ RB_COLOR(black, field) = RB_BLACK; \
+ RB_COLOR(red, field) = RB_RED; \
+} while (/*CONSTCOND*/ 0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x) do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
+ (tmp) = RB_RIGHT(elm, field); \
+ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
+ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_LEFT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
+ (tmp) = RB_LEFT(elm, field); \
+ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
+ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_RIGHT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
+attr struct type *name##_RB_INSERT(struct name *, struct type *); \
+attr struct type *name##_RB_FIND(struct name *, struct type *); \
+attr struct type *name##_RB_NFIND(struct name *, struct type *); \
+attr struct type *name##_RB_NEXT(struct type *); \
+attr struct type *name##_RB_PREV(struct type *); \
+attr struct type *name##_RB_MINMAX(struct name *, int); \
+ \
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define RB_GENERATE(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define RB_GENERATE_STATIC(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
+attr void \
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
+{ \
+ struct type *parent, *gparent, *tmp; \
+ while ((parent = RB_PARENT(elm, field)) != NULL && \
+ RB_COLOR(parent, field) == RB_RED) { \
+ gparent = RB_PARENT(parent, field); \
+ if (parent == RB_LEFT(gparent, field)) { \
+ tmp = RB_RIGHT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_RIGHT(parent, field) == elm) { \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_RIGHT(head, gparent, tmp, field); \
+ } else { \
+ tmp = RB_LEFT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_LEFT(parent, field) == elm) { \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_LEFT(head, gparent, tmp, field); \
+ } \
+ } \
+ RB_COLOR(head->rbh_root, field) = RB_BLACK; \
+} \
+ \
+attr void \
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{ \
+ struct type *tmp; \
+ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
+ elm != RB_ROOT(head)) { \
+ if (RB_LEFT(parent, field) == elm) { \
+ tmp = RB_RIGHT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+ struct type *oleft; \
+ if ((oleft = RB_LEFT(tmp, field)) \
+ != NULL) \
+ RB_COLOR(oleft, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_RIGHT(tmp, field)) \
+ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } else { \
+ tmp = RB_LEFT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+ struct type *oright; \
+ if ((oright = RB_RIGHT(tmp, field)) \
+ != NULL) \
+ RB_COLOR(oright, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_LEFT(head, tmp, oright, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_LEFT(tmp, field)) \
+ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } \
+ } \
+ if (elm) \
+ RB_COLOR(elm, field) = RB_BLACK; \
+} \
+ \
+attr struct type * \
+name##_RB_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *child, *parent, *old = elm; \
+ int color; \
+ if (RB_LEFT(elm, field) == NULL) \
+ child = RB_RIGHT(elm, field); \
+ else if (RB_RIGHT(elm, field) == NULL) \
+ child = RB_LEFT(elm, field); \
+ else { \
+ struct type *left; \
+ elm = RB_RIGHT(elm, field); \
+ while ((left = RB_LEFT(elm, field)) != NULL) \
+ elm = left; \
+ child = RB_RIGHT(elm, field); \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+ if (RB_PARENT(elm, field) == old) \
+ parent = elm; \
+ (elm)->field = (old)->field; \
+ if (RB_PARENT(old, field)) { \
+ if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+ RB_LEFT(RB_PARENT(old, field), field) = elm;\
+ else \
+ RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+ RB_AUGMENT(RB_PARENT(old, field)); \
+ } else \
+ RB_ROOT(head) = elm; \
+ RB_PARENT(RB_LEFT(old, field), field) = elm; \
+ if (RB_RIGHT(old, field)) \
+ RB_PARENT(RB_RIGHT(old, field), field) = elm; \
+ if (parent) { \
+ left = parent; \
+ do { \
+ RB_AUGMENT(left); \
+ } while ((left = RB_PARENT(left, field)) != NULL); \
+ } \
+ goto color; \
+ } \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+color: \
+ if (color == RB_BLACK) \
+ name##_RB_REMOVE_COLOR(head, parent, child); \
+ return (old); \
+} \
+ \
+/* Inserts a node into the RB tree */ \
+attr struct type * \
+name##_RB_INSERT(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp; \
+ struct type *parent = NULL; \
+ int comp = 0; \
+ tmp = RB_ROOT(head); \
+ while (tmp) { \
+ parent = tmp; \
+ comp = (cmp)(elm, parent); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ RB_SET(elm, parent, field); \
+ if (parent != NULL) { \
+ if (comp < 0) \
+ RB_LEFT(parent, field) = elm; \
+ else \
+ RB_RIGHT(parent, field) = elm; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = elm; \
+ name##_RB_INSERT_COLOR(head, elm); \
+ return (NULL); \
+} \
+ \
+/* Finds the node with the same key as elm */ \
+attr struct type * \
+name##_RB_FIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (NULL); \
+} \
+ \
+/* Finds the first node greater than or equal to the search key */ \
+attr struct type * \
+name##_RB_NFIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *res = NULL; \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) { \
+ res = tmp; \
+ tmp = RB_LEFT(tmp, field); \
+ } \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (res); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_NEXT(struct type *elm) \
+{ \
+ if (RB_RIGHT(elm, field)) { \
+ elm = RB_RIGHT(elm, field); \
+ while (RB_LEFT(elm, field)) \
+ elm = RB_LEFT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_PREV(struct type *elm) \
+{ \
+ if (RB_LEFT(elm, field)) { \
+ elm = RB_LEFT(elm, field); \
+ while (RB_RIGHT(elm, field)) \
+ elm = RB_RIGHT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+attr struct type * \
+name##_RB_MINMAX(struct name *head, int val) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *parent = NULL; \
+ while (tmp) { \
+ parent = tmp; \
+ if (val < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else \
+ tmp = RB_RIGHT(tmp, field); \
+ } \
+ return (parent); \
+}
+
+#define RB_NEGINF -1
+#define RB_INF 1
+
+#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
+#define RB_PREV(name, x, y) name##_RB_PREV(y)
+#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head) \
+ for ((x) = RB_MIN(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_FROM(x, name, y) \
+ for ((x) = (y); \
+ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
+ (x) = (y))
+
+#define RB_FOREACH_SAFE(x, name, head, y) \
+ for ((x) = RB_MIN(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
+ (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head) \
+ for ((x) = RB_MAX(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_FROM(x, name, y) \
+ for ((x) = (y); \
+ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
+ (x) = (y))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
+ for ((x) = RB_MAX(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
+ (x) = (y))
+
+#endif
diff --git a/killer/src/main/c/xh_core.c b/killer/src/main/c/xh_core.c
new file mode 100755
index 0000000..41fcd6f
--- /dev/null
+++ b/killer/src/main/c/xh_core.c
@@ -0,0 +1,676 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "queue.h"
+#include "tree.h"
+#include "xh_errno.h"
+#include "xh_log.h"
+#include "xh_elf.h"
+#include "xh_version.h"
+#include "xh_core.h"
+
+#define XH_CORE_DEBUG 0
+
+//registered hook info collection
+typedef struct xh_core_hook_info
+{
+#if XH_CORE_DEBUG
+ char *pathname_regex_str;
+#endif
+ regex_t pathname_regex;
+ char *symbol;
+ void *new_func;
+ void **old_func;
+ TAILQ_ENTRY(xh_core_hook_info,) link;
+} xh_core_hook_info_t;
+typedef TAILQ_HEAD(xh_core_hook_info_queue, xh_core_hook_info,) xh_core_hook_info_queue_t;
+
+//ignored hook info collection
+typedef struct xh_core_ignore_info
+{
+#if XH_CORE_DEBUG
+ char *pathname_regex_str;
+#endif
+ regex_t pathname_regex;
+ char *symbol; //NULL meaning for all symbols
+ TAILQ_ENTRY(xh_core_ignore_info,) link;
+} xh_core_ignore_info_t;
+typedef TAILQ_HEAD(xh_core_ignore_info_queue, xh_core_ignore_info,) xh_core_ignore_info_queue_t;
+
+//required info from /proc/self/maps
+typedef struct xh_core_map_info
+{
+ char *pathname;
+ uintptr_t base_addr;
+ xh_elf_t elf;
+ RB_ENTRY(xh_core_map_info) link;
+} xh_core_map_info_t;
+static __inline__ int xh_core_map_info_cmp(xh_core_map_info_t *a, xh_core_map_info_t *b)
+{
+ return strcmp(a->pathname, b->pathname);
+}
+typedef RB_HEAD(xh_core_map_info_tree, xh_core_map_info) xh_core_map_info_tree_t;
+RB_GENERATE_STATIC(xh_core_map_info_tree, xh_core_map_info, link, xh_core_map_info_cmp)
+
+//signal handler for SIGSEGV
+//for xh_elf_init(), xh_elf_hook(), xh_elf_check_elfheader()
+static int xh_core_sigsegv_enable = 1; //enable by default
+static struct sigaction xh_core_sigsegv_act_old;
+static volatile int xh_core_sigsegv_flag = 0;
+static sigjmp_buf xh_core_sigsegv_env;
+static void xh_core_sigsegv_handler(int sig)
+{
+ (void)sig;
+
+ if(xh_core_sigsegv_flag)
+ siglongjmp(xh_core_sigsegv_env, 1);
+ else
+ sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL);
+}
+static int xh_core_add_sigsegv_handler()
+{
+ struct sigaction act;
+
+ if(!xh_core_sigsegv_enable) return 0;
+
+ if(0 != sigemptyset(&act.sa_mask)) return (0 == errno ? XH_ERRNO_UNKNOWN : errno);
+ act.sa_handler = xh_core_sigsegv_handler;
+
+ if(0 != sigaction(SIGSEGV, &act, &xh_core_sigsegv_act_old))
+ return (0 == errno ? XH_ERRNO_UNKNOWN : errno);
+
+ return 0;
+}
+static void xh_core_del_sigsegv_handler()
+{
+ if(!xh_core_sigsegv_enable) return;
+
+ sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL);
+}
+
+
+static xh_core_hook_info_queue_t xh_core_hook_info = TAILQ_HEAD_INITIALIZER(xh_core_hook_info);
+static xh_core_ignore_info_queue_t xh_core_ignore_info = TAILQ_HEAD_INITIALIZER(xh_core_ignore_info);
+static xh_core_map_info_tree_t xh_core_map_info = RB_INITIALIZER(&xh_core_map_info);
+static pthread_mutex_t xh_core_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t xh_core_cond = PTHREAD_COND_INITIALIZER;
+static volatile int xh_core_inited = 0;
+static volatile int xh_core_init_ok = 0;
+static volatile int xh_core_async_inited = 0;
+static volatile int xh_core_async_init_ok = 0;
+static pthread_mutex_t xh_core_refresh_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_t xh_core_refresh_thread_tid;
+static volatile int xh_core_refresh_thread_running = 0;
+static volatile int xh_core_refresh_thread_do = 0;
+
+
+int xh_core_register(const char *pathname_regex_str, const char *symbol,
+ void *new_func, void **old_func)
+{
+ xh_core_hook_info_t *hi;
+ regex_t regex;
+
+ if(NULL == pathname_regex_str || NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;
+
+ if(xh_core_inited)
+ {
+ XH_LOG_ERROR("do not register hook after refresh(): %s, %s", pathname_regex_str, symbol);
+ return XH_ERRNO_INVAL;
+ }
+
+ if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL;
+
+ if(NULL == (hi = malloc(sizeof(xh_core_hook_info_t)))) return XH_ERRNO_NOMEM;
+ if(NULL == (hi->symbol = strdup(symbol)))
+ {
+ free(hi);
+ return XH_ERRNO_NOMEM;
+ }
+#if XH_CORE_DEBUG
+ if(NULL == (hi->pathname_regex_str = strdup(pathname_regex_str)))
+ {
+ free(hi->symbol);
+ free(hi);
+ return XH_ERRNO_NOMEM;
+ }
+#endif
+ hi->pathname_regex = regex;
+ hi->new_func = new_func;
+ hi->old_func = old_func;
+
+ pthread_mutex_lock(&xh_core_mutex);
+ TAILQ_INSERT_TAIL(&xh_core_hook_info, hi, link);
+ pthread_mutex_unlock(&xh_core_mutex);
+
+ return 0;
+}
+
+int xh_core_ignore(const char *pathname_regex_str, const char *symbol)
+{
+ xh_core_ignore_info_t *ii;
+ regex_t regex;
+
+ if(NULL == pathname_regex_str) return XH_ERRNO_INVAL;
+
+ if(xh_core_inited)
+ {
+ XH_LOG_ERROR("do not ignore hook after refresh(): %s, %s", pathname_regex_str, symbol ? symbol : "ALL");
+ return XH_ERRNO_INVAL;
+ }
+
+ if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL;
+
+ if(NULL == (ii = malloc(sizeof(xh_core_ignore_info_t)))) return XH_ERRNO_NOMEM;
+ if(NULL != symbol)
+ {
+ if(NULL == (ii->symbol = strdup(symbol)))
+ {
+ free(ii);
+ return XH_ERRNO_NOMEM;
+ }
+ }
+ else
+ {
+ ii->symbol = NULL; //ignore all symbols
+ }
+#if XH_CORE_DEBUG
+ if(NULL == (ii->pathname_regex_str = strdup(pathname_regex_str)))
+ {
+ free(ii->symbol);
+ free(ii);
+ return XH_ERRNO_NOMEM;
+ }
+#endif
+ ii->pathname_regex = regex;
+
+ pthread_mutex_lock(&xh_core_mutex);
+ TAILQ_INSERT_TAIL(&xh_core_ignore_info, ii, link);
+ pthread_mutex_unlock(&xh_core_mutex);
+
+ return 0;
+}
+
+static int xh_core_check_elf_header(uintptr_t base_addr, const char *pathname)
+{
+ if(!xh_core_sigsegv_enable)
+ {
+ return xh_elf_check_elfheader(base_addr);
+ }
+ else
+ {
+ int ret = XH_ERRNO_UNKNOWN;
+
+ xh_core_sigsegv_flag = 1;
+ if(0 == sigsetjmp(xh_core_sigsegv_env, 1))
+ {
+ ret = xh_elf_check_elfheader(base_addr);
+ }
+ else
+ {
+ ret = XH_ERRNO_SEGVERR;
+ XH_LOG_WARN("catch SIGSEGV when check_elfheader: %s", pathname);
+ }
+ xh_core_sigsegv_flag = 0;
+ return ret;
+ }
+}
+
+static void xh_core_hook_impl(xh_core_map_info_t *mi)
+{
+ //init
+ if(0 != xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname)) return;
+
+ //hook
+ xh_core_hook_info_t *hi;
+ xh_core_ignore_info_t *ii;
+ int ignore;
+ TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info
+ {
+ if(0 == regexec(&(hi->pathname_regex), mi->pathname, 0, NULL, 0))
+ {
+ ignore = 0;
+ TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info
+ {
+ if(0 == regexec(&(ii->pathname_regex), mi->pathname, 0, NULL, 0))
+ {
+ if(NULL == ii->symbol) //ignore all symbols
+ return;
+
+ if(0 == strcmp(ii->symbol, hi->symbol)) //ignore the current symbol
+ {
+ ignore = 1;
+ break;
+ }
+ }
+ }
+
+ if(0 == ignore)
+ xh_elf_hook(&(mi->elf), hi->symbol, hi->new_func, hi->old_func);
+ }
+ }
+}
+
+static void xh_core_hook(xh_core_map_info_t *mi)
+{
+ if(!xh_core_sigsegv_enable)
+ {
+ xh_core_hook_impl(mi);
+ }
+ else
+ {
+ xh_core_sigsegv_flag = 1;
+ if(0 == sigsetjmp(xh_core_sigsegv_env, 1))
+ {
+ xh_core_hook_impl(mi);
+ }
+ else
+ {
+ XH_LOG_WARN("catch SIGSEGV when init or hook: %s", mi->pathname);
+ }
+ xh_core_sigsegv_flag = 0;
+ }
+}
+
+static void xh_core_refresh_impl()
+{
+ char line[512];
+ FILE *fp;
+ uintptr_t base_addr;
+ uintptr_t prev_base_addr = 0;
+ char perm[5];
+ char prev_perm[5] = "---p";
+ unsigned long offset;
+ unsigned long prev_offset = 0;
+ int pathname_pos;
+ char *pathname;
+ char prev_pathname[512] = {0};
+ size_t pathname_len;
+ xh_core_map_info_t *mi, *mi_tmp;
+ xh_core_map_info_t mi_key;
+ xh_core_hook_info_t *hi;
+ xh_core_ignore_info_t *ii;
+ int match;
+ xh_core_map_info_tree_t map_info_refreshed = RB_INITIALIZER(&map_info_refreshed);
+
+ if(NULL == (fp = fopen("/proc/self/maps", "r")))
+ {
+ XH_LOG_ERROR("fopen /proc/self/maps failed");
+ return;
+ }
+
+ while(fgets(line, sizeof(line), fp))
+ {
+ if(sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue;
+
+ // do not touch the shared memory
+ if (perm[3] != 'p') continue;
+
+ // Ignore permission PROT_NONE maps
+ if (perm[0] == '-' && perm[1] == '-' && perm[2] == '-')
+ continue;
+
+ //get pathname
+ while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1))
+ pathname_pos += 1;
+ if(pathname_pos >= (int)(sizeof(line) - 1)) continue;
+ pathname = line + pathname_pos;
+ pathname_len = strlen(pathname);
+ if(0 == pathname_len) continue;
+ if(pathname[pathname_len - 1] == '\n')
+ {
+ pathname[pathname_len - 1] = '\0';
+ pathname_len -= 1;
+ }
+ if(0 == pathname_len) continue;
+ if('[' == pathname[0]) continue;
+
+ // Find non-executable map, we need record it. Because so maps can begin with
+ // an non-executable map.
+ if (perm[2] != 'x') {
+ prev_offset = offset;
+ prev_base_addr = base_addr;
+ memcpy(prev_perm, perm, sizeof(prev_perm));
+ strcpy(prev_pathname, pathname);
+ continue;
+ }
+
+ // Find executable map if offset == 0, it OK,
+ // or we need check previous map for base address.
+ if (offset != 0) {
+ if (strcmp(prev_pathname, pathname) || prev_offset != 0 || prev_perm[0] != 'r') {
+ continue;
+ }
+ // The previous map is real begin map
+ base_addr = prev_base_addr;
+ }
+
+ //check pathname
+ //if we need to hook this elf?
+ match = 0;
+ TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info
+ {
+ if(0 == regexec(&(hi->pathname_regex), pathname, 0, NULL, 0))
+ {
+ TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info
+ {
+ if(0 == regexec(&(ii->pathname_regex), pathname, 0, NULL, 0))
+ {
+ if(NULL == ii->symbol)
+ goto check_finished;
+
+ if(0 == strcmp(ii->symbol, hi->symbol))
+ goto check_continue;
+ }
+ }
+
+ match = 1;
+ check_continue:
+ break;
+ }
+ }
+ check_finished:
+ if(0 == match) continue;
+
+ //check elf header format
+ //We are trying to do ELF header checking as late as possible.
+ if(0 != xh_core_check_elf_header(base_addr, pathname)) continue;
+
+ //check existed map item
+ mi_key.pathname = pathname;
+ if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key)))
+ {
+ //exist
+ RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
+
+ //repeated?
+ //We only keep the first one, that is the real base address
+ if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))
+ {
+#if XH_CORE_DEBUG
+ XH_LOG_DEBUG("repeated map info when update: %s", line);
+#endif
+ free(mi->pathname);
+ free(mi);
+ continue;
+ }
+
+ //re-hook if base_addr changed
+ if(mi->base_addr != base_addr)
+ {
+ mi->base_addr = base_addr;
+ xh_core_hook(mi);
+ }
+ }
+ else
+ {
+ //not exist, create a new map info
+ if(NULL == (mi = (xh_core_map_info_t *)malloc(sizeof(xh_core_map_info_t)))) continue;
+ if(NULL == (mi->pathname = strdup(pathname)))
+ {
+ free(mi);
+ continue;
+ }
+ mi->base_addr = base_addr;
+
+ //repeated?
+ //We only keep the first one, that is the real base address
+ if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))
+ {
+#if XH_CORE_DEBUG
+ XH_LOG_DEBUG("repeated map info when create: %s", line);
+#endif
+ free(mi->pathname);
+ free(mi);
+ continue;
+ }
+
+ //hook
+ xh_core_hook(mi); //hook
+ }
+ }
+ fclose(fp);
+
+ //free all missing map item, maybe dlclosed?
+ RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)
+ {
+#if XH_CORE_DEBUG
+ XH_LOG_DEBUG("remove missing map info: %s", mi->pathname);
+#endif
+ RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
+ if(mi->pathname) free(mi->pathname);
+ free(mi);
+ }
+
+ //save the new refreshed map info tree
+ xh_core_map_info = map_info_refreshed;
+
+ XH_LOG_INFO("map refreshed");
+
+#if XH_CORE_DEBUG
+ RB_FOREACH(mi, xh_core_map_info_tree, &xh_core_map_info)
+ XH_LOG_DEBUG(" %"PRIxPTR" %s\n", mi->base_addr, mi->pathname);
+#endif
+}
+
+static void *xh_core_refresh_thread_func(void *arg)
+{
+ (void)arg;
+
+ pthread_setname_np(pthread_self(), "xh_refresh_loop");
+
+ while(xh_core_refresh_thread_running)
+ {
+ //waiting for a refresh task or exit
+ pthread_mutex_lock(&xh_core_mutex);
+ while(!xh_core_refresh_thread_do && xh_core_refresh_thread_running)
+ {
+ pthread_cond_wait(&xh_core_cond, &xh_core_mutex);
+ }
+ if(!xh_core_refresh_thread_running)
+ {
+ pthread_mutex_unlock(&xh_core_mutex);
+ break;
+ }
+ xh_core_refresh_thread_do = 0;
+ pthread_mutex_unlock(&xh_core_mutex);
+
+ //refresh
+ pthread_mutex_lock(&xh_core_refresh_mutex);
+ xh_core_refresh_impl();
+ pthread_mutex_unlock(&xh_core_refresh_mutex);
+ }
+
+ return NULL;
+}
+
+static void xh_core_init_once()
+{
+ if(xh_core_inited) return;
+
+ pthread_mutex_lock(&xh_core_mutex);
+
+ if(xh_core_inited) goto end;
+
+ xh_core_inited = 1;
+
+ //dump debug info
+ XH_LOG_INFO("%s\n", xh_version_str_full());
+#if XH_CORE_DEBUG
+ xh_core_hook_info_t *hi;
+ TAILQ_FOREACH(hi, &xh_core_hook_info, link)
+ XH_LOG_INFO(" hook: %s @ %s, (%p, %p)\n", hi->symbol, hi->pathname_regex_str,
+ hi->new_func, hi->old_func);
+ xh_core_ignore_info_t *ii;
+ TAILQ_FOREACH(ii, &xh_core_ignore_info, link)
+ XH_LOG_INFO(" ignore: %s @ %s\n", ii->symbol ? ii->symbol : "ALL ",
+ ii->pathname_regex_str);
+#endif
+
+ //register signal handler
+ if(0 != xh_core_add_sigsegv_handler()) goto end;
+
+ //OK
+ xh_core_init_ok = 1;
+
+ end:
+ pthread_mutex_unlock(&xh_core_mutex);
+}
+
+static void xh_core_init_async_once()
+{
+ if(xh_core_async_inited) return;
+
+ pthread_mutex_lock(&xh_core_mutex);
+
+ if(xh_core_async_inited) goto end;
+
+ xh_core_async_inited = 1;
+
+ //create async refresh thread
+ xh_core_refresh_thread_running = 1;
+ if(0 != pthread_create(&xh_core_refresh_thread_tid, NULL, &xh_core_refresh_thread_func, NULL))
+ {
+ xh_core_refresh_thread_running = 0;
+ goto end;
+ }
+
+ //OK
+ xh_core_async_init_ok = 1;
+
+ end:
+ pthread_mutex_unlock(&xh_core_mutex);
+}
+
+int xh_core_refresh(int async)
+{
+ //init
+ xh_core_init_once();
+ if(!xh_core_init_ok) return XH_ERRNO_UNKNOWN;
+
+ if(async)
+ {
+ //init for async
+ xh_core_init_async_once();
+ if(!xh_core_async_init_ok) return XH_ERRNO_UNKNOWN;
+
+ //refresh async
+ pthread_mutex_lock(&xh_core_mutex);
+ xh_core_refresh_thread_do = 1;
+ pthread_cond_signal(&xh_core_cond);
+ pthread_mutex_unlock(&xh_core_mutex);
+ }
+ else
+ {
+ //refresh sync
+ pthread_mutex_lock(&xh_core_refresh_mutex);
+ xh_core_refresh_impl();
+ pthread_mutex_unlock(&xh_core_refresh_mutex);
+ }
+
+ return 0;
+}
+
+void xh_core_clear()
+{
+ //stop the async refresh thread
+ if(xh_core_async_init_ok)
+ {
+ pthread_mutex_lock(&xh_core_mutex);
+ xh_core_refresh_thread_running = 0;
+ pthread_cond_signal(&xh_core_cond);
+ pthread_mutex_unlock(&xh_core_mutex);
+
+ pthread_join(xh_core_refresh_thread_tid, NULL);
+ xh_core_async_init_ok = 0;
+ }
+ xh_core_async_inited = 0;
+
+ //unregister the sig handler
+ if(xh_core_init_ok)
+ {
+ xh_core_del_sigsegv_handler();
+ xh_core_init_ok = 0;
+ }
+ xh_core_inited = 0;
+
+ pthread_mutex_lock(&xh_core_mutex);
+ pthread_mutex_lock(&xh_core_refresh_mutex);
+
+ //free all map info
+ xh_core_map_info_t *mi, *mi_tmp;
+ RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)
+ {
+ RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
+ if(mi->pathname) free(mi->pathname);
+ free(mi);
+ }
+
+ //free all hook info
+ xh_core_hook_info_t *hi, *hi_tmp;
+ TAILQ_FOREACH_SAFE(hi, &xh_core_hook_info, link, hi_tmp)
+ {
+ TAILQ_REMOVE(&xh_core_hook_info, hi, link);
+#if XH_CORE_DEBUG
+ free(hi->pathname_regex_str);
+#endif
+ regfree(&(hi->pathname_regex));
+ free(hi->symbol);
+ free(hi);
+ }
+
+ //free all ignore info
+ xh_core_ignore_info_t *ii, *ii_tmp;
+ TAILQ_FOREACH_SAFE(ii, &xh_core_ignore_info, link, ii_tmp)
+ {
+ TAILQ_REMOVE(&xh_core_ignore_info, ii, link);
+#if XH_CORE_DEBUG
+ free(ii->pathname_regex_str);
+#endif
+ regfree(&(ii->pathname_regex));
+ free(ii->symbol);
+ free(ii);
+ }
+
+ pthread_mutex_unlock(&xh_core_refresh_mutex);
+ pthread_mutex_unlock(&xh_core_mutex);
+}
+
+void xh_core_enable_debug(int flag)
+{
+ xh_log_priority = (flag ? ANDROID_LOG_DEBUG : ANDROID_LOG_WARN);
+}
+
+void xh_core_enable_sigsegv_protection(int flag)
+{
+ xh_core_sigsegv_enable = (flag ? 1 : 0);
+}
diff --git a/killer/src/main/c/xh_core.h b/killer/src/main/c/xh_core.h
new file mode 100755
index 0000000..3508794
--- /dev/null
+++ b/killer/src/main/c/xh_core.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_CORE_H
+#define XH_CORE_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int xh_core_register(const char *pathname_regex_str, const char *symbol,
+ void *new_func, void **old_func);
+
+int xh_core_ignore(const char *pathname_regex_str, const char *symbol);
+
+int xh_core_refresh(int async);
+
+void xh_core_clear();
+
+void xh_core_enable_debug(int flag);
+
+void xh_core_enable_sigsegv_protection(int flag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/killer/src/main/c/xh_elf.c b/killer/src/main/c/xh_elf.c
new file mode 100755
index 0000000..1d80371
--- /dev/null
+++ b/killer/src/main/c/xh_elf.c
@@ -0,0 +1,1046 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "xh_errno.h"
+#include "xh_log.h"
+#include "xh_util.h"
+#include "xh_elf.h"
+
+#define XH_ELF_DEBUG 0
+
+#ifndef EI_ABIVERSION
+#define EI_ABIVERSION 8
+#endif
+
+#if defined(__arm__)
+#define XH_ELF_R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT //.rel.plt
+#define XH_ELF_R_GENERIC_GLOB_DAT R_ARM_GLOB_DAT //.rel.dyn
+#define XH_ELF_R_GENERIC_ABS R_ARM_ABS32 //.rel.dyn
+#elif defined(__aarch64__)
+#define XH_ELF_R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT
+#define XH_ELF_R_GENERIC_GLOB_DAT R_AARCH64_GLOB_DAT
+#define XH_ELF_R_GENERIC_ABS R_AARCH64_ABS64
+#elif defined(__i386__)
+#define XH_ELF_R_GENERIC_JUMP_SLOT R_386_JMP_SLOT
+#define XH_ELF_R_GENERIC_GLOB_DAT R_386_GLOB_DAT
+#define XH_ELF_R_GENERIC_ABS R_386_32
+#elif defined(__x86_64__)
+#define XH_ELF_R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT
+#define XH_ELF_R_GENERIC_GLOB_DAT R_X86_64_GLOB_DAT
+#define XH_ELF_R_GENERIC_ABS R_X86_64_64
+#endif
+
+#if defined(__LP64__)
+#define XH_ELF_R_SYM(info) ELF64_R_SYM(info)
+#define XH_ELF_R_TYPE(info) ELF64_R_TYPE(info)
+#else
+#define XH_ELF_R_SYM(info) ELF32_R_SYM(info)
+#define XH_ELF_R_TYPE(info) ELF32_R_TYPE(info)
+#endif
+
+//iterator for plain PLT
+typedef struct
+{
+ uint8_t *cur;
+ uint8_t *end;
+ int is_use_rela;
+} xh_elf_plain_reloc_iterator_t;
+
+static void xh_elf_plain_reloc_iterator_init(xh_elf_plain_reloc_iterator_t *self,
+ ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela)
+{
+ self->cur = (uint8_t *)rel;
+ self->end = self->cur + rel_sz;
+ self->is_use_rela = is_use_rela;
+}
+
+static void *xh_elf_plain_reloc_iterator_next(xh_elf_plain_reloc_iterator_t *self)
+{
+ if(self->cur >= self->end) return NULL;
+
+ void *ret = (void *)(self->cur);
+ self->cur += (self->is_use_rela ? sizeof(ElfW(Rela)) : sizeof(ElfW(Rel)));
+ return ret;
+}
+
+//sleb128 decoder
+typedef struct
+{
+ uint8_t *cur;
+ uint8_t *end;
+} xh_elf_sleb128_decoder_t;
+
+static void xh_elf_sleb128_decoder_init(xh_elf_sleb128_decoder_t *self,
+ ElfW(Addr) rel, ElfW(Word) rel_sz)
+{
+ self->cur = (uint8_t *)rel;
+ self->end = self->cur + rel_sz;
+}
+
+static int xh_elf_sleb128_decoder_next(xh_elf_sleb128_decoder_t *self, size_t *ret)
+{
+ size_t value = 0;
+ static const size_t size = 8 * sizeof(value);
+ size_t shift = 0;
+ uint8_t byte;
+
+ do
+ {
+ if(self->cur >= self->end)
+ return XH_ERRNO_FORMAT;
+
+ byte = *(self->cur)++;
+ value |= ((size_t)(byte & 127) << shift);
+ shift += 7;
+ } while(byte & 128);
+
+ if(shift < size && (byte & 64))
+ {
+ value |= -((size_t)(1) << shift);
+ }
+
+ *ret = value;
+ return 0;
+}
+
+//iterator for sleb128 decoded packed PLT
+typedef struct
+{
+ xh_elf_sleb128_decoder_t decoder;
+ size_t relocation_count;
+ size_t group_size;
+ size_t group_flags;
+ size_t group_r_offset_delta;
+ size_t relocation_index;
+ size_t relocation_group_index;
+ ElfW(Rela) rela;
+ ElfW(Rel) rel;
+ ElfW(Addr) r_offset;
+ size_t r_info;
+ ssize_t r_addend;
+ int is_use_rela;
+} xh_elf_packed_reloc_iterator_t;
+
+const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1;
+const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
+const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
+const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
+
+static int xh_elf_packed_reloc_iterator_init(xh_elf_packed_reloc_iterator_t *self,
+ ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela)
+{
+ int r;
+
+ memset(self, 0, sizeof(xh_elf_packed_reloc_iterator_t));
+ xh_elf_sleb128_decoder_init(&(self->decoder), rel, rel_sz);
+ self->is_use_rela = is_use_rela;
+
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->relocation_count)))) return r;
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_offset)))) return r;
+ return 0;
+}
+
+static int xh_elf_packed_reloc_iterator_read_group_fields(xh_elf_packed_reloc_iterator_t *self)
+{
+ int r;
+ size_t val;
+
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_size)))) return r;
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_flags)))) return r;
+
+ if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG)
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_r_offset_delta)))) return r;
+
+ if(self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG)
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_info)))) return r;
+
+ if((self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) &&
+ (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG))
+ {
+ if(0 == self->is_use_rela)
+ {
+ XH_LOG_ERROR("unexpected r_addend in android.rel section");
+ return XH_ERRNO_FORMAT;
+ }
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &val))) return r;
+ self->r_addend += (ssize_t)val;
+ }
+ else if(0 == (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG))
+ {
+ self->r_addend = 0;
+ }
+
+ self->relocation_group_index = 0;
+ return 0;
+}
+
+static void *xh_elf_packed_reloc_iterator_next(xh_elf_packed_reloc_iterator_t *self)
+{
+ size_t val;
+
+ if(self->relocation_index >= self->relocation_count) return NULL;
+
+ if(self->relocation_group_index == self->group_size)
+ {
+ if(0 != xh_elf_packed_reloc_iterator_read_group_fields(self)) return NULL;
+ }
+
+ if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG)
+ {
+ self->r_offset += self->group_r_offset_delta;
+ }
+ else
+ {
+ if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL;
+ self->r_offset += val;
+ }
+
+ if(0 == (self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG))
+ if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &(self->r_info))) return NULL;
+
+ if(self->is_use_rela &&
+ (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) &&
+ (0 == (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG)))
+ {
+ if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL;
+ self->r_addend += (ssize_t)val;
+ }
+
+ self->relocation_index++;
+ self->relocation_group_index++;
+
+ if(self->is_use_rela)
+ {
+ self->rela.r_offset = self->r_offset;
+ self->rela.r_info = self->r_info;
+ self->rela.r_addend = self->r_addend;
+ return (void *)(&(self->rela));
+ }
+ else
+ {
+ self->rel.r_offset = self->r_offset;
+ self->rel.r_info = self->r_info;
+ return (void *)(&(self->rel));
+ }
+}
+
+//ELF header checker
+int xh_elf_check_elfheader(uintptr_t base_addr)
+{
+ ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base_addr;
+
+ //check magic
+ if(0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) return XH_ERRNO_FORMAT;
+
+ //check class (64/32)
+#if defined(__LP64__)
+ if(ELFCLASS64 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT;
+#else
+ if(ELFCLASS32 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT;
+#endif
+
+ //check endian (little/big)
+ if(ELFDATA2LSB != ehdr->e_ident[EI_DATA]) return XH_ERRNO_FORMAT;
+
+ //check version
+ if(EV_CURRENT != ehdr->e_ident[EI_VERSION]) return XH_ERRNO_FORMAT;
+
+ //check type
+ if(ET_EXEC != ehdr->e_type && ET_DYN != ehdr->e_type) return XH_ERRNO_FORMAT;
+
+ //check machine
+#if defined(__arm__)
+ if(EM_ARM != ehdr->e_machine) return XH_ERRNO_FORMAT;
+#elif defined(__aarch64__)
+ if(EM_AARCH64 != ehdr->e_machine) return XH_ERRNO_FORMAT;
+#elif defined(__i386__)
+ if(EM_386 != ehdr->e_machine) return XH_ERRNO_FORMAT;
+#elif defined(__x86_64__)
+ if(EM_X86_64 != ehdr->e_machine) return XH_ERRNO_FORMAT;
+#else
+ return XH_ERRNO_FORMAT;
+#endif
+
+ //check version
+ if(EV_CURRENT != ehdr->e_version) return XH_ERRNO_FORMAT;
+
+ return 0;
+}
+
+//ELF hash func
+static uint32_t xh_elf_hash(const uint8_t *name)
+{
+ uint32_t h = 0, g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000;
+ h ^= g;
+ h ^= g >> 24;
+ }
+
+ return h;
+}
+
+//GNU hash func
+static uint32_t xh_elf_gnu_hash(const uint8_t *name)
+{
+ uint32_t h = 5381;
+
+ while(*name != 0)
+ {
+ h += (h << 5) + *name++;
+ }
+ return h;
+}
+
+static ElfW(Phdr) *xh_elf_get_first_segment_by_type(xh_elf_t *self, ElfW(Word) type)
+{
+ ElfW(Phdr) *phdr;
+
+ for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++)
+ {
+ if(phdr->p_type == type)
+ {
+ return phdr;
+ }
+ }
+ return NULL;
+}
+
+static ElfW(Phdr) *xh_elf_get_first_segment_by_type_offset(xh_elf_t *self, ElfW(Word) type, ElfW(Off) offset)
+{
+ ElfW(Phdr) *phdr;
+
+ for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++)
+ {
+ if(phdr->p_type == type && phdr->p_offset == offset)
+ {
+ return phdr;
+ }
+ }
+ return NULL;
+}
+
+static int xh_elf_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ uint32_t hash = xh_elf_hash((uint8_t *)symbol);
+ const char *symbol_cur;
+ uint32_t i;
+
+ for(i = self->bucket[hash % self->bucket_cnt]; 0 != i; i = self->chain[i])
+ {
+ symbol_cur = self->strtab + self->symtab[i].st_name;
+
+ if(0 == strcmp(symbol, symbol_cur))
+ {
+ *symidx = i;
+ XH_LOG_INFO("found %s at symidx: %u (ELF_HASH)\n", symbol, *symidx);
+ return 0;
+ }
+ }
+
+ return XH_ERRNO_NOTFND;
+}
+
+static int xh_elf_gnu_hash_lookup_def(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ uint32_t hash = xh_elf_gnu_hash((uint8_t *)symbol);
+
+ static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8;
+ size_t word = self->bloom[(hash / elfclass_bits) % self->bloom_sz];
+ size_t mask = 0
+ | (size_t)1 << (hash % elfclass_bits)
+ | (size_t)1 << ((hash >> self->bloom_shift) % elfclass_bits);
+
+ //if at least one bit is not set, this symbol is surely missing
+ if((word & mask) != mask) return XH_ERRNO_NOTFND;
+
+ //ignore STN_UNDEF
+ uint32_t i = self->bucket[hash % self->bucket_cnt];
+ if(i < self->symoffset) return XH_ERRNO_NOTFND;
+
+ //loop through the chain
+ while(1)
+ {
+ const char *symname = self->strtab + self->symtab[i].st_name;
+ const uint32_t symhash = self->chain[i - self->symoffset];
+
+ if((hash | (uint32_t)1) == (symhash | (uint32_t)1) && 0 == strcmp(symbol, symname))
+ {
+ *symidx = i;
+ XH_LOG_INFO("found %s at symidx: %u (GNU_HASH DEF)\n", symbol, *symidx);
+ return 0;
+ }
+
+ //chain ends with an element with the lowest bit set to 1
+ if(symhash & (uint32_t)1) break;
+
+ i++;
+ }
+
+ return XH_ERRNO_NOTFND;
+}
+
+static int xh_elf_gnu_hash_lookup_undef(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ uint32_t i;
+
+ for(i = 0; i < self->symoffset; i++)
+ {
+ const char *symname = self->strtab + self->symtab[i].st_name;
+ if(0 == strcmp(symname, symbol))
+ {
+ *symidx = i;
+ XH_LOG_INFO("found %s at symidx: %u (GNU_HASH UNDEF)\n", symbol, *symidx);
+ return 0;
+ }
+ }
+ return XH_ERRNO_NOTFND;
+}
+
+static int xh_elf_gnu_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ if(0 == xh_elf_gnu_hash_lookup_def(self, symbol, symidx)) return 0;
+ if(0 == xh_elf_gnu_hash_lookup_undef(self, symbol, symidx)) return 0;
+ return XH_ERRNO_NOTFND;
+}
+
+static int xh_elf_find_symidx_by_name(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ if(self->is_use_gnu_hash)
+ return xh_elf_gnu_hash_lookup(self, symbol, symidx);
+ else
+ return xh_elf_hash_lookup(self, symbol, symidx);
+}
+
+static int xh_elf_replace_function(xh_elf_t *self, const char *symbol, ElfW(Addr) addr, void *new_func, void **old_func)
+{
+ void *old_addr;
+ unsigned int old_prot = 0;
+ unsigned int need_prot = PROT_READ | PROT_WRITE;
+ int r;
+
+ //already replaced?
+ //here we assume that we always have read permission, is this a problem?
+ if(*(void **)addr == new_func) return 0;
+
+ //get old prot
+ if(0 != (r = xh_util_get_addr_protect(addr, self->pathname, &old_prot)))
+ {
+ XH_LOG_ERROR("get addr prot failed. ret: %d", r);
+ return r;
+ }
+
+ if(old_prot != need_prot)
+ {
+ //set new prot
+ if(0 != (r = xh_util_set_addr_protect(addr, need_prot)))
+ {
+ XH_LOG_ERROR("set addr prot failed. ret: %d", r);
+ return r;
+ }
+ }
+
+ //save old func
+ old_addr = *(void **)addr;
+ if(NULL != old_func) *old_func = old_addr;
+
+ //replace func
+ *(void **)addr = new_func; //segmentation fault sometimes
+
+ if(old_prot != need_prot)
+ {
+ //restore the old prot
+ if(0 != (r = xh_util_set_addr_protect(addr, old_prot)))
+ {
+ XH_LOG_WARN("restore addr prot failed. ret: %d", r);
+ }
+ }
+
+ //clear cache
+ xh_util_flush_instruction_cache(addr);
+
+ XH_LOG_INFO("XH_HK_OK %p: %p -> %p %s %s\n", (void *)addr, old_addr, new_func, symbol, self->pathname);
+ return 0;
+}
+
+static int xh_elf_check(xh_elf_t *self)
+{
+ if(0 == self->base_addr)
+ {
+ XH_LOG_ERROR("base_addr == 0\n");
+ return 1;
+ }
+ if(0 == self->bias_addr)
+ {
+ XH_LOG_ERROR("bias_addr == 0\n");
+ return 1;
+ }
+ if(NULL == self->ehdr)
+ {
+ XH_LOG_ERROR("ehdr == NULL\n");
+ return 1;
+ }
+ if(NULL == self->phdr)
+ {
+ XH_LOG_ERROR("phdr == NULL\n");
+ return 1;
+ }
+ if(NULL == self->strtab)
+ {
+ XH_LOG_ERROR("strtab == NULL\n");
+ return 1;
+ }
+ if(NULL == self->symtab)
+ {
+ XH_LOG_ERROR("symtab == NULL\n");
+ return 1;
+ }
+ if(NULL == self->bucket)
+ {
+ XH_LOG_ERROR("bucket == NULL\n");
+ return 1;
+ }
+ if(NULL == self->chain)
+ {
+ XH_LOG_ERROR("chain == NULL\n");
+ return 1;
+ }
+ if(1 == self->is_use_gnu_hash && NULL == self->bloom)
+ {
+ XH_LOG_ERROR("bloom == NULL\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+#if XH_ELF_DEBUG
+
+static void xh_elf_dump_elfheader(xh_elf_t *self)
+{
+ static char alpha_tab[17] = "0123456789ABCDEF";
+ int i;
+ uint8_t ch;
+ char buff[EI_NIDENT * 3 + 1];
+
+ for(i = 0; i < EI_NIDENT; i++)
+ {
+ ch = self->ehdr->e_ident[i];
+ buff[i * 3 + 0] = alpha_tab[(int)((ch >> 4) & 0x0F)];
+ buff[i * 3 + 1] = alpha_tab[(int)(ch & 0x0F)];
+ buff[i * 3 + 2] = ' ';
+ }
+ buff[EI_NIDENT * 3] = '\0';
+
+ XH_LOG_DEBUG("Elf Header:\n");
+ XH_LOG_DEBUG(" Magic: %s\n", buff);
+ XH_LOG_DEBUG(" Class: %#x\n", self->ehdr->e_ident[EI_CLASS]);
+ XH_LOG_DEBUG(" Data: %#x\n", self->ehdr->e_ident[EI_DATA]);
+ XH_LOG_DEBUG(" Version: %#x\n", self->ehdr->e_ident[EI_VERSION]);
+ XH_LOG_DEBUG(" OS/ABI: %#x\n", self->ehdr->e_ident[EI_OSABI]);
+ XH_LOG_DEBUG(" ABI Version: %#x\n", self->ehdr->e_ident[EI_ABIVERSION]);
+ XH_LOG_DEBUG(" Type: %#x\n", self->ehdr->e_type);
+ XH_LOG_DEBUG(" Machine: %#x\n", self->ehdr->e_machine);
+ XH_LOG_DEBUG(" Version: %#x\n", self->ehdr->e_version);
+ XH_LOG_DEBUG(" Entry point address: %"XH_UTIL_FMT_X"\n", self->ehdr->e_entry);
+ XH_LOG_DEBUG(" Start of program headers: %"XH_UTIL_FMT_X" (bytes into file)\n", self->ehdr->e_phoff);
+ XH_LOG_DEBUG(" Start of section headers: %"XH_UTIL_FMT_X" (bytes into file)\n", self->ehdr->e_shoff);
+ XH_LOG_DEBUG(" Flags: %#x\n", self->ehdr->e_flags);
+ XH_LOG_DEBUG(" Size of this header: %u (bytes)\n", self->ehdr->e_ehsize);
+ XH_LOG_DEBUG(" Size of program headers: %u (bytes)\n", self->ehdr->e_phentsize);
+ XH_LOG_DEBUG(" Number of program headers: %u\n", self->ehdr->e_phnum);
+ XH_LOG_DEBUG(" Size of section headers: %u (bytes)\n", self->ehdr->e_shentsize);
+ XH_LOG_DEBUG(" Number of section headers: %u\n", self->ehdr->e_shnum);
+ XH_LOG_DEBUG(" Section header string table index: %u\n", self->ehdr->e_shstrndx);
+}
+
+static void xh_elf_dump_programheader(xh_elf_t *self)
+{
+ ElfW(Phdr) *phdr = self->phdr;
+ size_t i;
+
+ XH_LOG_DEBUG("Program Headers:\n");
+ XH_LOG_DEBUG(" %-8s " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-8s " \
+ "%-s\n",
+ "Type",
+ "Offset",
+ "VirtAddr",
+ "PhysAddr",
+ "FileSiz",
+ "MemSiz",
+ "Flg",
+ "Align");
+ for(i = 0; i < self->ehdr->e_phnum; i++, phdr++)
+ {
+ XH_LOG_DEBUG(" %-8x " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%-8x " \
+ "%"XH_UTIL_FMT_X"\n",
+ phdr->p_type,
+ phdr->p_offset,
+ phdr->p_vaddr,
+ phdr->p_paddr,
+ phdr->p_filesz,
+ phdr->p_memsz,
+ phdr->p_flags,
+ phdr->p_align);
+ }
+}
+
+static void xh_elf_dump_dynamic(xh_elf_t *self)
+{
+ ElfW(Dyn) *dyn = self->dyn;
+ size_t dyn_cnt = (self->dyn_sz / sizeof(ElfW(Dyn)));
+ size_t i;
+
+ XH_LOG_DEBUG("Dynamic section contains %zu entries:\n", dyn_cnt);
+ XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_S" " \
+ "%s\n",
+ "Tag",
+ "Val");
+ for(i = 0; i < dyn_cnt; i++, dyn++)
+ {
+ XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_X" " \
+ "%-"XH_UTIL_FMT_X"\n",
+ dyn->d_tag,
+ dyn->d_un.d_val);
+ }
+}
+
+static void xh_elf_dump_rel(xh_elf_t *self, const char *type, ElfW(Addr) rel_addr, ElfW(Word) rel_sz)
+{
+ ElfW(Rela) *rela;
+ ElfW(Rel) *rel;
+ ElfW(Word) cnt;
+ ElfW(Word) i;
+ ElfW(Sym) *sym;
+
+ if(self->is_use_rela)
+ {
+ rela = (ElfW(Rela) *)(rel_addr);
+ cnt = rel_sz / sizeof(ElfW(Rela));
+ }
+ else
+ {
+ rel = (ElfW(Rel) *)(rel_addr);
+ cnt = rel_sz / sizeof(ElfW(Rel));
+ }
+
+ XH_LOG_DEBUG("Relocation section '.rel%s%s' contains %u entries:\n",
+ (self->is_use_rela ? "a" : ""), type, cnt);
+ XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-8s " \
+ "%-8s " \
+ "%-8s " \
+ "%s\n",
+ "Offset",
+ "Info",
+ "Type",
+ "Sym.Idx",
+ "Sym.Val",
+ "Sym.Name");
+ const char *fmt = " %."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%.8x " \
+ "%.8u " \
+ "%.8x " \
+ "%s\n";
+ for(i = 0; i < cnt; i++)
+ {
+ if(self->is_use_rela)
+ {
+ sym = &(self->symtab[XH_ELF_R_SYM(rela[i].r_info)]);
+ XH_LOG_DEBUG(fmt,
+ rela[i].r_offset,
+ rela[i].r_info,
+ XH_ELF_R_TYPE(rela[i].r_info),
+ XH_ELF_R_SYM(rela[i].r_info),
+ sym->st_value,
+ self->strtab + sym->st_name);
+ }
+ else
+ {
+ sym = &(self->symtab[XH_ELF_R_SYM(rel[i].r_info)]);
+ XH_LOG_DEBUG(fmt,
+ rel[i].r_offset,
+ rel[i].r_info,
+ XH_ELF_R_TYPE(rel[i].r_info),
+ XH_ELF_R_SYM(rel[i].r_info),
+ sym->st_value,
+ self->strtab + sym->st_name);
+ }
+ }
+}
+
+static void xh_elf_dump_symtab(xh_elf_t *self)
+{
+ if(self->is_use_gnu_hash) return;
+
+ ElfW(Word) symtab_cnt = self->chain_cnt;
+ ElfW(Word) i;
+
+ XH_LOG_DEBUG("Symbol table '.dynsym' contains %u entries:\n", symtab_cnt);
+ XH_LOG_DEBUG(" %-8s " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%s\n",
+ "Idx",
+ "Value",
+ "Name");
+ for(i = 0; i < symtab_cnt; i++)
+ {
+ XH_LOG_DEBUG(" %-8u " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%s\n",
+ i,
+ self->symtab[i].st_value,
+ self->strtab + self->symtab[i].st_name);
+ }
+}
+
+static void xh_elf_dump(xh_elf_t *self)
+{
+ if(xh_log_priority < ANDROID_LOG_DEBUG) return;
+
+ XH_LOG_DEBUG("Elf Pathname: %s\n", self->pathname);
+ XH_LOG_DEBUG("Elf bias addr: %p\n", (void *)self->bias_addr);
+ xh_elf_dump_elfheader(self);
+ xh_elf_dump_programheader(self);
+ xh_elf_dump_dynamic(self);
+ xh_elf_dump_rel(self, ".plt", self->relplt, self->relplt_sz);
+ xh_elf_dump_rel(self, ".dyn", self->reldyn, self->reldyn_sz);
+ xh_elf_dump_symtab(self);
+}
+
+#endif
+
+int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname)
+{
+ if(0 == base_addr || NULL == pathname) return XH_ERRNO_INVAL;
+
+ //always reset
+ memset(self, 0, sizeof(xh_elf_t));
+
+ self->pathname = pathname;
+ self->base_addr = (ElfW(Addr))base_addr;
+ self->ehdr = (ElfW(Ehdr) *)base_addr;
+ self->phdr = (ElfW(Phdr) *)(base_addr + self->ehdr->e_phoff); //segmentation fault sometimes
+
+ //find the first load-segment with offset 0
+ ElfW(Phdr) *phdr0 = xh_elf_get_first_segment_by_type_offset(self, PT_LOAD, 0);
+ if(NULL == phdr0)
+ {
+ XH_LOG_ERROR("Can NOT found the first load segment. %s", pathname);
+ return XH_ERRNO_FORMAT;
+ }
+
+#if XH_ELF_DEBUG
+ if(0 != phdr0->p_vaddr)
+ XH_LOG_DEBUG("first load-segment vaddr NOT 0 (vaddr: %p). %s",
+ (void *)(phdr0->p_vaddr), pathname);
+#endif
+
+ //save load bias addr
+ if(self->base_addr < phdr0->p_vaddr) return XH_ERRNO_FORMAT;
+ self->bias_addr = self->base_addr - phdr0->p_vaddr;
+
+ //find dynamic-segment
+ ElfW(Phdr) *dhdr = xh_elf_get_first_segment_by_type(self, PT_DYNAMIC);
+ if(NULL == dhdr)
+ {
+ XH_LOG_ERROR("Can NOT found dynamic segment. %s", pathname);
+ return XH_ERRNO_FORMAT;
+ }
+
+ //parse dynamic-segment
+ self->dyn = (ElfW(Dyn) *)(self->bias_addr + dhdr->p_vaddr);
+ self->dyn_sz = dhdr->p_memsz;
+ ElfW(Dyn) *dyn = self->dyn;
+ ElfW(Dyn) *dyn_end = self->dyn + (self->dyn_sz / sizeof(ElfW(Dyn)));
+ uint32_t *raw;
+ for(; dyn < dyn_end; dyn++)
+ {
+ switch(dyn->d_tag) //segmentation fault sometimes
+ {
+ case DT_NULL:
+ //the end of the dynamic-section
+ dyn = dyn_end;
+ break;
+ case DT_STRTAB:
+ {
+ self->strtab = (const char *)(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->strtab) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_SYMTAB:
+ {
+ self->symtab = (ElfW(Sym) *)(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->symtab) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_PLTREL:
+ //use rel or rela?
+ self->is_use_rela = (dyn->d_un.d_val == DT_RELA ? 1 : 0);
+ break;
+ case DT_JMPREL:
+ {
+ self->relplt = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->relplt) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_PLTRELSZ:
+ self->relplt_sz = dyn->d_un.d_val;
+ break;
+ case DT_REL:
+ case DT_RELA:
+ {
+ self->reldyn = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->reldyn) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_RELSZ:
+ case DT_RELASZ:
+ self->reldyn_sz = dyn->d_un.d_val;
+ break;
+ case DT_ANDROID_REL:
+ case DT_ANDROID_RELA:
+ {
+ self->relandroid = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->relandroid) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_ANDROID_RELSZ:
+ case DT_ANDROID_RELASZ:
+ self->relandroid_sz = dyn->d_un.d_val;
+ break;
+ case DT_HASH:
+ {
+ //ignore DT_HASH when ELF contains DT_GNU_HASH hash table
+ if(1 == self->is_use_gnu_hash) continue;
+
+ raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT;
+ self->bucket_cnt = raw[0];
+ self->chain_cnt = raw[1];
+ self->bucket = &raw[2];
+ self->chain = &(self->bucket[self->bucket_cnt]);
+ break;
+ }
+ case DT_GNU_HASH:
+ {
+ raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT;
+ self->bucket_cnt = raw[0];
+ self->symoffset = raw[1];
+ self->bloom_sz = raw[2];
+ self->bloom_shift = raw[3];
+ self->bloom = (ElfW(Addr) *)(&raw[4]);
+ self->bucket = (uint32_t *)(&(self->bloom[self->bloom_sz]));
+ self->chain = (uint32_t *)(&(self->bucket[self->bucket_cnt]));
+ self->is_use_gnu_hash = 1;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ //check android rel/rela
+ if(0 != self->relandroid)
+ {
+ const char *rel = (const char *)self->relandroid;
+ if(self->relandroid_sz < 4 ||
+ rel[0] != 'A' ||
+ rel[1] != 'P' ||
+ rel[2] != 'S' ||
+ rel[3] != '2')
+ {
+ XH_LOG_ERROR("android rel/rela format error\n");
+ return XH_ERRNO_FORMAT;
+ }
+
+ self->relandroid += 4;
+ self->relandroid_sz -= 4;
+ }
+
+ //check elf info
+ if(0 != xh_elf_check(self))
+ {
+ XH_LOG_ERROR("elf init check failed. %s", pathname);
+ return XH_ERRNO_FORMAT;
+ }
+
+#if XH_ELF_DEBUG
+ xh_elf_dump(self);
+#endif
+
+ XH_LOG_INFO("init OK: %s (%s %s PLT:%u DYN:%u ANDROID:%u)\n", self->pathname,
+ self->is_use_rela ? "RELA" : "REL",
+ self->is_use_gnu_hash ? "GNU_HASH" : "ELF_HASH",
+ self->relplt_sz, self->reldyn_sz, self->relandroid_sz);
+
+ return 0;
+}
+
+static int xh_elf_find_and_replace_func(xh_elf_t *self, const char *section,
+ int is_plt, const char *symbol,
+ void *new_func, void **old_func,
+ uint32_t symidx, void *rel_common,
+ int *found)
+{
+ ElfW(Rela) *rela;
+ ElfW(Rel) *rel;
+ ElfW(Addr) r_offset;
+ size_t r_info;
+ size_t r_sym;
+ size_t r_type;
+ ElfW(Addr) addr;
+ int r;
+
+ if(NULL != found) *found = 0;
+
+ if(self->is_use_rela)
+ {
+ rela = (ElfW(Rela) *)rel_common;
+ r_info = rela->r_info;
+ r_offset = rela->r_offset;
+ }
+ else
+ {
+ rel = (ElfW(Rel) *)rel_common;
+ r_info = rel->r_info;
+ r_offset = rel->r_offset;
+ }
+
+ //check sym
+ r_sym = XH_ELF_R_SYM(r_info);
+ if(r_sym != symidx) return 0;
+
+ //check type
+ r_type = XH_ELF_R_TYPE(r_info);
+ if(is_plt && r_type != XH_ELF_R_GENERIC_JUMP_SLOT) return 0;
+ if(!is_plt && (r_type != XH_ELF_R_GENERIC_GLOB_DAT && r_type != XH_ELF_R_GENERIC_ABS)) return 0;
+
+ //we found it
+ XH_LOG_INFO("found %s at %s offset: %p\n", symbol, section, (void *)r_offset);
+ if(NULL != found) *found = 1;
+
+ //do replace
+ addr = self->bias_addr + r_offset;
+ if(addr < self->base_addr) return XH_ERRNO_FORMAT;
+ if(0 != (r = xh_elf_replace_function(self, symbol, addr, new_func, old_func)))
+ {
+ XH_LOG_ERROR("replace function failed: %s at %s\n", symbol, section);
+ return r;
+ }
+
+ return 0;
+}
+
+int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func)
+{
+ uint32_t symidx;
+ void *rel_common;
+ xh_elf_plain_reloc_iterator_t plain_iter;
+ xh_elf_packed_reloc_iterator_t packed_iter;
+ int found;
+ int r;
+
+ if(NULL == self->pathname)
+ {
+ XH_LOG_ERROR("not inited\n");
+ return XH_ERRNO_ELFINIT; //not inited?
+ }
+
+ if(NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;
+
+ XH_LOG_INFO("hooking %s in %s\n", symbol, self->pathname);
+
+ //find symbol index by symbol name
+ if(0 != (r = xh_elf_find_symidx_by_name(self, symbol, &symidx))) return 0;
+
+ //replace for .rel(a).plt
+ if(0 != self->relplt)
+ {
+ xh_elf_plain_reloc_iterator_init(&plain_iter, self->relplt, self->relplt_sz, self->is_use_rela);
+ while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))
+ {
+ if(0 != (r = xh_elf_find_and_replace_func(self,
+ (self->is_use_rela ? ".rela.plt" : ".rel.plt"), 1,
+ symbol, new_func, old_func,
+ symidx, rel_common, &found))) return r;
+ if(found) break;
+ }
+ }
+
+ //replace for .rel(a).dyn
+ if(0 != self->reldyn)
+ {
+ xh_elf_plain_reloc_iterator_init(&plain_iter, self->reldyn, self->reldyn_sz, self->is_use_rela);
+ while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))
+ {
+ if(0 != (r = xh_elf_find_and_replace_func(self,
+ (self->is_use_rela ? ".rela.dyn" : ".rel.dyn"), 0,
+ symbol, new_func, old_func,
+ symidx, rel_common, NULL))) return r;
+ }
+ }
+
+ //replace for .rel(a).android
+ if(0 != self->relandroid)
+ {
+ xh_elf_packed_reloc_iterator_init(&packed_iter, self->relandroid, self->relandroid_sz, self->is_use_rela);
+ while(NULL != (rel_common = xh_elf_packed_reloc_iterator_next(&packed_iter)))
+ {
+ if(0 != (r = xh_elf_find_and_replace_func(self,
+ (self->is_use_rela ? ".rela.android" : ".rel.android"), 0,
+ symbol, new_func, old_func,
+ symidx, rel_common, NULL))) return r;
+ }
+ }
+
+ return 0;
+}
diff --git a/killer/src/main/c/xh_elf.h b/killer/src/main/c/xh_elf.h
new file mode 100755
index 0000000..1697dc4
--- /dev/null
+++ b/killer/src/main/c/xh_elf.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_ELF_H
+#define XH_ELF_H 1
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ const char *pathname;
+
+ ElfW(Addr) base_addr;
+ ElfW(Addr) bias_addr;
+
+ ElfW(Ehdr) *ehdr;
+ ElfW(Phdr) *phdr;
+
+ ElfW(Dyn) *dyn; //.dynamic
+ ElfW(Word) dyn_sz;
+
+ const char *strtab; //.dynstr (string-table)
+ ElfW(Sym) *symtab; //.dynsym (symbol-index to string-table's offset)
+
+ ElfW(Addr) relplt; //.rel.plt or .rela.plt
+ ElfW(Word) relplt_sz;
+
+ ElfW(Addr) reldyn; //.rel.dyn or .rela.dyn
+ ElfW(Word) reldyn_sz;
+
+ ElfW(Addr) relandroid; //android compressed rel or rela
+ ElfW(Word) relandroid_sz;
+
+ //for ELF hash
+ uint32_t *bucket;
+ uint32_t bucket_cnt;
+ uint32_t *chain;
+ uint32_t chain_cnt; //invalid for GNU hash
+
+ //append for GNU hash
+ uint32_t symoffset;
+ ElfW(Addr) *bloom;
+ uint32_t bloom_sz;
+ uint32_t bloom_shift;
+
+ int is_use_rela;
+ int is_use_gnu_hash;
+} xh_elf_t;
+
+int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname);
+int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func);
+
+int xh_elf_check_elfheader(uintptr_t base_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/killer/src/main/c/xh_errno.h b/killer/src/main/c/xh_errno.h
new file mode 100755
index 0000000..e628cd7
--- /dev/null
+++ b/killer/src/main/c/xh_errno.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_ERRNO_H
+#define XH_ERRNO_H 1
+
+#define XH_ERRNO_UNKNOWN 1001
+#define XH_ERRNO_INVAL 1002
+#define XH_ERRNO_NOMEM 1003
+#define XH_ERRNO_REPEAT 1004
+#define XH_ERRNO_NOTFND 1005
+#define XH_ERRNO_BADMAPS 1006
+#define XH_ERRNO_FORMAT 1007
+#define XH_ERRNO_ELFINIT 1008
+#define XH_ERRNO_SEGVERR 1009
+
+#endif
diff --git a/killer/src/main/c/xh_log.c b/killer/src/main/c/xh_log.c
new file mode 100755
index 0000000..6cba947
--- /dev/null
+++ b/killer/src/main/c/xh_log.c
@@ -0,0 +1,27 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include
+#include "xh_log.h"
+
+android_LogPriority xh_log_priority = ANDROID_LOG_WARN;
diff --git a/killer/src/main/c/xh_log.h b/killer/src/main/c/xh_log.h
new file mode 100755
index 0000000..e108c4b
--- /dev/null
+++ b/killer/src/main/c/xh_log.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_LOG_H
+#define XH_LOG_H 1
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern android_LogPriority xh_log_priority;
+
+#define XH_LOG_TAG "xhook"
+#define XH_LOG_DEBUG(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_DEBUG) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_INFO(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_INFO) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_WARN(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_WARN) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_ERROR(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_ERROR) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/killer/src/main/c/xh_util.c b/killer/src/main/c/xh_util.c
new file mode 100755
index 0000000..0e2dca2
--- /dev/null
+++ b/killer/src/main/c/xh_util.c
@@ -0,0 +1,121 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "xh_util.h"
+#include "xh_errno.h"
+#include "xh_log.h"
+
+#define PAGE_START(addr) ((addr) & PAGE_MASK)
+#define PAGE_END(addr) (PAGE_START(addr + sizeof(uintptr_t) - 1) + PAGE_SIZE)
+#define PAGE_COVER(addr) (PAGE_END(addr) - PAGE_START(addr))
+
+int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot)
+{
+ uintptr_t start_addr = addr;
+ uintptr_t end_addr = addr + len;
+ FILE *fp;
+ char line[512];
+ uintptr_t start, end;
+ char perm[5];
+ int load0 = 1;
+ int found_all = 0;
+
+ *prot = 0;
+
+ if(NULL == (fp = fopen("/proc/self/maps", "r"))) return XH_ERRNO_BADMAPS;
+
+ while(fgets(line, sizeof(line), fp))
+ {
+ if(NULL != pathname)
+ if(NULL == strstr(line, pathname)) continue;
+
+ if(sscanf(line, "%"PRIxPTR"-%"PRIxPTR" %4s ", &start, &end, perm) != 3) continue;
+
+ if(perm[3] != 'p') continue;
+
+ if(start_addr >= start && start_addr < end)
+ {
+ if(load0)
+ {
+ //first load segment
+ if(perm[0] == 'r') *prot |= PROT_READ;
+ if(perm[1] == 'w') *prot |= PROT_WRITE;
+ if(perm[2] == 'x') *prot |= PROT_EXEC;
+ load0 = 0;
+ }
+ else
+ {
+ //others
+ if(perm[0] != 'r') *prot &= ~PROT_READ;
+ if(perm[1] != 'w') *prot &= ~PROT_WRITE;
+ if(perm[2] != 'x') *prot &= ~PROT_EXEC;
+ }
+
+ if(end_addr <= end)
+ {
+ found_all = 1;
+ break; //finished
+ }
+ else
+ {
+ start_addr = end; //try to find the next load segment
+ }
+ }
+ }
+
+ fclose(fp);
+
+ if(!found_all) return XH_ERRNO_SEGVERR;
+
+ return 0;
+}
+
+int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot)
+{
+ return xh_util_get_mem_protect(addr, sizeof(addr), pathname, prot);
+}
+
+int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot)
+{
+ if(0 != mprotect((void *)PAGE_START(addr), PAGE_COVER(addr), (int)prot))
+ return 0 == errno ? XH_ERRNO_UNKNOWN : errno;
+
+ return 0;
+}
+
+void xh_util_flush_instruction_cache(uintptr_t addr)
+{
+ __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr));
+}
diff --git a/killer/src/main/c/xh_util.h b/killer/src/main/c/xh_util.h
new file mode 100755
index 0000000..b57f8dc
--- /dev/null
+++ b/killer/src/main/c/xh_util.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_UTILS_H
+#define XH_UTILS_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__LP64__)
+#define XH_UTIL_FMT_LEN "16"
+#define XH_UTIL_FMT_X "llx"
+#else
+#define XH_UTIL_FMT_LEN "8"
+#define XH_UTIL_FMT_X "x"
+#endif
+
+#define XH_UTIL_FMT_FIXED_X XH_UTIL_FMT_LEN XH_UTIL_FMT_X
+#define XH_UTIL_FMT_FIXED_S XH_UTIL_FMT_LEN "s"
+
+int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot);
+int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot);
+int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot);
+void xh_util_flush_instruction_cache(uintptr_t addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/killer/src/main/c/xh_version.c b/killer/src/main/c/xh_version.c
new file mode 100755
index 0000000..b9ceae0
--- /dev/null
+++ b/killer/src/main/c/xh_version.c
@@ -0,0 +1,66 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include "xh_version.h"
+
+#define XH_VERSION_MAJOR 1
+#define XH_VERSION_MINOR 2
+#define XH_VERSION_EXTRA 0
+
+#define XH_VERSION ((XH_VERSION_MAJOR << 16) | (XH_VERSION_MINOR << 8) | (XH_VERSION_EXTRA))
+
+#define XH_VERSION_TO_STR_HELPER(x) #x
+#define XH_VERSION_TO_STR(x) XH_VERSION_TO_STR_HELPER(x)
+
+#define XH_VERSION_STR XH_VERSION_TO_STR(XH_VERSION_MAJOR) "." \
+ XH_VERSION_TO_STR(XH_VERSION_MINOR) "." \
+ XH_VERSION_TO_STR(XH_VERSION_EXTRA)
+
+#if defined(__arm__)
+#define XH_VERSION_ARCH "arm"
+#elif defined(__aarch64__)
+#define XH_VERSION_ARCH "aarch64"
+#elif defined(__i386__)
+#define XH_VERSION_ARCH "x86"
+#elif defined(__x86_64__)
+#define XH_VERSION_ARCH "x86_64"
+#else
+#define XH_VERSION_ARCH "unknown"
+#endif
+
+#define XH_VERSION_STR_FULL "libxhook "XH_VERSION_STR" ("XH_VERSION_ARCH")"
+
+unsigned int xh_version()
+{
+ return XH_VERSION;
+}
+
+const char *xh_version_str()
+{
+ return XH_VERSION_STR;
+}
+
+const char *xh_version_str_full()
+{
+ return XH_VERSION_STR_FULL;
+}
diff --git a/killer/src/main/c/xh_version.h b/killer/src/main/c/xh_version.h
new file mode 100755
index 0000000..b70b4f2
--- /dev/null
+++ b/killer/src/main/c/xh_version.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_VERSION_H
+#define XH_VERSION_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+unsigned int xh_version();
+
+const char *xh_version_str();
+
+const char *xh_version_str_full();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/killer/src/main/c/xhook.c b/killer/src/main/c/xhook.c
new file mode 100755
index 0000000..49dfc59
--- /dev/null
+++ b/killer/src/main/c/xhook.c
@@ -0,0 +1,56 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include "xh_core.h"
+#include "xhook.h"
+
+int xhook_register(const char *pathname_regex_str, const char *symbol,
+ void *new_func, void **old_func)
+{
+ return xh_core_register(pathname_regex_str, symbol, new_func, old_func);
+}
+
+int xhook_ignore(const char *pathname_regex_str, const char *symbol)
+{
+ return xh_core_ignore(pathname_regex_str, symbol);
+}
+
+int xhook_refresh(int async)
+{
+ return xh_core_refresh(async);
+}
+
+void xhook_clear()
+{
+ return xh_core_clear();
+}
+
+void xhook_enable_debug(int flag)
+{
+ return xh_core_enable_debug(flag);
+}
+
+void xhook_enable_sigsegv_protection(int flag)
+{
+ return xh_core_enable_sigsegv_protection(flag);
+}
diff --git a/killer/src/main/c/xhook.h b/killer/src/main/c/xhook.h
new file mode 100755
index 0000000..93dd5b4
--- /dev/null
+++ b/killer/src/main/c/xhook.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XHOOK_H
+#define XHOOK_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define XHOOK_EXPORT __attribute__((visibility("default")))
+
+int xhook_register(const char *pathname_regex_str, const char *symbol,
+ void *new_func, void **old_func) XHOOK_EXPORT;
+
+int xhook_ignore(const char *pathname_regex_str, const char *symbol) XHOOK_EXPORT;
+
+int xhook_refresh(int async) XHOOK_EXPORT;
+
+void xhook_clear() XHOOK_EXPORT;
+
+void xhook_enable_debug(int flag) XHOOK_EXPORT;
+
+void xhook_enable_sigsegv_protection(int flag) XHOOK_EXPORT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/killer/src/main/java/bin/mt/signature/KillerApplication.java b/killer/src/main/java/bin/mt/signature/KillerApplication.java
new file mode 100644
index 0000000..187d493
--- /dev/null
+++ b/killer/src/main/java/bin/mt/signature/KillerApplication.java
@@ -0,0 +1,197 @@
+package bin.mt.signature;
+
+import android.app.Application;
+import android.content.pm.PackageInfo;
+import android.content.pm.Signature;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Base64;
+
+import org.lsposed.hiddenapibypass.HiddenApiBypass;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class KillerApplication extends Application {
+ public static final String URL = "https://github.com/L-JINBIN/ApkSignatureKillerEx";
+
+ static {
+ String packageName = "bin.mt.signature";
+ String signatureData = "MIICwzCCAaugAwIBAgIERUjRgzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDEwdBbmRyb2lkMB4X\n" +
+ "DTIyMTIyNDE0NDkzMloXDTQ3MTIxODE0NDkzMlowEjEQMA4GA1UEAxMHQW5kcm9pZDCCASIwDQYJ\n" +
+ "KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKjVjd0eL4NPJW4uBR40hDkHtwdTQ7INP3hqgIs7U/kM\n" +
+ "gck2MtNIFSPYJVDZKwuLWgZLAKSDu9607indxUWftfTwJ9ynUfzoVq39+RAkRqe/XnL5WdLM0v5H\n" +
+ "CRtJW/nPBevunhJdoelCJJY1MsAl+WJCpSdfkkkeC0uXYeVYzCVwietIMfHEJOdEvjdXna0mdfuR\n" +
+ "1NqA85K8RGj9FLEdOKy0ZnMQbHzCp1/FwJSXpOqAuoKsttrmAji7FfsqXVRhk+dTBBGybCzVtaDH\n" +
+ "sIGyKzdsF2mKUPL3f0Q8XLKbkHRLmHGdVQlysIrrH7kn6Bx82cZTuYdPBUkrBO6w2NdMa+UCAwEA\n" +
+ "AaMhMB8wHQYDVR0OBBYEFNL0ebiSTntg/5Hcar3/MEUdlYRHMA0GCSqGSIb3DQEBCwUAA4IBAQBg\n" +
+ "v60JCBcJT+unHuVJge2wqEWjoUXV4JJG0Vn6kURbfiiC2rAtFOq6CFk+50HXyg2ZahosQ4ZPf8oT\n" +
+ "yG1/+JQaw9QUvB4TtwwdCr9i9IvAjjAFT6ariY0bOJNJvTjsmHJMptjNFQt4DPdveuknQv3Ztemb\n" +
+ "5BaxlpTegSZzL1ReOpKIygWf7qTqDnTtZsipt/OMttkn/dnhA9iiGJ5Jy+HLXQOc7+QgTYGPyAX5\n" +
+ "2IcWd9l5OrWShpflwsNHsAAU5MMAO/sWR/F/7zxKa50Ve67ta/7rUOkkcD3D0taUBsUeAo6n6rSs\n" +
+ "9Rk4tPEQRm59UJoof9cho7PxsMcTGb9UiNuJ\n";
+ killPM(packageName, signatureData);
+ killOpen(packageName);
+ }
+
+ private static void killPM(String packageName, String signatureData) {
+ Signature fakeSignature = new Signature(Base64.decode(signatureData, Base64.DEFAULT));
+ Parcelable.Creator originalCreator = PackageInfo.CREATOR;
+ Parcelable.Creator creator = new Parcelable.Creator() {
+ @Override
+ public PackageInfo createFromParcel(Parcel source) {
+ PackageInfo packageInfo = originalCreator.createFromParcel(source);
+ if (packageInfo.packageName.equals(packageName)) {
+ if (packageInfo.signatures != null && packageInfo.signatures.length > 0) {
+ packageInfo.signatures[0] = fakeSignature;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ if (packageInfo.signingInfo != null) {
+ Signature[] signaturesArray = packageInfo.signingInfo.getApkContentsSigners();
+ if (signaturesArray != null && signaturesArray.length > 0) {
+ signaturesArray[0] = fakeSignature;
+ }
+ }
+ }
+ }
+ return packageInfo;
+ }
+
+ @Override
+ public PackageInfo[] newArray(int size) {
+ return originalCreator.newArray(size);
+ }
+ };
+ try {
+ findField(PackageInfo.class, "CREATOR").set(null, creator);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ HiddenApiBypass.addHiddenApiExemptions("Landroid/os/Parcel;");
+ }
+ try {
+ Map, ?> mCreators = (Map, ?>) findField(Parcel.class, "mCreators").get(null);
+ //noinspection ConstantConditions
+ mCreators.clear();
+ } catch (Throwable ignored) {
+ }
+ try {
+ Map, ?> sPairedCreators = (Map, ?>) findField(Parcel.class, "sPairedCreators").get(null);
+ //noinspection ConstantConditions
+ sPairedCreators.clear();
+ } catch (Throwable ignored) {
+ }
+ }
+
+ private static Field findField(Class> clazz, String fieldName) throws NoSuchFieldException {
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return field;
+ } catch (NoSuchFieldException e) {
+ while (true) {
+ clazz = clazz.getSuperclass();
+ if (clazz == null || clazz.equals(Object.class)) {
+ break;
+ }
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return field;
+ } catch (NoSuchFieldException ignored) {
+ }
+ }
+ throw e;
+ }
+ }
+
+ private static void killOpen(String packageName) {
+ try {
+ System.loadLibrary("SignatureKiller");
+ } catch (Throwable e) {
+ System.err.println("Load SignatureKiller library failed");
+ return;
+ }
+ String apkPath = getApkPath(packageName);
+ if (apkPath == null) {
+ System.err.println("Get apk path failed");
+ return;
+ }
+ File apkFile = new File(apkPath);
+ File repFile = new File("/data/data/" + packageName + "/origin.apk");
+ try (ZipFile zipFile = new ZipFile(apkFile)) {
+ String name = "assets/SignatureKiller/origin.apk";
+ ZipEntry entry = zipFile.getEntry(name);
+ if (entry == null) {
+ System.err.println("Entry not found: " + name);
+ return;
+ }
+ if (!repFile.exists() || repFile.length() != entry.getSize()) {
+ try (InputStream is = zipFile.getInputStream(entry); OutputStream os = new FileOutputStream(repFile)) {
+ byte[] buf = new byte[102400];
+ int len;
+ while ((len = is.read(buf)) != -1) {
+ os.write(buf, 0, len);
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ hookApkPath(apkFile.getAbsolutePath(), repFile.getAbsolutePath());
+ }
+
+ private static String getApkPath(String packageName) {
+ try (BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] arr = line.split("\\s+");
+ String path = arr[arr.length - 1];
+ if (isApkPath(packageName, path)) {
+ return path;
+ }
+ }
+ return null;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static boolean isApkPath(String packageName, String path) {
+ if (!path.startsWith("/") || !path.endsWith(".apk")) {
+ return false;
+ }
+ String[] splitStr = path.substring(1).split("/", 6);
+ int splitCount = splitStr.length;
+ if (splitCount == 4 || splitCount == 5) {
+ if (splitStr[0].equals("data") && splitStr[1].equals("app") && splitStr[splitCount - 1].equals("base.apk")) {
+ return splitStr[splitCount - 2].startsWith(packageName);
+ }
+ if (splitStr[0].equals("mnt") && splitStr[1].equals("asec") && splitStr[splitCount - 1].equals("pkg.apk")) {
+ return splitStr[splitCount - 2].startsWith(packageName);
+ }
+ } else if (splitCount == 3) {
+ if (splitStr[0].equals("data") && splitStr[1].equals("app")) {
+ return splitStr[2].startsWith(packageName);
+ }
+ } else if (splitCount == 6) {
+ if (splitStr[0].equals("mnt") && splitStr[1].equals("expand") && splitStr[3].equals("app") && splitStr[5].equals("base.apk")) {
+ return splitStr[4].endsWith(packageName);
+ }
+ }
+ return false;
+ }
+
+ private static native void hookApkPath(String apkPath, String repPath);
+}
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..740aae5
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,17 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "ApkSignatureKillerEx"
+include ':app'
+include ':killer'