0. 前言
Android 应用gl,使用了加固i,老版本的应用gl是js源码,新版本更新后,刚开始以为是加密了源码,分析后才知道是使用了Hermes优化引擎。
1. Hermes简介
Hermes是Facebook为React Native开源的一款javascript优化引擎。
原来的index.android.bundle是javascript的,被Hermes优化后变成了Bytecode。
Hermes优化的优化效果如下:
优化前:
优化后:
2. 前期准备
2.1 绕过root检测
应用gl使用了加固i,在被root 的手机上运行,闪退。
绕过方法也很简单,在magisk的设置中,开启遵守排除列表,然后在配置排除列表中选择应用gl
应用被添加到排除列表后,将没办法使用lspoed,只能使用frida
2.2 绕过ssl pinning
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 hook_ssl() {
Java.perform(function () {
var ClassName = "com.android.org.conscrypt.Platform" ;
var Platform = Java.use(ClassName);
var targetMethod = "checkServerTrusted" ;
var len = Platform[targetMethod].overloads.length;
console.log(targetMethod, len );
for (var i = 0 ; i < len ; + + i) {
Platform[targetMethod].overloads[i].implementation = function () {
console.log( "class:" , ClassName, "target:" , targetMethod, " i:" , i, arguments);
};
}
var ClassName = "com.android.org.conscrypt.TrustManagerImpl" ;
var Platform = Java.use(ClassName);
var targetMethod = "checkTrustedRecursive" ;
var len = Platform[targetMethod].overloads.length;
console.log(targetMethod, len );
var ArrayList = Java.use( "java.util.ArrayList" )
var X509Certificate = Java.use( "java.security.cert.X509Certificate" );
for (var i = 0 ; i < len ; + + i) {
Platform[targetMethod].overloads[i].implementation = function () {
console.log( "class:" , ClassName, "target:" , targetMethod, " i:" , i, arguments);
return ArrayList.$new();
};
}
});
}
|
2.3 抓包
抓包可以看到登录接口有个signcode加密参数,69e34d747c5757236142a19f4595d313 是32位字符串,猜测跟md5相关
3. signcode分析
3.1 确定signcode的来源
由于我分析过应用gl前面版本,所以知道它是React Native开发的
但当你们拿到新app的时候,需要先对apk文件,app数据目录进行一次搜索
解压 apk, 使用命令 grep -r "signcode" *,搜索一下apk下有没有signcode,第一次没搜到(备注:应用gl把代码打包到assets/android-rn.zip里了)
在app的数据目录下,使用命令 grep -r "signcode" *,搜索一下,在文件./files/RN/rnbundle/index.android.bundle下搜索到了signcode
1 | Binary file . / files / RN / rnbundle / index.android.bundle matches
|
把./files/RN/rnbundle/index.android.bundle拿出来
1 2 3 4 | cp . / files / RN / rnbundle / index.android.bundle / data / local / tmp / index.android.bundle
chmod 777 / data / local / tmp / index.android.bundle
adb pull / data / local / tmp / index.android.bundle
|
使用010editor打开后,就看到如下图的内容:
刚打开index.android.bundle, 我就猜测这个文件难道被加密了?(备注:在这之前我没有分析过被Hermes优化的应用)。
因为应用gl有加固i,我并没有研究加固,于是只能研究React Native的源码,看不能找到最终加载源码的地方
3.2 通过React Native源码研究的Hermes调用流程
下载源码 https://github.com/facebook/react-native.git
在源码中搜索 LoadScript,找到2个可疑的函数
1 2 3 | private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously);
private native void jniLoadScriptFromFile(String fileName, String sourceURL, boolean loadSynchronously);
|
这2个函数是native,使用https://github.com/lasting-yang/frida_hook_libart/blob/master/hook_RegisterNatives.js,可以找到函数对应的so
1 2 3 4 5 6 7 8 9 10 11 12 13 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniLoadScriptFromAssets sig: (Landroid / content / res / AssetManager;Ljava / lang / String;Z)V fnPtr: 0xb4e99a85 fnOffset: 0x80a85 callee: 0xb4d2021d libhermes - executor - release.so!_ZN8facebook3jni6JClass15registerNativesESt16initializer_listI15JNINativeMethodE + 0x20
[RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniLoadScriptFromFile sig: (Ljava / lang / String;Ljava / lang / String;Z)V fnPtr: 0xb4e99aa1 fnOffset: 0x80aa1 callee: 0xb4d2021d libhermes - executor - release.so!_ZN8facebook3jni6JClass15registerNativesESt16initializer_listI15JNINativeMethodE + 0x20
这时候会遇到一个问题,callee在libhermes - executor - release.so,使用ida打开libhermes - executor - release.so后,去fnOffset: 0x80a85 和fnOffset: 0x80aa1 找不到对应的函数。
于是使用frida的 Process.findModuleByAddress(ptr( 0xb4e99a85 )) 找jniLoadScriptFromAssets函数所在的模块,在libreactnativejni.so里面
{
"base" : "0xb4e19000" ,
"name" : "libreactnativejni.so" ,
"path" : "/data/app/~~klydM6uPsWM_heluFCfyKQ==/com.xxxxx.xxx.xxxxxxxxxx.xxxxxx-zC5SUJ_WxA0rwldHieq_fg==/lib/arm/libreactnativejni.so" ,
"size" : 815104
}
|
对jniLoadScriptFromAssets和jniLoadScriptFromFile进行hook,需要配合hook_dlopen一起使用,因为frida注入的时候,libreactnativejni.so还没有加载。
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 | function hook_dlopen(module_name, fun) {
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext" );
if (android_dlopen_ext) {
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var pathptr = args[ 0 ];
if (pathptr) {
this.path = (pathptr).readCString();
if (this.path.indexOf(module_name) > = 0 ) {
this.canhook = true;
console.log( "android_dlopen_ext:" , this.path);
}
}
},
onLeave: function (retval) {
if (this.canhook) {
fun();
}
}
});
}
}
function hook_libreactnativejni() {
let base_libreactnativejni = Module.findBaseAddress( "libreactnativejni.so" );
if (base_libreactnativejni = = null) {
return ;
}
let jniLoadScriptFromAssets = base_libreactnativejni.add( 0x80a85 );
let jniLoadScriptFromFile = base_libreactnativejni.add( 0x80aa1 );
/ / private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously);
Interceptor.attach(jniLoadScriptFromAssets, {
onEnter(args) {
console.log( "jniLoadScriptFromAssets:" , Java.vm.tryGetEnv().getStringUtfChars(args[ 3 ]).readCString())
}
})
/ / private native void jniLoadScriptFromFile(String fileName, String sourceURL, boolean loadSynchronously);
Interceptor.attach(jniLoadScriptFromFile, {
onEnter(args) {
console.log( "jniLoadScriptFromFile:" , Java.vm.tryGetEnv().getStringUtfChars(args[ 2 ]).readCString(), Java.vm.tryGetEnv().getStringUtfChars(args[ 3 ]).readCString())
}
})
}
setImmediate(() = > {
hook_dlopen( "libreactnativejni.so" , hook_libreactnativejni)
hook_libreactnativejni();
})
|
jniLoadScriptFromFile被加载,传入参数为index.android.bundle文件路径
继续阅读源码,jniLoadScriptFromFile中会调用loadScriptFromString
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 | void CatalystInstanceImpl::jniLoadScriptFromFile(
const std::string &fileName,
const std::string &sourceURL,
bool loadSynchronously) {
auto reactInstance = instance_;
if (!reactInstance) {
return ;
}
switch (getScriptTagFromFile(fileName.c_str())) { / / 根据文件头判断脚本类型,
case ScriptTag::MetroHBCBundle: {
std::unique_ptr<const JSBigFileString> script;
RecoverableError::runRethrowingAsRecoverable<std::system_error>(
[&fileName, &script]() {
script = JSBigFileString::fromPath(fileName);
});
const char * buffer = script - >c_str();
uint32_t bufferLength = (uint32_t)script - >size();
uint32_t offset = 8 ;
while (offset < bufferLength) {
uint32_t segment = offset + 4 ;
uint32_t moduleLength =
bufferLength < segment ? 0 : * (((uint32_t * ) buffer ) + offset / 4 );
reactInstance - >loadScriptFromString(
std::make_unique<const JSBigStdString>(
std::string( buffer + segment, buffer + moduleLength + segment)),
sourceURL,
false);
offset + = ((moduleLength + 3 ) & ~ 3 ) + 4 ;
}
break ;
}
case ScriptTag::RAMBundle:
instance_ - >loadRAMBundleFromFile(fileName, sourceURL, loadSynchronously);
break ;
case ScriptTag::String:
default: {
std::unique_ptr<const JSBigFileString> script;
RecoverableError::runRethrowingAsRecoverable<std::system_error>(
[&fileName, &script]() {
script = JSBigFileString::fromPath(fileName);
});
instance_ - >loadScriptFromString(
std::move(script), sourceURL, loadSynchronously);
}
}
}
|
loadScriptFromString
1 2 3 4 5 6 7 8 9 10 11 | void Instance::loadScriptFromString(
std::unique_ptr<const JSBigString> string,
std::string sourceURL,
bool loadSynchronously) {
SystraceSection s( "Instance::loadScriptFromString" , "sourceURL" , sourceURL);
if (loadSynchronously) {
loadBundleSync(nullptr, std::move(string), std::move(sourceURL));
} else {
loadBundle(nullptr, std::move(string), std::move(sourceURL));
}
}
|
1 2 3 4 5 6 | let loadScriptFromString = base_libreactnativejni.add( 0xA0BF8 + 1 );
Interceptor.attach(loadScriptFromString, {
onEnter(args) {
console.log( "loadScriptFromString:" , (args[ 1 ]), ptr(args[ 2 ]).add(Process.pointerSize * 2 ).readPointer().readCString(), args[ 3 ])
}
})
|
hook loadScriptFromString后,可以知道loadSynchronously为0, 继续分析loadBundle
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 | void Instance::loadBundle(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
callback_ - >incrementPendingJSCalls();
SystraceSection s( "Instance::loadBundle" , "sourceURL" , sourceURL);
nativeToJsBridge_ - >loadBundle(
std::move(bundleRegistry), std::move(string), std::move(sourceURL));
}
/ / Instance::loadBundle调用了nativeToJsBridge_ - >loadBundle,
void NativeToJsBridge::loadBundle(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
runOnExecutorQueue(
[this,
bundleRegistryWrap = folly::makeMoveWrapper(std::move(bundleRegistry)),
startupScript = folly::makeMoveWrapper(std::move(startupScript)),
startupScriptSourceURL =
std::move(startupScriptSourceURL)](JSExecutor * executor) mutable {
auto bundleRegistry = bundleRegistryWrap.move();
if (bundleRegistry) {
executor - >setBundleRegistry(std::move(bundleRegistry));
}
try {
executor - >loadBundle(
std::move( * startupScript), std::move(startupScriptSourceURL));
} catch (...) {
m_applicationScriptHasFailure = true;
throw;
}
});
}
/ / NativeToJsBridge::loadBundle里面调用了NativeToJsBridge::runOnExecutorQueue
|
runOnExecutorQueue的参数是一个Lambda表达式,它的回调函数里面调用了loadBundle,在源码中搜索loadBundle,找到了很多处实现代码,这时候我们酒不知道loadBundle具体实现在哪里,继续写hook代码hook_loadBundle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function hook_loadBundle() {
/ / 根据前面分析RN的一些so,可以发现大部分函数符号都还在so里面,那么我们通过DebugSymbol的findFunctionsMatching,找到所有loadBundle函数,并进行hook
DebugSymbol.findFunctionsMatching( "*loadBundle*" ). map (addr = > {
Interceptor.attach(addr, {
onEnter(args) {
console.log( "loadBundle:" , DebugSymbol.fromAddress(addr));
}
})
})
}
hook 结果如下:
loadBundle: 0xbb70699d libreactnativejni.so!_ZN8facebook5react8Instance10loadBundleENSt6__ndk110unique_ptrINS0_17RAMBundleRegistryENS2_14default_deleteIS4_EEEENS3_IKNS0_11JSBigStringENS5_IS9_EEEENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE
loadBundle: 0xbb70a845 libreactnativejni.so!_ZN8facebook5react16NativeToJsBridge10loadBundleENSt6__ndk110unique_ptrINS0_17RAMBundleRegistryENS2_14default_deleteIS4_EEEENS3_IKNS0_11JSBigStringENS5_IS9_EEEENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE
loadBundle: 0xbb9a5ec5 libhermes - executor - release.so!_ZN8facebook5react11JSIExecutor10loadBundleENSt6__ndk110unique_ptrIKNS0_11JSBigStringENS2_14default_deleteIS5_EEEENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE
最终找到了libhermes - executor - release.so的facebook::react::JSIExecutor::loadBundle函数
|
facebook::react::JSIExecutor::loadBundle函数中调用了evaluateJavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void JSIExecutor::loadBundle(
std::unique_ptr<const JSBigString> script,
std::string sourceURL) {
SystraceSection s( "JSIExecutor::loadBundle" );
bool hasLogger(ReactMarker::logTaggedMarker);
std::string scriptName = simpleBasename(sourceURL);
if (hasLogger) {
ReactMarker::logTaggedMarker(
ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());
}
runtime_ - >evaluateJavaScript(
std::make_unique<BigStringBuffer>(std::move(script)), sourceURL);
flush();
if (hasLogger) {
ReactMarker::logTaggedMarker(
ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
}
}
|
hook "*evaluateJavaScript*",
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 | function hook_evaluateJavaScript() {
DebugSymbol.findFunctionsMatching( "*evaluateJavaScript*" ). map (addr = > {
Interceptor.attach(addr, {
onEnter(args) {
console.log( "evaluateJavaScript:" , DebugSymbol.fromAddress(addr), "\r\n" , print_native_stack( "evaluateJavaScript" , this));
}
})
})
}
并没有找到evaluateJavaScript函数,只找到了一个evaluateJavaScriptWithSourceMap函数
evaluateJavaScript: 0xbb866775 libhermes.so!_ZN8facebook6hermes13HermesRuntime31evaluateJavaScriptWithSourceMapERKNSt6__ndk110shared_ptrIKNS_3jsi6BufferEEES9_RKNS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE
[evaluateJavaScript] called from :
0xbb867431 libhermes.so! 0x11431
0xbb6605b9 libhermes - executor - common - release.so! 0x1c5b9
0xbb727051 libhermes - executor - release.so!_ZN8facebook5react11JSIExecutor10loadBundleENSt6__ndk110unique_ptrIKNS0_11JSBigStringENS2_14default_deleteIS5_EEEENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE + 0x18c
0xbb4b94e7 libreactnativejni.so! 0xa54e7
0xbb4ba133 libreactnativejni.so!_ZNKSt6__ndk18functionIFvPN8facebook5react10JSExecutorEEEclES4_ + 0x16
0xbb4a5ae1 libreactnativejni.so! 0x91ae1
0xbb4961d3 libreactnativejni.so!_ZN8facebook3jni6detail13MethodWrapperIMNS_5react15JNativeRunnableEFvvEXadL_ZNS4_3runEvEES4_vJEE8dispatchENS0_9alias_refIPNS1_8JTypeForINS0_11HybridClassIS4_NS3_8RunnableEE8JavaPartESB_vE11_javaobjectEEE + 0xe
0xbb49617f libreactnativejni.so!_ZN8facebook3jni6detail15FunctionWrapperIPFvNS0_9alias_refIPNS1_8JTypeForINS0_11HybridClassINS_5react15JNativeRunnableENS6_8RunnableEE8JavaPartES8_vE11_javaobjectEEEESD_vJEE4callEP7_JNIEnvP8_jobjectSG_ + 0x26
0xee6708df libart.so!art_quick_generic_jni_trampoline + 0x2e
0x71915663 boot - framework.oat! 0x54e663
|
evaluateJavaScriptWithSourceMap函数 在React Native源码中没有搜到,在另一个hermes项目(https://github.com/facebook/hermes)中找到了evaluateJavaScriptWithSourceMap函数和evaluateJavaScript函数
evaluateJavaScript函数对应so的地址0xbb867431 libhermes.so!0x11431
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | jsi::Value HermesRuntimeImpl::evaluateJavaScript(
const std::shared_ptr<const jsi:: Buffer > & buffer ,
const std::string &sourceURL) {
return evaluateJavaScriptWithSourceMap( buffer , nullptr, sourceURL);
}
jsi::Value HermesRuntime::evaluateJavaScriptWithSourceMap(
const std::shared_ptr<const jsi:: Buffer > & buffer ,
const std::shared_ptr<const jsi:: Buffer > &sourceMapBuf,
const std::string &sourceURL) {
return impl(this) - >evaluatePreparedJavaScript(
impl(this) - >prepareJavaScriptWithSourceMap(
buffer , sourceMapBuf, sourceURL));
}
|
evaluateJavaScriptWithSourceMap函数中调用了prepareJavaScriptWithSourceMap函数, 继续调用isHermesBytecode函数
1 2 3 4 5 6 7 8 9 10 | HermesRuntimeImpl::prepareJavaScriptWithSourceMap(
const std::shared_ptr<const jsi:: Buffer > &jsiBuffer,
const std::shared_ptr<const jsi:: Buffer > &sourceMapBuf,
std::string sourceURL) {
std::pair<std::unique_ptr<hbc::BCProvider>, std::string> bcErr{};
auto buffer = std::make_unique<BufferAdapter>(std::move(jsiBuffer));
vm::RuntimeModuleFlags runtimeFlags{};
runtimeFlags.persistent = true;
bool isBytecode = isHermesBytecode( buffer - >data(), buffer - >size());
|
isHermesBytecode用来判断是否是Hermes的Bytecode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | bool HermesRuntime::isHermesBytecode(const uint8_t * data, size_t len ) {
return hbc::BCProviderFromBuffer::isBytecodeStream(
llvh::ArrayRef<uint8_t>(data, len ));
}
static bool isBytecodeStream(llvh::ArrayRef<uint8_t> aref) {
const auto * header =
reinterpret_cast<const hbc::BytecodeFileHeader * >(aref.data());
return (
aref.size() > = sizeof(hbc::BytecodeFileHeader) &&
header - >magic = = hbc::MAGIC);
}
const static uint64_t MAGIC = 0x1F1903C103BC1FC6 ;
|
MAGIC和index.android.bunddle的前8个字节是一直的,至此已经知道index.android.bundle并不是被应用gl加密了,而是被Hermes编译成了bytecode
1 2 3 4 5 6 7 8 9 | BytecodeFileHeader(
uint64_t magic, / / 0x1F1903C103BC1FC6
uint32_t version, / / 0x54 表示是 84 版本,commit 0bac657c61ac47a3a9537d982921f5f6c9630d41 (HEAD, tag: v0. 8.1 )
......
}
hermes / include / hermes / BCGen / HBC / BytecodeVersion.h路径中有定义BYTECODE_VERSION
const static uint32_t BYTECODE_VERSION = 84 ;
|
3.3 反编译Hermes bytecode与回编译
前面的分析过程,已经知道了index.android.bundle是Hermes的bytecode,没办法直接查看源码,需要借助其他工具进行反编译
Hermes官方提供了hbcdump工具,可以进行反编译,但使用起来比较麻烦,有另一个开源工具hbctool可以对hermesbytecode进行反编译与回编译
https://github.com/bongtrop/hbctool 提供了59, 62, 74, 76版本的反编译
https://github.com/niosega/hbctool/tree/draft/hbc-v84 对84版本的Hermes进行补充
安装可以反编译84版本的hbctool
1 | pip install https: / / github.com / niosega / hbctool / archive / ac6fabb69a7229ed9764997d153d4f703d1381aa. zip
|
反编译
1 | hbctool disasm index.android.bundle bundle
|
回编译
1 | hbctool asm bundle index.android.bundle.re
|
bytecode反编译出来的代码如下:搜索两处能md5相关的代码
并不需要深入bytecode,指令详情,了解几个简单指令即可。
只需要写一个打印日志的函数,变成成bytecode,反编译后替换掉这两个函数就能看到这两个md5函数的输入参数
3.4 js代码编译成Hermes bytecode
先编译Hermes源码,会得到./build/bin/hermes工具,
test.js
1 2 3 4 5 6 7 8 | function hook_log(a, b) {
window.nativeLoggingHook(a + " " + b, 1 );
return " " ;
}
function test() {
hook_log( "333" , "555" );
}
|
写了一些简单的js代码,把test.js编译成bytecode
1 | . / build / bin / hermes - emit - binary - out test.hbc test.js
|
010editor打开test.hbc可以看到MAGIC
反编译test.hbc
1 | hbctool disasm test.hbc testdir
|
得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Function<hook_log> 1 ( 3 params, 14 registers, 0 symbols):
GetGlobalObject Reg8: 0 ; 先获取全局变量 Reg8: 0
TryGetById Reg8: 4 , Reg8: 0 , UInt8: 1 , UInt16: 7 ; 通过window字符串从Reg8: 0 全局变量中拿到window变量,存放在Reg8: 4
; Oper[ 3 ]: String( 7 ) 'window'
GetByIdShort Reg8: 3 , Reg8: 4 , UInt8: 2 , UInt8: 5 ; 从windows变量中拿nativeLoggingHook,存放在Reg8: 3
; Oper[ 3 ]: String( 5 ) 'nativeLoggingHook'
LoadParam Reg8: 1 , UInt8: 1
LoadConstString Reg8: 0 , UInt16: 0
; Oper[ 1 ]: String( 0 ) ' '
Add Reg8: 2 , Reg8: 1 , Reg8: 0
LoadParam Reg8: 1 , UInt8: 2
Add Reg8: 2 , Reg8: 2 , Reg8: 1
LoadConstUInt8 Reg8: 1 , UInt8: 1
Call3 Reg8: 1 , Reg8: 3 , Reg8: 4 , Reg8: 2 , Reg8: 1 ;调用window.nativeLoggingHook函数
Ret Reg8: 0
EndFunction
|
3.5 替换bytecode,得到signcode算法
把反编译出来的bytecode,替换到md5函数中
需要对字符串的id进行修改,字符串对应hbctool反编译出来的string.json中的字符串id
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | {
"id" : 253 ,
"isUTF16" : false,
"value" : "window"
},
{
"id" : 33087 ,
"isUTF16" : false,
"value" : "nativeLoggingHook"
},
{
"id" : 18880 ,
"isUTF16" : false,
"value" : " "
},
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Function<md5> 13508 ( 3 params, 14 registers, 0 symbols):
GetGlobalObject Reg8: 0
TryGetById Reg8: 4 , Reg8: 0 , UInt8: 1 , UInt16: 253 ; 修改字符串 id UInt16: 7 - > UInt16: 253
; Oper[ 3 ]: String( 254 ) 'window'
GetById Reg8: 3 , Reg8: 4 , UInt8: 2 , UInt16: 33087 ;修改字符串 id UInt8: 5 - > UInt16: 33087 , 从UInt8改成UInt16, 需要把GetByIdShort修改成GetById指令
; Oper[ 3 ]: String( 4397 ) 'nativeLoggingHook'
LoadParam Reg8: 1 , UInt8: 1
LoadConstString Reg8: 0 , UInt16: 18880 ; 修改字符串 id UInt16: 0 - > UInt16: 18880
; Oper[ 1 ]: String( 674 ) ' '
Add Reg8: 2 , Reg8: 1 , Reg8: 0
LoadParam Reg8: 1 , UInt8: 2
Add Reg8: 2 , Reg8: 2 , Reg8: 1
LoadConstUInt8 Reg8: 1 , UInt8: 1
Call3 Reg8: 1 , Reg8: 3 , Reg8: 4 , Reg8: 2 , Reg8: 1
Ret Reg8: 0
EndFunction
|
再次回编译,得到index.android.bundle.re
1 2 3 4 | hbctool asm bundle index.android.bundle.re
md5sum index.android.bundle.re
7eba6c20134268a82e5a0af948ca550b index.android.bundle.re
|
现在替换app目录下的index.android.bundle
1 2 3 4 5 6 7 | adb push index.android.bundle.re / data / local / tmp / index.android.bundle.re
adb shell
su
cd / data / data / com.pcakge.name / files / RN / rnbundle /
cp index.android.bundle index.android.bundle.bak
cp / data / local / tmp / index.android.bundle.re index.android.bundle
|
结束app, 运行 adb logcat "*:S ReactNative:V ReactNativeJS:V", 再次运行app就可以到得到md5函数的输入参数
1 2 3 | ReactNativeJS: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAPPKEYGL_RN_D02F48E720CACLIENTANDROIDDEVICEIDBBC6AD7E - 1561 - 4702 - 8C1E - 3AAC682B4D8BENCRYPT1PASSWORD6666666666TIMESTAMPUINFO666666666VERSION3 . 1.0XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX undefined
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX脱敏
|
算法分析完成
参考链接
https://github.com/facebook/react-native.git
https://github.com/lasting-yang/frida_hook_libart
https://github.com/facebook/hermes
https://github.com/bongtrop/hbctool
https://github.com/niosega/hbctool/tree/draft/hbc-v84
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
最后于 2022-7-4 22:16
被Imyang编辑
,原因: