篇幅有限 完整内容及源码关注公众号:ReverseCode,发送 冲 
抓包 安装某东9.2.2,启动postern,开启socks抓包
分析 jadx反编译搜索sign=或者getSign,出现地方太多随机挑一个
跟进HMACSHA256
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private static String HMACSHA256(byte[] bArr, byte[] bArr2) {     try {         SecretKeySpec secretKeySpec = new SecretKeySpec(bArr2, "HmacSHA256");         Mac instance = Mac.getInstance("HmacSHA256");         instance.init(secretKeySpec);         return byte2hex(instance.doFinal(bArr));     } catch (NoSuchAlgorithmException e2) {         e2.printStackTrace();         return null;     } catch (InvalidKeyException e3) {         e3.printStackTrace();         return null;     } } 
adb shell dumpsys activity activities|more 查看当前运行app的进程名为com.jingdong.app.mall,尝试使用frida对SecretKeySpec进行hook时SecretKeySpec报错secretKeySpec.$init.overload().implementation
启动了frida-server,app就进程卡死,被检测到了frida,具体方案查看多种特征检测 Frida 
尝试修改frida名字为fs1280arm64,不以默认端口27047启动./fs1280arm64 -l 127.0.0.1:8080,转发端口到主机adb forward tcp:8080 tcp:8080
Frida frida -H 127.0.0.1:8080 com.jingdong.app.mall -l jd.js 根据反编译源码Mac instance = Mac.getInstance("HmacSHA256");进行hook,结果并没有hook上,检测frida可以通过hluda反检测hluda-server-15.1.12-android-arm64.xz 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 setImmediate(function(){     Java.perform(function () {         var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');         secretKeySpec.$init.overload('[B','java.lang.String').implementation = function (a,b) {             var result = this.$init(a, b);             console.log(">>> 算法名" + b);             return result;         }         var mac = Java.use('javax.crypto.Mac');         mac.getInstance.overload('java.lang.String').implementation = function (a) {             // showStacks();             var result = this.getInstance(a);             console.log("mac ======================================");             console.log("算法名:" + a);             return result;         }         mac.update.overload('[B').implementation = function (a) {             this.update(a);             console.log("mac ======================================");             console.log("update:" + bytesToString(a))         }         mac.update.overload('[B','int','int').implementation = function (a,b,c) {             this.update(a,b,c)             console.log("mac ======================================");             console.log("update:" + bytesToString(a) + "|" + b + "|" + c);         }         mac.doFinal.overload().implementation = function () {             var result = this.doFinal();             console.log("mac ======================================");             console.log("doFinal结果(hex):" + bytesToHex(result));             console.log("doFinal结果(base64):" + bytesToBase64(result));             return result;         }         mac.doFinal.overload('[B').implementation = function (a) {             var result = this.doFinal(a);             console.log("mac ======================================");             console.log("doFinal参数:" + bytesToString(a));             console.log("doFinal结果(hex):" + bytesToHex(result));             console.log("doFinal结果(base):" + bytesToBase64(result));             return result;         }     }) }) 
我们尝试通过hook http,打印调用栈获取堆栈信息,或者hook系统时间函数(因为参数中拼装了st的时间戳),找到sign可能存在的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function Where(stack){     var at = ""     for(var i = 0; i < stack.length; ++i){         at += stack[i].toString() + "\n"     }     return at } var OkHttpClient = Java.use("okhttp3.OkHttpClient"); OkHttpClient.newCall.implementation = function (request) {     var result = this.newCall(request);     console.log(request.toString());     var stack = threadinstance.currentThread().getStackTrace();     console.log("http >>> Full call stack:" + Where(stack)); 	     return result; }; var SystemClass = Java.use('java.lang.System'); SystemClass.currentTimeMillis.implementation = function(){ 	var result = this.currentTimeMillis(); 	console.log("==== " + result + " ===="); 	return result; } 
在jadx中搜索com.jingdong.sdk.jdupgrade.inner也一无所获,大概率在so里完成的加密。通过在so中搜索grep "sign" *.so或者strings -f *.so | grep "Sign"批量搜索so中的字符串
使用ida打开libjdbitmapkit.so,搜索sign静态绑定函数,尝试hook该方法com.jingdong.common.utils.BitmapkitUtils
1 2 3 4 5 6 7 8 9 10 var checkHookG = Java.use('com.jingdong.common.utils.BitmapkitUtils'); checkHookG.getSignFromJni.implementation = function(a,b,c,d,e,f){ 	var result = this.getSignFromJni(a,b,c,d,e,f); 	console.log(">>> checkHookG = " + b + ' / ' + c + ' / ' + d + ' / ' + d + ' / ' + f + ' \n rc= ' + result); 	// var stack = threadinstance.currentThread().getStackTrace();     // console.log("Full call stack:" + Where(stack)); 	return result; } 
查看BitmapkitUtils类,一共5个参数都是String
主动调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function hookBitmapkitUtils() {     Java.perform(function () {         var currentApplication = Java.use('android.app.ActivityThread').currentApplication();          var context = currentApplication.getApplicationContext();         Java.choose("com.jingdong.common.utils.BitmapkitUtils",{             onMatch:function(instance){                 console.log("found instance =>",instance);                 var a = "search";                 var b = '{"addrFilter":"1","addressId":"0","articleEssay":"1","deviceidTail":"35","exposedCount":"0","gcLat":"0.0","gcLng":"0.0","imagesize":{"gridImg":"709x709","listImg":"455x455","longImg":"709x908"},"insertArticle":"1","insertScene":"1","insertedCount":"0","isCorrect":"1","keyword":"gg","locLat":"","locLng":"","newMiddleTag":"1","newVersion":"3","oneBoxMod":"1","orignalSearch":"1","orignalSelect":"1","page":"1","pageEntrance":"1","pagesize":"10","pvid":"","searchVersionCode":"9180","secondInsedCount":"0","showShopTab":"yes","showStoreTab":"1","stock":"1"}'                 var c = '-accf8528c046'                 var d = '-accf8528c046'                 var e = '9.2.2'                 var signature = instance.getSignFromJni(context, a, b, c, d, e)                 console.log(signature)                 return signature             },onComplete:function(){                 console.log('Search complete')             }         })     }) } 
Xposed 尝试主动调用com.jingdong.common.utils.BitmapkitUtils的getSignFromJni,传入通过frida hook得到的参数信息,暴露http请求
HookLoader public static native String getSignFromJni(Context context, String str, String str2, String str3, String str4, String str5);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 public class HookLoader implements IXposedHookLoadPackage {     private final static String TAG = "onejane";     public static void log(String s) {         Log.i(TAG, s);     }     public static Context applicationContext;     public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {         if (loadPackageParam.packageName.equals("com.jingdong.app.mall")) {             log("Im comming jd 1");             try {                 Class<?> ContextClass = XposedHelpers.findClass("android.content.ContextWrapper", loadPackageParam.classLoader);                 XposedHelpers.findAndHookMethod(ContextClass, "getApplicationContext", new XC_MethodHook() {                     @Override                     protected void afterHookedMethod(MethodHookParam param) throws Throwable {                         super.afterHookedMethod(param);                         if (applicationContext != null)                             return;                         applicationContext = (Context) param.getResult();                         log("-->得到上下文");                     }                 });             } catch (Throwable t) {                 log("-->获取上下文出错");                 // XposedBridge.log(t);             }             // http server             class myHttpServer extends NanoHTTPD {                 private static final String REQUEST_ROOT = "/";                 public myHttpServer() throws IOException {                     // 端口是8088,也就是说要通过http://127.0.0.1:8088来访当问                     super(8888);                     start(NanoHTTPD.SOCKET_READ_TIMEOUT, true);                     log("---onejane Server---");                 }                 @Override                 public Response serve(IHTTPSession session) {                     // log("serve");                     //这个就是之前分析,重写父类的一个参数的方法,                     //这里边已经把所有的解析操作已经在这里执行了                     return super.serve(session);                 }                 @Override                 public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) {                     // log("serve xxx");                     //这就是上边的serve方法最后一行调用的那个过时的方法,这里简单的做个判断就好了                     // if (!method.equals(Method.POST)) {//判断请求方式是否争取                     //    return newFixedLengthResponse("the request method is incoorect");                     // }                     log(uri);                     String strA = "";                     String strB = "";                     for (Map.Entry<String, String> entry : parms.entrySet()) {                         strA += " parms Key = " + entry.getKey() + ", Value = " + entry.getValue();                         log("parms Key = " + entry.getKey() + ", Value = " + entry.getValue());                     }                     for (Map.Entry<String, String> entry : files.entrySet()) {                         strB += " files Key = " + entry.getKey() + ", Value = " + entry.getValue();                         log("files Key = " + entry.getKey() + ", Value = " + entry.getValue());                     }                     Class<?> clazzJDUtils = null;                     try {                         clazzJDUtils = loadPackageParam.classLoader.loadClass("com.jingdong.common.utils.BitmapkitUtils");                         log("load class:" + clazzJDUtils);                     } catch (Exception e) {                         log("load class err:" + Log.getStackTraceString(e));                         return newFixedLengthResponse("BitmapkitUtils load class is null");                     }                     if (StringUtils.containsIgnoreCase(uri, "getSignFromJni")) {//判断uri是否正确                         String str = parms.get("str");                         String str2 = parms.get("str2");                         String str3 = parms.get("str3");                         String str4 = parms.get("str4");                         String str5 = parms.get("str5");                         log("getSignFromJni:" + str + " / " + str2 + " / " + str3 + " / " + str4 + " / " + str5);                         if (!StringUtils.isEmpty(str)) {//判断post过来的数据是否正确                             return getSignFromJni(clazzJDUtils, str, str2, str3, str4, str5);                         } else {                             return newFixedLengthResponse("getSignFromJni postData is null, " + strA + strB);                         }                     }                     //判断完了开始解析数据,如果是你想要的数据,那么你就给返回一个正确的格式就好了                     //举个栗子:return newFixedLengthResponse("{\"result\":0,\"success\":true}");                     return super.serve(uri, method, headers, parms, files);                 }                 public Response getSignFromJni(Class<?> clazzUse, String str, String str2, String str3, String str4, String str5) {                     if (applicationContext != null) {                         String rc = (String) XposedHelpers.callStaticMethod(clazzUse, "getSignFromJni", applicationContext, str, str2, str3, str4, str5);                         log("getSignFromJni = " + rc);                         return newFixedLengthResponse(rc);                     }                     return newFixedLengthResponse("getSignFromJni Context is null");                 }             }             new myHttpServer();         }     } } 
python调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requests def getKey(k1, k2, k3, k4, k5):     data = {         "str": k1,         "str2": k2,         "str3": k3,         "str4": k4,         "str5": k5,     }     sign = requests.get("http://172.20.103.239:8888/getSignFromJni", data).text     return sign if __name__ == '__main__':     body = '{"addrFilter":"1","addressId":"0","articleEssay":"1","deviceidTail":"35","exposedCount":"0","gcLat":"0.0","gcLng":"0.0","imagesize":{"gridImg":"709x709","listImg":"455x455","longImg":"709x908"},"insertArticle":"1","insertScene":"1","insertedCount":"0","isCorrect":"1","keyword":"冰箱","localNum":"0","newMiddleTag":"1","newVersion":"3","oneBoxMod":"1","orignalSearch":"1","orignalSelect":"1","page":"1","pageEntrance":"1","pagesize":"10","pvid":"","searchVersionCode":"9180","secondInsedCount":"0","showShopTab":"yes","showStoreTab":"1","stock":"1"}'     sign = getKey("search",                   body,                   "-accf8528c046",                   "-accf8528c046",                   "9.2.2")     print(sign) 
ExAndroidNativeEmu OnejaneNdk Android Studio新建C++项目,src/main/cpp/native-lib.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 // ---- 静态注册 ---- extern "C" JNIEXPORT jstring JNICALL Java_com_example_onejanendk_MainActivity_stringFromJNI(         JNIEnv *env,         jobject /* this */) {     std::string hello = "Hello from C++";     return env->NewStringUTF(hello.c_str()); } // ---- 动态注册 ---- // 获取数组的大小 # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) // 指定要注册的类,对应完整的java类名 #define JNIREG_CLASS "com/example/onejanendk/MainActivity" // 返回字符串"hello load jni" JNIEXPORT jstring JNICALL native_hello(JNIEnv *env, jclass clazz) {     return env->NewStringUTF("onejane hello load jni."); } // Java和JNI函数的绑定表 static JNINativeMethod method_table[] = {         {"HelloLoad", "()Ljava/lang/String;", (void *) native_hello},//绑定 }; // 注册native方法到java中 static int registerNativeMethods(JNIEnv *env, const char *className,                                  JNINativeMethod *gMethods, int numMethods) {     jclass clazz;     clazz = env->FindClass(className);     if (clazz == NULL) {         return JNI_FALSE;     }     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {         return JNI_FALSE;     }     return JNI_TRUE; } int register_ndk_load(JNIEnv *env) {     // 调用注册方法     return registerNativeMethods(env, JNIREG_CLASS, method_table, NELEM(method_table)); } JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {     JNIEnv *env = NULL;     jint result = -1;     if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {         return result;     }     register_ndk_load(env);     // 返回jni的版本     return JNI_VERSION_1_4; } 
com/example/onejanendk/MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MainActivity extends AppCompatActivity {     // Used to load the 'native-lib' library on application startup.     static {         System.loadLibrary("native-lib");     }     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         // Example of a call to a native method         TextView tv = findViewById(R.id.sample_text); //        tv.setText(stringFromJNI());         tv.setText(HelloLoad());     }       public native String stringFromJNI();     public native String HelloLoad(); } 
生成的包中包括'x86', 'armeabi-v7a', 'arm64-v8a', 'x86_64'这些cpu平台的libnative-lib.so
1 git clone https://github.com/maiyao1988/ExAndroidNativeEmu 
新建runemu.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 def hook_code(mu, address, size, user_data):     try:         emu = user_data         if (not emu.memory.check_addr(address, UC_PROT_EXEC)):             logger.error("addr 0x%08X out of range"%(address,))             sys.exit(-1)         #         # androidemu.utils.debug_utils.dump_registers(mu, sys.stdout)         # androidemu.utils.debug_utils.dump_code(emu, address, size, g_cfd)     except Exception as e:         logger.exception("exception in hook_code")         sys.exit(-1)     # # def hook_mem_read(uc, access, address, size, value, user_data):     pc = uc.reg_read(UC_ARM_REG_PC)         pass def hook_mem_write(uc, access, address, size, value, user_data):     pc = uc.reg_read(UC_ARM_REG_PC)     pass class MainActivity(metaclass=JavaClassDef, jvm_name='com/example/onejanendk/MainActivity'):     def __init__(self):         pass     @java_method_def(name='HelloLoad', signature='()Ljava/lang/String;', native=True)     def hello_load(self, mu):         pass      def main():     filename = "./libnative-lib.so"          # Initialize emulator     emulator = Emulator(         vfp_inst_set = True,         vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")     )     # Register Java class.     emulator.java_classloader.add_class(MainActivity)     emulator.mu.hook_add(UC_HOOK_CODE, hook_code, emulator)     emulator.mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write)     emulator.mu.hook_add(UC_HOOK_MEM_READ, hook_mem_read)     # Load all libraries.     lib_module = emulator.load_library(filename)     # androidemu.utils.debug_utils.dump_symbols(emulator, sys.stdout)     # Show loaded modules.     logger.info("Loaded modules:")     for module in emulator.modules:         logger.info("=> 0x%08x - %s" % (module.base, module.filename))     logger.info(">>> libnative-lib.so load_base = 0x%x" % (lib_module.base) )              try:         # Run JNI_OnLoad.         # JNI_OnLoad will call 'RegisterNatives'.         emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00)         # 第一种方法,直接调用         strRc = emulator.call_symbol(lib_module, 'Java_com_example_onejanendk_MainActivity_stringFromJNI',emulator.java_vm.jni_env.address_ptr,0x00)         print("stringFromJNI result call: %s" % strRc)         # 第二种方法,通过类成员函数来调用         # Do native stuff.         main_activity = MainActivity()         logger.info("Response from JNI call: %s" % main_activity.hello_load(emulator))         # Dump natives found.         logger.info("Exited EMU.")         logger.info("Native methods registered to MainActivity:")         for method in MainActivity.jvm_methods.values():             if method.native:                 logger.info("- [0x%08x] %s - %s" % (method.native_addr, method.name, method.signature))                      except UcError as e:         print("Exit at 0x%x" % emulator.mu.reg_read(UC_ARM_REG_PC))         raise 
python模拟 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 def main():     filename = "../jd/libjdbitmapkit.so"          # Initialize emulator     emulator = Emulator(         vfp_inst_set = True,         vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")     )     # Register Java class.          emulator.mu.hook_add(UC_HOOK_CODE, hook_code, emulator)     emulator.mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write)     emulator.mu.hook_add(UC_HOOK_MEM_READ, hook_mem_read)     # Load all libraries.     lib_module = emulator.load_library(filename)     # androidemu.utils.debug_utils.dump_symbols(emulator, sys.stdout)     # Show loaded modules.     logger.info("Loaded modules:")     for module in emulator.modules:         logger.info("=> 0x%08x - %s" % (module.base, module.filename))     logger.info(">>> libnative-lib.so load_base = 0x%x" % (lib_module.base) )              try:         # Run JNI_OnLoad.         # JNI_OnLoad will call 'RegisterNatives'.         emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00)       except UcError as e:         print("Exit at 0x%x" % emulator.mu.reg_read(UC_ARM_REG_PC))         raise 
python runemu.py
报错如下:RuntimeError: Could not find class 'com/jingdong/common/utils/BitmapkitUtils' for JNIEnv.
上文已经知道sign静态绑定函数是libjdbitmapkit.so的Java_com_jingdong_common_utils_BitmapkitUtils_getSignFromJni,在JNI中注册 BitmapkitUtils类 ,JavaClassDef指定类型
1 2 3 4 5 6 7 from androidemu.java.java_field_def import JavaFieldDef class BitmapkitUtils(metaclass=JavaClassDef, jvm_name='com/jingdong/common/utils/BitmapkitUtils'):     def __init__(self):         pass           emulator.java_classloader.add_class(BitmapkitUtils) 
报错如下:RuntimeError: Could not find static field ('a', 'Landroid/app/Application;') in class com/jingdong/common/utils/BitmapkitUtils.
1 2 3 4 5 6 7 8 9 class Application(metaclass=JavaClassDef, jvm_name='android/app/Application'):     def __init__(self):         pass class BitmapkitUtils(metaclass=JavaClassDef, jvm_name='com/jingdong/common/utils/BitmapkitUtils',jvm_fields=[JavaFieldDef("a", "Landroid/app/Application;", True, Application())]):     def __init__(self):         pass emulator.java_classloader.add_class(Application) emulator.java_classloader.add_class(BitmapkitUtils) 
报错如下:RuntimeError: Could not find class 'android/app/Activity' for JNIEnv.
1 2 3 4 class Activity(metaclass=JavaClassDef, jvm_name='android/app/Activity'):     def __init__(self):         pass emulator.java_classloader.add_class(Activity) 
报错如下:NameError: name 'Application' is not defined
添加Application
1 2 3 4 class Application(metaclass=JavaClassDef, jvm_name='android/app/Application'):     def __init__(self):         pass emulator.java_classloader.add_class(Application) 
报错如下:RuntimeError: Could not find class 'android/content/pm/Signature' for JNIEnv.
添加Signature
1 2 3 4 class Signature(metaclass=JavaClassDef, jvm_name='android/content/pm/Signature'):     def __init__(self):         pass emulator.java_classloader.add_class(Signature) 
报错如下:RuntimeError: Could not find method ('getPackageManager', '()Landroid/content/pm/PackageManager;') in class android/app/Activity.
1 2 3 4 5 6 7 8 class Activity(metaclass=JavaClassDef, jvm_name='android/app/Activity'):     def __init__(self):         pass     @java_method_def(name='getPackageManager', signature='()Landroid/content/pm/PackageManager;' , native=False)     def getPackageManager(self, mu):         logger.info("Im in Activity.getPackageManager")         pass 
报错如下:没有call_object_method的实现
getPackageManager方法大多在JNI_OnLoad中实现,IDA打开后进入check_status(v21),CallObjectMethod方法有三个参数,a1是JNIEnv,dword_175DC是十六进制地址,v4是方法id
call_object_method函数报错,从jni_env.py里面找到这个函数,发现没有实现这个函数,手动实现如下
1 2 3 4 @native_method def call_object_method(self, mu,env,obj_idx, method_id):     logger.debug("JNIEnv->call_object_method(%d,%s) was called" % (obj_idx,method_id) )     return self.__call_xxx_method(mu, env, obj_idx, method_id, None, 1) 
继续运行runemu.py 报错如下,3523215368 方法调用失败
报错的obj_idx和method_id打印出来为JNIEnv->call_object_method(4096,3523215368) was called
在jni_env.py中的find_class添加log
1 2 3 rc = self.add_local_reference(jclass(clazz)) logger.debug("JNIEnv->FindClass(%s) was called, rc = %d" % (name,rc)) return rc 
说明3523215368是android/app/Activity类的getPackageManager方法,该方法之前已经补过了,在__call_xxx_method中添加logger.debug(type(pyobj)),发现该对象是<class '__main__.Application'>,因为 Application 和 Activity 都是 ContextWrapper 的子类, getPackageManager 是 ContextWrapper 里的方法,获取的两个 jmethodid 自然是一样的。
我们在java_method_def.py中的方法java_method_def加一个参数jvm_id,并在初始化的时候做兼容
1 2 3 4 5 6 7 def __init__(self, func_name, func, name, signature, native, args_list=None, modifier=None, ignore=None,jvm_id = None):     if jvm_id == None:         self.jvm_id = next_method_id()     else:         self.jvm_id = jvm_id     logger.debug("JavaMethodDef name =%s,jvm_id = %s" % (name,self.jvm_id)) 
在runemu.py中为Application和Activity都加上getPackageManager 方法,设置jvm_id相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Application(metaclass=JavaClassDef, jvm_name='android/app/Application'):     def __init__(self):         pass     @java_method_def(name='getPackageManager', signature='()Landroid/content/pm/PackageManager;' , native=False,jvm_id=0xd2000000+0x1000)         def getPackageManager(self, mu):         logger.info("Im in Application.getPackageManager")         pass class Activity(metaclass=JavaClassDef, jvm_name='android/app/Activity'):     def __init__(self):         pass       @java_method_def(name='getPackageManager', signature='()Landroid/content/pm/PackageManager;' , native=False,jvm_id=0xd2000000+0x1000)     def getPackageManager(self, mu):         logger.info("Im in Activity.getPackageManager")         pass 
对抗AndroidNativeEmu思路,拿到Application的对象,然后也去调用Activity类的getPackageManager,跑到这个call_object_method,AndroidNativeEmu就会崩溃
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Java_com_example_onejanendk_MainActivity_stringFromJNI(         JNIEnv *env,         jobject /* this */) {     jclass AppClass = env->FindClass("android/app/Application");     jmethodID midA = env->GetMethodID(AppClass, "getPackageManager","()Landroid/content/pm/PackageManager;");     Method *pMid_a = (Method *) midA;     LOGD("midA = 0x%08X", midA);     LOGD("pMid_a = 0x%08X", pMid_a->nativeFunc);     jclass AppClassB = env->FindClass("android/app/Activity");     jmethodID midB = env->GetMethodID(AppClassB, "getPackageManager","()Landroid/content/pm/PackageManager;");     Method *pMid_b = (Method *) midB;     LOGD("midB = 0x%08X", midB);     LOGD("pMid_b = 0x%08X", pMid_b->nativeFunc);     //获取Activity Thread的实例对象     jclass activityThread = env->FindClass("android/app/ActivityThread");     jmethodID currentActivityThread = env->GetStaticMethodID(activityThread,                                                              "currentActivityThread",                                                              "()Landroid/app/ActivityThread;");     jobject at = env->CallStaticObjectMethod(activityThread, currentActivityThread);     //获取Application,也就是全局的Context     jmethodID getApplication = env->GetMethodID(activityThread, "getApplication",                                                 "()Landroid/app/Application;");     jobject ApplicationObj = env->CallObjectMethod(at, getApplication);     jobject packageManager = env->CallObjectMethod(ApplicationObj, midB);     std::string hello = "Hello from C++";     return env->NewStringUTF(hello.c_str()); } 
Could not find method ('getPackageName', '()Ljava/lang/String;') in class android/app/Activity.回到上面的报错,添加getPackageName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Application(metaclass=JavaClassDef, jvm_name='android/app/Application'):     def __init__(self):         pass     @java_method_def(name='getPackageManager', signature='()Landroid/content/pm/PackageManager;' , native=False,jvm_id=0xd2000000+0x1000)     def getPackageManager(self, mu):         logger.info("Im in Application.getPackageManager")         pass     @java_method_def(name='getPackageName', signature='()Ljava/lang/String;' , native=False,jvm_id=0xd2000000+0x1004)     def getPackageName(self, mu):         logger.info("Im in Application.getPackageName")         return "com.jingdong.app.mall" class Activity(metaclass=JavaClassDef, jvm_name='android/app/Activity'):     def __init__(self):         pass     @java_method_def(name='getPackageManager', signature='()Landroid/content/pm/PackageManager;' , native=False,jvm_id=0xd2000000+0x1000)     def getPackageManager(self, mu):         logger.info("Im in Activity.getPackageManager")         pass     @java_method_def(name='getPackageName', signature='()Ljava/lang/String;' , native=False,jvm_id=0xd2000000+0x1004)     def getPackageName(self, mu):         logger.info("Im in Application.getPackageName")         return "com.jingdong.app.mall" 
3523215368 从前面的日志能找到是getPackageInfo方法的jvm_id,打开androidemu.utils.debug_utils.dump_code(emu, address, size, g_cfd) 开关,会输出汇编代码,可以方便定位出错的代码位置。
IDA打开so,跳到0x00002BF4查看
之前调用 Application.getPackageManager() 来返回的PackageManager对象,在之前的代码里我们只打印了一下日志,并没有实现这个函数,引入原作者定义的PackageManager类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from androidemu.java.classes.package_manager import * class Application(metaclass=JavaClassDef, jvm_name='android/app/Application'):     def __init__(self):         self.__pkg_Manager = PackageManager()                 pass     @java_method_def(name='getPackageManager', signature='()Landroid/content/pm/PackageManager;' , native=False,jvm_id=0xd2000000+0x1000)         def getPackageManager(self, mu):         logger.info("Im in Application.getPackageManager")         return self.__pkg_Manager             @java_method_def(name='getPackageName', signature='()Ljava/lang/String;' , native=False,jvm_id=0xd2000000+0x1004)     def getPackageName(self, mu):         logger.info("Im in Application.getPackageName")         return "com.jingdong.app.mall" class Activity(metaclass=JavaClassDef, jvm_name='android/app/Activity'):     def __init__(self):         self.__pkg_Manager = PackageManager()                 pass     @java_method_def(name='getPackageManager', signature='()Landroid/content/pm/PackageManager;' , native=False,jvm_id=0xd2000000+0x1000)     def getPackageManager(self, mu):         logger.info("Im in Activity.getPackageManager")         return self.__pkg_Manager             @java_method_def(name='getPackageName', signature='()Ljava/lang/String;' , native=False,jvm_id=0xd2000000+0x1004)     def getPackageName(self, mu):         logger.info("Im in Application.getPackageName")         return "com.jingdong.app.mall" 
继续报错Could not find field ('signatures', '[Landroid/content/pm/Signature;') in class android/content/pm/PackageInfo.,于是我们将实现的Signature类挪到package_manager.py的PackageInfo中
1 2 3 4 5 6 7 8 9 10 11 12 class Signature(metaclass=JavaClassDef, jvm_name='android/content/pm/Signature'):     def __init__(self):         pass class PackageInfo(metaclass=JavaClassDef, jvm_name='android/content/pm/PackageInfo', jvm_fields=[            JavaFieldDef('applicationInfo', 'Landroid/content/pm/ApplicationInfo;', False),            JavaFieldDef("signatures", "[Landroid/content/pm/Signature;", False),            ]):     def __init__(self):         self.applicationInfo = ApplicationInfo()         self.signatures = [Signature(),] 
报错Could not find method ('toByteArray', '()[B') in class android/content/pm/Signature.,增加toByteArray, 它的返回值是个字节数组,暂时返回一个1234567890
1 2 3 4 5 6 7 8 class Signature(metaclass=JavaClassDef, jvm_name='android/content/pm/Signature'):     def __init__(self):         pass     @java_method_def(name='toByteArray', signature='()[B')     def toByteArray(self,mu):         logger.info("Im in Signature.toByteArray 3082")         return bytearray.fromhex('1234567890') 
报错NotImplementedError
get_byte_array_elements方法没有实现,将jbyteArray转成jbyte指针,参数有2个,jbyteArray对象和一个bool型的变量,GetByteArrayElements和ReleaseByteArrayElements是成对出现的,我们在jni_eny.py中添加函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @native_method def get_byte_array_elements(self, mu, env,array_idx,isCopy):     logger.debug("JNIEnv->get_byte_array_elements (%d,%d) was called ", array_idx,isCopy)     obj = self.get_local_reference(array_idx)     if not isinstance(obj,jbyteArray):         raise ValueError("Expected a jbyteArray")     data_ptr = self._emu.native_memory.allocate(len(obj.value))     mu.mem_write(data_ptr,bytes(obj.value))     return data_ptr @native_method def release_byte_array_elements(self, mu, env,jbArr,jb_ptr):     logger.debug("JNIEnv->release_byte_array_elements was called")     self._emu.native_memory.free(jb_ptr)             # raise NotImplementedError() 
在memory.py中添加free函数
1 2 def free(self,data_ptr):     return self._memory.unmap(data_ptr) 
修改memory_map.py中的unmap函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 def unmap(self, addr, size = None):     if not self.__is_multiple(addr):         raise RuntimeError('addr was not multiple of page size (%d, %d).' % (addr, PAGE_SIZE))     if size == None:         size = page_end(addr) - addr     else:                     size = page_end(addr+size) - addr              try:         print("unmap 0x%08X sz=0x0x%08X end=0x0x%08X"%(addr,size, addr+size))         if (addr in self.__file_map_addr):             file_map_attr = self.__file_map_addr[addr]             if (addr+size != file_map_attr[0]):                 raise RuntimeError("unmap error, range 0x%08X-0x%08X does not match file map range 0x%08X-0x%08X from file %s"                 %(addr, addr+size, addr, file_map_attr[0]))             #             self.__file_map_addr.pop(addr)         #         self.__mu.mem_unmap(addr, size)     #     except unicorn.UcError as e:         #TODO:just for debug         for r in self.__mu.mem_regions():             print("region begin :0x%08X end:0x%08X, prot:%d"%(r[0], r[1], r[2]))         #         raise         return -1     #     return 0 
报错:Could not find class 'com/jingdong/jdsdk/widget/ToastUtils' for JNIEnv.
runemu.py添加类ToastUtils
1 2 3 4 5 6 7 8 9 10 class ToastUtils(metaclass=JavaClassDef, jvm_name='com/jingdong/jdsdk/widget/ToastUtils'):     def __init__(self):         pass     @java_method_def(name='longToast',args_list=["jstring"], signature='(Ljava/lang/String;)V')     def longToast(self, mu, *args, **kwargs):         logger.info("longToast %r" % args)         pass emulator.java_classloader.add_class(ToastUtils) 
终于将JNI_OnLoad  跑通了
getSignFromJni函数我们之前分析过,它一共有6个参数,第一个参数是Context,后面5个都是String
1 2 3 4 5 from androidemu.java.classes.activity_thread import * activity_Th = ActivityThread() result = emulator.call_symbol(lib_module, 'Java_com_jingdong_common_utils_BitmapkitUtils_getSignFromJni',emulator.java_vm.jni_env.address_ptr,0x00, activity_Th.getSystemContext(emulator),"asynInteface", '{"intefaceType":"asynIntefaceType","skuId":"100008667315"}', "99001184062989-f460e22c02fa", "android", "9.2.2") logger.info("Response from JNI call: %s" % result.toString(emulator)) 
IDA反编译出的伪代码中比java多了两个参数,JNIEnv *env 和 jobject obj
JNIEnv类型实际上代表了Java环境,通过这个JNIEnv* 指针,就可以对Java端的代码进行操作。例如,创建Java类中的对象,调用Java对象的方法,获取Java对象中的属性等等
jobject obj参数表示了 如果native方法不是static的话,这个obj就代表这个native方法的类实例,如果native方法是static的话,这个obj就代表这个native方法的类的class对象实例(static方法不需要类实例的,所以就代表这个类的class对象)
报错:RuntimeError: Could not find class 'java/lang/StringBuffer' for JNIEnv.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 from androidemu.java.jni_env import JNIEnv class StringBuffer(metaclass=JavaClassDef, jvm_name='java/lang/StringBuffer'):     def __init__(self):         self._str = ''         pass          @java_method_def(name='<init>', signature='()V', native=False)     def init(self, emu):         pass     @java_method_def(name='append', args_list=["jstring"], signature='(Ljava/lang/String;)Ljava/lang/StringBuffer;', native=False)     def append(self, emu,*args, **kwargs):         logger.info("append %r" % args)         pyobj = JNIEnv.jobject_to_pyobject(args[0])         self._str += pyobj         return self     @java_method_def(name='toString', signature='()Ljava/lang/String;', native=False)     def toString(self, emu):         logger.info("toString %r" % self._str)         return String(self._str) class Integer(metaclass=JavaClassDef, jvm_name='java/lang/Integer'):     def __init__(self):         self._int = -1         pass     @java_method_def(name='<init>', args_list=["jint"], signature='(I)V', native=False)     def init(self, emu,*args, **kwargs):         # pyobj = JNIEnv.jobject_to_pyobject(args[0])         logger.info("Integer init %d" % args[0])         self._int = args[0]         pass     @java_method_def(name='toString', signature='()Ljava/lang/String;', native=False)     def toString(self, emu):         logger.info("Integer toString %d" % self._int)         return str(self._int)      class String(metaclass=JavaClassDef, jvm_name='java/lang/String'):     def __init__(self,inStr=''):         self._str = inStr         pass     @java_method_def(name='getBytes', args_list=["jstring"], signature='(Ljava/lang/String;)[B', native=False)     def getBytes(self, emu,*args, **kwargs):         logger.info("%r String getBytes %r" % (self._str, args) )         return bytearray(self._str.encode('utf8'))     @java_method_def(name='toString', signature='()Ljava/lang/String;', native=False)     def toString(self, emu):         return self._str          emulator.java_classloader.add_class(StringBuffer) emulator.java_classloader.add_class(String) emulator.java_classloader.add_class(Integer) 
Unidbg 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class JingDong extends AbstractJni {     private final AndroidEmulator emulator;     private final VM vm;     private Module module;     private DvmClass bitMapKitUtils;     // jd apk 文件路径     public String apkPath = "";     // jd apk so 文件路径     public String soPath = "";     private static LibraryResolver createLibraryResolver() {         return new AndroidResolver(23);     }     private static AndroidEmulator createARMEmulator() {         return AndroidEmulatorBuilder.for32Bit().setProcessName("").build();     }     public JingDong() {         emulator = createARMEmulator();         final Memory memory = emulator.getMemory();         memory.setLibraryResolver(createLibraryResolver());         vm = emulator.createDalvikVM(new File(apkPath));         vm.setVerbose(true);         DalvikModule dm = vm.loadLibrary(new File(soPath), false);         vm.setJni(this);         dm.callJNI_OnLoad(emulator);         module = dm.getModule();     }     public static void main(String[] args) throws IOException {         JingDong jd = new JingDong();         jd.destroy();     }     private void destroy() throws IOException {         emulator.close();     } } 
 1 2 3 4 5 6 7 8 9 10 @Override public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {     switch (signature) {         case "com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;":             // resolveClass 创建目标类,newObject创建实例              return vm.resolveClass("android/content/Application").newObject(null);     }     return super.getStaticObjectField(vm, dvmClass, signature); } 
这里dvmMethod为空,打个log看看
1 2 3 4 5 6 7 public static void main(String[] args) throws IOException {     Logger.getLogger(DalvikVM.class).setLevel(Level.DEBUG);     Logger.getLogger(BaseVM.class).setLevel(Level.DEBUG);     JingDong jd = new JingDong();     jd.destroy(); } 
从常规的jni开发来说,一般就是findClass,找到类对象,再通过类对象,找到方法的methodID,再通过callxxmethod来进行调用,第一个参数是jobject(打个比方,如果是static的话,就是jclass),第二个参数就是methodID,第三个参数就是输入参数,根据https://github.com/zhkl0228/unidbg/issues/315 
MethodID不匹配,需要明确继承关系。so里面 Activity  类根本就没有实例化,可以在DalvikVM.java中加上硬编码转换或者把Application换成Activity也可以,毕竟Activity也是可以获取Application对象的
或者在getStaticObjectField中表明继承关系
unidbg中getMethodId方法中表明将每个类生成一个hashmap,以signature作为hashcode的key,类方法作为DvmMethod作为value,getMethod时先从hashmap中查找,找不到就从超类中查找,实现继承关关系就是为了DvmMethod的查找,如果application不继承自身hashmap没有,自然超类也没有。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17     @Override     public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {         switch (signature) {             case "com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;":                 // resolveClass 创建目标类,newObject创建实例                 /**                  * DvmClass activity=vm.resolveClass("android/app/Activity")                  * DvmClass application=vm.resolveClass("android/app/Application",activity);                  * return application.newObject(signature);                  */                 return vm.resolveClass("android/app/Application",vm.resolveClass("android/app/Activity")).newObject(null);                 // return vm.resolveClass("android/app/Activity", vm.resolveClass("android/content/ContextWrapper", vm.resolveClass("android/content/Context"))).newObject(null); //                return vm.resolveClass("android/app/Application").newObject(null);         }         return super.getStaticObjectField(vm, dvmClass, signature);     } 
1 2 3 4 5 6 7 8 @Override public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {     switch (signature) {         case "android/app/Application->getApplicationInfo()Landroid/content/pm/ApplicationInfo;":             return new ApplicationInfo(vm);     }     return super.callObjectMethod(vm, dvmObject, signature, varArg); } 
1 2 3 4 5 6 7 @Override public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {     if ("android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;".equals(signature)) {         return new StringObject(vm, apkPath);     }     return super.getObjectField(vm, dvmObject, signature); } 
objection -N -h 127.0.0.1 -p 8080 -g com.jingdong.app.mall explore -P ~/.objection/plugins –startup-command “android hooking watch class_method com.jingdong.common.utils.BitmapkitZip.unZip –dump-args –dump-return” 远程objection的spawn启动,查看入参并未检测到
frida -U -f com.jingdong.app.mall -l hook_RegisterNatives.js 修改针对性输出并没有想要的信息
IDA打开libjdbitmapkit.so,shift+F12搜索unZip
F5拿到伪C代码
尝试hook so的导出函数和偏移地址,findBaseAddress并没有拿到地址,hook失败。
mikrom以listen_wait启动,frida -U 京东 -l hook_libart.js > jd.log,unzip的三个参数分别是app安装目录 第二个参数为META-INF 目录 第三个是RSA,反编译后拿到META-INF下的JINGDONG.RSA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {     switch (signature) {         case "com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B": {             StringObject udApkPath = varArg.getObjectArg(0);             StringObject udDirectory = varArg.getObjectArg(1);             StringObject udFilename = varArg.getObjectArg(2);             if (apkPath.equals(udApkPath.getValue()) &&                     "META-INF/".equals(udDirectory.getValue()) &&                     ".RSA".equals(udFilename.getValue())) {                 byte[] data = vm.unzip("META-INF/JINGDONG.RSA");                 return new ByteArray(vm, data);             }         }     }     return super.callStaticObjectMethod(vm ,dvmClass, signature, varArg); } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {     switch (signature) {         case "sun/security/pkcs/PKCS7-><init>([B)V": {             ByteArray array = varArg.getObjectArg(0);             try {                 return vm.resolveClass("sun/security/pkcs/PKCS7").newObject(new PKCS7(array.getValue()));             } catch (ParsingException e) {                 throw new IllegalStateException(e);             }         }     }     return super.newObject(vm, dvmClass, signature, varArg); } 
1 2 3 4 5 6 7 8 9 10 11 @Override public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {     switch (signature) {         case "sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;": {             PKCS7 pkcs7 = (PKCS7) dvmObject.getValue();             X509Certificate[] certificates = pkcs7.getCertificates();             return ProxyDvmObject.createObject(vm, certificates);         }     }     return super.callObjectMethod(vm, dvmObject, signature, varArg); } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Override public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {     switch (signature) {         case "com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B": {             DvmObject<?> obj = varArg.getObjectArg(0);             byte[] bytes = objectToBytes(obj.getValue());             return new ByteArray(vm, bytes);         }     } } private static byte[] objectToBytes(Object obj) {     try {         ByteArrayOutputStream baos = new ByteArrayOutputStream();         ObjectOutputStream oos = new ObjectOutputStream(baos);         oos.writeObject(obj);         oos.flush();         byte[] array = baos.toByteArray();         oos.close();         baos.close();         return array;     } catch (IOException e) {         throw new IllegalStateException(e);     } } 
环境修补完成,开始调用函数
1 2 3 4 5 6 7 8 9 10 11 public void callSign() {     DvmClass cBitmapkitUtils = vm.resolveClass("com/jingdong/common/utils/BitmapkitUtils");     StringObject ret = cBitmapkitUtils.callStaticJniMethodObject(emulator, "getSignFromJni()(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",             vm.resolveClass("android/content/Context").newObject(null),             "clientImage",             "{\"moduleParams\":{\"18\":\"1565611060638\",\"19\":\"1565229712150\",\"25\":\"1567478504636\",\"27\":\"1602488415048\",\"28\":\"1631069159956\",\"30\":\"1567404005627\",\"32\":\"1567997588476\",\"34\":\"1593508185597\",\"35\":\"1568708316462\",\"37\":\"1630293538664\",\"42\":\"1623741761542\",\"44\":\"1569247647090\",\"46\":\"1588839806224\",\"47\":\"1571295610042\",\"61\":\"1582091758495\",\"70\":\"1585279774645\",\"74\":\"1586781606615\"}}",             "d5a585639f505b18",             "android",             "10.2.0");     System.out.println(ret.getValue()); } 
1 2 3 4 5 6 7 8 9 @Override public  DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {    switch  (signature) {         case  "java/lang/StringBuffer-><init>()V" : {             return  vm.resolveClass("java/lang/StringBuffer" ).newObject(new  StringBuffer());         }     }     return  super .newObjectV(vm, dvmClass, signature, vaList); } 
1 2 3 4 5 6 7 8 9 10 11 12 @Override public  DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {    switch  (signature) {         case  "java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;" : {             StringBuffer buffer = (StringBuffer) dvmObject.getValue();             StringObject str = vaList.getObjectArg(0 );             buffer.append(str.getValue());             return  dvmObject;         }     }     return  super .callObjectMethodV(vm, dvmObject, signature, vaList); } 
1 2 3 4 5 6 7 8 9 10 11 @Override public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {     switch (signature) {         case "java/lang/Integer-><init>(I)V": {             int intArg = vaList.getIntArg(0);             Integer integer = Integer.valueOf(intArg);             return vm.resolveClass("java/lang/Integer").newObject(integer);         }     }     return super.newObjectV(vm, dvmClass, signature, vaList); } 
1 2 3 4 5 6 7 8 9 10 @Override public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {     switch (signature) {         case "java/lang/Integer->toString()Ljava/lang/String;": {             Integer integer = (Integer) dvmObject.getValue();             return new StringObject(vm, integer.toString());         }     }     return super.callObjectMethodV(vm, dvmObject, signature, vaList); } 
1 2 3 4 5 6 7 8 9 10 @Override public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {     switch (signature) {         case "java/lang/StringBuffer->toString()Ljava/lang/String;": {             StringBuffer stringBuffer = (StringBuffer) dvmObject.getValue();             return new StringObject(vm, stringBuffer.toString());         }     }     return super.callObjectMethodV(vm, dvmObject, signature, vaList); }