@@ -127,59 +127,110 @@ void LooseFileLoader::hook() {
127
127
return ;
128
128
}
129
129
130
- const auto via_io_file = tdb->find_type (" via.io.file" );
130
+ auto initial_candidate = [&]() -> std::optional<uintptr_t > {
131
+ const auto via_io_file = tdb->find_type (" via.io.file" );
131
132
132
- if (via_io_file == nullptr ) {
133
- spdlog::error (" [LooseFileLoader] Failed to find via.io.file" );
134
- return ;
135
- }
133
+ if (via_io_file == nullptr ) {
134
+ spdlog::error (" [LooseFileLoader] Failed to find via.io.file" );
135
+ return std::nullopt ;
136
+ }
136
137
137
- // "exist" and "exists" are 2 different functions. one searches in pak and other doesnt.
138
- const auto exists_fn = via_io_file->get_method (" exists(System.String)" );
138
+ // "exist" and "exists" are 2 different functions. one searches in pak and other doesnt.
139
+ const auto exists_fn = via_io_file->get_method (" exists(System.String)" );
139
140
140
- if (exists_fn == nullptr ) {
141
- spdlog::error (" [LooseFileLoader] Failed to find via.io.file.exists(System.String)" );
142
- return ;
143
- }
141
+ if (exists_fn == nullptr ) {
142
+ spdlog::error (" [LooseFileLoader] Failed to find via.io.file.exists(System.String)" );
143
+ return std::nullopt ;
144
+ }
144
145
145
- const auto exists_ptr = exists_fn->get_function ();
146
+ const auto exists_ptr = exists_fn->get_function ();
146
147
147
- if (exists_ptr == nullptr ) {
148
- spdlog::error (" [LooseFileLoader] Failed to get function pointer for via.io.file.exists(System.String)" );
149
- return ;
150
- }
148
+ if (exists_ptr == nullptr ) {
149
+ spdlog::error (" [LooseFileLoader] Failed to get function pointer for via.io.file.exists(System.String)" );
150
+ return std::nullopt;
151
+ }
152
+
153
+ return (uintptr_t )exists_ptr;
154
+ }();
151
155
152
- std::optional<uintptr_t > candidate{};
153
156
const auto game_module = utility::get_executable ();
154
157
const auto game_module_size = utility::get_module_size (game_module).value_or (0 );
155
158
const auto game_module_end = (uintptr_t )game_module + game_module_size;
156
159
157
- utility::exhaustive_decode ((uint8_t *)exists_ptr, 500 , [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult {
158
- if (candidate) {
159
- return utility::ExhaustionResult::BREAK;
160
- }
160
+ std::optional<uintptr_t > candidate{};
161
161
162
- // We do not care about the address if it is not in the game module, e.g. inside of kernel32.dll
163
- if (ctx.addr < (uintptr_t )game_module || ctx.addr > game_module_end) {
164
- return utility::ExhaustionResult::BREAK;
165
- }
162
+ auto check_fn = [&](uintptr_t exists_ptr) {
163
+ spdlog::info (" [LooseFileLoader] Scanning for path_to_hash candidate at {:x}" , exists_ptr);
166
164
167
- // This means we have just entered call or something
168
- if (ctx. branch_start == ctx. addr ) {
169
- if ( auto bs = utility::find_string_reference_in_path (ctx. branch_start , L" \\ " , false ); bs. has_value ()) {
170
- spdlog::info ( " [LooseFileLoader] Found a backslash reference at {:x} " , bs-> addr );
165
+ utility::exhaustive_decode (( uint8_t *)exists_ptr, 500 , [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult {
166
+ if (candidate ) {
167
+ return utility::ExhaustionResult::BREAK;
168
+ }
171
169
172
- // Now check if murmur hash constant is in the path
173
- if (utility::find_pattern_in_path ((uint8_t *)ctx.branch_start , 500 , true , " ? ? 6B CA EB 85" )) {
174
- candidate = ctx.branch_start ;
175
- spdlog::info (" [LooseFileLoader] Found path_to_hash candidate at {:x}" , *candidate);
176
- return utility::ExhaustionResult::BREAK;
170
+ // We do not care about the address if it is not in the game module, e.g. inside of kernel32.dll
171
+ if (ctx.addr < (uintptr_t )game_module || ctx.addr > game_module_end) {
172
+ return utility::ExhaustionResult::BREAK;
173
+ }
174
+
175
+ // This means we have just entered call or something
176
+ if (ctx.branch_start == ctx.addr ) {
177
+ if (auto bs = utility::find_string_reference_in_path (ctx.branch_start , L" \\ " , false ); bs.has_value ()) {
178
+ spdlog::info (" [LooseFileLoader] Found a backslash reference at {:x}" , bs->addr );
179
+
180
+ // Now check if murmur hash constant is in the path
181
+ if (utility::find_pattern_in_path ((uint8_t *)ctx.branch_start , 500 , true , " ? ? 6B CA EB 85" )) {
182
+ candidate = ctx.branch_start ;
183
+ spdlog::info (" [LooseFileLoader] Found path_to_hash candidate at {:x}" , *candidate);
184
+ return utility::ExhaustionResult::BREAK;
185
+ }
177
186
}
178
187
}
179
- }
180
188
181
- return utility::ExhaustionResult::CONTINUE;
182
- });
189
+ return utility::ExhaustionResult::CONTINUE;
190
+ });
191
+ };
192
+
193
+ if (!initial_candidate) {
194
+ // Basically what we're doing here is finding an initial "mov r8d, 800h"
195
+ // and then finding a "mov r8d, 400h" in the function, as well as another "mov r8d, 800h"
196
+ // I call this a landmark scan, where we find a sequence of instructions that are unique to the function
197
+ // but they don't need to be near each other, they just need to be in the same function.
198
+ // Some other giveaways of the function are the uses of the UTF-16 backslash characters
199
+ // and the two calls to murmur hash functions at the end (these can be found in System.String's GetHashCode)
200
+ const std::string start_seq {" 41 B8 00 08 00 00" }; // mov r8d, 800h
201
+ std::vector<std::string> patterns_in_function {
202
+ " BA 00 04 00 00" ,
203
+ " 41 B8 00 08 00 00" ,
204
+ };
205
+
206
+ std::unordered_set<uintptr_t > analyzed_fns{};
207
+
208
+ for (auto new_candidate = utility::find_landmark_sequence (game_module, start_seq, patterns_in_function, false );
209
+ new_candidate.has_value ();
210
+ new_candidate = utility::find_landmark_sequence (new_candidate->addr + new_candidate->instrux .Length , game_module_end - (new_candidate->addr + 1 ), start_seq, patterns_in_function, false ))
211
+ {
212
+ const auto fn_start = utility::find_function_start_with_call (new_candidate->addr );
213
+
214
+ if (!fn_start.has_value ()) {
215
+ spdlog::error (" [LooseFileLoader] Failed to find path_to_hash candidate's start, cannot continue" );
216
+ continue ;
217
+ }
218
+
219
+ if (analyzed_fns.contains (fn_start.value ())) {
220
+ continue ;
221
+ }
222
+
223
+ analyzed_fns.insert (fn_start.value ());
224
+
225
+ check_fn (*fn_start);
226
+
227
+ if (candidate) {
228
+ break ;
229
+ }
230
+ }
231
+ } else {
232
+ check_fn (initial_candidate.value ());
233
+ }
183
234
184
235
if (!candidate) {
185
236
spdlog::error (" [LooseFileLoader] Failed to find path_to_hash candidate, cannot continue" );
0 commit comments