首先感谢原作者在国内互联网分享这篇教程,因为在国内这方面资料基本没有。

这篇教程的很强大,先不说网游。 说一些单机游戏 类似于 EM引擎 DM引擎 开发的游戏
都可以通过这种方法来调试。
源代码可以去原贴查看

原贴 http://www.cehigh.com/forum.php?mod=viewthread&tid=724

前言

本次修改的游戏是一个使用了LuaJIT制作的梦幻西游类单机游戏,名字叫做《梦战:碧海旭梦》。
这个游戏在我的硬盘里放了有很长一段时间了,当时水平有限,就没有再修改,直到这几天才重新开始研究。
网上关于LuaJIT的分析资料并不多,而有关单机游戏修改的是没有了,所以我基本上是从零开始研究。
经过几天不断的研究,终于对LuaJIT有了一点点了解,也找到了一些修改的方法。
不过说实话研究这个东西没什么用处,纯粹是我的兴趣,各位姑且看看就好。
因为我也是新手,所以文章中难免有很多错误,如果发现错误请一定要告诉我。

什么是Lua?和LuaJIT有什么关系?

Lua是一个脚本语言,通常是用来嵌入应用程序中的,在游戏中的应用范围也很广,尤其是手游。
而LuaJIT则是Lua的一个高效版本。
用好Lua+Unity,让性能飞起来—LuaJIT性能坑详解 这篇文章对LuaJIT的工作原理有了一定的阐述。
若想了解更多,可以去官方网站看看。
Lua:The Programming Language Lua
LuaJIT:The LuaJIT Project

LuaJIT的字节码和虚拟机

LuaJIT可以把.lua脚本编译成字节码,然后放到虚拟机中运行。
这里的字节码和我们平常看到的机器码不同,LuaJIT的字节码有自己对应的一套指令,
然后在程序中有一块地方负责解释这些字节码(在windows上就是个dll),
这块地方就是LuaJIT虚拟机。
关于LuaJIT的指令网上已经有人总结,Lua LuaJit 指令表(整理)。
也可以直接翻阅LuaJIT的源码(官网可以下载),在lj_bc.h中有定义。
官网的wiki中也有文档,LuaJIT 2.0 Bytecode Instructions。
如果有人有改过Lua的游戏就知道非常难改,因为到处都是“共用代码”。
而这里的“共用代码”实际上就是虚拟机,我们直接修改机器码就等于破坏了虚拟机,所以我们要从字节码下手。

开始修改

首先我们先研究一下跟游戏目录下的文件,在游戏目录中有个lua51.dll,我们随便拿一个PE工具看一下。
这里我用ExeinfoPe查看

dll文件被加壳了,但是问题不大,只是个压缩壳,不脱也没关系。

点击PE查看导出表信息,发现LuaJIT字样,基本上就确定游戏跟LuaJIT有关系了。

第一次进入游戏要过一段剧情,等待游戏正式开始就好。
注意,修改前请先备份存档,游戏有反修改机制,被检测到后直接杀掉存档。(存档文件为save.bhs)
我们尝试修改一下潜力点

LuaJIT中的数据都是double类型的,所以我们用double类型来搜索,并找出是什么修改了这个地址。

其实如果只是为了自己玩游戏的话这里就直接改数值就好了,只要不改太高游戏的反修改检测都不会杀掉你的存档,但是今天主要是研究一下LuaJIT。
这里出现了两条代码,看第一条就行了。记下代码的地址和潜力点的地址,转到x32dbg或者OD中分析,这里我用x32dbg来分析。

Ctrl+G输入6B52F6AF来到该代码处,下F2断点,然后发现游戏马上就被断下来了,这很正常,因为这里的代码就是用来解释字节码的虚拟机,在lua51.dll中。
我们切换到断点页面,选中刚才下的断点,按Ctrl+E来编辑设置断点条件

这里的ecx == 0x0DD35300 意思就是在ecx等于这个数值的时候才断下,而这里的0DD35300就是我们刚才搜到的潜力点的地址。

