@@ -173,34 +173,100 @@ void pe_add_sections(XEXFile& file)
173
173
}
174
174
}
175
175
176
- void pe_setup_netnode (XEXFile& file)
176
+ void pe_parse_pdata (XEXFile& file)
177
177
{
178
- netnode penode;
179
- penode.create (PE_NODE);
178
+ // Hybrid pdata parser:
179
+ // - our code will first read from pdata and create functions based on it
180
+ // - then defer to eh_parse which will create .pdata dwords & set up exception handlers
181
+ // From testing this results in more functions getting created, and allows autoanalysis to parse functions much earlier
180
182
181
- const uint8_t * pe_data = file.pe_data ();
182
- IMAGE_DOS_HEADER* dos_header = (IMAGE_DOS_HEADER*)pe_data;
183
+ bool has_pdata = false ;
183
184
184
- // Set PE header node data
185
- // Make a copy of PE header and update with correct values first, since IDA/eh_parse reads some info from this
186
- IMAGE_NT_HEADERS nt_header = *(IMAGE_NT_HEADERS*)(pe_data + dos_header->AddressOfNewExeHeader );
187
- nt_header.OptionalHeader .ImageBase = file.base_address ();
185
+ // Try reading & marking functions from .pdata section
186
+ for (const auto & section : file.sections ())
187
+ {
188
+ // New buffer for section name so we can null-terminate it
189
+ char name[9 ];
190
+ std::copy_n (section.Name , 8 , name);
191
+ name[8 ] = ' \0 ' ;
192
+ if (strcmp (name, " .pdata" ))
193
+ continue ;
188
194
189
- penode. set (&nt_header, sizeof (IMAGE_NT_HEADERS)) ;
195
+ has_pdata = true ;
190
196
191
- // Update imagebase
192
- penode.altset (PE_ALT_IMAGEBASE, file.base_address ());
197
+ uint32 sec_addr = section.VirtualAddress ;
198
+ uint32 sec_size = section.VirtualSize ;
199
+ if (file.header ().Magic == MAGIC_XEX3F || file.header ().Magic == MAGIC_XEX0)
200
+ {
201
+ sec_addr = section.PointerToRawData ;
202
+ sec_size = section.SizeOfRawData ; // TODO: verify this?
203
+ }
204
+ ea_t seg_addr = (ea_t )file.base_address () + (ea_t )section.VirtualAddress ;
205
+ // Size could be beyond file bounds, if so fix the size to what we can fit
206
+ if (sec_addr + sec_size > file.image_size ())
207
+ sec_size = file.image_size () - sec_addr;
193
208
194
- bool has_pdata = false ;
195
- for (const auto & section : file.sections ())
196
- {
197
- if (!strncmp (section.Name , " .pdata" , 6 )) {
198
- has_pdata = true ;
199
- break ;
209
+ struct RUNTIME_FUNCTION_INFO // bitfield portion of RUNTIME_FUNCTION
210
+ {
211
+ uint32_t PrologLength : 8 ;
212
+ uint32_t FunctionLength : 22 ;
213
+ uint32_t FunctionType : 2 ;
214
+ inline xex::RuntimeFunctionType RuntimeFunctionType () {
215
+ return (xex::RuntimeFunctionType)FunctionType;
216
+ }
217
+ };
218
+
219
+ // Read function addrs from .pdata into vector so we can get a count before asking IDA to create functions for them
220
+ std::unordered_map<uint32_t , RUNTIME_FUNCTION_INFO> funcs;
221
+ int offset = 0 ;
222
+ while (offset < sec_size)
223
+ {
224
+ auto * fn_ptr = reinterpret_cast <const xe::be<uint32_t >*>(file.pe_data () + sec_addr + offset);
225
+ uint32_t fn_ea = fn_ptr[0 ];
226
+ if (fn_ea)
227
+ {
228
+ uint32_t fn_info_raw = fn_ptr[1 ]; // endian-swap the field for us
229
+ RUNTIME_FUNCTION_INFO fn_info = *(RUNTIME_FUNCTION_INFO*)&fn_info_raw; // and then convert to RUNTIME_FUNCTION_INFO
230
+ funcs.insert ({ fn_ea, fn_info });
231
+ }
232
+ offset += 8 ;
233
+ }
234
+
235
+ msg (" Parsing .pdata and creating %d functions...\n " , int (funcs.size ()));
236
+
237
+ // display messagebox prompt to user so they can cancel if needed
238
+ show_wait_box (" Marking functions from .pdata... (0/%d)" , int (funcs.size ()));
239
+ size_t num = 0 ;
240
+ for (auto & kvp : funcs)
241
+ {
242
+ if (kvp.second .FunctionLength && !get_fchunk (kvp.first ))
243
+ {
244
+ if (create_insn (kvp.first ))
245
+ {
246
+ show_auto (kvp.first );
247
+ // Don't create function for savevmx/restvmx, we handle it in label_regsaveloads
248
+ auto fn_type = kvp.second .RuntimeFunctionType ();
249
+ if (fn_type != xex::RuntimeFunctionType::SaveMillicode && fn_type != xex::RuntimeFunctionType::RestoreMillicode)
250
+ {
251
+ func_t fn (kvp.first , kvp.first + (kvp.second .FunctionLength * 4 ));
252
+ add_func_ex (&fn);
253
+ }
254
+ }
255
+ }
256
+ if (user_cancelled ())
257
+ break ;
258
+ // update every few funcs
259
+ if (++num % 50 == 0 )
260
+ {
261
+ replace_wait_box (" Marking functions from .pdata... (%d/%d)" , int (num), int (funcs.size ()));
262
+ }
200
263
}
264
+
265
+ hide_wait_box ();
266
+ break ;
201
267
}
202
268
203
- // Ask eh_parse to parse .pdata for us
269
+ // Ask eh_parse to parse .pdata, for it to mark xrefs & EH
204
270
// Load it here early so user won't need to wait for autoanalysis to complete
205
271
if (has_pdata)
206
272
{
@@ -211,7 +277,30 @@ void pe_setup_netnode(XEXFile& file)
211
277
run_plugin (plugin, 0 );
212
278
}
213
279
}
280
+ }
281
+
282
+ void pe_setup_netnode (XEXFile& file)
283
+ {
284
+ netnode penode;
285
+ penode.create (PE_NODE);
286
+
287
+ const uint8_t * pe_data = file.pe_data ();
288
+ IMAGE_DOS_HEADER* dos_header = (IMAGE_DOS_HEADER*)pe_data;
289
+
290
+ // Set PE header node data
291
+ // Make a copy of PE header and update with correct values first, since IDA/eh_parse reads some info from this
292
+ IMAGE_NT_HEADERS nt_header = *(IMAGE_NT_HEADERS*)(pe_data + dos_header->AddressOfNewExeHeader );
293
+ nt_header.OptionalHeader .ImageBase = file.base_address ();
294
+
295
+ penode.set (&nt_header, sizeof (IMAGE_NT_HEADERS));
296
+
297
+ // Update imagebase
298
+ penode.altset (PE_ALT_IMAGEBASE, file.base_address ());
299
+
300
+ // Parse pdata if it exists
301
+ pe_parse_pdata (file);
214
302
303
+ // Update IDA with any codeview data
215
304
size_t cv_length = 0 ;
216
305
auto * cv_data = file.codeview_data (0 , &cv_length);
217
306
if (cv_data)
0 commit comments