NorthwaveSecurity / fridax

Fridax enables you to read variables and intercept/hook functions in Xamarin/Mono JIT and AOT compiled iOS/Android applications.
MIT License
161 stars 21 forks source link

Export not found: `mono_aot_get_method` in JIT-compiled APK #3

Closed Techbrunch closed 4 years ago

Techbrunch commented 4 years ago

Hello,

I can't get the modify_class_function_argument.js script to work.

I'm working with this app: https://github.com/xamarin/xamarin-forms-samples/tree/master/WebServices/TodoREST

I'm trying to intercerpt the GetTasksAsync function of the TodoItemManager:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace TodoREST
{
    public class TodoItemManager
    {
        IRestService restService;

        public TodoItemManager (IRestService service)
        {
            restService = service;
        }

        public Task<List<TodoItem>> GetTasksAsync ()
        {
            return restService.RefreshDataAsync (); 
        }

        public Task SaveTaskAsync (TodoItem item, bool isNewItem = false)
        {
            return restService.SaveTodoItemAsync (item, isNewItem);
        }

        public Task DeleteTaskAsync (TodoItem item)
        {
            return restService.DeleteTodoItemAsync (item.ID);
        }
    }
}

Source for the class: https://github.com/xamarin/xamarin-forms-samples/blob/master/WebServices/TodoREST/TodoREST/Data/TodoItemManager.cs

I'm using this script:

import { MonoApiHelper, MonoApi } from '../vendors/frida-mono-api'
import ClassHelper from '../libraries/class_helper'

// Intercept settings
var settingClassName = "TodoREST.TodoItemManager";
var settingMethodName = "GetTasksAsync";
var settingMethodArgCount = 0;

// The root AppDomain is the initial domain created by the runtime when it is initialized. Programs execute on this AppDomain.
const domain = MonoApi.mono_get_root_domain()

console.log('domain: ' + classInformation);

// Get a reference to a certain class within the Xamarin application.
var classInformation = ClassHelper.getClassByName(settingClassName);

console.log('classInformation: ' + classInformation);

// Get the pointer to the ahead-of-time (AOT) compiled method
let methodInformation = MonoApiHelper.ClassGetMethodFromName(classInformation, settingMethodName, settingMethodArgCount)

console.log('methodInformation: ' + methodInformation);

// Allocate enough memory for MonoError initialization
let monoErrorMemory = Memory.alloc(32) 

// Get the pointer to the method
let nativeMethodPointer = MonoApi.mono_aot_get_method(domain, methodInformation, monoErrorMemory)

// Attach interceptor and fish out the first method argument
Interceptor.attach(nativeMethodPointer, {
    onEnter: function(args) {
        console.log("Entered " + settingMethodName + " with " + settingMethodArgCount + " argument(s).");
        console.log("Value of `string id`: " + MonoApiHelper.StringToUtf8(args[3]));

        args[3] = MonoApiHelper.StringNew('This is the replaced value of `string c`.', domain);
    },
    onLeave: function onLeave(log, retval, state) {
        console.log("Left " + settingMethodName + ".");
    }
})

console.log(`'modify_function_argument.js' attached and ready.`)

The stacktrace I'm getting:


node fridax.js inject --scripts scripts/todo-modify.js
[*] Awaiting storage initialization.
[*] Awaiting USB device.
[*] Up and running on Android Emulator 5554.
? Which application do you want to inject? TodoREST
[*] Happy hacking.
[*] Attached to application (session: 3803).
[*] Injected a test script (this runs from within the injected application)!
domain: undefined
classInformation: 0x98f63330
methodInformation: 0x84b95058
Error: Export not found: mono_aot_get_method
    at vendors/frida-mono-api/mono-api.js:799
    at scripts/todo-modify.js:45
    at o (node_modules/browser-pack/_prelude.js:1)
    at r (node_modules/browser-pack/_prelude.js:1)
    at /script2.js:1217
