AI-Assisted on RASP Analysis

I. Introduction
Disclaimer: This blog does not cover bypassing BShield. It is simply a summary of how I used AI as a tool to assist with analysis, debugging, environment setup, and attempting to reverse engineer the methods BShield uses to detect unsafe environments on Android devices.
Recently, I worked on a small side project using LLM agents to automate the analysis and identification of SSL pinning mechanisms in Android applications, then generate app-specific bypass scripts. I tested it on several apps developed by ********. Everything went fairly smoothly — until I tried it on the ********** application.
Basically, this is an application for managing SIM cards and convenient tech shopping. Users can activate SIMs, track data plans, data usage, orders, and loyalty points - all in a clean, easy-to-use interface. A while back, I had also tried analyzing this app. At that time, it was pretty basic with no significant environment checks. However, this time around, things were very different.
II. It all started with the device…
A quick overview of the device I used for testing: it's an Android 12 phone rooted via KernelSU. I also installed several KernelSU modules including Zygisk Next, LSPosed, SUSFS, and Magisk Frida.
When I installed and opened **********, the app immediately reported "Custom ROM Detected." This was quite confusing since my device was running the manufacturer's stock ROM.
At this point, I had already identified that the app was using RASP. The first thing I thought of was to pull the base.apk and hand it to Claude for a preliminary analysis.
Using adb to pull base.apk and split_config.arm64_v8a.apk of ********** app (**.***.*******.***). Analysis and detect app's RASP solution
This was fairly straightforward for Opus 4.6, and the result helped me confirm that ********** uses BShield as its RASP solution.
2. Evidence that ********** uses BShield (Objective #1)
E-1 — io.bshield identifier string found in native lib
libtdaouibii.so (JNI loader of the app-wide instance) directly references the BShield SDK's callback class:
$ strings arm64-v8a/libtdaouibii.so | grep bshield
io/bshield/callback/ShieldDataProtection
Lio/bshield/callback/ShieldDataProtection;
io.bshield is BShield's official namespace — this is the strongest vendor attribution evidence.
E-2 — Loader.java hardcodes BShield endpoint + config file names
File: 1.4.6/JADX/sources/p079xq/Loader.java (verbatim excerpt):
package p079xq;
public final class Loader {
public static final String byvrkwn_k = "";
public static final String byvrkwn_s = "";
public static final String byvrkwn_v = "byvrkwn.dat";
public static final String ehfbad_k = "https://exits-bshield-mvno.********.com.vn/";
public static final String ehfbad_s = "";
public static final String ehfbad_v = "ehfbad.dat";
}
The domain exits-bshield-mvno.********.com.vn matches the alert endpoint of BShield'. The suffixes _v (.dat file), _k (key/URL), _s (signature) follow BShield's instance management format.
E-3 — Class named BShield in the eKYC SDK
File: 1.4.6/JADX/sources/vn/p076ai/faceauth/sdk/presentation/presentation/utils/BShield.java:
package vn.p076ai.faceauth.sdk.presentation.presentation.utils;
...
public class BShield {
public static HashMap<String, String> shieldData(List<String> list, String str) {
byte[] bArrFnerd = new hzuusw(list, str).fnerd(); // -> iywxe.fxg() (native)
if (bArrFnerd == null || bArrFnerd.length < 32) { ... return null; }
byte[] bArrCopyOfRange = Arrays.copyOfRange(bArrFnerd, 0, 32); // authentication code
byte[] bArrCopyOfRange2 = Arrays.copyOfRange(bArrFnerd, 32, bArrFnerd.length);
HashMap<String, String> map = new HashMap<>();
map.put(iywxe.nlaz(284), Base64.encodeToString(bArrCopyOfRange2, 2)); // payload
map.put(iywxe.nlaz(285), Base64.encodeToString(bArrCopyOfRange, 2)); // tag
return map;
}
}
BShield.shieldData() is BShield's "data protection / signing" API — the counterpart to io.bshield.callback.ShieldDataProtection from E-1.
E-4 — BShield configuration assets with version info
$ ls 1.4.6/JADX/resources/assets/
bshield.dat (858 bytes, high-entropy encrypted)
byvrkwn.dat (17 KB)
ehfbad.dat (216 KB)
shield/********_v1.3_android_debug_2.8.4.dat
shield/********_v1.3_android_release_2.8.4.dat
The filename ********_v1.3_android_release_2.8.4.dat reveals: app-profile ********_v1.3, platform android, BShield engine version 2.8.4. The bshield.dat header (08 25 10 9b 07 1a d2 06 …) resembles an encrypted protobuf wire-format.
▎ Objective #1 Conclusion: Confirmed that ********** 1.4.6 uses BShield (io.bshield) v2.8.4, deployed as 2 native instances (libehfbad.so + libbyvrkwn.so) with JNI loader libtdaouibii.so.
After confirming that ********** uses BShield, I searched for existing bypass methods and found a fairly useful document: BShieldExperimentation
Overall, it's a fairly comprehensive document about bypassing BShield's environment checks using KernelSU modules. For error code 12 that I was encountering, the author suggested installing the module TEESimulator and adding the package name to target.txt.
I tried following the instructions, but the app still showed the same error on launch:
At this point, Claude continued doing its job:
Currently, my ********** application is encountering error code 12. Based on BShield's inspection mechanisms and the bypass methods in the file @DETECTION_vi_vn.md, I have added package name to target.txt and it doesn't help. Help me fix this error.
Claude summary output:
● BShield Error Code 12 — Custom ROM Detection Bypass
Problem
********** app (**.***.*******.*** v1.4.6) was triggering BShield error code 12 (custom ROM detection) and killing the app on launch. The user had already added the package name to TrickyStore's target.txt, but it didn't help.
Root Cause
BShield detects custom ROMs by reading system properties via _system_property* (native) and popen("getprop") (shell). Two properties were leaking test-keys:
ro.build.display.id = SKQ1.211006.001 test-keys
ro.build.keys = test-keys
TrickyStore/TEESimulator only handle key attestation and Play Integrity — they do not spoof system properties, which is why adding the package name to target.txt had no effect on code 12.
Fix Applied
Created and installed a KernelSU module (bshield_prop_bypass) that runs resetprop at post-fs-data on every boot:
Strips test-keys from ro.build.display.id
Sets ro.build.keys to release-keys
Also sets safe defaults for ro.build.tags, ro.build.type, ro.secure, ro.debuggable, boot state properties, and clears root-related service properties
Result
After applying the module and rebooting, ********** launches and runs normally — no BShield warning or app kill.
Just by granting Claude access to adb, it was able to independently inspect the device information, identify the problem, and immediately code a KernelSU module that resets system properties on my device to bypass BShield's error code 12. At this point, I asked myself another question: can Claude do even more than this?
III. Force Claude to Reverse BShield Logic…
When I revisited the list of BShield's environment check mechanisms in the document: BShieldExperimentation
I had a new question: besides the mechanisms already documented, are there other mechanisms hiding in BShield's source code? So I ran an experiment: using Claude Opus 4.6 to reverse engineer and perform static analysis on the BShield engine to identify all of its detection mechanisms.
Role: You are an elite Mobile Security Researcher, Reverse Engineer, and Expert in breaking Runtime Application Self-Protection (RASP) technologies. Your current target is the BShield Android RASP engine.
Task: Perform an exhaustive Static Analysis and Reverse Engineering review of the provided decompiled Java code (JADX) and Native C/C++ pseudocode of BShield. Your primary objective is to identify, locate, and document BOTH the
standard error-coded checks and the undocumented, stealthy anti-analysis mechanisms embedded in the native layer.
Context & Target Environment:
The decompiled Java/Kotlin source code of the main application bundle (base.apk) is located and referenced at: @1.4.6/JADX/
The target Native C/C++ libraries (.so files) responsible for low-level security enforcement are located and referenced within the @1.4.6/arm64-v8a/
BShield uses a public Error Code matrix (1-17), but relies heavily on advanced, undocumented C/C++ native techniques implemented within the native libraries to detect sophisticated instrumentation frameworks and reverse engineering attempts.
Available Research Lab Environment (Your Execution Capabilities):
You have full access to a dedicated mobile security testing lab to verify findings dynamically. You can freely utilize and orchestrate the following toolsets:
Physical device connected via ADB (Android Debug Bridge) with full root privileges (
adb shell,adb logcat).Ghidra Decompiler environment integrated via Ghidra-MCP for automated interactive analysis queries, symbols exploration, and cross-references.
radare2 / iaito for immediate command-line binary patching, static analysis, and offset verification.
apktool: For extracting, decoding resources, modifying Smali, and rebuilding the APK (
apktool d app.apk).JADX / JADX-GUI: For decompiling DEX bytecode and tracking cross-layer JNI calls (
jadx app.apk -d output/).strace: For low-level syscall tracing directly from the device runtime (
strace -f -p <pid>).ISOLATED FRIDA BINARY MANDATE (Frida v17.9.11): To prevent tampering with the host's global Frida installation (v17.9.10), you are strictly restricted to using the standalone executable binaries located inside the project's environment folder. You MUST NOT use global or naked commands. Every generated command, script execution, or automation routine MUST use the explicit absolute binary paths:
Isolated Frida Command Path: @.********_env/bin/frida
Isolated Frida-ps Command Path: @.********_env/bin/frida-ps
Isolated Frida-trace Command Path: @.********_env/bin/frida-trace
Isoladted other frida bindary in @.********_env/bin/. Instruction: All generated bypass proof-of-concepts, dynamic tracing commands, or injection workflows must be compatible with Frida 17.9.11 and structured to call the exact binary paths defined above.
Instructions for Analysis:
Meticulously scan the provided code/pseudocode from both directories to isolate function signatures, JNI bindings (Java_com_...), string constants, system calls, and inline assembly that correspond to the following two layers of
defense:
LAYER 1: Documented Error Code Logic (Matrix 1-17) in @DETECTION_vi_vn.md
Identify the routines, switch-cases, or conditional blocks in the Java source code that map to native dynamic calls triggering the known error categories (Errors 1-17 including App Modification, Emulator, Blacklisted Apps, Debugger, Root, Bootloader, App Cloner, ADB, Developer Options, Custom ROM, Accessibility, Screen Mirroring).
LAYER 2: Undocumented & Advanced Anti-Analysis Mechanisms (The Stealth Layer)
Look closely inside the Native Libraries pseudocode for low-level implementations targeting advanced instrumentation and root hiding:
Advanced Frida & Hooking Detection: Memory Map Scanning (/proc/self/maps anomalies), Named Pipes/Unix Sockets (frida- patterns, port 27042), Inline Hook Detection (Text segment integrity validation on critical libc/libart functions via BKPT checking), and Thread Name/Count verification (gmain, gum-js-loop).
Kernel-Assisted Root & Stealth Bypass Detection: SUSFS/APatch/KernelSU indicators, loop device image scans (/dev/block/loop*), SELinux enforcing context, and mount point mutations.
Hardware Breakpoint & Anti-Debugging Controls: Debug register inspections (BVR/BCR registers on ARM64) to catch live debuggers, Ptrace mitigation, and raw Direct Syscalls (SVC #0) to completely bypass user-mode libc hooks.
Code Obfuscation: Identify OLLVM Control Flow Flattening and guide real execution reconstruction.
Output Format Required:
Present your findings in a structured Markdown report:
BShield Defense Architecture: High-level overview of how Java layer and Native libraries (.so) sync via JNI.
Known Error Code Matrix: Table mapping Error Code | Target Vector | Code Location / Native Symbol name.
Undocumented Stealth Layer Deep-Dive: Comprehensive technical analysis of the Frida-hook, Syscall, and KernelSU detection logic found in the native libraries.
Interactive Bypass & Verification Guide: Provide the exact multi-tool workflow (e.g., specific
adbmonitoring filters,stracetriggers for syscall interception, Ghidra-mcp analysis steps, radare2 binary patching arguments, and ready-to-run Frida 17.9.11 scripts using the isolated environment path).
After receiving the prompt, Claude divided the analysis process into stages and executed them sequentially.
First, Claude scanned the arm64-v8a/ directory to identify which libraries belong to BShield among the 20 .so files in the APK. Using filenames and cross-referencing with Java source, Claude identified 3 libraries:
libbyvrkwn.so(9.4MB, primary engine),libehfbad.so(11.3MB, secondary engine), andlibtdaouibii.so(bootstrap loader). It then importedlibbyvrkwn.sointo Ghidra via MCP to begin analysis.Next, Claude read through the entire JADX source to map the Java-layer architecture. Here, Claude identified 3 JNI classes (
iywxe,qxbmnn,kvkdam) with a total of 73+ native methods, along with the periodic scanning mechanism via ScheduledExecutorService and ContentObservers monitoring ADB, Developer Mode, and Accessibility.With the overall picture in hand, Claude began mapping each known error code from the documentation to specific native implementations — identifying entry points, decompiling functions via Ghidra MCP, and verifying detection mechanisms for each error code from 1 to 12.
The heaviest part was analyzing Layer 2 — the stealth mechanisms not covered in the documentation. Claude combined Ghidra MCP (decompile, xref, call graph), radare2 (string search, SVC scan, export listing), and ran a background agent in parallel to analyze libehfbad.so.
IV. What Claude Found
For the documented portion, Claude confirmed 12 active error codes (1-8, 10-12), with error code 5 (Root Detection) broken down into 8 separate sub-vectors: system properties, memory map injection, SELinux enforcement, root manager packages, custom launcher, bootloader state, suspicious mounts, and KSU/AP loop devices. Error code 12 (Custom ROM) appears to be exclusive to **********.
The more valuable findings were the mechanisms not covered in the documentation. Claude discovered 12 additional stealth mechanisms:
BShield doesn't just look for the name "frida" in /proc/self/maps — it opens the maps file, finds entries with r-xp/r--p permissions, then mmaps the original library from disk and compares it byte-by-byte against the version currently in memory. Any discrepancy - inline hooks, Frida gadget injection, code patching - gets detected. This mechanism is located at FUN_00b0b3a8 in libbyvrkwn.so.
Regarding SELinux, BShield doesn't simply call getenforce and check the result. It embeds the entire libsepol library to parse the binary SEPolicy directly — checking ocontexts (fsuse, genfs, nodecon, portcon...), detecting permissive domains and modified policies. This is a much deeper level of inspection than what was previously known. Additionally, there are 6 getxattr call sites to verify the security.selinux extended attribute on APK files, system binaries, and process contexts.
Claude also found 17 SVC #0 instructions in libbyvrkwn.so - direct syscalls that completely bypass libc. After filtering, 2 at aligned addresses were confirmed as real code; the rest were in data sections or OLLVM artifacts. Additionally, there are syscall() wrappers calling getcpu (syscall 0xa8) for timing-based anti-debug and gettid (syscall 0xb2) for thread tracking. Notably, libehfbad.so doesn't use inline SVC at all — it relies 100% on the libc syscall() wrapper.
For emulator detection, BShield parses /proc/cpuinfo to extract and cross-validate 8 fields: processor, CPU part, Features (fp/asimd/atomics/crc32/pmull/sha1/sha2/cpuid...), CPU variant, CPU revision, CPU implementer (0x41=ARM, 0x51=Qualcomm, 0x53=Samsung), CPU architecture, and Hardware (matching against "darcy", "Tegra AP", "Texas Instruments").
Another interesting finding: BShield uses dl_iterate_phdr to enumerate all shared libraries loaded in the process. The callback (FUN_00691608) checks each library name against an encrypted blacklist. libehfbad.so additionally uses dladdr to resolve symbols from addresses.
libehfbad.so also imports mincore and madvise - mincore checks which pages are resident in RAM (detecting memory manipulation), while madvise interferes with memory dumping. Combined with mprotect and prctl (setting PR_SET_DUMPABLE=0), this forms a fairly comprehensive anti-dump system.
BShield also embeds the full BoringSSL library in libehfbad.so (the build path leaked: /home/vkms/Documents/CI/AzureAgentPool/_work/4/s/libs/boringssl/crypto/) for native-level TLS pinning. There are also 6 native handlers intercepting MotionEvent and KeyEvent to detect automated testing frameworks, and a mechanism that reads /proc/pid/cmdline to detect sandboxed execution.
Finally, popen() is called with encrypted commands — decrypted at runtime via FUN_00289ec4 — so static analysis cannot determine what commands BShield is executing on the system.
V. Troubleshooting
The process of getting Claude to analyze BShield was far from smooth. BShield is designed to resist static analysis, so there were many points where Claude hit dead ends or had to change its approach.
The first and biggest challenge: BShield encrypts all security-related strings. Normally when reversing a RASP engine, the first step is to search for strings - "frida", "/proc/self/maps", "magisk", "su" — then trace back to the functions containing the detection logic. When I asked Claude to search for these strings across both native libraries, the results were all 0. Not a single string existed in plaintext. Claude had to switch to a different approach: instead of finding strings and tracing backward, it started from imported functions (fopen, getxattr, dl_iterate_phdr, syscall...) and traced forward to see where they were called and what they did. This approach was much slower, but still produced results.
The second challenge was OLLVM Control Flow Flattening. All of BShield's critical functions are obfuscated: the original logic is transformed into a while(true) loop with a state variable controlling execution via switch-case. When Claude decompiled through Ghidra, the pseudocode came back fragmented and Ghidra reported "Could not recover jumptable." Claude could still identify patterns within individual state blocks and infer the function's purpose, but couldn't fully reconstruct the complete logic with precision.
Another issue was Dynamic JNI Registration. BShield doesn't use the conventional Java_com_* function naming; instead, it registers all native methods via RegisterNatives inside JNI_OnLoad. Claude traced the call chain down to the RegisterNatives dispatch, but the actual mapping table — which Java method calls which native function — resides in the data section and can only be resolved at runtime. Static analysis stops here.
Then there was the anti-hook mechanism that only appears at runtime. The pattern mov x16,x16; mov x17,x17 used to block Frida's Interceptor.attach doesn't appear in the binary files. This prologue isn't baked into the .so file but is injected into memory at runtime via mprotect. Claude detected 3 call sites but couldn't determine which functions would receive the prologue.
Overall, BShield is designed to counter exactly what we were trying to do: string encryption neutralizes string search, OLLVM flattening breaks decompilation, dynamic JNI registration hides mappings, and runtime code patching means the static binary doesn't reflect actual runtime behavior. Claude got fairly far by combining Ghidra MCP, radare2, and import tracing — but certain aspects like JNI mapping, runtime prologues, and kill function addresses can only be fully resolved through dynamic analysis on an actual device.
VI. Closing Thoughts
So far, I've only managed to deploy Claude as a hub that integrates various tools to perform static analysis. Ghidra MCP allows Claude to decompile and trace xrefs automatically - something it couldn't do with just raw binaries. radare2 adds string search and byte pattern scanning capabilities. JADX source provides Java-layer context. Providing the right set of tools gives Claude more context to perform effective static analysis.
I also tried giving Claude access to adb and Frida for dynamic analysis. However, there are currently some limitations.
First, BShield currently detects Frida hooks very aggressively. When hooking with Frida, the ********** app crashes immediately. I tried using the indicators Claude identified to find bypass approaches, and also asked Claude to identify new bypass methods. However, I noticed that Claude gets stuck trying to write a Frida script to bypass the detection, rather than exploring other approaches like leveraging LSPosed or building a custom Frida build.
Second, my current workflow is missing a library/tool that allows Claude to see and interact with the device for dynamic analysis proactively. I'm currently experimenting with adding droidrun to address this issue.
As for bypassing BShield's Frida hook detection, that will probably be a story for another blog post…





