SO逆向之京东sign分析

篇幅有限

完整内容及源码关注公众号:ReverseCode,发送

抓包

安装某东9.2.2,启动postern,开启socks抓包

image-20211123171157406

分析

jadx反编译搜索sign=或者getSign,出现地方太多随机挑一个

image-20211123173546558

跟进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

image-20211123192936635

启动了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;
}

image-20211230154658576

在jadx中搜索com.jingdong.sdk.jdupgrade.inner也一无所获,大概率在so里完成的加密。通过在so中搜索grep "sign" *.so或者strings -f *.so | grep "Sign"批量搜索so中的字符串

image-20211230162135941

使用ida打开libjdbitmapkit.so,搜索sign静态绑定函数,尝试hook该方法com.jingdong.common.utils.BitmapkitUtils

image-20221016100152017

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;
}

image-20211230162530040

查看BitmapkitUtils类,一共5个参数都是String

image-20211230164000220

主动调用

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)

image-20211230173844944

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();
}

image-20220125184743541

生成的包中包括'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

image-20220125185332559

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.soJava_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的实现

image-20220126194818565

getPackageManager方法大多在JNI_OnLoad中实现,IDA打开后进入check_status(v21)CallObjectMethod方法有三个参数,a1是JNIEnv,dword_175DC是十六进制地址,v4是方法id

image-20220126194947360

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 方法调用失败

image-20220126195157837

报错的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

image-20220203211156510

说明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

image-20220203213736103

对抗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());
}

image-20220203220458542

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"

image-20220203221456850

3523215368 从前面的日志能找到是getPackageInfo方法的jvm_id,打开androidemu.utils.debug_utils.dump_code(emu, address, size, g_cfd) 开关,会输出汇编代码,可以方便定位出错的代码位置。

image-20220203224324971

IDA打开so,跳到0x00002BF4查看

image-20220203224419175

之前调用 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

image-20220203225957177

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.

image-20220203231327089

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 跑通了

image-20220203231647424

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)

image-20220203233109402

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();
}
}

image-20221016101120072

img
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);
}

image-20221016110306928

这里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对象的

image-20221016113550223

或者在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);
}

image-20221016132922153

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);
}

image-20221016133520551

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);
}

image-20221016133613151

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 修改针对性输出并没有想要的信息

image-20221017223407852

IDA打开libjdbitmapkit.so,shift+F12搜索unZip

image-20221017223541992

F5拿到伪C代码

image-20221017223657914

尝试hook so的导出函数和偏移地址,findBaseAddress并没有拿到地址,hook失败。

mikrom以listen_wait启动,frida -U 京东 -l hook_libart.js > jd.log,unzip的三个参数分别是app安装目录 第二个参数为META-INF 目录 第三个是RSA,反编译后拿到META-INF下的JINGDONG.RSA

image-20221019224404088

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);
}

image-20221019224714810

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);
}

image-20221019225022099

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);
}

image-20221019225446358

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);
}
}

环境修补完成,开始调用函数

image-20221019225712615

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());
}

image-20221019225833907

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);
}

image-20221019225916585

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);
}

image-20221019225947613

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);
}

image-20221019230832505

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);
}

image-20221019231058837

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);
}

image-20221019231204254

文章作者: J
文章链接: http://onejane.github.io/2021/11/21/SO逆向之京东sign分析/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 万物皆可逆向
支付宝打赏
微信打赏