首先,我们在ida中分析lua引擎tmgs.dll文件,然后定位到luaV_execute函数(搜索字符串“ 'for' limit must be a number ”),发现switch下的case的参数(lua的opcode)是乱序的,到这里我们就能够确认,该题的lua虚拟机opcode被修改了。
# 循环扫码文件的每一行 for i in range(len(lines)):
line = lines
if line.find('case') != -1:
line = line.replace('case', '')
line = line.replace(' ', '')
line = line.replace('\n','')
line = line.replace('u:', '')
o1 = get_opcode(u'基础版opcode.txt')
o2 = get_opcode(u'进阶版opcode.txt')# 还原for i in range(len(o1)):
print '基础版:',o1,'\t进阶版:',o2# 映射opcode获取修复表op_tbl = [-1 for i in range(64)]for i in range(len(o1)):
o1opcode = o1[0]
o1opcode = o1opcode.replace('0x','')
for o2opcode in o2:
o2opcode = o2opcode.replace('0x','')
op_tbl[int(o2opcode,16)] = int(o1opcode,16)print '修复表:',op_tbl
运行结果:
char *RegisterOrConstant(Function * F, int r)
{
if (IS_CONSTANT(r)) {
return DecompileConstant(F->f, r - 256); // TODO: Lua5.1 specific. Should change to MSR!!! } else {
char *copy;
char *reg = GetR(F, r);
if (error)
return NULL;
// add by littleNA // if(){} if (reg == NULL && F->Rcall[r] != 0)
{
reg = "return more";
}
copy = malloc(strlen(reg) + 1);
strcpy(copy, reg);
return copy;
}
}
void OutputAssignments(Function * F){
int i, srcs, size;
StringBuffer *vars;
StringBuffer *exps;
if (!SET_IS_EMPTY(F->tpend))
return;
vars = StringBuffer_new(NULL);
exps = StringBuffer_new(NULL);
size = SET_CTR(F->vpend);
srcs = 0;
for (i = 0; i < size; i++) {
int r = F->vpend->regs;
if (!(r == -1 || PENDING(r))) {
SET_ERROR(F,"Attempted to generate an assignment, but got confused about usage of registers");
return;
}
if (i > 0)
StringBuffer_prepend(vars, ", ");
StringBuffer_prepend(vars, F->vpend->dests);
if (F->vpend->srcs && (srcs > 0 || (srcs == 0 && strcmp(F->vpend->srcs, "nil") != 0) || i == size-1)) {
// add by littleNA // if() if (strcmp(F->vpend->srcs, "return more") != 0)
{
if (srcs > 0)
StringBuffer_prepend(exps, ", ");
StringBuffer_prepend(exps, F->vpend->srcs);
srcs++;
}
}
}
...
}
修复六
问题6: 当函数只有一个renturn的时候会反编译错误。
解决6:
case OP_RETURN:
{
...
// add by littleNA // 新增的if if (pc != 0)
{
for (i = a; i < limit; i++) {
char* istr;
if (i > a)
StringBuffer_add(str, ", ");
istr = GetR(F, i);
TRY(StringBuffer_add(str, istr));
}
TRY(AddStatement(F, str));
}
break;
}
修复七
问题7: 部分table初始化会出错。
解决7:
char *GetR(Function * F, int r)
{
if (IS_TABLE(r)) {
// add by littleNA return "{ }";
// PrintTable(F, r, 0); // if (error) return NULL; }
...
}
修复八
问题8: 可变参数部分解析出错,但是工具反编译时是不报错误的。
解决8: is_vararg为7时,F->freeLocal多加了一次:
if (f->is_vararg==7) {
TRY(DeclareVariable(F, "arg", F->freeLocal));
F->freeLocal++;
}
// add by littleNA // 修改if为else if else if ((f->is_vararg&2) && (functionnum!=0)) {
F->freeLocal++;
}
修复九
问题9: 反编译工具输出的中文为url类型的字符(类似 “\230\176\148\231\150\151\230\156\175”),不是中文。
char *DecompileString(const Proto * f, int n)
{
...
default:
//add by littleNA// if (*s < 32 || *s > 127) {// char* pos = &(ret[p]);// sprintf(pos, "\\%d", *s);// p += strlen(pos);// } else { ret[p++] = *s;// } break;
...
}
然后再下面3处增加判断的约束条件,因为中文字符的话,char字节是负数,这样isalpha和isalnum函数就会出错,所以增加约束条件,小于等于127:
void MakeIndex(Function * F, StringBuffer * str, char* rstr, int self){
...
int dot = 0;
/*
* see if index can be expressed without quotes
*/ if (rstr[0] == '\"') {
void StartTable(Function * F, int r, int b, int c){
DecTable *tbl = NewTable(r, F, b, c);
AddToList(&(F->tables), (ListItem *) tbl);
F->Rtabl[r] = 1;
F->Rtabl[r] = 1;
if (b == 0 && c == 0) {
// add by littleNA // for(){} for (int npc = F->pc + 1; npc < F->f->sizecode; npc++)
{
Instruction i = F->f->code[npc];
OpCode o = GET_OPCODE(i);
if ((o != OP_SETLIST && o != OP_SETTABLE) && r == GETARG_A(i))
{
PrintTable(F, r, 1);
return;
}
else if ((o == OP_SETLIST || o == OP_SETTABLE) && r == GETARG_A(i))
{
return;
}
}
PrintTable(F, r, 1);
if (error)
return;
}
}void SetList(Function * F, int a, int b, int c){
...
// add by littleNA // if(){} if (b == 0)
{
Instruction i = F->f->code[F->pc-1];
OpCode o = GET_OPCODE(i);
if (o == OP_CALL)
{
int aa = GETARG_A(i);
for (i = a + 1; i < aa + 1; i++)
{
char* rstr = GetR(F, i);
if (error)
return;
AddToTable(F, tbl, rstr, NULL);
if (error)
return;
}
}
else {
for (i = 1;;i++) {
char* rstr = GetR(F, a + i);
if (rstr == NULL)
return;
AddToTable(F, tbl, rstr, NULL);
if (error)
return;
}
}
}
...
}
StartTable 增加的for循环表示,如果执行了newtable(r 0 0),后面非初始化table的操作覆盖了r寄存器(把table覆盖了),那就表明new出来的table是空的,后面没有对table的赋值;如果后面有对r寄存器初始化,证明此时new出了的table不是空的,是可变参数的table。
某航线的一个检测lua在反编译后不修改内容重新编译不会有任何改动,但是如果修改了这个lua的内容,重新编译后内容会发生改变,游戏结算也会失败
这是原来的内容
function AddCfgBaseInfo(slot0)
for slot4, slot5 in ipairs(slot0.all) do
if rawget(slot0[slot5], "base") ~= nil then
rawset(slot6, "base", nil)
setmetatable(slot6, {
__index = slot0[slot7]
})
end
end
end
function GetSpeNum(slot0, slot1)
for slot5, slot6 in pairs(slot0) do
if type(slot6) == "number" then
slot1 = slot1 + slot6
elseif slot7 == "table" then
slot1 = GetSpeNum(slot6, slot1)
elseif slot7 == "string" then
slot1 = slot1 + (tonumber(slot6) or 0)
end
end
return slot1
end
function GetDataValue(slot0)
for slot5, slot6 in ipairs(slot0.all) do
slot1 = GetSpeNum(slot0[slot6], 0)
end
return slot1
end
function CheckTables(slot0, slot1)
for slot5, slot6 in pairs(slot0) do
if type(slot6) == "number" or slot7 == "string" then
if slot6 ~= slot1[slot5] then
return false
end
elseif slot7 == "table" and not CheckTables(slot6, slot1[slot5]) then
return false
end
end