首页
社区
课程
招聘
记一次xlua框架手游 dump脚本
发表于: 2024-1-18 16:42 4433

记一次xlua框架手游 dump脚本

2024-1-18 16:42
4433

逆向经历

这是我第一次进行逆向操作。前几天我曾发布过一个求助帖,在疯狂搜索论坛dalao的Lua文章后,发现自己dump的buff中缺少了几个字节,成功解决问题。

首先,我使用手机安装国际服无期,在MT管理器中,我发现base.apk只有20MB,而没有lib库。后来发现lib库在split_config.arm64_v8a.apk中,将其提取到电脑,里面包含了xlua.so和il2cpp.so文件,肯定是Lua手游。

本来我想使用Perfare大佬的Il2CppDumper分析il2cpp.so文件,但是找不到global-metadata文件。于是,我尝试了Perfare的Zygisk-Il2CppDumper方案成功dump出了Il2Cpp的相关信息dump.cs。

根据论坛前辈所说,lua代码块都会通过luaL_loadbuffer这个函数加载,hook它dump就行,在dump.cs中搜索,可以找到它的相关信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Dll : Assembly-CSharp-firstpass.dll
// Namespace: XLua.LuaDLL
 sealed class Lua
{
    // Fields
    private const String LUADLL; // 0x0
 
    // Properties
 
