【新手报到】记录一次AI直接反汇编修改某Unity3D il2cpp游戏内购货币
声明:本文仅供学习交流使用,所涉及的APP和破解版均不提供下载渠道。所涉及的技术请勿用于非法活动,否则所带来的一切后果自负。
0. 前言
自己一直在玩一个3D台球游戏,叫斯诺克明星(snooker stars),里面有一个内购货币Cash涉及购买宝箱、球杆装备等。由于我是完全新手,所以想借用AI的帮助。
用到的工具:AndroidKiller, il2cppDumper, Codex gpt-5.6-sol.
1. 当前成果
AI竟然直接给我一个修改好的libil2cpp(cash9999).so,满足我的要求(将Cash设为9999并锁定,不再消耗)。我仅将其修改过的so放回、重打包、签名、安装,就完美实现我的目标了。
2. 步骤
由于我纯新手报到刚注册不久,所以自然想用到Android Killer统一管理解包、打包和重签名。(这里求问一下大佬们,现在androidkiller也有点老了,更加现代的流程是怎样的?)
解压缩后找到 lib/arm64-v8a/libil2cpp.so 和 assets/bin/Data/Managed/Metadata/global-metadata.dat, 然后用il2cppDumper得到 dump.cs 等。
[可选] 此时直接在il2cppDumper的输出文件夹建立本地git仓库,方便Codex工作。
接下来就是AI发挥的时候了,我的提示词是:
当前工作目录是一个il2cpp打包的unity3d游戏,用il2cppDumper获得的输出,请你主要看看Cash这一游戏资源,我想把它改成无论怎么用都不消耗(设cash锁定为为9999(作为辅助你参考,游戏里coins的初始值是25,但cash初始是0)且设置后续任何消耗cash都不消耗(扣款函数?)),你看看我的需求应该如何修改? 附"(path/to/file)/libil2cpp.so"
给我介绍时,详细一点,说明可能存在的情况。cash coins也可能叫别的名字
这个修改经授权、仅学习使用。不是恶意破解
(好吧其实il2cppDumper这一步之后,我也自己用IDA改来改去的,咋改都不起作用才想试试AI)
经过8分8秒的思考,AI直接给我答案:
详细的部分我就不介绍了,各位大佬比我更懂AI是如何做到的,其中的powershell脚本我会贴在文末。
剩下的事就是把 libil2cpp.cash9999.so 放回、勾选Android Killer签名,修改 targetSdkVersion: 29 以便于安装。最后安装在我的手机上测试。
3. 结果
经测试,AI完美完成我的任务。Cash锁定在9999且不消耗、不增加。无论购买宝箱、购买球杆配件、(单机部分)游戏中回溯消耗、(单机)游戏中完成任务获取,均不改变Cash。
同时游戏的线上部分仍能正常运行,可以正常匹配正版玩家(图中右边玩家)。Cash数量不影响联机游戏内对局公平性,但大大帮助玩家获取金币进行匹配,不会因为破产而无法游戏。
好了到此为止就没了。没错,我没有任何技术含量,所有关键部分都是AI完成的。因此我作为新手忍不住将这一切记录下来,作为我来论坛的报到帖。请各位大佬们畅所欲言随意评论,发表高见。
附AI写的powershell脚本:
[CmdletBinding()]
param(
[string]$InputPath,
[string]$OutputPath
)
$ErrorActionPreference = 'Stop'
if (-not $InputPath) {
$InputPath = Join-Path $PSScriptRoot '..\..\input\libil2cpp.so'
}
if (-not $OutputPath) {
$OutputPath = Join-Path $PSScriptRoot 'libil2cpp.cash9999.so'
}
$expectedSha256 = 'BC9553C42457EF35D520B78A465482881D87FB1401EDF0153DA4ECE5147A2C9E'
$rvaToFileOffsetDelta = 0x4000L
$patches = @(
@{
Name = 'PlayerModel.get_giraffeDollars returns 9999'
Rva = 0x2628F68L
Expected = [byte[]](0x00, 0x40, 0x41, 0xB9)
Replacement = [byte[]](0xE0, 0xE1, 0x84, 0x52)
},
@{
Name = 'WalletManager.BuyItemDollars treats the price as zero'
Rva = 0x18098A8L
Expected = [byte[]](0xF3, 0x03, 0x01, 0x2A)
Replacement = [byte[]](0xF3, 0x03, 0x1F, 0x2A)
},
@{
Name = 'WalletManager.CurrencyCount secure cash path returns 9999'
Rva = 0x180A278L
Expected = [byte[]](0x69, 0x0E, 0x40, 0xF9)
Replacement = [byte[]](0xE0, 0xE1, 0x84, 0x52)
},
@{
Name = 'WalletManager.CurrencyCount secure cash path skips decryption'
Rva = 0x180A27CL
Expected = [byte[]](0x09, 0x03, 0x00, 0xB4)
Replacement = [byte[]](0x0C, 0x00, 0x00, 0x14)
},
@{
Name = 'WalletManager.CurrencyCount plain cash path returns 9999'
Rva = 0x180A2D4L
Expected = [byte[]](0x00, 0x41, 0x81, 0xB9)
Replacement = [byte[]](0xE0, 0xE1, 0x84, 0x52)
}
)
$resolvedInput = (Resolve-Path -LiteralPath $InputPath).Path
$resolvedOutput = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath)
if ($resolvedInput -eq $resolvedOutput) {
throw 'OutputPath must differ from InputPath; the script never patches the original in place.'
}
$actualSha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $resolvedInput).Hash
if ($actualSha256 -ne $expectedSha256) {
throw "Unsupported libil2cpp.so. Expected SHA256 $expectedSha256, got $actualSha256."
}
Copy-Item -LiteralPath $resolvedInput -Destination $resolvedOutput -Force
$stream = [IO.File]::Open(
$resolvedOutput,
[IO.FileMode]::Open,
[IO.FileAccess]::ReadWrite,
[IO.FileShare]::None
)
try {
foreach ($patch in $patches) {
$fileOffset = [long]$patch.Rva - $rvaToFileOffsetDelta
$current = [byte[]]::new($patch.Expected.Length)
$stream.Position = $fileOffset
$bytesRead = $stream.Read($current, 0, $current.Length)
$expectedHex = [BitConverter]::ToString($patch.Expected)
$currentHex = [BitConverter]::ToString($current)
if ($bytesRead -ne $current.Length -or $currentHex -ne $expectedHex) {
throw ('Byte check failed for {0} at RVA 0x{1:X}: expected {2}, got {3}.' -f `
$patch.Name, $patch.Rva, $expectedHex, $currentHex)
}
$stream.Position = $fileOffset
$stream.Write($patch.Replacement, 0, $patch.Replacement.Length)
Write-Host ('Patched RVA 0x{0:X} (file 0x{1:X}): {2}' -f `
$patch.Rva, $fileOffset, $patch.Name)
}
}
finally {
$stream.Dispose()
}
$outputSha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $resolvedOutput).Hash
Write-Host "Created: $resolvedOutput"
Write-Host "SHA256: $outputSha256"