点击保存后开启断点然后重新运行游戏,发现游戏会变得很卡,这也很正常,因为这里是虚拟机,执行的次数很多,调试器在筛选的时候难免会卡。
随便把某个属性加一点使潜能点改变,游戏就会被断下来,切回到调试器中查看。
这时候的esi值就是指向当前解释的字节码的地址,我们右键点击esi寄存器选择“在内存窗口中转到”,并观察左下角内存窗口。
04B36748就是当前esi的值
这里就是LuaJIT的字节码,每条指令的长度都是4个字节,第1个字节为OP。
注意这里的04B36748的指令是下一条要执行的指令,
实际上改变潜力点的指令在前面04B36744处,对应的字节码就是3A 07 07 06,
顺便再注意下04B36740处的1F 07 05 07
我们查一下指令表(前面有),看看这些是什么东西。

看到之后是不是感觉蒙了,一开始我也是蒙的,后来经过不断的研究发现这里的07 05 07 或者 07 07 06其实是索引,
对应的是LuaJIT栈上的一些东西,这里我就不详细讨论了,主要是我也不太懂。
这两行大概意思就是两个变量相减,然后结果赋值给另一个变量(也就是潜力点)。
这些东西需要自己去写一些LuaJIT的简单程序,然后去调试看内存,再看看LuaJIT对应的源码才能搞清楚一点。
我也读不透LuaJIT的源码,这里我就简单讲一下怎么改就好了。
查看指令表发现这样一条指令,可以直接对变量赋值。

那我们只要修改1F 07 05 07就好了,比如我们要把潜力点变成10000,就改成27 07 10 27 (注意小端序,10 27就是10000的16进制)
看一下效果,

修改成功,想要做成修改器也方便,只要搜一下字节码再改就好了,和aobscan是差不多的。

后记

这游戏还有一些反修改还没有处理,可能会出二,也可能不会。
有关LuaJIT的知识还有很多,我这里讲的只占了很少,当作是抛砖引玉了,有兴趣的可以深入研究,有问题也可以在群里找我,当然我不一定能解答就是了。

前言

本次修改的游戏还是在(一)中所讲的游戏,这一次的目的是要把游戏的反修改处理掉。但是跟(一)中不同的是这一次用到的技术会更高级一点,直接对LuaJIT进行钩我们可以直接使用的Lua自带的调试库来获得大量的信息,并且能够进行修改。
参考了国外大神的文章:
挂钩LuaJIT(原文)
看我如何通过hook攻击LuaJIT(译文)

注入Lua代码以

注入我们自己的Lua代码,我们需要获得游戏调用luaL_newstate返回的lua_State对象,其实就是Lua代码运行的一个环境。如国外大神的文章所说,直接挂钩luaL_newstate这个函数是不太妥当的,因为这时候库还没有加载,debug功能无法使用,所以可以选择挂钩luaL_openlibs这个函数,当然如果挂钩了luaL_newstate也可以的,只需要自己手动一下一下luaL_openlibs就行。为了加载我们的代码,我们还需要得到luaL_loadfilex和lua_pcall两个函数的地址。
确定了所需要的挂钩的函数之后,接下来就是确定挂钩的方式。一般而言是用dll来完成我们的挂钩,但是注入dll的时机却是个问题。luaL_openlibs一般是在luaL_newstate之后马上调用,而luaL_newstate一般是在程序一开始时就会调用。一开始我使用SetWindowsHookEx和第一个参数使用WH_SHELL来注入dll,这样就可以被注入进去,但是经过实验之后效果并不理想,因为这个游戏在创建窗口之前已经被调用了luaL_openlibs,所以我们的hook代码并没有被执行。于是我采用了第二种方法,创建进程的时候直接注入dll。 ,写了一个注入工具(只支持32位)。
接下来就是dll的代码

typedef void* lua_State;
typedef int(*_luaL_loadfilex)(lua_State *L, const char *filename, const char mode);
typedef int(
_luaL_openlibs)(lua_State L);
typedef int(
_lua_pcall)(lua_State *L, int nargs, int nresults, int errfunc);

_luaL_openlibs luaL_openlibs_original;
_luaL_loadfilex luaL_loadfilex;
_lua_pcall lua_pcall;
BYTE orig_code[5];
BYTE jmp_code[5] = { 0xe9 };

