本帖最后由 cqy666 于 2026-9-7 18:15 编辑
x64雷神9安卓模拟器,ce看代码,frida直接patch arm64代码,雷神会实时转义arm64到x64代码,效果出来了。
手动清缓存后
地址确保不变情况下,
优化一键启动pwsh命令:
[Python] 纯文本查看 复制代码
$PKG = "com.FDGEntertainment.redball4.gp";adb shell am force-stop $PKG; adb shell monkey -p $PKG -c android.intent.category.LAUNCHER 1; & "F:\newfrida\.venv\Scripts\frida.exe" -UF $PKG -l "F:\core\frida\fridaroot\无限命x64.js"
原理:
- T0 首次启动:CE获取 SO 内存基址,记录目标函数偏移
- T1 Force Stop:清空应用缓存与旧映射
- T2 二次启动:Linker 加载 ARM64 SO 到内存
- T3 注入 Patch:在 Houdini 翻译前,通过 Frida/CE调试器写入补丁(⚡ 窗口期极短)
- T4 Houdini 翻译:读取已 Patch 的 ARM64 指令
- T5 执行生效:后续调用直接使用新 TB,补丁落地
- 把 Houdini 想象成一个同声传译员,ARM64 代码是"外语演讲稿",x86 代码是"中文译文":
- 先听一遍记地址:第一次启动 App,记下演讲稿放在桌上哪个位置
- 擦掉旧译文:Force Stop,把翻译官脑子里的旧译文全部擦掉
- 重新摆上演讲稿:第二次启动,演讲稿重新放到桌上
- 抢在翻译前改稿子:趁翻译官还没开口,冲上去把稿子上的关键句子改掉
- 翻译官读到改过的稿子:他照着修改后的内容翻译,中文译文自然就包含了你的改动
- 听众听到新译文:App 执行的就是带补丁的新逻辑
无需借助CE,Frida完整实例(20260907):
[C#] 纯文本查看 复制代码
public void RunBall()
{
const string ProcessName = "Red Ball 4";
const string DeviceId = "emulator-5554";
Session session = null;
Script script = null;
//pwsh手动
//$PKG = "com.FDGEntertainment.redball4.gp";adb shell am force-stop $PKG;adb shell am start -a android.settings.APPLICATION_DETAILS_SETTINGS -d package:$PKG
//清缓存
string pkg = "com.FDGEntertainment.redball4.gp";
var psi = new ProcessStartInfo("adb")
{
Arguments = $"shell \"am force-stop {pkg}; monkey -p {pkg} -c android.intent.category.LAUNCHER 1\"",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true
};
using var process0 = Process.Start(psi);
process0?.WaitForExit();
string output = process0?.StandardOutput.ReadToEnd();
try
{
// 1. 查找设备与进程
var deviceManager = new DeviceManager();
var device = deviceManager.EnumerateDevices()
.FirstOrDefault(d => d.Id == DeviceId)
?? throw new InvalidOperationException($"未找到设备: {DeviceId}");
var process = device.EnumerateProcesses()
.FirstOrDefault(p => p.Name == ProcessName)
?? throw new InvalidOperationException($"未找到进程: {ProcessName}");
Log($"[Frida] 已附加到 {process.Name} (PID={process.Pid})");
session = device.Attach(process.Pid);
// 2. JS脚本:监听C#发来的指令,执行后回传结果
var js = @"
function getModuleBase(name) {
// 1. 标准 API
var mod = Process.findModuleByName(name);
if (mod) return mod.base;
// 2. /proc/self/maps: r-xp + offset=0 + 路径含name + 最低地址
var base = null;
try {
File.readAllText(""/proc/self/maps"").split(""\n"").forEach(function (l) {
if (l.indexOf(name) !== -1 && l.indexOf("" r-xp "") !== -1 && l.split(/\s+/)[2] === ""00000000"") {
var addr = ptr(""0x"" + l.trim().split(""-"")[0]);
if (!base || addr.compare(base) < 0) base = addr;
}
});
if (base) return base;
} catch (e) { }
// 3. 内存扫描 ELF magic + 路径含name + 最低地址
var res = [];
Process.enumerateRanges('r--').forEach(function (r) {
if (r.size < 4e6 || r.size > 1e8) return;
try {
if (r.base.readU32() !== 0x464C457F) return;
if (r.file && r.file.path.indexOf(name.replace(/^lib|\.so$/g, """")) !== -1)
res.push(r.base);
} catch (e) { }
});
res.sort(function (a, b) { return a.compare(b); });
return res.length ? res[0] : null;
}
recv('patch', function(msg) {
try {
var il2cpp = getModuleBase('libil2cpp.so');
if (!il2cpp) { send({ type:'result', ok:false, error:'libil2cpp.so not found' }); return; }
var target = il2cpp.add(msg.offset);
console.log(' Target address: ' + target);
Memory.protect(target, 8, 'rwx');
target.writeU32(msg.opcode); // MOV W0, #N
target.add(4).writeU32(0xD65F03C0); // RET
send({ type:'result', ok:true, address: target.toString() });
} catch(e) {
send({ type:'result', ok:false, error: e.message });
}
});
";
script = session.CreateScript(js);
// 3. 用 TaskCompletionSource 将 Message 回调转为同步等待
var tcs = new TaskCompletionSource<string>();
script.Message += (s, msg) =>
{
Log($"[Frida] {msg.Message}");
try
{
var json = JObject.Parse(msg.Message);
if (json["type"]?.ToString() == "send")
{
var payload = json["payload"];
if (payload?["type"]?.ToString() == "result")
tcs.TrySetResult(payload.ToString());
}
}
catch { /* 非结构化消息忽略 */ }
};
script.Load();
// 4. 发送指令(替代字符串拼接构建JS)
var command = JsonConvert.SerializeObject(new
{
type = "patch",
offset = 0x1138810,
opcode = 0x52800060 // MOV W0, #3
});
script.Post(command);
// 5. 同步等待结果(带超时保护)
bool completed = tcs.Task.Wait(TimeSpan.FromSeconds(10));
if (!completed)
throw new TimeoutException("Frida 脚本响应超时");
var result = JObject.Parse(tcs.Task.Result);
if (result["ok"]?.Value<bool>() == true)
Log($"[Frida] ✓ 无限生命补丁已写入 @ {result["address"]}");
else
Log($"[Frida] ✗ 补丁失败: {result["error"]}");
}
catch (Exception ex)
{
Log($"[Frida] 错误: {ex.Message}");
}
finally
{
script?.Unload();
session?.Detach();
}
}
|