^C[*] Script unloaded.
[*] Script unloaded.```

If you need more info let me know.
tijme commented 4 years ago

I just fixed this in the latest release. The mono_aot_get_method function was one of the signatures that I removed yesterday. I added it again and used some code from https://github.com/freehuntx/frida-mono-api/pull/6 (thanks to @alxbl) to prevent Fridax from crashing if some of the defined signatures do not exist in the Mono runtime.

tijme commented 4 years ago

By the way, please be aware of the fact that GetTasksAsync returns a Task instead of a raw value. You will need some extra code to be able to read the results of that task. I think a colleague of mine has some code snippets of this in his notes, feel free to ping me for those notes.

Techbrunch commented 4 years ago

@tijme Bad news but I don't think that this issue is resolved, you actually never removed the mono_aot_get_method function from the signatures. It is now twice in the list and I'm still getting the same error.

tijme commented 4 years ago

I will debug this when I have the time. I think it could be due to the APK not being compiled AOT, but I'm not sure. It looks like somehow the mono_aot_get_method method is not exported in your Mono runtime.

I think you need to use mono_compile_method (source) to JIT-compile the method. This will return a pointer to the native code produced, which you can use to intercept. You can use this script as a reference, since it uses mono_compile_method and I think it does exactly what you want. I'll try to make an example script when I have the time.

tijme commented 4 years ago

Would you mind sharing the APK that you compiled?

RobertDiep commented 4 years ago

It could very well be that your APK is compiled with an older version of Xamarin/Mono. AOT compilation for Android was introduced in version 5.1 (source, search for AotAssemblies).

Additionally, AOT compilation is not the default on Android.

Techbrunch commented 4 years ago

Hum then that must be the issue:

AOT Compilation

The AOT Compilation option (on the Packaging Properties page) enables Ahead-of-Time (AOT) compilation of assemblies. When this option is enabled, Just In Time (JIT) startup overhead is minimized by precompiling assemblies before runtime. The resulting native code is included in the APK along with the uncompiled assemblies. This results in shorter application startup time, but at the expense of slightly larger APK sizes.

The AOT Compilation option requires an Enterprise license or higher. AOT compilation is available only when the project is configured for Release mode, and it is disabled by default. For more information about AOT Compilation, see AOT.

Source: https://docs.microsoft.com/en-us/xamarin/android/deploy-test/release-prep/?tabs=macos#aot-compilation

Is there another way to hook into functions if AOT Compilation is disabled ?

Techbrunch commented 4 years ago

This is the last APK I compiled (just rename it to .apk):

com.companyname.todorest-Signed.zip

tijme commented 4 years ago

Is there another way to hook into functions if AOT Compilation is disabled ?

As I stated, I think you need to use mono_compile_method (source) to JIT-compile the method. This will return a pointer to the native code produced, which you can use to intercept. You can use this script as a reference, since it uses mono_compile_method and I think it does exactly what you want.

When I have the time I will take a look and see if I can make an example script for this. Or just a method which allows you to intercept both JIT and AOT compiled methods. 👍

Techbrunch commented 4 years ago

Thanks I'll look into that !

alxbl commented 4 years ago

As I stated, I think you need to use mono_compile_method

That's correct.

Just as a heads up, in the code you merged from my PR, I have included a helper method to hook JITTed/unJITTed (non-AOT) methods. Like @tijme said, AOT must be hooked differently.

I'm not sure if you fully merged it or only took the bits that deal with mono runtime image name, but the PR adds MonoApiHelper.Intercept which behaves like Interceptor but takes a mono class definition, a method name and hooks. There's no documentation as of right now (we have an upcoming blog on hooking managed methods in Mono, but it's been backlogged since February. Once it's done I'll link it to you if interested.)

The method is at https://github.com/freehuntx/frida-mono-api/pull/6/files#diff-1620eb23fbd98d7f220961acb8cf39b3R98

Hope this can help a bit!

Techbrunch commented 4 years ago

@tijme @alxbl It works like a charm thanks !

My script:

import { MonoApiHelper, MonoApi } from '../vendors/frida-mono-api'
import ClassHelper from '../libraries/class_helper'

// Intercept settings
var className = "CompanyName.ProjectName.Views.Web.Html.HtmlWebView";

// The root AppDomain is the initial domain created by the runtime when it is initialized. Programs execute on this AppDomain.
const domain = MonoApi.mono_get_root_domain()

// Get a reference to a certain class within the Xamarin application.
var classInformation = ClassHelper.getClassByName(className);

console.log('classInformation: ' + classInformation);

MonoApiHelper.Intercept(classInformation, 'GetElement', {
    onEnter: function(args) {
        console.log("Entered GetElement");

        var arg0 = MonoApiHelper.StringToUtf8(args[0]);
        console.log('Old arg0: ' + arg0)
        var newArg0 = MonoApiHelper.StringNew(url.replace('old', 'new'), domain);
        console.log('New arg0: ' + MonoApiHelper.StringToUtf8(newArg0))
        args[0] = newArg0;
    },
    onLeave: function onLeave(log, retval, state) {
        console.log("Left GetElement.");
    }
})

console.log(`'modify_function_argument.js' attached and ready.`)

FYI: I was also able to patch the APK permanantly using Objection (https://github.com/sensepost/objection/pull/329#issuecomment-609438408)

@alxbl Looking forward to read the blog post, I might write one in the meantime.

tijme commented 4 years ago

I'm glad that it's working for you @Techbrunch. Thanks to @alxbl for writing this method. I've added an example script in the repo that looks like the script above.

https://github.com/NorthwaveNL/fridax/blob/master/examples/jit_modify_class_function_argument.js

alxbl commented 4 years ago

@Techbrunch The blog is live now if you're still interested in reading it. It showcases how to bypass certificate pinning in Xamarin using Frida.

The work was done before Fridax released, but I added a reference to fridax at the end since I feel like this project's goal is a superset of what we were trying to achieve.

Cheers!

Techbrunch commented 4 years ago

@alxbl Thanks, for the link. I was wondering how did you go about intercepting the traffic in the first place ? In my case I had to redirect inbound requests by modifying the host file on the phone as suggested by Burp documentation (https://portswigger.net/support/using-burp-suites-invisible-proxy-settings-to-test-a-non-proxy-aware-thick-client-application). I was wondering if you had a better solution ?

alxbl commented 4 years ago

So, I replied in PM, but I'll leave the relevant part in this issue in case it can help others. I setup MITMProxy in transparent mode, and then used USB reverse tunneling (adb reverse) and iptables to force HTTP/S traffic through the reverse tunnel and through MITMProxy.

The commands:

# On the MITM with rooted Android phone plugged through USB
adb reverse tcp:8080 tcp:8080

# On the Phone (adb shell, su)
iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to-destination 127.0.0.1:8080
iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 127.0.0.1:8080
wczarnecki commented 1 year ago

By the way, please be aware of the fact that GetTasksAsync returns a Task instead of a raw value. You will need some extra code to be able to read the results of that task. I think a colleague of mine has some code snippets of this in his notes, feel free to ping me for those notes.

Hi, any chances to get those code snippets? :)