move entry point to C, bypass dalvik executable by directly linking against libdvm.so

This commit is contained in:
Mis012
2022-07-23 00:06:32 +02:00
parent 1b5a52ccc8
commit 8fedbfb438
4 changed files with 291 additions and 55 deletions

251
src/main.c Normal file
View File

@@ -0,0 +1,251 @@
#include <gtk/gtk.h>
#include "../jni/defines.h"
#include "../jni/util.h"
#include "../jni/org_launch_main.h"
#include "../jni/android_app_Activity.h"
#include <dlfcn.h>
GtkWidget *window;
// standard Gtk Application stuff, more or less
gboolean app_exit(GtkWindow* self, JNIEnv *env) // TODO: do more cleanup?
{
/* -- run the main activity's onDestroy -- */
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onDestroy, NULL);
return false;
}
// FIXME: used by hacks in GLSurfaceView
int FIXME__WIDTH;
int FIXME__HEIGHT;
char *construct_classpath(char *prefix, char *path_to_prepend, char **cp_array, size_t len)
{
size_t result_len = strlen(prefix);
for(int i = 0; i < len; i++) {
result_len += strlen(path_to_prepend) + strlen(cp_array[i]) + 1; // the 1 is for either : or the final \0
}
char *result = malloc(result_len);
strcpy(result, prefix);
for(int i = 0; i < len; i++) {
strcat(result, path_to_prepend);
strcat(result, cp_array[i]);
if (i < (len - 1))
strcat(result, ":");
}
return result;
}
char *construct_boot_classpath(char *prefix, char **cp_array, size_t len)
{
char *android_root_path = getenv("ANDROID_ROOT");
char *framework_dir = "/framework/";
char *framework_dir_path = malloc(strlen(android_root_path) + strlen(framework_dir) + 1);
strcpy(framework_dir_path, android_root_path);
strcat(framework_dir_path, framework_dir);
char *result = construct_classpath(prefix, framework_dir_path, cp_array, len);
free(framework_dir_path);
return result;
}
JNIEnv* create_vm(char *apk_classpath) {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[3];
args.version = JNI_VERSION_1_6;
args.nOptions = 3;
// TODO: should probably not hardcode this
char *boot_cp_arr[] = {
"core-hostdex.jar",
"core-junit-hostdex.jar",
"conscrypt-hostdex.jar",
"okhttp-hostdex.jar",
"bouncycastle-hostdex.jar",
"apachehttp-hostdex.jar",
"apache-xml-hostdex.jar",
};
options[0].optionString = construct_boot_classpath("-Xbootclasspath:", boot_cp_arr, ARRAY_SIZE(boot_cp_arr));
// or this
char *cp_array[] = {
".",
"hax_arsc_parser.dex",
"hax.dex",
"%s",
"com.google.android.gms.apk",
};
char *cp_format_str = construct_classpath("-Djava.class.path=", "", cp_array, ARRAY_SIZE(cp_array));
size_t cp_len = strlen(cp_format_str) - strlen("%s") + strlen(apk_classpath) + 1;
options[1].optionString = malloc(cp_len);
snprintf(options[1].optionString, cp_len, cp_format_str, apk_classpath);
options[2].optionString = "-verbose:jni";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
int ret = JNI_CreateJavaVM(&jvm, (void **)&env, &args);
if (ret<0){
printf("Unable to Launch JVM\n");
} else {
printf("JVM launched successfully\n");
}
return env;
}
bool _Z17dvmLoadNativeCodePKcP6ObjectPPc(const char* pathName, void* classLoader, char** detail);
void * _Z20dvmDecodeIndirectRefP6ThreadP8_jobject(void* self, jobject jobj);
void * _Z13dvmThreadSelfv(void);
struct jni_callback_data { char *apk_main_activity_class; uint32_t window_width; uint32_t window_height;};
static void open(GtkApplication *app, GFile** files, gint nfiles, const gchar* hint, struct jni_callback_data *d)
{
//TODO: pass all files to classpath
/*
printf("nfiles: %d\n", nfiles);
for(int i = 0; i < nfiles; i++) {
printf(">- [%s]\n", g_file_get_path(files[i]));
}
*/
char *apk_classpath = g_file_get_path(files[0]);
if(apk_classpath == NULL) {
printf("error: the specified file path doesn't seem to be valid\n");
exit(1);
}
JNIEnv* env = create_vm(apk_classpath);
if(!d->apk_main_activity_class) {
printf("error: missing required option --launch-activity <activity>.\nyou can specify --help to see the list of options\n");
exit(1);
}
set_up_handle_cache(env, d->apk_main_activity_class);
jclass display_class = (*env)->FindClass(env, "android/view/Display");
_SET_STATIC_INT_FIELD(display_class, "window_width", d->window_width);
_SET_STATIC_INT_FIELD(display_class, "window_height", d->window_height);
FIXME__WIDTH = d->window_width;
FIXME__HEIGHT = d->window_height;
window = gtk_application_window_new (app);
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.set_window, _INTPTR(window));
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
gtk_window_set_title(GTK_WINDOW(window), "com.example.demo_application");
gtk_window_set_default_size(GTK_WINDOW(window), d->window_width, d->window_height);
g_signal_connect(window, "close-request", G_CALLBACK (app_exit), env);
// TODO: set icon according to how android gets it for the purposes of displaying it in the launcher
// gtk_window_set_icon_name(window, "weather-clear");
gtk_widget_show(window);
/* -- register our JNI library under the appropriate classloader -- */
/* 'android/view/View' is part of the "hax.dex" package, any other function from that package would serve just as well */
jmethodID getClassLoader = _METHOD((*env)->FindClass(env, "java/lang/Class"), "getClassLoader", "()Ljava/lang/ClassLoader;");
jobject class_loader = (*env)->CallObjectMethod(env, handle_cache.view.class, getClassLoader);
char* reason = NULL;
if (!_Z17dvmLoadNativeCodePKcP6ObjectPPc("libtranslation_layer_main.so", _Z20dvmDecodeIndirectRefP6ThreadP8_jobject(_Z13dvmThreadSelfv(), class_loader), &reason)) {
printf("[main] dvmLoadNativeCode failed for libtranslation_layer_main.so: %s", reason);
exit(1);
}
/* -- run the main activity's onCreate -- */
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onCreate, NULL);
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
// TODO: some apps wait for this to actually do stuff
/* (*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onWindowFocusChanged, true);
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);*/
}
static void activate(GtkApplication *app, struct jni_callback_data *d)
{
printf("error: usage: ./main [app.apk] [path/to/activity]\nyou can specify --help to see the list of options\n");
exit(1);
}
void init_cmd_parameters(GApplication *app, struct jni_callback_data *d)
{
const GOptionEntry cmd_params[] =
{
{
.long_name = "launch-activity",
.short_name = 'l',
.flags = G_OPTION_FLAG_NONE,
.arg = G_OPTION_ARG_STRING,
.arg_data = &d->apk_main_activity_class,
.description = "the fully quilifed name of the activity you wish to launch (usually the apk's main activity)",
.arg_description = NULL,
},
{
.long_name = "window-width",
.short_name = 'w',
.flags = G_OPTION_FLAG_NONE,
.arg = G_OPTION_ARG_INT,
.arg_data = &d->window_width,
.description = "window width to launch with (some apps react poorly to runtime window size adjustments)",
.arg_description = NULL,
},
{
.long_name = "window-height",
.short_name = 'h',
.flags = G_OPTION_FLAG_NONE,
.arg = G_OPTION_ARG_INT,
.arg_data = &d->window_height,
.description = "window height to launch with (some apps react poorly to runtime window size adjustments)",
.arg_description = NULL,
},
{NULL}
};
g_application_add_main_option_entries (G_APPLICATION (app), cmd_params);
}
int main(int argc, char **argv/*, JNIEnv *env*/)
{
GtkApplication *app;
int status;
struct jni_callback_data *callback_data = malloc(sizeof(struct jni_callback_data));
callback_data->apk_main_activity_class = NULL;
callback_data->window_width = 960;
callback_data->window_height = 540;
app = gtk_application_new("com.example.demo_application", G_APPLICATION_NON_UNIQUE | G_APPLICATION_HANDLES_OPEN);
// cmdline related setup
init_cmd_parameters(G_APPLICATION(app), callback_data);
g_application_set_option_context_summary(G_APPLICATION(app), "actual usage:\nLD_PRELOAD=libpthread_bio.so ./dalvik/dalvik -verbose:jni -cp hax_arsc_parser.dex:hax_xmlpull.dex:hax.dex:main.dex:${1}:com.google.android.gms.apk org/launch/main ${2}\nwhere ${1} is the path to the apk and ${2} is the cmdline");
g_signal_connect(app, "activate", G_CALLBACK (activate), callback_data);
g_signal_connect(app, "open", G_CALLBACK (open), callback_data);
status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
const char dl_loader[] __attribute__((section(".interp"))) =
"/lib/ld-linux.so.2";