{"action":"opened","issue":{"url":"https://api.github.com/repos/m4b/goblin/issues/76","repository_url":"https://api.github.com/repos/m4b/goblin","labels_url":"https://api.github.com/repos/m4b/goblin/issues/76/labels{/name}","comments_url":"https://api.github.com/repos/m4b/goblin/issues/76/comments","events_url":"https://api.github.com/repos/m4b/goblin/issues/76/events","html_url":"https://github.com/m4b/goblin/issues/76","id":293408788,"number":76,"title":"Parsing PE with empty export data directory","user":{"login":"tathanhdinh","id":79418,"avatar_url":"https://avatars3.githubusercontent.com/u/79418?v=4","gravatar_id":"","url":"https://api.github.com/users/tathanhdinh","html_url":"https://github.com/tathanhdinh","followers_url":"https://api.github.com/users/tathanhdinh/followers","following_url":"https://api.github.com/users/tathanhdinh/following{/other_user}","gists_url":"https://api.github.com/users/tathanhdinh/gists{/gist_id}","starred_url":"https://api.github.com/users/tathanhdinh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tathanhdinh/subscriptions","organizations_url":"https://api.github.com/users/tathanhdinh/orgs","repos_url":"https://api.github.com/users/tathanhdinh/repos","events_url":"https://api.github.com/users/tathanhdinh/events{/privacy}","received_events_url":"https://api.github.com/users/tathanhdinh/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-02-01T05:31:58Z","updated_at":"2018-02-01T05:31:58Z","closed_at":null,"author_association":"NONE","body":"Some well-formed PE may have its export data directory with an empty table of `NumberOfFunctions` (as well as `NumberOfNames`,  `AddressOfFunctions`, etc.), for example the `Export Data Directory` of `apisetschema.dll` (a dll for [API redirection](https://blog.quarkslab.com/runtime-dll-name-resolution-apisetschema-part-i.html) from Windows 7) on my machine `Windows7.SP1`:\r\n\r\n![apisetschema](https://user-images.githubusercontent.com/79418/35662192-18e7d004-0717-11e8-8fff-368edf4e963f.PNG)\r\n\r\nBut the PE parser of goblin will refuse such a PE, that is because of some checks, for example when parsing the [`AddressOfNames`](https://github.com/m4b/goblin/blob/master/src/pe/export.rs#L76):\r\n\r\n`let name_pointer_table_offset = &mut utils::find_offset_or(export_directory_table.name_pointer_rva as usize, sections, &format!(\"Cannot map export_directory_table.name_pointer_rva ({:#x}) into offset\", export_directory_table.name_pointer_rva))?;`\r\n\r\nbecause `export_directory_table.name_pointer_rva` will be zero then the including function `parse` returns immediately with an `Err(_)`.\r\n\r\nThe same with `AddressOfNameOrdinals` and `AddressOfFunctions` tables, a quick-and-dirty fix might be check the value of `export_directory_table.number_of_name_pointers` and of ``export_directory_table.address_table_entries` with zero before parsing offsets of these tables:\r\n\r\n```\r\nlet mut export_name_pointer_table: ExportNamePointerTable = Vec::with_capacity(number_of_name_pointers);\r\nlet mut export_ordinal_table: ExportOrdinalTable = Vec::with_capacity(number_of_name_pointers);\r\n\r\nif number_of_name_pointers > 0 {\r\n    let name_pointer_table_offset = &mut utils::find_offset_or(export_directory_table.name_pointer_rva as usize, sections, &format!(\"Cannot map export_directory_table.name_pointer_rva ({:#x}) into offset\", export_directory_table.name_pointer_rva))?;\r\n    for _ in 0..number_of_name_pointers {\r\n        export_name_pointer_table.push(bytes.gread_with(name_pointer_table_offset, scroll::LE)?);\r\n    }\r\n\r\n    let export_ordinal_table_offset = &mut utils::find_offset_or(export_directory_table.ordinal_table_rva as usize, sections, &format!(\"Cannot map export_directory_table.ordinal_table_rva ({:#x}) into offset\", export_directory_table.ordinal_table_rva))?;\r\n\r\n    for _ in 0..number_of_name_pointers {\r\n        export_ordinal_table.push(bytes.gread_with(export_ordinal_table_offset, scroll::LE)?);\r\n    }\r\n}\r\n\r\nlet mut export_address_table: ExportAddressTable = Vec::with_capacity(address_table_entries);\r\nif address_table_entries > 0 {\r\n    let export_address_table_offset = utils::find_offset_or(export_directory_table.export_address_table_rva as usize, sections, &format!(\"Cannot map export_directory_table.export_address_table_rva ({:#x}) into offset\", export_directory_table.export_address_table_rva))?;\r\n    let export_end = export_rva + size;\r\n    let offset = &mut export_address_table_offset.clone();\r\n    \r\n    for _ in 0..address_table_entries {\r\n        let rva: u32 = bytes.gread_with(offset, scroll::LE)?;\r\n        if utils::is_in_range(rva as usize, export_rva, export_end) {\r\n            export_address_table.push(ExportAddressTableEntry::ForwarderRVA(rva));\r\n        } else {\r\n            export_address_table.push(ExportAddressTableEntry::ExportRVA(rva));\r\n        }\r\n    }\r\n}\r\n```"}}
{"actor":{"display_login":"tathanhdinh"}}