void MyHook();
void ChooseProc();

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
ChooseProc();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

int luaL_openlibs_hook(lua_State *L)
{
WriteProcessMemory(GetCurrentProcess(), luaL_openlibs_original, &orig_code, 5, NULL); //恢复原函数
int ret = luaL_openlibs_original(L);
WriteProcessMemory(GetCurrentProcess(), luaL_openlibs_original, &jmp_code, 5, NULL); //修改原函数
luaL_loadfilex(L, “debug.lua”, NULL) || lua_pcall(L, 0, -1, 0); //需要注入的lua脚本,放在游戏目录下
MessageBox(NULL, L"Hook success", L"Success", MB_OK);
return ret;
}

void MyHook()
{
HMODULE hModule = GetModuleHandle(L"lua51.dll");
if (!hModule)
{
MessageBox(NULL, L"lua51.dll not found!", L"Fail", MB_OK);
return;
}
luaL_openlibs_original = (_luaL_openlibs)GetProcAddress(hModule, “luaL_openlibs”);
luaL_loadfilex = (_luaL_loadfilex)GetProcAddress(hModule, “luaL_loadfilex”);
lua_pcall = (_lua_pcall)GetProcAddress(hModule, “lua_pcall”);
if (!luaL_openlibs_original || !luaL_loadfilex || !lua_pcall)
{
MessageBox(NULL, L"function not found", L"Fail", MB_OK);
return;
}
//保存原函数字节
if (!ReadProcessMemory(GetCurrentProcess(), luaL_openlibs_original, &orig_code, 5, NULL))
{
MessageBox(NULL, L"ReadProcessMemory fail", L"Fail", MB_OK);
}

    //修改原函数*(DWORD*)(jmp_code + 1) = (DWORD)luaL_openlibs_hook - (DWORD)luaL_openlibs_original - 5;if (!WriteProcessMemory(GetCurrentProcess(), luaL_openlibs_original, &jmp_code, 5, NULL)){MessageBox(NULL, L"WriteProcessMemory fail", L"Fail", MB_OK);}

}

void ChooseProc()
{
WCHAR szPath[MAX_PATH];
WCHAR *p = NULL;

    GetModuleFileName(NULL, szPath, MAX_PATH);p = wcsrchr(szPath, L'\\');if (!wcscmp(p + 1, L"梦战.exe"))        //要hook的进程名称{MyHook();MessageBox(NULL, L"DLL inject", L"Success", MB_OK);}

}

通过GetProcAddress就可以直接获得我们需要的三个函数的地址,其中luaL_openlibs是我们需要hook的函数,luaL_loadfilex和lua_pcall则是来加载我们写的 代码比较丑,各种英语语法问题请见谅,看得懂就行。其中ChooseProc是我用SetWindowsHookEx时使用筛选注入进程的,但是因为后来用了注入工具,所以这里会重叠比较多余

。debug.lua

这个就是我们获取信息的lua脚本。参考了国外大神的代码并结合实际游戏之后,我的代码如下。
[Lua]纯文本查看 复制代码
?
01
–lua无法debug即时编译过后的代码,根据实际情况选择是否关闭jit。
jit.off()

function re_print(t,prefix,file)
for k,v in pairs(t) do
if type(v) == “function” then
file:write(string.format("%s => %s","_G." … k,v) … “\n”)
–[[
else
file:write(prefix … “.” … k … “\n”)
–]]
end
if type(v) == “table” and k ~= “_G” and k ~= “_G._G” and not v.package then
re_print(v, “\t” … prefix … “.” … k, file)
end
end
end

function dumpGlobals()
local fname = “globals_” … “.txt”
local globalsFile = io.open(fname, “w”)

re_print(_G,"_G",globalsFile)globalsFile:flush()
globalsFile:close()

end

function trace(event, line)
local info = debug.getinfo(2)

