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 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'