Skip to content

Commit 3b9b3a3

Browse files
impl cheatengine format
1 parent c3fdd31 commit 3b9b3a3

File tree

4 files changed

+284
-248
lines changed

4 files changed

+284
-248
lines changed

src/lua_bytecode_parser.cc

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#include "lua_bytecode_parser.h"
22
#include <algorithm>
3+
#include <format>
4+
#include <print>
35
#include <stdexcept>
46

7+
58
#define LUA_SIGNATURE_53 "\x1BLua"
69
#define LUAC_VERSION_53_MAJOR 0x5
710
#define LUAC_VERSION_53_MINOR 0x3
@@ -17,60 +20,38 @@ template <typename T> T bytes_to_value(const char *bytes) {
1720
}
1821

1922
LuaBytecodeParser::LuaBytecodeParser(const std::vector<char> &bytecode_data)
20-
: data_(bytecode_data), offset_(0) {}
23+
: data_(bytecode_data), offset_(0), encrypt_key_(0),
24+
is_ce_bytecode_(false) {}
2125

2226
void LuaBytecodeParser::error(const std::string &msg) {
2327
throw std::runtime_error("Bytecode parsing error: " + msg);
2428
}
2529

26-
std::uint8_t LuaBytecodeParser::read_byte() {
27-
if (offset_ + sizeof(std::uint8_t) > data_.size()) {
28-
error("truncated (byte)");
29-
}
30-
std::uint8_t value = bytes_to_value<std::uint8_t>(&data_[offset_]);
31-
offset_ += sizeof(std::uint8_t);
32-
return value;
33-
}
34-
35-
std::uint32_t LuaBytecodeParser::read_int() {
36-
if (offset_ + sizeof(std::uint32_t) > data_.size()) {
37-
error("truncated (int)");
38-
}
39-
std::uint32_t value = bytes_to_value<std::uint32_t>(&data_[offset_]);
40-
offset_ += sizeof(std::uint32_t);
41-
return value;
42-
}
43-
44-
lua_Number LuaBytecodeParser::read_number() {
45-
if (offset_ + sizeof(lua_Number) > data_.size()) {
46-
error("truncated (number)");
47-
}
48-
lua_Number value = bytes_to_value<lua_Number>(&data_[offset_]);
49-
offset_ += sizeof(lua_Number);
50-
return value;
51-
}
52-
53-
lua_Integer LuaBytecodeParser::read_integer() {
54-
if (offset_ + sizeof(lua_Integer) > data_.size()) {
55-
error("truncated (integer)");
56-
}
57-
lua_Integer value = bytes_to_value<lua_Integer>(&data_[offset_]);
58-
offset_ += sizeof(lua_Integer);
59-
return value;
60-
}
61-
6230
void LuaBytecodeParser::read_block(char *buffer, size_t size) {
6331
if (offset_ + size > data_.size()) {
6432
error("truncated (block)");
6533
}
6634
std::copy(data_.begin() + offset_, data_.begin() + offset_ + size, buffer);
35+
36+
if (is_ce_bytecode_ && encrypt_key_ != 0) {
37+
std::println("decrypting: key = {}, size = {}", encrypt_key_, size);
38+
for (size_t i = 0; i < size; ++i) {
39+
char decrypt_byte;
40+
if ((((i + (i >> 3) & 7) & 7) - ((i >> 3) & 7)) != 0) {
41+
decrypt_byte = (char)(encrypt_key_ >> (i % 0xe));
42+
} else {
43+
decrypt_byte = (char)0xce;
44+
}
45+
buffer[i] ^= decrypt_byte;
46+
}
47+
}
6748
offset_ += size;
6849
}
6950

