Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug - Hooking lab | java.lang.Object #18

Closed
4val0v opened this issue May 31, 2020 · 12 comments
Closed

Bug - Hooking lab | java.lang.Object #18

4val0v opened this issue May 31, 2020 · 12 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@4val0v
Copy link

4val0v commented May 31, 2020

Describe the bug:
Incorrect parameters generation for method overloading !


Steps to reproduce:
Custom Logging Implementation: (See methods)
Dump TAB

Go to "hook lab", select a Logging class: (highlighted)
Hook lab
Generated:

hookclass.d.overload("java.lang.Class","java.lang.String","java.lang.Object[]").implementation = function (v0,v1,v2)
Current Must be
java.lang.Object[] [Ljava.lang.Object;

Desktop:

  • Browser: Chrome v83
  • Python - Frida Tools version: 12.9.4

Smartphone:

  • Frida Server version: 12.9.4
@4val0v
Copy link
Author

4val0v commented May 31, 2020

  1. Because of this problem, hooking does not work (Only methods with Object) !
  2. For the void methods you do not need to generate out !

@m0bilesecurity
Copy link
Owner

Hey @4val0v can you kindly share the APK?
Many thanks

@4val0v
Copy link
Author

4val0v commented May 31, 2020

Hi @m0bilesecurity! No difference what will it be application

Here look:

Here is the worst code to reproduce the error: (Click)
public class MainActivity extends AppCompatActivity {
    public void test(char[] chars, Object[] objects, Object obj, String[][] strArr, Long[][] arrLong, String[][][][] lol) {
        System.out.println("Hello");
    }
}

Hook lab -> generated code:
Hook lab

wrong fixed
char[] [C
java.lang.Object[] [Ljava.lang.Object;
java.lang.String[][] [[Ljava.lang.String;
java.lang.Long[][] [[Ljava.lang.Long;
java.lang.String[][][][] [[[[Ljava.lang.String;

app-debug.apk.zip

@4val0v
Copy link
Author

4val0v commented May 31, 2020

I'll leave it here, suddenly someone will be interested:
https://stackoverflow.com/a/19098229/3469132

@4val0v
Copy link
Author

4val0v commented May 31, 2020

@m0bilesecurity Run the generated code using frida to see the error
Frida

frida -U --no-pause -f com.wtf.myapplication -l test.js

@m0bilesecurity m0bilesecurity added bug Something isn't working help wanted Extra attention is needed labels Jun 1, 2020
@m0bilesecurity
Copy link
Owner

Hey @4val0v, many thanks for all the precious info provided.
The issue is clear, notation for overloading methods with an array as arg is in smali.
So according to this table:

Element Type         Encoding
boolean              Z
byte                 B
char                 C
class or interface   Lclassname;
double               D
float                F
int                  I
long                 J
short                S

we need to adjust the laodmethod in default.js in order to convert arrays in the right notation. Does exist something ready to use?

@4val0v
Copy link
Author

4val0v commented Jun 2, 2020

@m0bilesecurity Java - Frida methods: https://neo-geo2.gitbook.io/adventures-on-security/frida-scripting-guide/methods

Java Frida
int int
byte byte
short short
long long
float float
double double
char char
<Object> (eg. String) <package>.<Object> (eg. java.lang.String)
int[] [I
byte[] [B
short[] [S
long[] [J
float[] [F
double[] [D
char[] [C
<Object>[] [L<package>.<Object> (eg. [Ljava.lang.String)

https://android.googlesource.com/platform/dalvik/+/gingerbread/docs/dex-format.html

@4val0v
Copy link
Author

4val0v commented Jun 2, 2020

@m0bilesecurity If you have time, you can continue...

Java.perform(function() {

    var targetClass = Java.use("com.wtf.myapplication.MainActivity");
    var currentMethods = targetClass.class.getDeclaredMethods(); // all methods declared in a Java Class

    console.log("Count methods for class: " + currentMethods.length);

    for (var i = 0; i < currentMethods.length; i++) {
        var args = currentMethods[i].toString().split('(')[1].split(')')[0].split(',');

        console.log("\nCurrent method: " + currentMethods[i].getName());
        console.log("args: " + args);
        
        // TODO for i
     
     };
     
});

Take a look here: https://juejin.im/post/5e967cb5f265da47a74134a6#heading-9 (It didn't work for me)

@m0bilesecurity
Copy link
Owner

m0bilesecurity commented Jun 2, 2020

Please check this quick and dirty solution.

// check if the current arg is an array
var arg = args_array[i]
if(arg.includes("[]")){
  // arg is an array --> smali notation conversion
      if (arg.includes(".")) arg="L"+arg+";"
      else if((/boolean/i).test(arg)) arg="Z"+arg.replace(/boolean/i, ""); 
      else if((/byte/i).test(arg)) arg="B"+arg.replace(/byte/i, ""); 
      else if((/char/i).test(arg)) arg="C"+arg.replace(/char/i, ""); 
      else if((/double/i).test(arg)) arg="D"+arg.replace(/double/i, ""); 
      else if((/float/i).test(arg)) arg="F"+arg.replace(/float/i, ""); 
      else if((/int/i).test(arg)) arg="I"+arg.replace(/int/i, ""); 
      else if((/long/i).test(arg)) arg="J"+arg.replace(/long/i, ""); 
      else if((/short/i).test(arg)) arg="S"+arg.replace(/short/i, ""); 
      else arg="L"+arg+";"
  }
  while(arg.includes("[]")){
      arg=arg.replace("[]", "")
      arg="["+arg
  }

Some tests are needed. If you want to help me, just replace the loadmethods function inside default.js with the code below:

loadmethods: function (loaded_classes) {
    var loaded_methods = {};
    Java.perform(function () {
      loaded_classes.forEach(function (className, index) {
        var jClass;
        var classMethods_dirty;

        //catch possible issues
        try{
          jClass = Java.use(className);
          classMethods_dirty = jClass.class.getDeclaredMethods();
        }catch(err){
          send("Exception while loading methods for "+className);
          //skip current loop
          loaded_methods[className] = []
          return;
        }
        var classMethods = []

        classMethods_dirty.forEach(function (m) {
          var method_and_args = {};
          //Cleaning up
          m = m.toString();
          //add info for the UI
          method_and_args["ui_name"] = m.replace(className + ".", "")
          // Remove generics from the method
          while (m.includes("<")) {
            m = m.replace(/<.*?>/g, "");
          }
          // remove "Throws" 
          if (m.indexOf(" throws ") !== -1) {
            m = m.substring(0, m.indexOf(" throws "));
          }
          // remove scope and return type declarations 
          m = m.slice(m.lastIndexOf(" "));
          // remove the class name
          m = m.replace(className + ".", "");

          // remove the signature (args) 
          method_and_args["name"] = m.split("(")[0].trim()

          // get the args 
          var args_dirty = ((/\((.*?)\)/.exec(m)[1]).trim())

          // add quotes between every arg
          var args_array = args_dirty.split(",")
          var args_srt = ""
          for (var i = 0; i < args_array.length; i++) {

            // check if the current arg is an array
            var arg = args_array[i]
            if(arg.includes("[]")){
              // arg is an array --> smali notation conversion
                  if (arg.includes(".")) arg="L"+arg+";"
                  else if((/boolean/i).test(arg)) arg="Z"+arg.replace(/boolean/i, ""); 
                  else if((/byte/i).test(arg)) arg="B"+arg.replace(/byte/i, ""); 
                  else if((/char/i).test(arg)) arg="C"+arg.replace(/char/i, ""); 
                  else if((/double/i).test(arg)) arg="D"+arg.replace(/double/i, ""); 
                  else if((/float/i).test(arg)) arg="F"+arg.replace(/float/i, ""); 
                  else if((/int/i).test(arg)) arg="I"+arg.replace(/int/i, ""); 
                  else if((/long/i).test(arg)) arg="J"+arg.replace(/long/i, ""); 
                  else if((/short/i).test(arg)) arg="S"+arg.replace(/short/i, ""); 
                  else arg="L"+arg+";"
              }
              while(arg.includes("[]")){
                  arg=arg.replace("[]", "")
                  arg="["+arg
              }

            args_srt = args_srt + ("\"" + arg + "\"")
            //add a comma if the current item is not the last one
            if (i + 1 < args_array.length) args_srt = args_srt + ",";
          }

          method_and_args["args"] = args_srt
          classMethods.push(method_and_args);

        });

        loaded_methods[className] = classMethods;
      });

    })
    //DEBUG console.log("loaded_classes.length: " + loaded_classes.length)
    //DEBUG console.log("loaded_methods.length: " + Object.keys(loaded_methods).length)
    return loaded_methods;
  },

@m0bilesecurity
Copy link
Owner

@4val0v can you kindly add a button to your test app in order to call the "test" method?

@4val0v
Copy link
Author

4val0v commented Jun 3, 2020

@m0bilesecurity Now everything is OK !

Still need fixed a functional on page "Dump TAB" -> "Hook all methods"

@m0bilesecurity
Copy link
Owner

m0bilesecurity commented Jun 3, 2020

FIXED 🚀! Check the last commit.
I prefer to keep the signature of the method without the smali notation because it's more easy to read.

Under the hood, the hook is managed correctly also in the dump page ;)
I tested your sample app and now I'm able to hook the "test" method.
Many thanks for the detailed info provided!
Best
Paolo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants