love2d引擎 手游修改一例

love 2d 介绍

LÖVE是一个使用 Lua 作为编程语言的 2D 游戏框架,网络上关于此引擎的修改教程还是相对较少。此次下手的目标为王国保卫战,这个手游已经有很多破解版本。这里还是记录一下我自己的摸索过程,从定位关键代码到修改。


1. 定位资源文件

一般手游的程序逻辑都是以脚本的形式存储在apk的资源文件中,这次目标apk是从 google play 下载,从 /data/app 目录中pull出apk,得到apk的解压结果如下:

base.apk
├── AndroidManifest.xml
├── META-INF
├── assets
├── classes.dex
├── error_prone
├── fabric
├── jsr305_annotations
├── lib
├── res
├── resources.arsc
└── third_party

看了一下大小,才9.5m大小,zip包中没发现资源文件:

ls -l base.apk 
-rw-r--r--@ 1 max  admin  9469378  base.apk

这就很奇怪了,看了一下空间占用了近 200m ,而且 /data/data 目录下也没有找到关键资源文件:

size.png

不过还是在一个名为 DownloadsDB 数据库文件中发现了一个下载链接
download.png

看到google的url,我怀疑是Play Store的特殊机制,因此在国内的应用中较为少见。网上查了一下,果不其然,这应该就是 obb 文件了,用于资源文件分发,减小apk体积。
这样应用更新的时候就不必重新下载整个apk, 同时,google 的服务器在国外的稳定性与速度都不差,也可以为开发者节省部分cdn费用。打开压缩包,果不其然,里面存放着游戏脚本,大小也有100m多,这就正好可以对上号了:
resource.png

2. 定位关键代码

根据上一步资源文件的解压结果来看,这些 lua 脚本应该就是程序逻辑了。打开一个lua文件,先看看hex,为 luajit 文件:

main.lua.png

使用 luajit-decompiler (https://gitlab.com/znixian/luajit-decompiler) 把 luajit 文件反编译成明文,使用方法如下:
python3 ./main.py --recursive ./<input directory> --dir_out ./<output directory> --catch_asserts
这样就可以得到明文 lua 脚本:
main_dec.png

手游里,想要定位钻石有关代码,一般都是搜索 gem 关键字,搜索到一个可疑文件 platform_services_ads.lua ,代码如下:

--- 观看广告时会调用
function ads:show_video_ad(provider, reward_amount)
    local function cb_show_video_ad(status, req)
        local success = status == 0

        if success then
            log.debug("ad complete: rewarding %s gems", reward_amount)
            --- 获取存档中的物品
            local slot = storage:load_slot()

            if slot then
                --- 观看广告后所得钻石增加 reward_amount 个
                slot.gems = slot.gems + reward_amount

                --- 把增加后的钻石再保存到存档中
                storage:save_slot(slot, nil, true)
            else
                log.error("error giving gems reward. slot could not be loaded")
            end
        end

        signal.emit(SGN_PS_AD_SHOW_VIDEO_FINISHED, "ads", success, reward_amount)
    end
  ......

上面那段代码,在每次观看广告获取钻石时被调用,这样我们的修改思路就很明确了,只要让 storage:load_slot() 函数每次返回的钻石数为我们指定的,就可以达到目的了。

3. 修改脚本

/all/storage.lua 中直接定位到 storage:load_slot() 的实现:

function storage:load_slot(idx, force)
    ... 参数检查
    local input = self:load_lua(string.format(self.SLOT_FILE_FMT, idx), force)
    ... 参数检查

    --- 读取 SLOT_ADDITIONAL_DATA 到 result,并返回
    for k, v in pairs(SLOT_ADDITIONAL_DATA) do
        if not input[k] then
            input[k] = v
        end
    end

    return input
end

再看一眼 SLOT_ADDITIONAL_DATAgems 就是我们要动手的目标了:

local SLOT_ADDITIONAL_DATA = {
    gems = 0,
    bag = {}
}

直接 patch storage:load_slot() 函数,添加两行代码,改的时候可以多尝试一下,改错了会导致游戏蓝屏或闪退:

function storage:load_slot(idx, force)
    ... 

    for k, v in pairs(SLOT_ADDITIONAL_DATA) do
        if not input[k] then
            input[k] = v
        end
    end

    input["gems"] = 88888
    print("[MaxLog]", input["gems"])

    return input

使用 luajit 编译修改好的文件
luajit-2.1.0-beta3 -bg ./all/storage.lua ./storage.lj

这里要注意的是,由于 love2d (https://github.com/love2d/love) 使用的是 2.1.0-beta1 版的 luajit,而 brew install luajit 直接安装的版本为 2.0.5,用老版的 luajit 编译出来的二进制文件,游戏引擎会直接解析失败崩溃,这也是我踩过的坑。我这里测试,使用 beta3版是没有问题的,用以下命令安装:
brew install --HEAD luajit

4. 测试运行

obb 文件是通过使用时 mount 到应用沙盒 /data/data 目录下,我在文件管理器中未找到真实文件,具体原理还没有研究 obb 机制。我的解决方案是 Hook open() 函数,把原脚本文件替换为我们修改过的的文件。

  1. 把修改过的脚本文件放入 /data/local/tmp 中:
    adb push storage.lj /data/local/tmp
  2. Hook libc 的 open() 函数:
int new_open(const char *name, int flags, ...) {
    mode_t mode = 0;
    if ((flags & O_CREAT) != 0) {
        va_list args;
        va_start(args, flags);
        mode = static_cast<mode_t >(va_arg(args, int));
        va_end(args);
    }

    LOGD("Tag", "open(\"%s\", %d)", name, flags);
    if (strstr(name, "all/storage.lua")) {
        // LOGD("Tag", "open(\"%s\", %d)", name, flags);
        return old_open("/data/local/tmp/storage.lj", O_RDONLY);
    }
    return old_open(name, flags, mode);
}
  1. 运行游戏,在 Lua 脚本中打的 Log ,也可以查看到:


    log.png
  2. 游戏里的钻石也成为指定的数额:


    screenshot.png

总结

其实破解修改手游大致都可以总结以下几个套路:

  1. 解密脚本
    常见的几个游戏引擎u3d、cocos2dx,都可以解出游戏逻辑脚本,这些脚本也是我们主要关注的目标。需要注意的是可能存在jit、aot等情况,这时候就需要进行加密解密操作。
  2. 定位代码
    通过搜索可以关键字,分析代码逻辑,找到需要修改的地方。
  3. 修改测试
    明文脚本可以直接修改,密文脚本注意还原,还可以使用Hook等手段直接动态修改内存。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容