if not info then return end
if not info.name then return enddumpGlobals()--下面注释的代码是获取函数信息的,结合了实际情况之后我并没有使用,详情参考国外大神文章
--[[
local fname = "trace_" .. ".txt"
local traceFile = io.open(fname, "a")
traceFile:write(info.name .. "()\n")local a = 1
while true dolocal name, value = debug.getlocal(2, a)if not name then break endif not value then break endtraceFile:write(tostring(name) .. ": " .. tostring(value) .. "\n")a = a + 1
end
traceFile:flush()
traceFile:close()
--]]

end

debug.sethook(trace, “c”)

_G是Lua的类别对象表,这个表储存了很多跟游戏相关的关键信息,有很大的价值。其中re_print函数是我从网上参考来的一份可以遍历所有表的代码,因为我只想要获得跟函数有关的信息,所以我在其中加了一句if type(v)==“ function”来筛选出_G表中存储的函数。
debug.sethook(痕量, “C”)使Lua中在每个函数完成之前调用跟踪这个函数,在跟踪中,我们就可以调用dumpGlobals了。但是因为不知道游戏什么时候会把全局变量当调用脚本后,可能会比较卡,但开启游戏后就可以马上关掉游戏了,一般来说游戏开启后在我们没有反应过来时变量都已经分配好了。

观察_G变量变量表内容

我们使用工具,选择dll的路径和游戏的路径并单击执行,在开启游戏后就可以立马关闭游戏,我们可以看到游戏目录下多了个个globals_ .txt文件,这就是我们打印出来的变量变量表,我在里面发现了很有意思的东西。
[AppleScript]纯文本查看 复制代码
?
1
2
3
4
5
6
7
…省略一大部分…
_G.领取押镖任务 => function: 0x02b31430
_G.防修改 => function: 0x0562d190 <------防修改关键函数
_G.是否拥有效果 => function: 0x02b314f0
_G.取效果剩余时间 => function: 0x02b31508
_G.洗点 => function: 0x02b31610
…省略一大部分…

这个游戏的Lua脚本很多都是用了中文,这个防修改就直接写贝壳了,一下就看到了。知道了函数名称后我们就可以干坏事了

。debug.lua(改)

这里我偷个懒,直接修改一下debug.lua内容,把防修改过掉。
[Lua]纯文本查看 复制代码
?
1
2
3
4
5
6
7
8
9
function 反防修改()
return
end

function trace(event, line)
防修改 = 反防修改
end

debug.sethook(trace, “c”)

文件记得使用GBK编码格式,不然这个游戏不认文件中的汉字。

我们把防修改直接替换成我们写的反防修改,反防修改是一个空函数,游戏调用防修改这个函数的时候,就相当这样在调用我们的反防修改函数,然后就是啥都没有做。将debug.lua放在游戏目录下并使用注入工具注入我们的dll就可以过掉反修改。

效果图如下,
没有反防修改时:

有反防修改时:

。当然这个方法非常的不优雅,不过因为我对Lua中并不是很熟悉,我所以没有也。再多研究了

