├── .gitignore ├── .idea ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── CMakeLists.txt │ └── str-crypt.c │ ├── java │ └── org │ │ └── cf │ │ └── nativeharness │ │ ├── Cryptor.java │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── harness ├── .gitignore ├── Android.mk ├── Makefile ├── decrypt_string.py ├── harness.c ├── server.c ├── server.h ├── vm.c └── vm.h ├── native-harness-target.ipr └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### Android template 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # Intellij 38 | *.iml 39 | .idea/workspace.xml 40 | .idea/tasks.xml 41 | .idea/gradle.xml 42 | .idea/dictionaries 43 | .idea/libraries 44 | 45 | # Keystore files 46 | *.jks 47 | 48 | # External native build folder generated in Android Studio 2.2 and later 49 | .externalNativeBuild 50 | 51 | # Google Services (e.g. APIs or Firebase) 52 | google-services.json 53 | 54 | # Freeline 55 | freeline.py 56 | freeline/ 57 | freeline_project_description.json 58 | ### JetBrains template 59 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 60 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 61 | 62 | # User-specific stuff: 63 | .idea/**/workspace.xml 64 | .idea/**/tasks.xml 65 | .idea/dictionaries 66 | 67 | # Sensitive or high-churn files: 68 | .idea/**/dataSources/ 69 | .idea/**/dataSources.ids 70 | .idea/**/dataSources.xml 71 | .idea/**/dataSources.local.xml 72 | .idea/**/sqlDataSources.xml 73 | .idea/**/dynamic.xml 74 | .idea/**/uiDesigner.xml 75 | 76 | # Gradle: 77 | .idea/**/gradle.xml 78 | .idea/**/libraries 79 | 80 | # Mongo Explorer plugin: 81 | .idea/**/mongoSettings.xml 82 | 83 | ## File-based project format: 84 | *.iws 85 | 86 | ## Plugin-specific files: 87 | 88 | # IntelliJ 89 | /out/ 90 | 91 | # mpeltonen/sbt-idea plugin 92 | .idea_modules/ 93 | 94 | # JIRA plugin 95 | atlassian-ide-plugin.xml 96 | 97 | # Crashlytics plugin (for Android Studio and IntelliJ) 98 | com_crashlytics_export_strings.xml 99 | crashlytics.properties 100 | crashlytics-build.properties 101 | fabric.properties 102 | 103 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Python 2.7.10 virtualenv at ~/repos/apkid/venv interpreter library 18 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 1.7 30 | 31 | 36 | 37 | 38 | 39 | 40 | 41 | Android API 25 Platform 42 | 43 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Native Harness Target 2 | 3 | This is a demonstration app and native library harness. 4 | 5 | **!! NOTE: This project isn't maintained. All efforts should be directed to [native-shim](https://github.com/rednaga/native-shim) !!** 6 | 7 | Original idea inspired from Tim "diff" Strazzere's [native-shim](https://github.com/rednaga/native-shim). 8 | 9 | Everything is explained here: [Calling JNI Functions with Java Object Arguments from the Command Line](https://calebfenton.github.io/2017/04/14/calling_jni_functions_with_java_object_arguments_from_the_command_line/) 10 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '25.0.2' 6 | defaultConfig { 7 | applicationId 'org.cf.nativeharness' 8 | minSdkVersion 19 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | externalNativeBuild { 13 | cmake { 14 | arguments '-DANDROID_TOOLCHAIN=clang' 15 | } 16 | } 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | externalNativeBuild { 25 | cmake { 26 | path "src/main/cpp/CMakeLists.txt" 27 | } 28 | } 29 | productFlavors { 30 | // IntelliJ uses the first one by default. #magic Is this documented anywhere? No idea. 31 | universal { 32 | ndk { 33 | abiFilters 'mips', 'mips64', 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' 34 | } 35 | } 36 | arm7 { 37 | ndk { 38 | abiFilter 'armeabi-v7a' 39 | } 40 | } 41 | arm8 { 42 | ndk { 43 | abiFilters 'arm64-v8a' 44 | } 45 | } 46 | arm { 47 | ndk { 48 | abiFilter 'armeabi' 49 | } 50 | } 51 | x86 { 52 | ndk { 53 | abiFilter 'x86' 54 | } 55 | } 56 | x86_64 { 57 | ndk { 58 | abiFilter 'x86_64' 59 | } 60 | } 61 | mips { 62 | ndk { 63 | abiFilters 'mips', 'mips64' 64 | } 65 | } 66 | } 67 | } 68 | 69 | dependencies { 70 | compile fileTree(dir: 'libs', include: ['*.jar']) 71 | compile 'com.android.support:appcompat-v7:25.2.0' 72 | compile 'com.android.support.constraint:constraint-layout:1.0.1' 73 | } 74 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/gfan/dev/sdk_current/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | add_library(str-crypt SHARED 4 | str-crypt.c) 5 | 6 | target_link_libraries(str-crypt 7 | android 8 | log) 9 | -------------------------------------------------------------------------------- /app/src/main/cpp/str-crypt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | void xor_crypt(char *key, char *str, int str_len); 7 | 8 | jint JNI_OnLoad(JavaVM *vm, void *reserved) 9 | { 10 | return JNI_VERSION_1_4; 11 | } 12 | 13 | JNIEXPORT jstring JNICALL 14 | Java_org_cf_nativeharness_Cryptor_decryptString( JNIEnv *env, 15 | jobject thiz, 16 | jstring encryptedString ) 17 | { 18 | // Explicitly cast to char * to avoid warning: 19 | // initializing 'char *' with an expression of type 'const char *' 20 | // We're going to be changing bytes in xor_crypt 21 | char *str = (char *)(*env)->GetStringUTFChars(env, encryptedString, NULL); 22 | xor_crypt("{$}", str, strlen(str)); 23 | 24 | return (*env)->NewStringUTF(env, str); 25 | } 26 | 27 | void xor_crypt(char *key, char *str, int str_len) 28 | { 29 | int i; 30 | int key_len = strlen(key); 31 | for(i = 0; i < str_len; i++) { 32 | str[i] = str[i] ^ key[i % key_len]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/org/cf/nativeharness/Cryptor.java: -------------------------------------------------------------------------------- 1 | package org.cf.nativeharness; 2 | 3 | public class Cryptor { 4 | 5 | private static Cryptor instance = null; 6 | 7 | public static Cryptor getInstance() { 8 | if (instance == null) { 9 | instance = new Cryptor(); 10 | } 11 | 12 | return instance; 13 | } 14 | 15 | public native String decryptString(String encryptedString); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/org/cf/nativeharness/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.cf.nativeharness; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.widget.TextView; 6 | 7 | public class MainActivity extends AppCompatActivity { 8 | 9 | static { 10 | System.loadLibrary("str-crypt"); 11 | } 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | TextView tv = (TextView) findViewById(R.id.hello_textview); 18 | 19 | byte[] encryptedStringBytes = new byte[]{40, 65, 24, 16, 4, 27, 9, 65, 24, 31, 75, 16, 91, 69, 19, 31, 4, 31, 30, 71, 18, 22, 65, 93, 24, 69, 13, 15, 77, 11, 30, 4, 18, 29, 4, 4, 20, 81, 15, 91, 64, 24, 8, 77, 15, 30, 87, 83, 91, 119, 24, 30, 79, 93, 31, 77, 14, 24, 77, 13, 23, 77, 19, 30, 4, 28, 21, 64, 93, 29, 77, 19, 31, 4, 4, 20, 81, 15, 91, 72, 20, 25, 65, 15, 15, 93, 83}; 20 | Cryptor c = Cryptor.getInstance(); 21 | tv.setText(c.decryptString(new String(encryptedStringBytes))); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CalebFenton/native-harness-target/aacadcedaeffdbeb79e75767720914ce97776b8c/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CalebFenton/native-harness-target/aacadcedaeffdbeb79e75767720914ce97776b8c/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CalebFenton/native-harness-target/aacadcedaeffdbeb79e75767720914ce97776b8c/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CalebFenton/native-harness-target/aacadcedaeffdbeb79e75767720914ce97776b8c/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CalebFenton/native-harness-target/aacadcedaeffdbeb79e75767720914ce97776b8c/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Native Harness Target 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'idea' 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | jcenter() 15 | } 16 | } 17 | 18 | task clean(type: Delete) { 19 | delete rootProject.buildDir 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CalebFenton/native-harness-target/aacadcedaeffdbeb79e75767720914ce97776b8c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Feb 05 19:05:18 IST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /harness/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | libs/ 3 | obj/ -------------------------------------------------------------------------------- /harness/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_SRC_FILES := harness.c server.c vm.c 6 | LOCAL_C_INCLUDE := ${ANDROID_NDK_ROOT}/platforms/android-21/arch-arm/usr/include/ 7 | 8 | LOCAL_MODULE := harness 9 | LOCAL_MODULE_TAGS := optional 10 | 11 | # Allow execution on android-16 12 | LOCAL_CFLAGS += -pie -fPIE 13 | LOCAL_LDFLAGS += -pie -fPIE -Wl,--export-dynamic 14 | 15 | APP_ABI := armeabi armeabi-v7a x86 16 | 17 | include $(BUILD_STATIC_EXECUTABLE) 18 | 19 | include $(BUILD_EXECUTABLE) 20 | 21 | include $(call all-makefiles-under,$(LOCAL_PATH)) 22 | -------------------------------------------------------------------------------- /harness/Makefile: -------------------------------------------------------------------------------- 1 | LOCAL_ARM_MODE := arm armeabi armeabi-v7a x86 2 | 3 | all: 4 | ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk 5 | 6 | install: 7 | adb push libs/x86/harness /data/local/tmp/ 8 | 9 | clean: 10 | rm -fr *~ obj/ libs/ -------------------------------------------------------------------------------- /harness/decrypt_string.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import socket 4 | 5 | 6 | HOST_IP = '127.0.0.1' 7 | PORT_NO = 5001 8 | BUFFER_SIZE = 2048 9 | 10 | ENC_STR_BYTES = [40, 65, 24, 16, 4, 27, 9, 65, 24, 31, 75, 16, 91, 69, 19, 31, 4, 31, 30, 71, 18, 22, 65, 93, 24, 69, 13, 15, 77, 11, 30, 4, 18, 29, 4, 4, 20, 81, 15, 91, 64, 24, 8, 77, 15, 30, 87, 83, 91, 119, 24, 30, 79, 93, 31, 77, 14, 24, 77, 13, 23, 77, 19, 30, 4, 28, 21, 64, 93, 29, 77, 19, 31, 4, 4, 20, 81, 15, 91, 72, 20, 25, 65, 15, 15, 93, 83] 11 | 12 | # Be sure to run forward ports from emulator with: 13 | # adb forward tcp:5001 tcp:5001 14 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 15 | s.connect((HOST_IP, PORT_NO)) 16 | 17 | print 'Sending encrypted string' 18 | enc_str = "".join(map(chr, ENC_STR_BYTES)) 19 | s.sendall('{}\n'.format(enc_str)) 20 | 21 | data = s.recv(BUFFER_SIZE) 22 | s.close() 23 | 24 | 25 | print("Decrypted string: \"{}\"".format(data)) -------------------------------------------------------------------------------- /harness/harness.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Native Harness 3 | * 4 | * Starts a socket server which exposes a native library function. 5 | * 6 | * Original idea from native-shim by Tim "diff" Strazere 7 | * https://github.com/rednaga/native-shim 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "vm.h" 18 | #include "server.h" 19 | 20 | #define PORT_NO 5001 21 | 22 | 23 | typedef int(*JNI_OnLoadFunc)(JavaVM *vm, void *reserved); 24 | 25 | int main(int argc, const char * argv[]) { 26 | printf("[*] Native Harness\n"); 27 | 28 | printf(" [+] Loading target: [ %s ]\n", argv[1]); 29 | if (access(argv[1], F_OK) == -1) { 30 | printf(" [!] File does not exist!\n"); 31 | return -1; 32 | } 33 | 34 | void * handle = dlopen(argv[1], RTLD_LAZY); 35 | if (handle == NULL) { 36 | printf(" [!] Could not dlopen file! (%s)\n", dlerror()); 37 | return -1; 38 | } 39 | printf(" [+] Library Loaded!\n"); 40 | 41 | JNI_OnLoadFunc onLoadFunc = dlsym(handle, "JNI_OnLoad"); 42 | if (onLoadFunc == NULL) { 43 | printf(" [!] No JNI_OnLoad found!\n"); 44 | return -1; 45 | } 46 | printf(" [+] Found JNI_OnLoad, good\n"); 47 | 48 | printf(" [+] Initializing JavaVM Instance\n"); 49 | JavaVM *vm = NULL; 50 | JNIEnv *env = NULL; 51 | int status = init_jvm(&vm, &env); 52 | if (status == 0) { 53 | printf(" [+] Initialization success (vm=%p, env=%p)\n", vm, env); 54 | } else { 55 | printf(" [!] Initialization failure (%i)\n", status); 56 | return -1; 57 | } 58 | 59 | printf(" [+] Calling JNI_OnLoad\n"); 60 | onLoadFunc(vm, NULL); 61 | 62 | decryptString_t decryptString = dlsym(handle, "Java_org_cf_nativeharness_Cryptor_decryptString"); 63 | if (decryptString == NULL) { 64 | printf(" [!] Couldn't find decryptString function\n"); 65 | } else { 66 | printf(" [+] Found decryptString function, good (%p)\n", decryptString); 67 | } 68 | 69 | // Because decryptString is an instance method, to properly call the method, need instance of class. 70 | // If decryptString were static, could just pass NULL for instance. 71 | printf(" [+] Finding Cryptor class\n"); 72 | jclass cryptor_class; 73 | cryptor_class = (*env)->FindClass(env, "org.cf.nativeharness.Cryptor"); 74 | if (cryptor_class == NULL) { 75 | printf(" [!] Couldn't find Cryptor on the class path\n"); 76 | return -1; 77 | } else { 78 | printf(" [+] Found Cryptor class: %p\n", cryptor_class); 79 | } 80 | 81 | jmethodID getinstance_method; 82 | getinstance_method = (*env)->GetStaticMethodID(env, cryptor_class, "getInstance", "()Lorg/cf/nativeharness/Cryptor;"); 83 | if (getinstance_method == NULL) { 84 | printf(" [!] Could not find Cryptor.getInstance() method\n"); 85 | return -1; 86 | } else { 87 | printf(" [+] Found Cryptor.getInstance(): %p\n", getinstance_method); 88 | } 89 | 90 | jobject cryptor_instance; 91 | cryptor_instance = (*env)->CallStaticObjectMethod(env, cryptor_class, getinstance_method, NULL); 92 | if (cryptor_instance == NULL) { 93 | printf(" [!] Couldn't call Cryptor.getInstance()\n"); 94 | return -1; 95 | } else { 96 | printf(" [+] Instantiated Cryptor class: %p\n", cryptor_instance); 97 | } 98 | 99 | printf(" [+] Starting socket server on port %i\n", PORT_NO); 100 | start_server(PORT_NO, vm, env, decryptString, cryptor_instance); 101 | 102 | printf(" [+] Cleaning up VM\n"); 103 | (*vm)->DestroyJavaVM(vm); 104 | 105 | printf(" [+] Closing target library\n"); 106 | dlclose(handle); 107 | 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /harness/server.c: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | 3 | void start_server(int port, JavaVM *vm, JNIEnv *env, decryptString_t decryptString, jobject cryptor_instance) { 4 | int sockfd, newsockfd; 5 | socklen_t clilen; 6 | char *buffer; 7 | struct sockaddr_in serv_addr, cli_addr; 8 | int pid; 9 | int ret; 10 | 11 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 12 | int option = 1; 13 | // Set socket to be reusable because we use fork and socket might get stuck 14 | setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, & option, sizeof(option)); 15 | if (sockfd < 0) { 16 | perror(" [!] ERROR opening socket"); 17 | return; 18 | } 19 | 20 | bzero((char *) &serv_addr, sizeof(serv_addr)); 21 | 22 | serv_addr.sin_family = AF_INET; 23 | serv_addr.sin_addr.s_addr = INADDR_ANY; 24 | serv_addr.sin_port = htons(port); 25 | 26 | if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { 27 | perror(" [!] ERROR binding socket"); 28 | return; 29 | } 30 | 31 | listen(sockfd, 5); 32 | clilen = sizeof(cli_addr); 33 | 34 | while (1) { 35 | newsockfd = accept(sockfd, (struct sockaddr*)&cli_addr, &clilen); 36 | if (newsockfd < 0) { 37 | perror(" [!] ERROR on socket accept"); 38 | return; 39 | } 40 | 41 | pid = fork(); 42 | if (pid < 0) { 43 | perror(" [!] ERROR on socket fork"); 44 | return; 45 | } 46 | 47 | jstring enc_str = NULL; 48 | if (pid == 0) { 49 | // Child process 50 | close(sockfd); 51 | 52 | printf(" [+] Connection accepted\n"); 53 | do { 54 | buffer = (char *) malloc(sizeof(char) * (MAX_LINE_LEN + 1)); 55 | ret = read_line(newsockfd, buffer, MAX_LINE_LEN); 56 | if (ret < 0) { 57 | break; 58 | } 59 | 60 | if (ret == 0) { 61 | free(buffer); 62 | break; 63 | } 64 | 65 | enc_str = (*env)->NewStringUTF(env, buffer); 66 | jstring result = decryptString(env, cryptor_instance, enc_str); 67 | const char *str = (*env)->GetStringUTFChars(env, result, NULL); 68 | printf(" [+] Sending decrypted string\n"); 69 | write(newsockfd, str, strlen(str)); 70 | 71 | free(buffer); 72 | } while (1); 73 | 74 | exit(0); 75 | } else { 76 | // Parent 77 | close(newsockfd); 78 | } 79 | } 80 | } 81 | 82 | static ssize_t read_line(int fd, void *buffer, size_t n) { 83 | ssize_t numRead; 84 | size_t totRead; 85 | char *buf; 86 | char ch; 87 | 88 | if (n <= 0 || buffer == NULL) { 89 | errno = EINVAL; 90 | return -1; 91 | } 92 | 93 | buf = buffer; 94 | 95 | totRead = 0; 96 | for (;;) { 97 | numRead = read(fd, &ch, 1); 98 | 99 | if (numRead == -1) { 100 | if (errno == EINTR) { 101 | continue; 102 | } else { 103 | return -1; 104 | } 105 | } else if (numRead == 0) { 106 | if (totRead == 0) { 107 | return 0; 108 | } else { 109 | break; 110 | } 111 | } else { 112 | // Don't include newline char in buffer 113 | if (ch == '\n') { 114 | break; 115 | } 116 | 117 | if (totRead < n - 1) { 118 | totRead++; *buf++ = ch; 119 | } 120 | } 121 | } 122 | 123 | *buf = '\0'; 124 | return totRead; 125 | } -------------------------------------------------------------------------------- /harness/server.h: -------------------------------------------------------------------------------- 1 | #ifndef SERVER_H 2 | #define SERVER_H 3 | 4 | #define MAX_LINE_LEN 2048 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | // Target function type signature 18 | typedef jstring(*decryptString_t)(JNIEnv *, jobject, jstring); 19 | 20 | void start_server(int port, JavaVM *vm, JNIEnv *env, decryptString_t decryptString, jobject cryptor_instance); 21 | static ssize_t read_line(int fd, void *buffer, size_t n); 22 | 23 | #endif -------------------------------------------------------------------------------- /harness/vm.c: -------------------------------------------------------------------------------- 1 | #include "vm.h" 2 | 3 | int init_jvm(JavaVM **p_vm, JNIEnv **p_env) { 4 | //https://android.googlesource.com/platform/frameworks/native/+/ce3a0a5/services/surfaceflinger/DdmConnection.cpp 5 | JavaVMOption opt[4]; 6 | opt[0].optionString = "-Djava.class.path=/data/local/tmp/target-app.apk"; 7 | opt[1].optionString = "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y"; 8 | opt[2].optionString = "-Djava.library.path=/data/local/tmp"; 9 | opt[3].optionString = "-verbose:jni"; // may want to remove this, it's noisy 10 | 11 | JavaVMInitArgs args; 12 | args.version = JNI_VERSION_1_6; 13 | args.options = opt; 14 | args.nOptions = 4; 15 | args.ignoreUnrecognized = JNI_FALSE; 16 | 17 | void *libdvm_dso = dlopen("libdvm.so", RTLD_NOW); 18 | void *libandroid_runtime_dso = dlopen("libandroid_runtime.so", RTLD_NOW); 19 | 20 | if (!libdvm_dso) { 21 | libdvm_dso = dlopen("libart.so", RTLD_NOW); 22 | } 23 | 24 | if (!libdvm_dso || !libandroid_runtime_dso) { 25 | return -1; 26 | } 27 | 28 | JNI_CreateJavaVM_t JNI_CreateJavaVM; 29 | JNI_CreateJavaVM = (JNI_CreateJavaVM_t) dlsym(libdvm_dso, "JNI_CreateJavaVM"); 30 | if (!JNI_CreateJavaVM) { 31 | return -2; 32 | } 33 | 34 | registerNatives_t registerNatives; 35 | registerNatives = (registerNatives_t) dlsym(libandroid_runtime_dso, "Java_com_android_internal_util_WithFramework_registerNatives"); 36 | if (!registerNatives) { 37 | // From Android 7.1.1, withFramework functions are removed. 38 | // If the "old" functions did not exists, use the new ones 39 | registerNatives = (registerNatives_t) dlsym(libandroid_runtime_dso, "registerFrameworkNatives"); 40 | if(!registerNatives) { 41 | return -3; 42 | } 43 | } 44 | 45 | if (JNI_CreateJavaVM(&(*p_vm), &(*p_env), &args)) { 46 | return -4; 47 | } 48 | 49 | if (registerNatives(*p_env, 0)) { 50 | return -5; 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | JNIEXPORT void InitializeSignalChain() { 57 | 58 | } 59 | 60 | JNIEXPORT void ClaimSignalChain() { 61 | 62 | } 63 | 64 | JNIEXPORT void UnclaimSignalChain() { 65 | 66 | } 67 | 68 | JNIEXPORT void InvokeUserSignalHandler() { 69 | 70 | } 71 | 72 | JNIEXPORT void EnsureFrontOfChain() { 73 | 74 | } 75 | 76 | JNIEXPORT void AddSpecialSignalHandlerFn() { 77 | 78 | } 79 | 80 | JNIEXPORT void RemoveSpecialSignalHandlerFn() { 81 | 82 | } 83 | -------------------------------------------------------------------------------- /harness/vm.h: -------------------------------------------------------------------------------- 1 | #ifndef VM_H 2 | #define VM_H 3 | 4 | #include 5 | #include 6 | 7 | typedef int(*JNI_CreateJavaVM_t)(void *, void *, void *); 8 | typedef jint(*registerNatives_t)(JNIEnv *env, jclass clazz); 9 | 10 | int init_jvm(JavaVM **p_vm, JNIEnv **p_env); 11 | 12 | #endif -------------------------------------------------------------------------------- /native-harness-target.ipr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 1.6 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------