Skip to content

Commit

Permalink
gadget: Support loading assets from APK on Android
Browse files Browse the repository at this point in the history
On Android, when apps have `extractNativeLibs` set to `false`, the
configuration and potentially script files for frida-gadget are not
extracted and cannot be read from the filesystem.

Co-authored-by: Ole André Vadla Ravnås <[email protected]>
  • Loading branch information
gergesh and oleavr committed Oct 20, 2024
1 parent e11938c commit d774d0e
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 9 deletions.
71 changes: 62 additions & 9 deletions lib/gadget/gadget.vala
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ namespace Frida.Gadget {

string config_data;
try {
FileUtils.get_contents (config_path, out config_data);
load_asset_text (config_path, out config_data);
} catch (FileError e) {
if (e is FileError.NOENT)
return new Config ();
Expand Down Expand Up @@ -1226,7 +1226,7 @@ namespace Frida.Gadget {
private ScriptConfig load_config (string path) throws Error {
string data;
try {
FileUtils.get_contents (path, out data);
load_asset_text (path, out data);
} catch (FileError e) {
if (e is FileError.NOENT)
return new ScriptConfig ();
Expand Down Expand Up @@ -1349,9 +1349,9 @@ namespace Frida.Gadget {
try {
var path = this.path;

uint8[] contents;
Bytes contents;
try {
FileUtils.get_data (path, out contents);
load_asset_bytes (path, out contents);
} catch (FileError e) {
throw new Error.INVALID_ARGUMENT ("%s", e.message);
}
Expand All @@ -1360,11 +1360,10 @@ namespace Frida.Gadget {
options.name = Path.get_basename (path).split (".", 2)[0];

ScriptEngine.ScriptInstance instance;
if (contents.length > 0 && contents[0] == QUICKJS_BYTECODE_MAGIC) {
instance = yield engine.create_script (null, new Bytes (contents), options);
} else {
instance = yield engine.create_script ((string) contents, null, options);
}
if (contents.length > 0 && contents[0] == QUICKJS_BYTECODE_MAGIC)
instance = yield engine.create_script (null, contents, options);
else
instance = yield engine.create_script ((string) contents.get_data (), null, options);

if (id.handle != 0)
yield engine.destroy_script (id);
Expand Down Expand Up @@ -2076,6 +2075,60 @@ namespace Frida.Gadget {
}
#endif

private void load_asset_text (string filename, out string text) throws FileError {
Bytes raw_contents;
load_asset_bytes (filename, out raw_contents);

unowned string str = (string) raw_contents.get_data ();
if (!str.validate ())
throw new FileError.FAILED ("%s: invalid UTF-8", filename);

text = str;
}

private void load_asset_bytes (string filename, out Bytes bytes) throws FileError {
#if ANDROID
if (maybe_load_asset_bytes_from_apk (filename, out bytes))
return;
#endif

uint8[] data;
FileUtils.get_data (filename, out data);
bytes = new Bytes.take ((owned) data);
}

#if ANDROID
private bool maybe_load_asset_bytes_from_apk (string filename, out Bytes contents) throws FileError {
contents = null;

var tokens = filename.split ("!", 2);
if (tokens.length != 2 || !tokens[0].has_suffix (".apk"))
return false;
unowned string apk_path = tokens[0];
unowned string file_path = tokens[1];

var reader = Minizip.Reader.create ();
try {
if (reader.open_file (apk_path) != OK)
throw new FileError.FAILED ("Unable to open APK");

if (reader.locate_entry (file_path[1:], true) != OK)
throw new FileError.FAILED ("Unable to locate %s inside APK", file_path);

var size = reader.entry_save_buffer_length ();
var data = new uint8[size + 1];
if (reader.entry_save_buffer (data[:size]) != OK)
throw new FileError.FAILED ("Unable to extract %s from APK", file_path);

contents = new Bytes.take ((owned) data);
return true;
} finally {
reader.close ();
Minizip.Reader.destroy (ref reader);
}
}
#endif

private Json.Node make_empty_json_object () {
return new Json.Node.alloc ().init_object (new Json.Object ());
}
Expand Down
5 changes: 5 additions & 0 deletions lib/gadget/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ if host_os_family == 'darwin'
extra_link_args += '-Wl,-framework,Foundation'
endif

if host_os == 'android'
extra_vala_args += '--pkg=minizip'
platform_deps += minizip_dep
endif

if host_os_family == 'windows'
if host_toolchain != 'microsoft'
symfile = 'frida-gadget.symbols'
Expand Down
7 changes: 7 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,13 @@ else
gio_unix_dep = dependency('gio-unix-2.0')
endif

if host_os == 'android'
minizip_dep = dependency('minizip', required: false, default_options: [
'zlib=enabled',
'lzma=disabled',
])
endif

have_local_backend = get_option('local_backend').allowed()
have_fruity_backend = get_option('fruity_backend') \
.disable_auto_if(not host_docks_mobile_devices) \
Expand Down
48 changes: 48 additions & 0 deletions vapi/minizip.vapi
Original file line number Diff line number Diff line change
@@ -1,3 +1,51 @@
[CCode (cprefix = "", gir_namespace = "Minizip", gir_version = "1.0", lower_case_cprefix = "mz_")]
namespace Minizip {
[SimpleType]
[CCode (cheader_filename = "minizip/mz_strm.h,minizip/mz_zip.h,minizip/mz_zip_rw.h", cname = "gpointer", cprefix = "mz_zip_reader_",
has_destroy_function = false)]
public struct Reader {
public static Reader create (out Reader stream = null);
[CCode (cname = "mz_zip_reader_delete")]
public static void destroy (ref Reader stream);

public Status open_file (string path);
public Status close ();

public Status locate_entry (string filename, bool ignore_case);

public Status entry_save_file (string path);
public Status entry_save_buffer (uint8[] buf);
public int32 entry_save_buffer_length ();
}

[CCode (cheader_filename = "minizip/mz.h", cname = "int32_t", cprefix = "MZ_", has_type_id = false)]
public enum Status {
OK,
STREAM_ERROR,
DATA_ERROR,
MEM_ERROR,
BUF_ERROR,
VERSION_ERROR,

END_OF_LIST,
END_OF_STREAM,

PARAM_ERROR,
FORMAT_ERROR,
INTERNAL_ERROR,
CRC_ERROR,
CRYPT_ERROR,
EXIST_ERROR,
PASSWORD_ERROR,
SUPPORT_ERROR,
HASH_ERROR,
OPEN_ERROR,
CLOSE_ERROR,
SEEK_ERROR,
TELL_ERROR,
READ_ERROR,
WRITE_ERROR,
SIGN_ERROR,
SYMLINK_ERROR,
}
}

0 comments on commit d774d0e

Please sign in to comment.