自己制作的一个小工具, 工作场景为某些cocos2dx手游, 虽然lua可以直接拿到lua_state指针注入, 但在某些场景下直接注入文件是更加方便的, 所以自己研究了一下字节码, 笔记是以前写的. 思路很清晰, 自己动手做的话容易犯小错误
Luajit版本2.1.0 beta2
https://luajit.org/download/LuaJIT-2.1.0-beta2.zip
mingw64
010editor中有bt模板, 但是由于版本变化, 指令解析存在问题, 结构解析无问题
示例lua文件代码:
可以看到, 在luajit的二进制文件中, proto的顺序是依次往下的嵌套关系, 主函数体在最外层, 也就是倒数第二个
在proto结构中, 存有头信息, 二进制字节码, 常量信息.
在头信息中我们要关心的主要有size, complex_constants_count, instructions_count三个字段, 他们的大小由变长数字uleb128描述, 互相转化的方法如下
在我们修改文件的时候需要修正这几条数据.
修改二进制数据, 插入require("inject")语句. 在luajit中, require语句由三条指令构成, 主要是加载require字符串, 待注入文件名, call三步
字节码 36 00 00 00
第一个字节为opcode, 第二个字节为寄存器编号, 第三个字节为常量池下表(从下往上数)
字节码为27 01 01 00
第一个字节为opcode, 第二个字节为寄存器编号, 第三个字节为常量池下表(从下往上数)
42 00 02 01
这里我有点没太明白, 按照官网表格的注释A+C-1=0, 参数为0个. 不过可以确定的是第二个字节为require的寄存器编号
首先跳过前置段, 找到main, 然后解析main proto的信息
将原本的信息解析完毕后, 需要插入常量和指令
最后更新字段数据
最终效果成功注入
完整代码见https://github.com/stickycookie/luajitInject
参考资料:https://www.anquanke.com/post/id/87281
https://en.wikipedia.org/wiki/LEB128
https://www.mickaelwalter.fr/reverse-engineering-luajit/
-
-
target.lua
function Test(x)
print
(
"Test "
.. x)
end
Test(
"enter main==>test()"
)
require(
"addLib"
)
function fun1()
print
(
"Test in target.lua fun1 function..."
)
end
function fun2()
print
(
"Test in target.lua fun2 function..."
)
end
print
(
"Test in target.lua main function..."
)
print
(add(
1
,
2
))
-
-
target.lua
function Test(x)
print
(
"Test "
.. x)
end
Test(
"enter main==>test()"
)
require(
"addLib"
)
function fun1()
print
(
"Test in target.lua fun1 function..."
)
end
function fun2()
print
(
"Test in target.lua fun2 function..."
)
end
print
(
"Test in target.lua main function..."
)
print
(add(
1
,
2
))
int
ReadUleb128(unsigned char
*
& buf)
{
unsigned
int
result;
unsigned char cur;
result
=
*
buf
+
+
;
if
(result >
0x7f
)
{
cur
=
*
buf
+
+
;
result
=
(result &
0x7f
) | (unsigned
int
)((cur &
0x7f
) <<
7
);
if
(cur >
0x7f
)
{
cur
=
*
buf
+
+
;
result |
=
(unsigned
int
)(cur &
0x7f
) <<
14
;
if
(cur >
0x7f
)
{
cur
=
*
buf
+
+
;
result |
=
(unsigned
int
)(cur &
0x7f
) <<
21
;
if
(cur >
0x7f
)
{
cur
=
*
buf
+
+
;
result |
=
(unsigned
int
)cur <<
28
;
}
}
}
}
return
result;
}
void WriteUleb128(unsigned char
*
& buf,
int
x)
{
unsigned
int
denominator
=
0x80
;
unsigned char flag
=
0x80
;
for
(
int
i
=
0
; i <
5
; i
+
+
)
{
if
(x < denominator)
{
buf[i]
=
(unsigned char)x;
i
+
+
;
buf
+
=
i;
return
;
}
buf[i]
=
(flag) | (unsigned char)(x
%
denominator);
x
=
x >>
7
;
}
}
int
ReadUleb128(unsigned char
*
& buf)
{
unsigned
int
result;
unsigned char cur;
result
=
*
buf
+
+
;
if
(result >
0x7f
)
{
cur
=
*
buf
+
+
;
result
=
(result &
0x7f
) | (unsigned
int
)((cur &
0x7f
) <<
7
);
if
(cur >
0x7f
)
{
cur
=
*
buf
+
+
;
result |
=
(unsigned
int
)(cur &
0x7f
) <<
14
;
if
(cur >
0x7f
)
{
cur
=
*
buf
+
+
;
result |
=
(unsigned
int
)(cur &
0x7f
) <<
21
;
if
(cur >
0x7f
)
{
cur
=
*
buf
+
+
;
result |
=
(unsigned
int
)cur <<
28
;
}
}
}
}
return
result;
}
void WriteUleb128(unsigned char
*
& buf,
int
x)
{
unsigned
int
denominator
=
0x80
;
unsigned char flag
=
0x80
;
for
(
int
i
=
0
; i <
5
; i
+
+
)
{
if
(x < denominator)
{
buf[i]
=
(unsigned char)x;
i
+
+
;
buf
+
=
i;
return
;
}
buf[i]
=
(flag) | (unsigned char)(x
%
denominator);
x
=
x >>
7
;
}
}
\OP** |
\A** |
\B** |
\C/D** |
\Description** |
GGET |
dst |
|
str |
A = _G[D] |
\OP** |
\A** |
\D** |
\Description** |
KSTR |
dst |
str |
Set A to string constant D |
\OP** |
\A** |
\B** |
\C/D** |
\Description** |
CALL |
base |
lit |
lit |
Call: A, ..., A+B-2 = A(A+1, ..., A+C-1) |
struct ProtoHeader
{
int
protoSize, complexCnt, numericCnt, instructionCnt;
unsigned char flags, argCnt, frameSize, upValueCnt;
};
struct Proto
{
ProtoHeader ph;
unsigned char
*
instructions,
*
constants;
int
instructionsSize, constantsSize;
};
void IngoreSeg(unsigned char
*
& buf,
int
n)
{
while
(n
-
-
)
{
int
size
=
ReadUleb128(buf);
buf
+
=
size;
}
}
Proto
*
ReadProto(unsigned char
*
buf)
{
unsigned char
*
ed
=
buf;
int
size
=
ReadUleb128(ed);
ed
+
=
size;
Proto
*
proto
=
new Proto;
proto
-
>ph.protoSize
=
ReadUleb128(buf);
proto
-
>ph.flags
=
ReadU8(buf);
proto
-
>ph.argCnt
=
ReadU8(buf);
proto
-
>ph.frameSize
=
ReadU8(buf);
proto
-
>ph.upValueCnt
=
ReadU8(buf);
proto
-
>ph.complexCnt
=
ReadUleb128(buf);
proto
-
>ph.numericCnt
=
ReadUleb128(buf);
proto
-
>ph.instructionCnt
=
ReadUleb128(buf);
proto
-
>instructionsSize
=
4
*
proto
-
>ph.instructionCnt;
/
/
每条指令
4
个字节
proto
-
>instructions
=
new unsigned char[proto
-
>instructionsSize];
memcpy(proto
-
>instructions, buf, proto
-
>instructionsSize);
buf
+
=
4
*
proto
-
>ph.instructionCnt;
proto
-
>constantsSize
=
ed
-
buf;
proto
-
>constants
=
new unsigned char[proto
-
>constantsSize];
memcpy(proto
-
>constants, buf, proto
-
>constantsSize);
buf
+
=
proto
-
>constantsSize;
return
proto;
}
IngoreSeg(filePtr, targetSeg);
Proto
*
proto
=
ReadProto(filePtr);
struct ProtoHeader
{
int
protoSize, complexCnt, numericCnt, instructionCnt;
unsigned char flags, argCnt, frameSize, upValueCnt;
};
struct Proto
{
ProtoHeader ph;
unsigned char
*
instructions,
*
constants;
int
instructionsSize, constantsSize;
};
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2022-3-22 09:51
被juice4fun编辑
,原因: