androidhookxposed-frameworkxposed

Can Xposed log current method name?


I want to test an application but it's too large to do by static approach.

I started googling. I found a interesting tool called Xposed Framework.

I read a lot of documents / examples. But I cant find the theads about getting method name of application.

My purpose is to log current method name when I press a button in app. Which parameters are sent when the methods are called?

For more information, the application I want to test is a chat application. I want to check that is it secured to use ? Is it true that developers claims the application uses blah blah blah encryption ? Is it real end-to-end encryption?.

According to the large of application, I need some tools to help me analyse this. When I send a message what methods are called ? what values are sent along with ?


Solution

  • If it is opensource you can easily insert a few logs in the source code and recompile. Some code coverage tools allow you to log the executed methods but i am unsure about the parameters (e.g. EMMA coverage).

    If it is closed-source then you can do it with Xposed, but it has some challenges. Xposed allows you to hook Java methods, if it is opensource you can lookup the specific methods you want to intercept and print their parameters. If it is closed source, you can always check the method names through decompiling the app with apktool.

    Check the Xposed tutorial on how to register hooks. Assuming you created your class that extends XC_MethodHook, the following methods should do the trick for primitive parameters:

    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
    
        String threadname = Thread.currentThread().getName();
        String d = _dateFormat.format(new Date());
        Class<?> cls = param.this.getClass();
    
        //Extend XC_MethodHook to receive the mthName in the constructor
        //and store it as a static field.
    
        Log.v("MethodInvocation", "[A][" + d + "]"
                + "[" + cls.getName() + "."
                + mthName 
                + "(" + args(param) + ")" + " = " + result(param)
                + "]" + " [" + threadname + "]");
    }
    
    public boolean shouldPrintContent(Object o) {
        if (o.getClass().isPrimitive()
            || o.getClass().getName().contains("java.lang"))
                return true;
        return false;
    }
    
    public String args(MethodHookParam param) {
        String args = "";
        int counter = 0;
        if (param != null) {
            for (Object o : param.args) {
                if (counter > 0)
                    args += ",";
    
                // argument can be null
                if (o == null) {
                    args += "null";
    
    
                } else { // if it is an object lets print its type and content.
                    args += printclean(o.getClass().getName());
    
                    if (shouldPrintContent(o)) {
    
                        args += ":" + printclean(o.toString());
                    } else
                        args += ":nonPrimitiveOrJavaLang";
                }
                counter++;
            }
        }
        return args;
    }
    
    //avoid identation chars in strings
    public String printclean(String str) {
        char[] res = str.toCharArray();
        for (int i = 0; i < str.length(); i++) {
            if (res[i] == '\n' || res[i] == '\r' || res[i] == '\t'
                    || res[i] == '[' || res[i] == ']') {
                res[i] = '*';
            }
        }
        return String.valueOf(res);
    }
    
    public String result(MethodHookParam param) {
        String res = "";
        Object retobj = param.getResult();
        if (retobj == null) {
            res += "null";
        } else {
            res += printclean(retobj.getClass().getName());
    
            if (shouldPrintContent(retobj)) {
                res += printclean(retobj.toString());
            } else
                res += "(nonPrimitiveOrJavaLang)";
        }
    
        return res;
    }
    

    Note that printing any object is not trivial. Here I printed only known types like primitives or other java.lang objects. More complex objects (including collections) you can try using Gson to represent them but this also comes with limitations (e.g. can't often handle reference loops).

    Finally, be careful with what method you hook as hooking and logging methods that are called often will impact the performance of the app.

    Good luck!