    // Methods
....................................................
    // RVA: 0x37a36d8 VA: 0x785a7bc6d8
     static Void luaL_getmetatable(IntPtr L, String meta) { }
    // RVA: 0x37a3768 VA: 0x785a7bc768
     static extern Int32 xluaL_loadbuffer(IntPtr L, IntPtr buff, Int32 size, IntPtr name) { }
    // RVA: 0x37a3810 VA: 0x785a7bc810
     static Int32 luaL_loadbuffer(IntPtr L, String buff, String name) { }
    // RVA: 0x37a38b8 VA: 0x785a7bc8b8

至于怎么hook它,直接修改Zygisk-Il2CppDumper的源码就行,加入dobbyhook库,这里一开始hook的是luaL_loadbuffer打印name,结果没有任何信息,改为xluaL_loadbuffer后正常。

1
2
3
4
5
6
7
8
9
10
11
12
#define GETXLUAFUNC(method) getFunctionAddress("XLua.LuaDLL", "Lua", method)
 
void new_luaL_loadbuffer(lua_State *L, char *buff, size_t sz,const char *name) {
  if (name != NULL) {
    LOGI("name: %s\n", name);
  return old_luaL_loadbuffer(L, buff, sz, name);
}
void hook_luaL_loadbuffer(const char *outDir) {
  outDirBuff = outDir;
  DobbyHook((void *)GETXLUAFUNC("xluaL_loadbuffer"),
            (void *)new_luaL_loadbuffer, (void **)&old_luaL_loadbuffer);
}



dump后的文件头是不正常的,ida直接打开xlua.so,字符串搜索lua,可以发现是lua 5.4版本,并且不是luajit,下载lua5.4源码准备进行对比。搜索xluaL_loadbuffer函数,一路往下跟踪对比lua源码:
luaL_loadbufferx--lua_load--luaD_protectedparser--f_parser

1
2
3
4
5
6
7
1B 58 58 00    aXx DCB 0x1B,"XX",0 ;
//检测第一个hex0x1B
  if ( v5 == (unsigned __int8)aXx[0] )
  {
    checkmode(L, *((const unsigned __int8 **)ud + 10), "binary");
    cl = luaU_undump(L, *(ZIO **)ud, *((const unsigned __int8 **)ud + 11));
  }

继续跟踪下去luaU_undump--checkHeader
可以发现checkHeader与lua5.4源码明显不同,这里就是关键。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void __cdecl checkHeader(LoadState *S)
{
  checkliteral(S, (const unsigned __int8 *)"XX", "not a binary chunk");                        //检测是否58 58
  if ( loadByte(S) != 7 )              //检测是否07
    error(S, "format mismatch");
  fchecksize(S, 4uLL, "Instruction"); //检测是否04
  fchecksize(S, 8uLL, "lua_Integer"); //检测是否08
  fchecksize(S, 8uLL, "lua_Number");  //检测是否08
}
//lua 5.4源码
static void checkHeader (LoadState *S) {
  /* skip 1st char (already read and checked) */
  checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk");
  if (loadByte(S) != LUAC_VERSION)
    error(S, "version mismatch");
  if (loadByte(S) != LUAC_FORMAT)
    error(S, "format mismatch");
  checkliteral(S, "\x19\x93\r\n\x1a\n", "corrupted chunk");
  checksize(S, Instruction);
  checksize(S, lua_Integer);
  checksize(S, lua_Number);
  if (loadInteger(S) != LUAC_INT)
    error(S, "integer format mismatch");
  if (loadNumber(S) != LUAC_NUM)
    error(S, "float format mismatch");
}

对比源码可以补全正确的文件头,在dump时先写入正确的31个字节文件头,再写入删了不同文件头的buff.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void new_luaL_loadbuffer(lua_State *L, char *buff, size_t sz,
                         const char *name) {
  if (name != NULL) {
    LOGI("name: %s\n", name);
    static bool exc = false;
    std::string utf8_string = name;
    if (true) {
      std::replace(utf8_string.begin(), utf8_string.end(), '.', '/');
      std::string filepath =
          std::string(outDirBuff) + "/files/lua/" + utf8_string.c_str();
      if (filepath.substr(filepath.find_last_of(".") + 1) != "lua") {
        filepath += ".lua";
      }
      std::string directory = filepath.substr(0, filepath.find_last_of('/'));
      std::filesystem::create_directories(directory);
      std::ofstream file(filepath, std::ios::binary);
      if (!file.is_open()) {
        LOGI("Error: Could not open file %s", filepath.c_str());
        return;
      }
      if (buff[0] == 0x1b) {
        char prefix[] = {
0x1b, 0x4c, 0x75, 0x61, 0x54, 0x00, 0x19, 0x93,0x0d, 0x0a, 0x1a, 0x0a, 0x04, 0x08, 0x08, 0x78,0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x28, 0x77, 0x40};
        file.write(prefix, sizeof(prefix));
        file.write(buff + 7, sz - 7);
      } else
        file.write(buff, sz);
      file.close();
    }
  } else {
    LOGI("no name");
  }
  return old_luaL_loadbuffer(L, buff, sz, name);
}

ok,直接使用unluac反编译dump出来的一个文件,成功:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
local activity_SignReward_Award = {
  [0] = 3,
  [2] = true,
  [4] = true,
  [5] = 5,
  [7] = {
    [8] = {
      [0] = 1
    },
    [10] = {
      [0] = 2
    }
  }
}
local activity_SignReward = {
  [0] = 23,
  [13] = true,
  [5] = 4,
  [7] = {
    [15] = {
      [0] = 1
    },
    [16] = {
      [0] = 2,
      [17] = true
    },
    [18] = activity_SignReward_Award,
    [19] = {
      [0] = 6
    }
  }
}
local activity_DailyChallenge = {
  [0] = 38,
  [13] = true,
  [5] = 5,
  [7] = {
    [22] = {
      [0] = 1
    },
    [23] = {
      [0] = 2
    },
    [24] = {
      [0] = 3
    },
    [25] = {
      [0] = 4,
      [26] = true
    },
    [27] = {
      [0] = 6
    }
  }
}
local activity_HeroTrial_Award = {
  [0] = 3,
  [26] = true,
  [4] = true,
  [5] = 5,
  [7] = {
    [8] = {
      [0] = 1
    },
    [10] = {
      [0] = 2
    }
  }
}
local activity_HeroTrial = {
  [0] = 47,
  [13] = true,
  [5] = 3,
  [7] = {
    [29] = {
      [0] = 1
    },
    [30] = {
      [0] = 2
    },
    [18] = activity_HeroTrial_Award
  }
}
local activity_ActivityGiveItems = {
  [0] = 55,
  [2] = true,
  [4] = true,
  [5] = 5,
  [7] = {
    [8] = {
      [0] = 1
    },
    [10] = {
      [0] = 2
    }
  }
}
local activity_ActivityParmData = {
  [0] = 58,
  [13] = true,
  [5] = 5,
  [7] = {
    [33] = {
      [0] = 1
    },
    [34] = {
      [0] = 2,
      [2] = true
    },
    [35] = {
      [0] = 5,
      [2] = true
    },
    [36] = {
      [0] = 8,
      [17] = true,
      [2] = true
    },
    [38] = {
      [0] = 11
    }
  }
}
local activity = {
  [8] = {
    [0] = 1
  },
  [40] = {
    [0] = 2,
    [17] = true
  },
  [41] = {
    [0] = 3
  },
  [42] = {
    [0] = 4
  },
  [43] = {
    [0] = 5
  },
  [44] = {
    [0] = 6,
    [26] = true
  },
  [45] = {
    [0] = 8
  },
  [46] = {
    [0] = 9
  },
  [48] = {
    [0] = 10,
    [26] = true
  },
  [50] = {
    [0] = 12
  },
  [52] = {
    [0] = 13,
    [2] = true
  },
  [54] = {
    [0] = 16
  },
  [56] = {
    [0] = 17
  },
  [58] = {
    [0] = 18
  },
  [60] = {
    [0] = 19
  },
  [62] = {
    [0] = 20
  },
  [64] = {
    [0] = 21
  },
  [66] = {
    [0] = 22,
    [17] = true
  },
  [68] = activity_SignReward,
  [69] = {
    [0] = 25
  },
  [71] = {
    [0] = 26
  },
  [73] = {
    [0] = 27,
    [2] = true
  },
  [75] = {
    [0] = 30
  },
  [77] = {
    [0] = 31
  },
  [79] = {
    [0] = 32,
    [2] = true
  },
  [81] = {
    [0] = 35,
    [2] = true
  },
  [83] = activity_DailyChallenge,
  [84] = {
    [0] = 40
  },
  [86] = {
    [0] = 41
  },
  [88] = {
    [0] = 42
  },
  [90] = {
    [0] = 43
  },
  [92] = {
    [0] = 44,
    [26] = true
  },
  [94] = {
    [0] = 46
  },
  [96] = activity_HeroTrial,
  [97] = {
    [0] = 49
  },
  [99] = {
    [0] = 50,
    [101] = true
  },
  [102] = {
    [0] = 51,
    [101] = true
  },
  [104] = {
    [0] = 52
  },
  [106] = {
    [0] = 53
  },
  [108] = {
    [0] = 54
  },
  [110] = activity_ActivityGiveItems,
  [111] = activity_ActivityParmData,
  [112] = {
    [0] = 60,
    [2] = true
  },
  [114] = {
    [0] = 63,
    [2] = true
  },
  [116] = {
    [0] = 66,
    [101] = true
  },
  [118] = {
    [0] = 67,
    [26] = true
  },
  [120] = {
    [0] = 69,
    [26] = true
  },
  [122] = {
    [0] = 71,
    [26] = true
  },
  [124] = {
    [0] = 73,
    [26] = true
  },
  [126] = {
    [0] = 75,
    [26] = true
  }
}
local tb = {
  [5] = 50,
  [7] = activity,
  [128] = false
}
return tb

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-1-18 16:50 被mb_jqythzmb编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (2)
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-1-18 18:48
0
雪    币: 3004
活跃值: (30866)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2024-1-19 11:19
1
游客
登录 | 注册 方可回帖
返回
//