From 1e93278b4ca1fe630852b371dd4129efb14e5c61 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Tue, 8 Apr 2025 17:48:17 +0200 Subject: [PATCH] skeleton for merged test apk --- test_apks/dyn_and_ref/.gitignore | 3 + test_apks/dyn_and_ref/AndroidManifest.xml | 23 +++ test_apks/dyn_and_ref/Makefile | 74 ++++++++ .../com/example/theseus/dynandref/AMain.java | 7 + .../example/theseus/dynandref/Collider.java | 7 + .../classes/com/example/theseus/Utils.java | 38 ++++ .../dynandref/ClassLoaderContextActivity.java | 136 +++++++++++++++ .../example/theseus/dynandref/Collider.java | 7 + .../theseus/dynandref/MainActivity.java | 147 ++++++++++++++++ .../theseus/dynandref/MethodActivity.java | 164 ++++++++++++++++++ 10 files changed, 606 insertions(+) create mode 100644 test_apks/dyn_and_ref/.gitignore create mode 100644 test_apks/dyn_and_ref/AndroidManifest.xml create mode 100644 test_apks/dyn_and_ref/Makefile create mode 100644 test_apks/dyn_and_ref/java/a/com/example/theseus/dynandref/AMain.java create mode 100644 test_apks/dyn_and_ref/java/a/com/example/theseus/dynandref/Collider.java create mode 100644 test_apks/dyn_and_ref/java/classes/com/example/theseus/Utils.java create mode 100644 test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/ClassLoaderContextActivity.java create mode 100644 test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/Collider.java create mode 100644 test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/MainActivity.java create mode 100644 test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/MethodActivity.java diff --git a/test_apks/dyn_and_ref/.gitignore b/test_apks/dyn_and_ref/.gitignore new file mode 100644 index 0000000..487375c --- /dev/null +++ b/test_apks/dyn_and_ref/.gitignore @@ -0,0 +1,3 @@ +build +ToyKey.keystore +java/classes/com/example/theseus/dynloading/R.java diff --git a/test_apks/dyn_and_ref/AndroidManifest.xml b/test_apks/dyn_and_ref/AndroidManifest.xml new file mode 100644 index 0000000..a5cafdb --- /dev/null +++ b/test_apks/dyn_and_ref/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + diff --git a/test_apks/dyn_and_ref/Makefile b/test_apks/dyn_and_ref/Makefile new file mode 100644 index 0000000..396ec02 --- /dev/null +++ b/test_apks/dyn_and_ref/Makefile @@ -0,0 +1,74 @@ +VERSION=34.0.0 +VERSION_B=$(basename $(basename $(VERSION))) +ANDROID_HOME ?= $(HOME)/Android/Sdk +SDK_TOOLS=$(ANDROID_HOME) +JAVA_PATH=/usr/lib/jvm/java-17-openjdk/bin +JAVAC=$(JAVA_PATH)/javac +JAR=$(JAVA_PATH)/jar +JARSIGNER=$(JAVA_PATH)/jarsigner +KEYTOOL=$(JAVA_PATH)/keytool + +ADB=$(SDK_TOOLS)/platform-tools/adb +D8=$(SDK_TOOLS)/build-tools/$(VERSION)/d8 +AAPT=$(SDK_TOOLS)/build-tools/$(VERSION)/aapt +ZIPALIGN=$(SDK_TOOLS)/build-tools/$(VERSION)/zipalign +APKSIGNER=$(SDK_TOOLS)/build-tools/$(VERSION)/apksigner + +APP=test_dyn_and_ref +PACKAGE=com.example.theseus.dynandref +MAIN_ACTIVITY=MainActivity + +JAVAC_ARGS = +D8_ARGS = + +pass=ahahah + +export PATH := $(JAVA_PATH):$(PATH) + +all: $(shell mkdir -p build) +all: clean build/$(APP).apk +signature_v1: clean build/$(APP).v1.apk + +debug: JAVAC_ARGS += -g +debug: D8_ARGS += --debug +debug: all + +test: all + $(ADB) install build/$(APP).apk + $(ADB) shell am start -n $(PACKAGE)/.$(MAIN_ACTIVITY) + +build/%.v1signed.apk: ./build/%.unsigned.apk ./ToyKey.keystore + $(JARSIGNER) -verbose -keystore ./ToyKey.keystore -storepass $(pass) -keypass $(pass) -signedjar $@ $< SignKey + +build/%.v1.apk: ./build/%.v1signed.apk + $(SDK_TOOLS)/build-tools/$(VERSION)/zipalign -v -f 4 $< $@ + +# TODO: fix dep somehow? cannot find a way to use % or $* in (shell ..) +build/%/classes: $(shell find java/ -type f -regex ".*\.java" ) + mkdir -p ./build/$*/classes + $(JAVAC) $(JAVAC_ARGS) -d ./build/$*/classes -classpath build/deps.jar:$(SDK_TOOLS)/platforms/android-$(VERSION_B)/android.jar $$(find java/$*/ -type f -regex ".*\.java") + +build/%/classes.dex: build/%/classes + mkdir -p ./build/$* + $(D8) $(D8_ARGS) --classpath $(SDK_TOOLS)/platforms/android-$(VERSION_B)/android.jar $(shell find build/$*/classes -type f -regex ".*\.class" -printf "'%p'\n") --output ./build/$*/ + +build/%.unsigned.apk: build/classes/classes.dex build/a/classes.dex + mkdir -p ./build/$*_files ./build/$*_files/assets + mv ./build/classes/classes.dex ./build/$*_files/classes.dex + mv build/a/classes.dex ./build/$*_files/assets/a.dex + $(AAPT) package -v -f -M ./AndroidManifest.xml -I $(SDK_TOOLS)/platforms/android-$(VERSION_B)/android.jar -F $@ ./build/$*_files + +build/%.v2aligned.apk: ./build/%.unsigned.apk ./ToyKey.keystore + $(ZIPALIGN) -v -f 4 $< $@ + +build/%.apk: ./build/%.v2aligned.apk + $(APKSIGNER) sign -ks ./ToyKey.keystore --v2-signing-enabled true --in $< --out $@ --ks-pass pass:$(pass) + +ToyKey.keystore : + $(KEYTOOL) -genkeypair -validity 1000 -dname "CN=SomeKey,O=SomeOne,C=FR" -keystore $@ -storepass $(pass) -keypass $(pass) -alias SignKey -keyalg RSA -v + +clean: + $(RM) -r build/* + +clean_all: clean + $(RM) ToyKey.keystore diff --git a/test_apks/dyn_and_ref/java/a/com/example/theseus/dynandref/AMain.java b/test_apks/dyn_and_ref/java/a/com/example/theseus/dynandref/AMain.java new file mode 100644 index 0000000..aa3352b --- /dev/null +++ b/test_apks/dyn_and_ref/java/a/com/example/theseus/dynandref/AMain.java @@ -0,0 +1,7 @@ +package com.example.theseus.dynandref; + +public class AMain { + public static String getColliderId() { + return Collider.getColliderId(); + } +} diff --git a/test_apks/dyn_and_ref/java/a/com/example/theseus/dynandref/Collider.java b/test_apks/dyn_and_ref/java/a/com/example/theseus/dynandref/Collider.java new file mode 100644 index 0000000..d9eb741 --- /dev/null +++ b/test_apks/dyn_and_ref/java/a/com/example/theseus/dynandref/Collider.java @@ -0,0 +1,7 @@ +package com.example.theseus.dynandref; + +public class Collider { + public static String getColliderId() { + return "A"; + } +} diff --git a/test_apks/dyn_and_ref/java/classes/com/example/theseus/Utils.java b/test_apks/dyn_and_ref/java/classes/com/example/theseus/Utils.java new file mode 100644 index 0000000..09006b6 --- /dev/null +++ b/test_apks/dyn_and_ref/java/classes/com/example/theseus/Utils.java @@ -0,0 +1,38 @@ +package com.example.theseus; + +import android.app.Activity; +import android.app.AlertDialog; +import android.util.Log; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +public class Utils { + public static String source() { + return "Secret"; + } + public static String source(String tag) { + return "[" + tag + "] Secret"; + } + + public static void popup(Activity ac, String title, String msg) { + Log.e("THESEUS", "POPUP, title: " + title + ", msg: " + msg); + (new AlertDialog.Builder(ac)) + .setMessage(msg) + .setTitle(title) + .create() + .show(); + } + + public static void sink(Activity ac, String data) { + popup(ac, "Data leak:", data); + } + public static void copy(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + int read; + while((read = in.read(buffer)) != -1){ + out.write(buffer, 0, read); + } + } +} diff --git a/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/ClassLoaderContextActivity.java b/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/ClassLoaderContextActivity.java new file mode 100644 index 0000000..acbaf2a --- /dev/null +++ b/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/ClassLoaderContextActivity.java @@ -0,0 +1,136 @@ +package com.example.theseus.dynandref; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.LinearLayout; +import android.view.ViewGroup; +import android.view.View; +import android.widget.Button; +import android.content.res.ColorStateList; + +import java.lang.ClassLoader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.ClassNotFoundException; + +import android.content.Intent; +import android.content.res.AssetManager; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.File; +import java.io.FileOutputStream; +import java.lang.reflect.InvocationTargetException; +import android.content.Context; +import dalvik.system.PathClassLoader; +import dalvik.system.DelegateLastClassLoader; +import java.lang.reflect.Method; + +import com.example.theseus.Utils; + + +import java.util.Arrays; + +public class ClassLoaderContextActivity extends Activity { + public String classLoaderName; + + public String getdexfile(String name) { + File dexfile = new File(getCacheDir(), name); + dexfile.setReadOnly(); + Log.e("DEBUG", dexfile.getPath()); + return dexfile.getPath(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + classLoaderName = intent.getStringExtra("classLoaderName"); + + ColorStateList buttonColor = ColorStateList.valueOf(0xff808080); + + RelativeLayout relLayout = new RelativeLayout(this); + relLayout.generateViewId(); + + ScrollView scrollView = new ScrollView(this); + scrollView.generateViewId(); + + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + lp.addRule(RelativeLayout.CENTER_IN_PARENT); + + LinearLayout linLayout = new LinearLayout(this); + linLayout.generateViewId(); + linLayout.setLayoutParams(lp); + linLayout.setOrientation(LinearLayout.VERTICAL); + + + Button b1 = new Button(this); + b1.generateViewId(); + linLayout.addView(b1); + + Button b2 = new Button(this); + b2.generateViewId(); + linLayout.addView(b2); + + Button b3 = new Button(this); + b3.generateViewId(); + linLayout.addView(b3); + + Button b4 = new Button(this); + b4.generateViewId(); + linLayout.addView(b4); + + scrollView.addView(linLayout); + relLayout.addView(scrollView); + setContentView(relLayout); + + Activity ac = this; + + b1.setText("Direct With Parent"); + b1.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + nextActivity(classLoaderName, true, true); + } + }); + + b2.setText("Direct Without Parent"); + b2.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + nextActivity(classLoaderName, true, false); + } + }); + + b3.setText("Indirect With Parent"); + b3.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + } + }); + + b4.setText("Indirect Without Parent"); + b4.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + nextActivity(classLoaderName, false, false); + } + }); + } + + public void nextActivity(String classLoaderName, boolean isDirect, boolean hasParent) { + Intent intent = new Intent(this, MethodActivity.class); + intent.putExtra("classLoaderName", classLoaderName); + intent.putExtra("direct", isDirect); + intent.putExtra("parent", hasParent); + startActivity(intent); + } +} diff --git a/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/Collider.java b/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/Collider.java new file mode 100644 index 0000000..4772963 --- /dev/null +++ b/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/Collider.java @@ -0,0 +1,7 @@ +package com.example.theseus.dynandref; + +public class Collider { + public static String getColliderId() { + return "MainAPK"; + } +} diff --git a/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/MainActivity.java b/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/MainActivity.java new file mode 100644 index 0000000..72484b7 --- /dev/null +++ b/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/MainActivity.java @@ -0,0 +1,147 @@ +package com.example.theseus.dynandref; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.LinearLayout; +import android.view.ViewGroup; +import android.view.View; +import android.widget.Button; +import android.content.res.ColorStateList; + +import java.lang.ClassLoader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.ClassNotFoundException; + +import android.content.Intent; +import android.content.res.AssetManager; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.File; +import java.io.FileOutputStream; +import java.lang.reflect.InvocationTargetException; +import android.content.Context; +import dalvik.system.PathClassLoader; +import dalvik.system.DelegateLastClassLoader; +import java.lang.reflect.Method; + +import com.example.theseus.Utils; + + +import java.util.Arrays; + +public class MainActivity extends Activity { + + public void setup() { + AssetManager assetManager = getAssets(); + InputStream in = null; + OutputStream out = null; + File outFile = null; + try { + in = assetManager.open("a.dex"); + outFile = new File(getCacheDir(), "a.dex_"); // .dex_ because android does not like people writing .dex + out = new FileOutputStream(outFile); + Utils.copy(in, out); + outFile.renameTo(new File(getCacheDir(), "a.dex")); // security? + } catch (IOException e) {} + try { + in.close(); + } catch (IOException e) {} + try { + out.close(); + } catch (IOException e) {} + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setup(); + + ColorStateList buttonColor = ColorStateList.valueOf(0xff808080); + + RelativeLayout relLayout = new RelativeLayout(this); + relLayout.generateViewId(); + + ScrollView scrollView = new ScrollView(this); + scrollView.generateViewId(); + + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + lp.addRule(RelativeLayout.CENTER_IN_PARENT); + + LinearLayout linLayout = new LinearLayout(this); + linLayout.generateViewId(); + linLayout.setLayoutParams(lp); + linLayout.setOrientation(LinearLayout.VERTICAL); + + + Button b1 = new Button(this); + b1.generateViewId(); + linLayout.addView(b1); + + Button b2 = new Button(this); + b2.generateViewId(); + linLayout.addView(b2); + + Button b3 = new Button(this); + b3.generateViewId(); + linLayout.addView(b3); + + Button b4 = new Button(this); + b4.generateViewId(); + linLayout.addView(b4); + + scrollView.addView(linLayout); + relLayout.addView(scrollView); + setContentView(relLayout); + + Activity ac = this; + + b1.setText("DelegateLastClassLoader"); + b1.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + nextActivity("DelegateLastClassLoader"); + } + }); + + b2.setText("DexClassLoader"); + b2.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + nextActivity("DexClassLoader"); + } + }); + + b3.setText("InMemoryDexClassLoader"); + b3.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + nextActivity("InMemoryDexClassLoader"); + } + }); + + b4.setText("PathClassLoader"); + b4.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + nextActivity("PathClassLoader"); + } + }); + } + + public void nextActivity(String classLoaderName) { + Intent intent = new Intent(this, ClassLoaderContextActivity.class); + intent.putExtra("classLoaderName", classLoaderName); + startActivity(intent); + } +} diff --git a/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/MethodActivity.java b/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/MethodActivity.java new file mode 100644 index 0000000..3bd39f4 --- /dev/null +++ b/test_apks/dyn_and_ref/java/classes/com/example/theseus/dynandref/MethodActivity.java @@ -0,0 +1,164 @@ +package com.example.theseus.dynandref; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.LinearLayout; +import android.view.ViewGroup; +import android.view.View; +import android.widget.Button; +import android.content.res.ColorStateList; + +import java.lang.ClassLoader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.ClassNotFoundException; + +import android.content.Intent; +import android.content.res.AssetManager; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.File; +import java.io.FileOutputStream; +import java.lang.reflect.InvocationTargetException; +import android.content.Context; +import dalvik.system.PathClassLoader; +import dalvik.system.DelegateLastClassLoader; +import java.lang.reflect.Method; + +import com.example.theseus.Utils; + + +import java.util.Arrays; + +public class MethodActivity extends Activity { + public String classLoaderName; + public boolean hasParent; + public boolean isDirect; + + + public String getdexfile(String name) { + File dexfile = new File(getCacheDir(), name); + dexfile.setReadOnly(); + Log.e("DEBUG", dexfile.getPath()); + return dexfile.getPath(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + classLoaderName = intent.getStringExtra("classLoaderName"); + isDirect = intent.getBooleanExtra("direct", false); + hasParent = intent.getBooleanExtra("parent", false); + + ColorStateList buttonColor = ColorStateList.valueOf(0xff808080); + + RelativeLayout relLayout = new RelativeLayout(this); + relLayout.generateViewId(); + + ScrollView scrollView = new ScrollView(this); + scrollView.generateViewId(); + + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + lp.addRule(RelativeLayout.CENTER_IN_PARENT); + + LinearLayout linLayout = new LinearLayout(this); + linLayout.generateViewId(); + linLayout.setLayoutParams(lp); + linLayout.setOrientation(LinearLayout.VERTICAL); + + + Button b1 = new Button(this); + b1.generateViewId(); + linLayout.addView(b1); + + Button b2 = new Button(this); + b2.generateViewId(); + linLayout.addView(b2); + + Button b3 = new Button(this); + b3.generateViewId(); + linLayout.addView(b3); + + Button b4 = new Button(this); + b4.generateViewId(); + linLayout.addView(b4); + + Button b5 = new Button(this); + b5.generateViewId(); + linLayout.addView(b5); + + Button b6 = new Button(this); + b6.generateViewId(); + linLayout.addView(b6); + + scrollView.addView(linLayout); + relLayout.addView(scrollView); + setContentView(relLayout); + + Activity ac = this; + + b1.setText("Virtual"); + b1.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + Utils.popup(ac, "MSG", classLoaderName + ", has parent:" + hasParent + ", direct: "+ isDirect + ", method: Virtual"); + } + }); + + b2.setText("Static"); + b2.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + Utils.popup(ac, "MSG", classLoaderName + ", has parent:" + hasParent + ", direct: "+ isDirect + ", method: Static"); + } + }); + + b3.setText("Extended"); + b3.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + Utils.popup(ac, "MSG", classLoaderName + ", has parent:" + hasParent + ", direct: "+ isDirect + ", method: Extended"); + } + }); + + b4.setText("Interface"); + b4.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + Utils.popup(ac, "MSG", classLoaderName + ", has parent:" + hasParent + ", direct: "+ isDirect + ", method: Interface"); + } + }); + + b5.setText("Interface Static"); + b5.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + Utils.popup(ac, "MSG", classLoaderName + ", has parent:" + hasParent + ", direct: "+ isDirect + ", method: Interface Static"); + } + }); + + b6.setText("Factory Pattern"); + b6.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.setBackgroundTintList(buttonColor); + Utils.popup(ac, "MSG", classLoaderName + ", has parent:" + hasParent + ", direct: "+ isDirect + ", method: Factory Pattern"); + } + }); + } + + public void nextActivity(String classLoaderName) { + Intent intent = new Intent(this, MethodActivity.class); + intent.putExtra("classLoaderName", classLoaderName); + startActivity(intent); + } +}