Help! Intercepting and Modifying Lua Scripts in Unity IL2CPP Game (BLEACH: Soul Resonance) - XLua Issues

lola1337

Platinian
Game: BLEACH: Soul Resonance (Unity + IL2CPP + XLua)

Platform: Android (x86_64 emulator)

Tools: Frida, Il2CppDumper, ZygiskFrida/frida-server

Goal:Intercept and modify Lua scripts that control combat mechanics (attack speed, damage, abilities).

Problem:All combat logic is implemented in Lua, and C# functions are not called directly during combat.

What We Tried (Didn't Work):
  1. Hooking C# functions via IL2CPP offsets:
  • EntityLogicComp$$SetRateSpeed (RVA: 0x2660104)
  • BaseEntity$$SetRateSpeed (RVA: 0x2582408)
  • get_AtkSpeed (RVA: 0x265F464)
  • Result: Functions are not called during combat
  1. Hooking XLua wrappers:
  • XLua.CSObjectWrap.BaseEntityWrap$$_m_SetRateSpeed
  • XLua.CSObjectWrap.EntityLogicCompWrap$$_m_SetRateSpeed
  • Result: Wrappers are not called
  1. Intercepting Lua via XLua functions:
  • XLua.LuaEnv$$DoString (Address: 40901428, RVA: 0x26F0C34)
  • XLua.LuaEnv$$LoadString (Address: 40902012, RVA: 0x26F0E9C)
  • Result: Hooking DoString/LoadString causes the game to freeze on a black screen after loading
Current Approach:Using Frida Server on emulator:
  • Installed frida-server-17.5.1-android-x86_64
  • Connection via MCP server
  • Hook on DoString at address base + 0x26F0C34
  • Problem: DoString is not called during combat (hook is installed, but no calls detected)
Questions:
  1. Why is DoString not being called? Could Lua be loaded through a different mechanism?
  2. How to safely intercept Lua without freezing the game?
  3. Are there alternative ways to modify Lua in XLua (e.g., via Lua tables in memory)?
  4. Could it be that combat logic runs in already-loaded Lua functions rather than through DoString?
Technical Details:
  • Architecture: Unity 2021.x + IL2CPP + XLua
  • Process: com.bleach.apj (PID: 3928)
  • Module: libil2cpp.so (base: 0x763867709000)
  • DoString offset: 0x26F0C34 (from Il2CppDumper)
  • Frida version: 17.5.1
What We Need: Advice on intercepting/modifying Lua in XLua without freezing the game, or alternative approaches to modifying combat logic.
 
The bad side i can't make Mod Menu to enable or disable function because the game load LUA only once time. Maybe it's possible if i can swith back original LUA by force call? But i don't know about these things yet. As for now i'm okay because i can hack it
Yes, it's possible, but with caveats. If you already replaced the entire chunk during loading, the original chunk isn't loaded into memory, only the modified chunk is in memory, but the constants in k[] are still in memory after loading. After loading the modified chunk, find the needed Proto in memory via loadFunction or by searching the structure, save a pointer to k[] and the constant index, for example k[14]. In the mod menu, when the slider changes, modify the value in k[] via Frida in real time, and damage will change dynamically without reloading Lua. You don't need to reload Lua - work with the already loaded chunk in memory, modify only the constants in k[], don't touch the Proto structure. If you need to toggle on/off, save the original constant value from the modified chunk and switch between it and the new value. Even if the chunk was replaced during loading, the constants in k[] remain in memory and can be modified dynamically through the mod menu.
 
The bad side i can't make Mod Menu to enable or disable function because the game load LUA only once time. Maybe it's possible if i can swith back original LUA by force call? But i don't know about these things yet. As for now i'm okay because i can hack it
How you decompile lua from dump lua in lualbufferx ?
 
How you decompile lua from dump lua in lualbufferx ?
Lua is dumped from luaL_loadbufferx by hooking that function in the Frida script lua_w26_proto_patch.js; when a target chunk (e.g. combat/api/damageFunc) is loaded, the buffer and size are taken from the hook’s arguments, sent via send as lua_dump, and run_frida_js.py writes them to work/lua_dumps/*.bin. There is no bytecode-to-source decompiler: the dump is first parsed with lua_undump_proto.py (same format as luaU_undump/loadFunction), then the chosen Proto is disassembled by lua_disasm_from_proto.py into W26 opcodes with constant annotations.

 
Back
Top Bottom