React Native Hermes 逆向實踐

語言: CN / TW / HK

本文為看雪論壇優秀文章
看雪論壇作者ID:Imyang

Android 應用gl,使用了加固i,老版本的應用gl是js原始碼,新版本更新後,剛開始以為是加密了原始碼,分析後才知道是使用了Hermes優化引擎。

Hermes簡介

Hermes是Facebook為React Native開源的一款javascript優化引擎。

原來的index.android.bundle是javascript的,被Hermes優化後變成了Bytecode。

Hermes優化的優化效果如下:

優化前:

優化後:

前期準備

2.1 繞過root檢測

應用gl使用了加固i,在被root 的手機上執行,閃退。

繞過方法也很簡單,在magisk的設定中,開啟遵守排除列表,然後在配置排除列表中選擇應用gl。

應用被新增到排除列表後,將沒辦法使用lspoed,只能使用frida。

2.2 繞過ssl pinning

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相關。

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。

Binary file ./files/RN/rnbundle/index.android.bundle matches

把./files/RN/rnbundle/index.android.bundle拿出來。

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個可疑的函式。

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。

[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還沒有載入。

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。

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

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

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。

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。

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*",

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

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函式。

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。

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。

  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。

pip install https://github.com/niosega/hbctool/archive/ac6fabb69a7229ed9764997d153d4f703d1381aa.zip

反編譯

hbctool disasm index.android.bundle bundle

回編譯

hbctool asm bundle index.android.bundle.re

bytecode反編譯出來的程式碼如下:搜尋兩處能md5相關的程式碼。

並不需要深入bytecode,指令詳情,瞭解幾個簡單指令即可。

只需要寫一個列印日誌的函式,變成成bytecode,反編譯後替換掉這兩個函式就能看到這兩個md5函式的輸入引數。

3.4 js程式碼編譯成Hermes bytecode

先編譯Hermes原始碼,會得到./build/bin/hermes工具,

test.js

function hook_log(a, b) {
window.nativeLoggingHook(a + " " + b, 1);
return " ";
}


function test() {
hook_log("333", "555");
}

寫了一些簡單的js程式碼,把test.js編譯成bytecode

./build/bin/hermes  -emit-binary -out test.hbc test.js

010editor開啟test.hbc可以看到MAGIC

反編譯test.hbc

hbctool disasm test.hbc testdir

得到

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。

{
"id": 253,
"isUTF16": false,
"value": "window"
},
{
"id": 33087,
"isUTF16": false,
"value": "nativeLoggingHook"
},
{
"id": 18880,
"isUTF16": false,
"value": " "
},
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。

hbctool asm bundle index.android.bundle.re


md5sum index.android.bundle.re
7eba6c20134268a82e5a0af948ca550b index.android.bundle.re

現在替換app目錄下的index.android.bundle。

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函式的輸入引數。

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

看雪ID:Imyang

https://bbs.pediy.com/user-home-548459.htm

*本文由看雪論壇 Imyang 原創,轉載請註明來自看雪社群

#

往期推薦

1. 2022CISCN初賽 ez_usb WriteUp

2. Flutter APP逆向實踐

3. APT Turla樣本分析

4. Il2Cpp恢復符號過程分析

5. 記一次安全產品的漏洞挖掘

6. CVE-2016-3309提權漏洞學習筆記

球分享

球點贊

球在看

點選“閱讀原文”,瞭解更多!