对LuaJIT制作的游戏的简单修改(转载)相关推荐

  1. 用代码制作小游戏:简单制作给孩子幸福童年

    利用jQuery实现拼图游戏: 代码结构 引入CSS 1.<link rel="stylesheet" href="css/index.css"> ...

  2. 自己动手制作植物大战僵尸简单修改器(2)

    自己动手制作植物大战僵尸简单修改器2 地上物品自动拾取 地上物品自动拾取 上次的内容自己动手制作植物大战僵尸简单修改器 地上物品的地址是 0x006A9EC0 + 0x768 + 0xe4 + 0xd ...

  3. cs6制作拼图游戏 dreamweaver_教你做简单的拼图游戏[图、实例]

    其实照着教程做起了很简单,就是代码要研究明白了需要废点时间,我把我学的那个教程copy了一遍,大家都来做做看,真得不难! --------------------------------------- ...

  4. 自己动手制作植物大战僵尸简单修改器(3)

    自己动手制作植物大战僵尸简单修改器3 目的 过程 找到阳光地址 获取修改阳光的指令 找到自动汇编窗口 找到代码注入 改变阳光增量 结果 目的 这一篇并不是制作,改变每一次捡起阳光时阳光增加量. 过程 ...

  5. 用JAVA制作小游戏——推箱子(三)

    本篇博客主要是对推箱子地图编辑器功能的代码讲解. 首先给出这段代码的部分运行截图: 重难点: 地图编辑器主要有三个重难点: 需要有一个绘制地图的界面 能够实现地图绘制的功能 地图绘制完成后需要将地图内 ...

  6. 用JAVA制作小游戏——推箱子(一)

    本篇博客是对推箱子游戏首页代码的展示 效果图: 难点: 在背景图上添加按钮. 由于直接使用add方法,会变成图片一块区域,按钮一块区域,彼此互不重叠,形成的界面效果不太好,因此用了JLayeredPa ...

  7. 街机游戏-FC游戏的hack修改rom

    这个文章算是一个记录,目前没空再去改游戏了,我怕等我有空的时候,把这方面的知识忘了. 还有关于这个文章的一些纠正. 关于修改街机游戏的一些方法 https://blog.csdn.net/oChunC ...

  8. 【游戏外挂、修改器、辅助研究1】认识外挂

    1.什么是游戏外挂 随着电子游戏的兴起,许多人都在以游戏娱乐, 自然,有人的地方就有需求,就有竞争, 人们竞争着等级.排名.装备.物品等等, 可是,要怎么获得这些东西呢? 自然是刷怪,无穷无尽地刷怪, ...

  9. 游戏感虚拟感觉的游戏设计师_从零到游戏设计师:即使您没有任何经验,如何开始制作视频游戏...

    游戏感虚拟感觉的游戏设计师 by Angela He 通过何安佳 从零到游戏设计师:即使您没有任何经验,如何开始制作视频游戏 (From Zero to Game Designer: how to s ...

  10. 开发者分享在PC上制作iOS游戏的经验(下)

    开发者分享在PC上制作iOS游戏的经验(下) 发布时间:2013-05-01 09:48:18  Tags: AI, 市场营销, 手势, 测试, 游戏平衡 简介 这时候我们已经创造了一款可行的游戏,即 ...

最新文章

  1. Maven手动将jar包放入本地仓库
  2. .net bitmap rgb数据_在3D空间,用点云数据学行人重识别特征
  3. CVPR2019 | 目标检测新文:Generalized Intersection over Union
  4. 互联网日报 | 华为前三季度营收6713亿元;新央企南水北调集团揭牌;易车私有化议案获股东大会通过...
  5. docker 本地部署 mysql_Docker 部署Mysql 服务和Redis 服务的方法
  6. 透过一个编译报错,总结两个Go程序编译的重要知识
  7. Java-Collection、List
  8. c语言代码姓名全拼,巧用拼音首字母输入人名(代码)
  9. 免费可视化报表,好用的web报表设计器
  10. 线上nginx偶尔出现502错误
  11. Linux系统重装时保留重要分区
  12. 详解统计指标PV、IP、UV、VV
  13. vcruntime140_1.dll缺失
  14. png转jpg具体操作方法
  15. javaScript:实现倒计时定时器setInterval的开始计时、暂停计时、停止计时效果
  16. 五部搞定Android开发环境部署——费UC噶不过详细的Android开发环境搭建教程
  17. 无BUG微信去水印小程序源码(可运营美化版+送免费接口)
  18. 反射型xss和Dom型xss区别
  19. python字符串换行的三种方式_Python:怎样拼装超长字符串和包含回车换行字符串...
  20. android os 小米系统,小米全新 OS 自研底层,要挑战最强的 iOS 系统

热门文章

  1. 海思芯片上LDC(镜头畸变校正)功能原理浅析
  2. 天涯明月刀手游服务器维护到几点,天涯明月刀手游11月11日服务器维护更新公告...
  3. 北京1954坐标系3度带和6度带及中央子午线对照表
  4. Windows Server AppFabric分布式缓存研究
  5. OpenV2X开源社区亮相全球边缘计算大会
  6. C1驾考 科目二 (超详细!文字+实拍图)
  7. Java调用WebService接口的四种方式
  8. matlab对多项式求导的命令,matlab多项式求导
  9. 用友政务U8补丁包下载地址
  10. XJTU-SY滚动轴承加速寿命试验数据集