7051
std::string LuaBytecodeParser::read_string() {
7152
size_t size = read_byte();
7253
if (size == 0xFF) {
73-
size = read_integer();
54+
size = read_uint64();
7455
}
7556

7657
if (size == 0) {
@@ -83,13 +64,22 @@ std::string LuaBytecodeParser::read_string() {
8364
}
8465
}
8566

67+
static std::string hexdump(const std::string &s) {
68+
std::string result;
69+
for (unsigned char c : s) {
70+
result += std::format("{:02x} ", c);
71+
}
72+
return result;
73+
}
74+
8675
void LuaBytecodeParser::check_literal(const std::string &expected,
8776
const std::string &error_msg) {
8877
std::string buffer(expected.length(), '\0');
8978
read_block(&buffer[0], expected.length());
9079

9180
if (buffer != expected) {
92-
error(error_msg);
81+
error(error_msg + std::format("expected '{}', got '{}'", hexdump(expected),
82+
hexdump(buffer)));
9383
}
9484
}
9585

@@ -114,34 +104,52 @@ void LuaBytecodeParser::check_header() {
114104
error("version mismatch");
115105
}
116106

117-
if (read_byte() != LUAC_FORMAT_53) {
118-
error("format mismatch");
107+
std::uint8_t format = read_byte();
108+
if (format != LUAC_FORMAT_53) {
109+
if (format == 1) {
110+
encrypt_key_ = read_uint64();
111+
} else {
112+
error("format mismatch");
113+
}
119114
}
120115

121116
check_literal(LUAC_DATA_53, "corrupted data section");
122117

123118
check_size(sizeof(int), "int");
124-
check_size(sizeof(size_t), "size_t");
119+
if (is_ce_bytecode_) {
120+
check_size(8, "LUA_DUMP_STRINGSIZE_FORMAT2");
121+
} else {
122+
check_size(sizeof(size_t), "size_t");
123+
}
125124
check_size(sizeof(Instruction), "Instruction");
126125
check_size(sizeof(lua_Integer), "lua_Integer");
127126
check_size(sizeof(lua_Number), "lua_Number");
128127

129-
if (read_integer() != LUAC_INT_53) {
128+
if (read_uint64() != LUAC_INT_53) {
130129
error("endianness mismatch");
131130
}
132-
if (read_number() != LUAC_NUM_53) {
131+
if (read_double() != LUAC_NUM_53) {
133132
error("float format mismatch");
134133
}
134+
135+
if (format == 1)
136+
is_ce_bytecode_ = true;
135137
}
136138

137139
void LuaBytecodeParser::load_code(LuaProto &f) {
138-
int n = read_int();
140+
int n = read_uint32();
139141
f.code.resize(n);
140142
read_block(reinterpret_cast<char *>(f.code.data()), n * sizeof(Instruction));
143+
144+
if (is_ce_bytecode_ && encrypt_key_ != 0) {
145+
for (int i = 0; i < n; ++i) {
146+
f.code[i] -= (i & 3);
147+
}
148+
}
141149
}
142150

143151
void LuaBytecodeParser::load_constants(LuaProto &f) {
144-
std::uint32_t n = read_int();
152+
std::uint32_t n = read_uint32();
145153
f.constants.resize(n);
146154
for (std::uint32_t i = 0; i < n; ++i) {
147155
LuaType t = static_cast<LuaType>(read_byte());
@@ -153,10 +161,10 @@ void LuaBytecodeParser::load_constants(LuaProto &f) {
153161
f.constants[i].bval = (read_byte() != 0);
154162
break;
155163
case LUA_TNUMFLT:
156-
f.constants[i].nval = read_number();
164+
f.constants[i].nval = read_double();
157165
break;
158166
case LUA_TNUMINT:
159-
f.constants[i].ival = read_integer();
167+
f.constants[i].ival = read_uint64();
160168
break;
161169
case LUA_TSHRSTR:
162170
case LUA_TLNGSTR:
@@ -169,7 +177,7 @@ void LuaBytecodeParser::load_constants(LuaProto &f) {
169177
}
170178

171179
void LuaBytecodeParser::load_upvalues(LuaProto &f) {
172-
int n = read_int();
180+
int n = read_uint32();
173181
f.upvalues.resize(n);
174182
for (int i = 0; i < n; ++i) {
175183
f.upvalues[i].instack = read_byte();
@@ -178,7 +186,7 @@ void LuaBytecodeParser::load_upvalues(LuaProto &f) {
178186
}
179187

180188
void LuaBytecodeParser::load_protos(LuaProto &f) {
181-
int n = read_int();
189+
int n = read_uint32();
182190
f.protos.resize(n);
183191
for (int i = 0; i < n; ++i) {
184192
f.protos[i] = std::make_shared<LuaProto>();
@@ -187,20 +195,20 @@ void LuaBytecodeParser::load_protos(LuaProto &f) {
187195
}
188196

189197
void LuaBytecodeParser::load_debug(LuaProto &f) {
190-
std::uint32_t n_lineinfo = read_int();
198+
std::uint32_t n_lineinfo = read_uint32();
191199
f.lineinfo.resize(n_lineinfo);
192200
read_block(reinterpret_cast<char *>(f.lineinfo.data()),
193201
n_lineinfo * sizeof(std::uint32_t));
194202

195-
std::uint32_t n_locvars = read_int();
203+
std::uint32_t n_locvars = read_uint32();
196204
f.locvars.resize(n_locvars);
197205
for (std::uint32_t i = 0; i < n_locvars; ++i) {
198206
f.locvars[i].varname = read_string();
199-
f.locvars[i].startpc = read_int();
200-
f.locvars[i].endpc = read_int();
207+
f.locvars[i].startpc = read_uint32();
208+
f.locvars[i].endpc = read_uint32();
201209
}
202210

203-
std::uint32_t n_upvalue_names = read_int();
211+
std::uint32_t n_upvalue_names = read_uint32();
204212
f.upvalue_names.resize(n_upvalue_names);
205213
for (std::uint32_t i = 0; i < n_upvalue_names; ++i) {
206214
f.upvalue_names[i] = read_string();
@@ -212,8 +220,8 @@ void LuaBytecodeParser::load_function(LuaProto &f, const std::string &psource) {
212220
if (f.source.empty()) {
213221
f.source = psource;
214222
}
215-
f.linedefined = read_int();
216-
f.lastlinedefined = read_int();
223+
f.linedefined = read_uint32();
224+
f.lastlinedefined = read_uint32();
217225
f.numparams = read_byte();
218226
f.is_vararg = read_byte();
219227
f.maxstacksize = read_byte();
@@ -408,7 +416,7 @@ void LuaBytecodeWriter::write_string(const std::string &s) {
408416
write_byte(0);
409417
return;
410418
}
411-
419+
412420
size_t size = s.length() + 1;
413421
if (size < 0xFF) {
414422
write_byte(static_cast<std::uint8_t>(size));

0 commit comments

Comments
 (0)