Skip to content

Commit

Permalink
Fix #360
Browse files Browse the repository at this point in the history
  • Loading branch information
vfsfitvnm committed Sep 4, 2023
1 parent e97fb50 commit ad0a65f
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 47 deletions.
22 changes: 22 additions & 0 deletions src/utils/android.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/** @internal */
namespace Android {
export declare const apiLevel: number | null;
// prettier-ignore
getter(Android, "apiLevel", () => {
const value = getProperty("ro.build.version.sdk");
return value ? parseInt(value) : null;
}, lazy);

function getProperty(name: string): string | undefined {
const handle = Module.findExportByName("libc.so", "__system_property_get");

if (handle) {
const __system_property_get = new NativeFunction(handle, "void", ["pointer", "pointer"]);

const value = Memory.alloc(92).writePointer(NULL);
__system_property_get(Memory.allocUtf8String(name), value);

return value.readCString() ?? undefined;
}
}
}
97 changes: 50 additions & 47 deletions src/utils/native-wait.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,22 @@
/** @internal */
type StringEncoding = "utf8" | "utf16" | "ansi";
interface ResolvedExport {
handle: NativePointer;
readString: (handle: NativePointer) => string | null;
}

/** @internal */
class Target {
readonly address: NativePointer;

private constructor(responsible: string | null, name: string, readonly stringEncoding: StringEncoding) {
this.address = Module.findExportByName(responsible, name) ?? NULL;
}

static get targets(): Target[] {
function info(): [string | null, ...[string, StringEncoding][]] | undefined {
switch (Process.platform) {
case "linux":
try {
if (UnityVersion.gte(Java.androidVersion, "12")) {
return [null, ["__loader_dlopen", "utf8"]];
} else {
return ["libdl.so", ["dlopen", "utf8"], ["android_dlopen_ext", "utf8"]];
}
} catch (e) {
return [null, ["dlopen", "utf8"]];
}
case "darwin":
return ["libdyld.dylib", ["dlopen", "utf8"]];
case "windows":
const ll = "LoadLibrary";
return ["kernel32.dll", [`${ll}W`, "utf16"], [`${ll}ExW`, "utf16"], [`${ll}A`, "ansi"], [`${ll}ExA`, "ansi"]];
}
function forModule(...moduleNames: string[]): Promise<string> {
function find(
moduleName: string | null,
name: string,
readString: (handle: NativePointer) => string | null = _ => _.readUtf8String()
): ResolvedExport | undefined {
const handle = Module.findExportByName(moduleName, name) ?? NULL;
if (!handle.isNull()) {
return { handle, readString };
}

const [responsible, ...targets] = info()!;
return targets.map(([name, encoding]) => new Target(responsible, name, encoding)).filter(_ => !_.address.isNull());
}

readString(pointer: NativePointer): string | null {
switch (this.stringEncoding) {
case "utf8":
return pointer.readUtf8String();
case "utf16":
return pointer.readUtf16String();
case "ansi":
return pointer.readAnsiString();
}
}
}

/** @internal */
function forModule(...moduleNames: string[]): Promise<string> {
return new Promise<string>(resolve => {
for (const moduleName of moduleNames) {
const module = Process.findModuleByName(moduleName);
Expand All @@ -57,10 +26,44 @@ function forModule(...moduleNames: string[]): Promise<string> {
}
}

const interceptors = Target.targets.map(target =>
Interceptor.attach(target.address, {
let targets: (ResolvedExport | undefined)[] = [];

switch (Process.platform) {
case "linux":
switch (Android.apiLevel) {
case null:
targets = [find(null, "dlopen")];
break;
default:
// prettier-ignore
targets = Android.apiLevel >= 31
? [find(null, "__loader_dlopen")]
: [find("libdl.so", "dlopen"), find("libdl.so", "android_dlopen_ext")];
}
break;
case "darwin":
targets = [find("libdyld.dylib", "dlopen")];
break;
case "windows":
targets = [
find("kernel32.dll", "LoadLibraryW", _ => _.readUtf16String()),
find("kernel32.dll", "LoadLibraryExW", _ => _.readUtf16String()),
find("kernel32.dll", "LoadLibraryA", _ => _.readAnsiString()),
find("kernel32.dll", "LoadLibraryExA", _ => _.readAnsiString())
];
break;
}

targets = targets.filter(_ => _);

if (targets.length == 0) {
throw new Error("There are no targets to hook, please file a bug");
}

const interceptors = targets.map(_ =>
Interceptor.attach(_!.handle, {
onEnter(args: InvocationArguments) {
this.modulePath = target.readString(args[0]) ?? "";
this.modulePath = _!.readString(args[0]) ?? "";
},
onLeave(returnValue: InvocationReturnValue) {
if (returnValue.isNull()) return;
Expand Down

0 comments on commit ad0a65f

Please sign in to comment.