From d6e355aa94e4e3482a8d766692614cbaacc4cd9c Mon Sep 17 00:00:00 2001 From: warpmaster <32202890+jared-perry@users.noreply.github.com> Date: Mon, 20 Jul 2026 22:58:12 -0400 Subject: [PATCH] fix: resolve ws2_32/oleaut32/wsock32 ordinal imports in imphash (match pefile) --- CHANGELOG.md | 19 ++ Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 1 + src/ordinals.rs | 762 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pe.rs | 13 +- 6 files changed, 793 insertions(+), 6 deletions(-) create mode 100644 src/ordinals.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cbbb9a..d2a3456 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to OffsetScan are documented in this file. The project follows semantic versioning. +## [0.1.3] - 2026-07-20 + +### Fixed + +- **imphash now resolves ordinal imports from `ws2_32`/`wsock32`/`oleaut32` to their real + function names, matching pefile and VirusTotal.** Previously these rendered as `ord`, + so any binary importing those libraries by ordinal — a common malware networking + pattern — produced an imphash that did not match the value on VirusTotal, defeating the + imphash's purpose of correlating a sample against threat intel. Verified against pefile + across a 150-file corpus (44 exercising the special-ordinal path): 0 mismatches, and 15 + binaries that previously diverged now agree. Other ordinal imports still render `ord`, + exactly as pefile does. + +### Added + +- `src/ordinals.rs`: pefile's `ws2_32`/`wsock32`/`oleaut32` ordinal->name tables (696 + entries), generated verbatim from pefile 2024.8.26, with unit tests. Kept in lockstep + with OffsetInspect's `Core.PE.Ordinals.ps1`. + ## [0.1.2] - 2026-07-20 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 089407e..8820291 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -487,7 +487,7 @@ dependencies = [ [[package]] name = "offsetscan" -version = "0.1.2" +version = "0.1.3" dependencies = [ "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index e130116..f632ae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "offsetscan" -version = "0.1.2" +version = "0.1.3" edition = "2021" authors = ["DreadHost Research"] description = "Standalone native corpus-scale engine for PE parsing, entropy, string extraction, and IOC panels — schema-compatible with OffsetInspect.Result/ThreatScanResult JSON output." diff --git a/src/main.rs b/src/main.rs index ff982fb..061b942 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod entropy; mod ioc; +mod ordinals; mod pe; mod schema; mod strings; diff --git a/src/ordinals.rs b/src/ordinals.rs new file mode 100644 index 0000000..b9ecad1 --- /dev/null +++ b/src/ordinals.rs @@ -0,0 +1,762 @@ +//! pefile-compatible ordinal->name resolution for the "special" import libraries +//! (ws2_32.dll, wsock32.dll, oleaut32.dll). imphash resolves ordinals imported from +//! these to the real function name, exactly as pefile / VirusTotal do; every other +//! ordinal import renders "ord". Generated verbatim from pefile 2024.8.26's +//! ordlookup tables; names are pre-lowercased to match imphash lowercasing. +//! +//! DO NOT EDIT BY HAND - regenerate from pefile if the reference tables change. + +/// Resolve an ordinal import to its lowercased function name for one of the three +/// special libraries. `dll_lower` is the DLL name WITH extension, lowercased +/// (e.g. "ws2_32.dll"). Returns `None` for non-special libraries or ordinals absent +/// from pefile's table; the caller then renders "ord". +pub fn special_ordinal_name(dll_lower: &str, ordinal: u16) -> Option<&'static str> { + match dll_lower { + "ws2_32.dll" => ws2_32(ordinal), + "wsock32.dll" => wsock32(ordinal), + "oleaut32.dll" => oleaut32(ordinal), + _ => None, + } +} + +fn ws2_32(o: u16) -> Option<&'static str> { + Some(match o { + 1 => "accept", + 2 => "bind", + 3 => "closesocket", + 4 => "connect", + 5 => "getpeername", + 6 => "getsockname", + 7 => "getsockopt", + 8 => "htonl", + 9 => "htons", + 10 => "ioctlsocket", + 11 => "inet_addr", + 12 => "inet_ntoa", + 13 => "listen", + 14 => "ntohl", + 15 => "ntohs", + 16 => "recv", + 17 => "recvfrom", + 18 => "select", + 19 => "send", + 20 => "sendto", + 21 => "setsockopt", + 22 => "shutdown", + 23 => "socket", + 24 => "wsapsetpostroutine", + 25 => "freeaddrinfoex", + 26 => "freeaddrinfoexw", + 27 => "freeaddrinfow", + 28 => "getaddrinfoexa", + 29 => "getaddrinfoexcancel", + 30 => "getaddrinfoexoverlappedresult", + 31 => "getaddrinfoexw", + 32 => "getaddrinfow", + 33 => "gethostnamew", + 34 => "getnameinfow", + 35 => "inetntopw", + 36 => "inetptonw", + 37 => "processsocketnotifications", + 38 => "setaddrinfoexa", + 39 => "setaddrinfoexw", + 40 => "wpucompleteoverlappedrequest", + 41 => "wpugetproviderpathex", + 42 => "wsaaccept", + 43 => "wsaaddresstostringa", + 44 => "wsaaddresstostringw", + 45 => "wsaadvertiseprovider", + 46 => "wsacloseevent", + 47 => "wsaconnect", + 48 => "wsaconnectbylist", + 49 => "wsaconnectbynamea", + 50 => "wsaconnectbynamew", + 51 => "gethostbyaddr", + 52 => "gethostbyname", + 53 => "getprotobyname", + 54 => "getprotobynumber", + 55 => "getservbyname", + 56 => "getservbyport", + 57 => "gethostname", + 58 => "wsacreateevent", + 59 => "wsaduplicatesocketa", + 60 => "wsaduplicatesocketw", + 61 => "wsaenumnamespaceprovidersa", + 62 => "wsaenumnamespaceprovidersexa", + 63 => "wsaenumnamespaceprovidersexw", + 64 => "wsaenumnamespaceprovidersw", + 65 => "wsaenumnetworkevents", + 66 => "wsaenumprotocolsa", + 67 => "wsaenumprotocolsw", + 68 => "wsaeventselect", + 69 => "wsagetoverlappedresult", + 70 => "wsagetqosbyname", + 71 => "wsagetserviceclassinfoa", + 72 => "wsagetserviceclassinfow", + 73 => "wsagetserviceclassnamebyclassida", + 74 => "wsagetserviceclassnamebyclassidw", + 75 => "wsahtonl", + 76 => "wsahtons", + 77 => "wsainstallserviceclassa", + 78 => "wsainstallserviceclassw", + 79 => "wsaioctl", + 80 => "wsajoinleaf", + 81 => "wsalookupservicebegina", + 82 => "wsalookupservicebeginw", + 83 => "wsalookupserviceend", + 84 => "wsalookupservicenexta", + 85 => "wsalookupservicenextw", + 86 => "wsanspioctl", + 87 => "wsantohl", + 88 => "wsantohs", + 89 => "wsapoll", + 90 => "wsaprovidercompleteasynccall", + 91 => "wsaproviderconfigchange", + 92 => "wsarecv", + 93 => "wsarecvdisconnect", + 94 => "wsarecvfrom", + 95 => "wsaremoveserviceclass", + 96 => "wsaresetevent", + 97 => "wsasend", + 98 => "wsasenddisconnect", + 99 => "wsasendmsg", + 100 => "wsasendto", + 101 => "wsaasyncselect", + 102 => "wsaasyncgethostbyaddr", + 103 => "wsaasyncgethostbyname", + 104 => "wsaasyncgetprotobynumber", + 105 => "wsaasyncgetprotobyname", + 106 => "wsaasyncgetservbyport", + 107 => "wsaasyncgetservbyname", + 108 => "wsacancelasyncrequest", + 109 => "wsasetblockinghook", + 110 => "wsaunhookblockinghook", + 111 => "wsagetlasterror", + 112 => "wsasetlasterror", + 113 => "wsacancelblockingcall", + 114 => "wsaisblocking", + 115 => "wsastartup", + 116 => "wsacleanup", + 117 => "wsasetevent", + 118 => "wsasetservicea", + 119 => "wsasetservicew", + 120 => "wsasocketa", + 121 => "wsasocketw", + 122 => "wsastringtoaddressa", + 123 => "wsastringtoaddressw", + 124 => "wsaunadvertiseprovider", + 125 => "wsawaitformultipleevents", + 126 => "wscdeinstallprovider", + 127 => "wscdeinstallprovider32", + 128 => "wscdeinstallproviderex", + 129 => "wscenablensprovider", + 130 => "wscenablensprovider32", + 131 => "wscenumnamespaceproviders32", + 132 => "wscenumnamespaceprovidersex32", + 133 => "wscenumprotocols", + 134 => "wscenumprotocols32", + 135 => "wscenumprotocolsex", + 136 => "wscgetapplicationcategory", + 137 => "wscgetapplicationcategoryex", + 138 => "wscgetproviderinfo", + 139 => "wscgetproviderinfo32", + 140 => "wscgetproviderpath", + 141 => "wscgetproviderpath32", + 142 => "wscinstallnamespace", + 143 => "wscinstallnamespace32", + 144 => "wscinstallnamespaceex", + 145 => "wscinstallnamespaceex2", + 146 => "wscinstallnamespaceex32", + 147 => "wscinstallprovider", + 148 => "wscinstallprovider64_32", + 149 => "wscinstallproviderandchains64_32", + 150 => "wscinstallproviderex", + 151 => "__wsafdisset", + 152 => "wscsetapplicationcategory", + 153 => "wscsetapplicationcategoryex", + 154 => "wscsetproviderinfo", + 155 => "wscsetproviderinfo32", + 156 => "wscuninstallnamespace", + 157 => "wscuninstallnamespace32", + 158 => "wscuninstallnamespaceex2", + 159 => "wscupdateprovider", + 160 => "wscupdateprovider32", + 161 => "wscupdateproviderex", + 162 => "wscwritenamespaceorder", + 163 => "wscwritenamespaceorder32", + 164 => "wscwriteproviderorder", + 165 => "wscwriteproviderorder32", + 166 => "wscwriteproviderorderex", + 167 => "wahcloseapchelper", + 168 => "wahclosehandlehelper", + 169 => "wahclosenotificationhandlehelper", + 170 => "wahclosesockethandle", + 171 => "wahclosethread", + 172 => "wahcompleterequest", + 173 => "wahcreatehandlecontexttable", + 174 => "wahcreatenotificationhandle", + 175 => "wahcreatesockethandle", + 176 => "wahdestroyhandlecontexttable", + 177 => "wahdisablenonifshandlesupport", + 178 => "wahenablenonifshandlesupport", + 179 => "wahenumeratehandlecontexts", + 180 => "wahinserthandlecontext", + 181 => "wahnotifyallprocesses", + 182 => "wahopenapchelper", + 183 => "wahopencurrentthread", + 184 => "wahopenhandlehelper", + 185 => "wahopennotificationhandlehelper", + 186 => "wahqueueuserapc", + 187 => "wahreferencecontextbyhandle", + 188 => "wahremovehandlecontext", + 189 => "wahwaitfornotification", + 190 => "wahwritelspevent", + 191 => "freeaddrinfo", + 192 => "getaddrinfo", + 193 => "getnameinfo", + 194 => "inet_ntop", + 195 => "inet_pton", + 500 => "wep", + _ => return None, + }) +} + +fn wsock32(o: u16) -> Option<&'static str> { + Some(match o { + 1 => "accept", + 2 => "bind", + 3 => "closesocket", + 4 => "connect", + 5 => "getpeername", + 6 => "getsockname", + 7 => "getsockopt", + 8 => "htonl", + 9 => "htons", + 10 => "inet_addr", + 11 => "inet_ntoa", + 12 => "ioctlsocket", + 13 => "listen", + 14 => "ntohl", + 15 => "ntohs", + 16 => "recv", + 17 => "recvfrom", + 18 => "select", + 19 => "send", + 20 => "sendto", + 21 => "setsockopt", + 22 => "shutdown", + 23 => "socket", + 24 => "migratewinsockconfiguration", + 51 => "gethostbyaddr", + 52 => "gethostbyname", + 53 => "getprotobyname", + 54 => "getprotobynumber", + 55 => "getservbyname", + 56 => "getservbyport", + 57 => "gethostname", + 101 => "wsaasyncselect", + 102 => "wsaasyncgethostbyaddr", + 103 => "wsaasyncgethostbyname", + 104 => "wsaasyncgetprotobynumber", + 105 => "wsaasyncgetprotobyname", + 106 => "wsaasyncgetservbyport", + 107 => "wsaasyncgetservbyname", + 108 => "wsacancelasyncrequest", + 109 => "wsasetblockinghook", + 110 => "wsaunhookblockinghook", + 111 => "wsagetlasterror", + 112 => "wsasetlasterror", + 113 => "wsacancelblockingcall", + 114 => "wsaisblocking", + 115 => "wsastartup", + 116 => "wsacleanup", + 151 => "__wsafdisset", + 500 => "wep", + 1000 => "wsapsetpostroutine", + 1100 => "inet_network", + 1101 => "getnetbyname", + 1102 => "rcmd", + 1103 => "rexec", + 1104 => "rresvport", + 1105 => "sethostname", + 1106 => "dn_expand", + 1107 => "wsarecvex", + 1108 => "s_perror", + 1109 => "getaddressbynamea", + 1110 => "getaddressbynamew", + 1111 => "enumprotocolsa", + 1112 => "enumprotocolsw", + 1113 => "gettypebynamea", + 1114 => "gettypebynamew", + 1115 => "getnamebytypea", + 1116 => "getnamebytypew", + 1117 => "setservicea", + 1118 => "setservicew", + 1119 => "getservicea", + 1120 => "getservicew", + 1130 => "nploadnamespaces", + 1140 => "transmitfile", + 1141 => "acceptex", + 1142 => "getacceptexsockaddrs", + _ => return None, + }) +} + +fn oleaut32(o: u16) -> Option<&'static str> { + Some(match o { + 2 => "sysallocstring", + 3 => "sysreallocstring", + 4 => "sysallocstringlen", + 5 => "sysreallocstringlen", + 6 => "sysfreestring", + 7 => "sysstringlen", + 8 => "variantinit", + 9 => "variantclear", + 10 => "variantcopy", + 11 => "variantcopyind", + 12 => "variantchangetype", + 13 => "varianttimetodosdatetime", + 14 => "dosdatetimetovarianttime", + 15 => "safearraycreate", + 16 => "safearraydestroy", + 17 => "safearraygetdim", + 18 => "safearraygetelemsize", + 19 => "safearraygetubound", + 20 => "safearraygetlbound", + 21 => "safearraylock", + 22 => "safearrayunlock", + 23 => "safearrayaccessdata", + 24 => "safearrayunaccessdata", + 25 => "safearraygetelement", + 26 => "safearrayputelement", + 27 => "safearraycopy", + 28 => "dispgetparam", + 29 => "dispgetidsofnames", + 30 => "dispinvoke", + 31 => "createdisptypeinfo", + 32 => "createstddispatch", + 33 => "registeractiveobject", + 34 => "revokeactiveobject", + 35 => "getactiveobject", + 36 => "safearrayallocdescriptor", + 37 => "safearrayallocdata", + 38 => "safearraydestroydescriptor", + 39 => "safearraydestroydata", + 40 => "safearrayredim", + 41 => "safearrayallocdescriptorex", + 42 => "safearraycreateex", + 43 => "safearraycreatevectorex", + 44 => "safearraysetrecordinfo", + 45 => "safearraygetrecordinfo", + 46 => "varparsenumfromstr", + 47 => "varnumfromparsenum", + 48 => "vari2fromui1", + 49 => "vari2fromi4", + 50 => "vari2fromr4", + 51 => "vari2fromr8", + 52 => "vari2fromcy", + 53 => "vari2fromdate", + 54 => "vari2fromstr", + 55 => "vari2fromdisp", + 56 => "vari2frombool", + 57 => "safearraysetiid", + 58 => "vari4fromui1", + 59 => "vari4fromi2", + 60 => "vari4fromr4", + 61 => "vari4fromr8", + 62 => "vari4fromcy", + 63 => "vari4fromdate", + 64 => "vari4fromstr", + 65 => "vari4fromdisp", + 66 => "vari4frombool", + 67 => "safearraygetiid", + 68 => "varr4fromui1", + 69 => "varr4fromi2", + 70 => "varr4fromi4", + 71 => "varr4fromr8", + 72 => "varr4fromcy", + 73 => "varr4fromdate", + 74 => "varr4fromstr", + 75 => "varr4fromdisp", + 76 => "varr4frombool", + 77 => "safearraygetvartype", + 78 => "varr8fromui1", + 79 => "varr8fromi2", + 80 => "varr8fromi4", + 81 => "varr8fromr4", + 82 => "varr8fromcy", + 83 => "varr8fromdate", + 84 => "varr8fromstr", + 85 => "varr8fromdisp", + 86 => "varr8frombool", + 87 => "varformat", + 88 => "vardatefromui1", + 89 => "vardatefromi2", + 90 => "vardatefromi4", + 91 => "vardatefromr4", + 92 => "vardatefromr8", + 93 => "vardatefromcy", + 94 => "vardatefromstr", + 95 => "vardatefromdisp", + 96 => "vardatefrombool", + 97 => "varformatdatetime", + 98 => "varcyfromui1", + 99 => "varcyfromi2", + 100 => "varcyfromi4", + 101 => "varcyfromr4", + 102 => "varcyfromr8", + 103 => "varcyfromdate", + 104 => "varcyfromstr", + 105 => "varcyfromdisp", + 106 => "varcyfrombool", + 107 => "varformatnumber", + 108 => "varbstrfromui1", + 109 => "varbstrfromi2", + 110 => "varbstrfromi4", + 111 => "varbstrfromr4", + 112 => "varbstrfromr8", + 113 => "varbstrfromcy", + 114 => "varbstrfromdate", + 115 => "varbstrfromdisp", + 116 => "varbstrfrombool", + 117 => "varformatpercent", + 118 => "varboolfromui1", + 119 => "varboolfromi2", + 120 => "varboolfromi4", + 121 => "varboolfromr4", + 122 => "varboolfromr8", + 123 => "varboolfromdate", + 124 => "varboolfromcy", + 125 => "varboolfromstr", + 126 => "varboolfromdisp", + 127 => "varformatcurrency", + 128 => "varweekdayname", + 129 => "varmonthname", + 130 => "varui1fromi2", + 131 => "varui1fromi4", + 132 => "varui1fromr4", + 133 => "varui1fromr8", + 134 => "varui1fromcy", + 135 => "varui1fromdate", + 136 => "varui1fromstr", + 137 => "varui1fromdisp", + 138 => "varui1frombool", + 139 => "varformatfromtokens", + 140 => "vartokenizeformatstring", + 141 => "varadd", + 142 => "varand", + 143 => "vardiv", + 144 => "bstr_userfree64", + 145 => "bstr_usermarshal64", + 146 => "dispcallfunc", + 147 => "variantchangetypeex", + 148 => "safearrayptrofindex", + 149 => "sysstringbytelen", + 150 => "sysallocstringbytelen", + 151 => "bstr_usersize64", + 152 => "vareqv", + 153 => "varidiv", + 154 => "varimp", + 155 => "varmod", + 156 => "varmul", + 157 => "varor", + 158 => "varpow", + 159 => "varsub", + 160 => "createtypelib", + 161 => "loadtypelib", + 162 => "loadregtypelib", + 163 => "registertypelib", + 164 => "querypathofregtypelib", + 165 => "lhashvalofnamesys", + 166 => "lhashvalofnamesysa", + 167 => "varxor", + 168 => "varabs", + 169 => "varfix", + 170 => "oabuildversion", + 171 => "clearcustdata", + 172 => "varint", + 173 => "varneg", + 174 => "varnot", + 175 => "varround", + 176 => "varcmp", + 177 => "vardecadd", + 178 => "vardecdiv", + 179 => "vardecmul", + 180 => "createtypelib2", + 181 => "vardecsub", + 182 => "vardecabs", + 183 => "loadtypelibex", + 184 => "systemtimetovarianttime", + 185 => "varianttimetosystemtime", + 186 => "unregistertypelib", + 187 => "vardecfix", + 188 => "vardecint", + 189 => "vardecneg", + 190 => "vardecfromui1", + 191 => "vardecfromi2", + 192 => "vardecfromi4", + 193 => "vardecfromr4", + 194 => "vardecfromr8", + 195 => "vardecfromdate", + 196 => "vardecfromcy", + 197 => "vardecfromstr", + 198 => "vardecfromdisp", + 199 => "vardecfrombool", + 200 => "geterrorinfo", + 201 => "seterrorinfo", + 202 => "createerrorinfo", + 203 => "vardecround", + 204 => "vardeccmp", + 205 => "vari2fromi1", + 206 => "vari2fromui2", + 207 => "vari2fromui4", + 208 => "vari2fromdec", + 209 => "vari4fromi1", + 210 => "vari4fromui2", + 211 => "vari4fromui4", + 212 => "vari4fromdec", + 213 => "varr4fromi1", + 214 => "varr4fromui2", + 215 => "varr4fromui4", + 216 => "varr4fromdec", + 217 => "varr8fromi1", + 218 => "varr8fromui2", + 219 => "varr8fromui4", + 220 => "varr8fromdec", + 221 => "vardatefromi1", + 222 => "vardatefromui2", + 223 => "vardatefromui4", + 224 => "vardatefromdec", + 225 => "varcyfromi1", + 226 => "varcyfromui2", + 227 => "varcyfromui4", + 228 => "varcyfromdec", + 229 => "varbstrfromi1", + 230 => "varbstrfromui2", + 231 => "varbstrfromui4", + 232 => "varbstrfromdec", + 233 => "varboolfromi1", + 234 => "varboolfromui2", + 235 => "varboolfromui4", + 236 => "varboolfromdec", + 237 => "varui1fromi1", + 238 => "varui1fromui2", + 239 => "varui1fromui4", + 240 => "varui1fromdec", + 241 => "vardecfromi1", + 242 => "vardecfromui2", + 243 => "vardecfromui4", + 244 => "vari1fromui1", + 245 => "vari1fromi2", + 246 => "vari1fromi4", + 247 => "vari1fromr4", + 248 => "vari1fromr8", + 249 => "vari1fromdate", + 250 => "vari1fromcy", + 251 => "vari1fromstr", + 252 => "vari1fromdisp", + 253 => "vari1frombool", + 254 => "vari1fromui2", + 255 => "vari1fromui4", + 256 => "vari1fromdec", + 257 => "varui2fromui1", + 258 => "varui2fromi2", + 259 => "varui2fromi4", + 260 => "varui2fromr4", + 261 => "varui2fromr8", + 262 => "varui2fromdate", + 263 => "varui2fromcy", + 264 => "varui2fromstr", + 265 => "varui2fromdisp", + 266 => "varui2frombool", + 267 => "varui2fromi1", + 268 => "varui2fromui4", + 269 => "varui2fromdec", + 270 => "varui4fromui1", + 271 => "varui4fromi2", + 272 => "varui4fromi4", + 273 => "varui4fromr4", + 274 => "varui4fromr8", + 275 => "varui4fromdate", + 276 => "varui4fromcy", + 277 => "varui4fromstr", + 278 => "varui4fromdisp", + 279 => "varui4frombool", + 280 => "varui4fromi1", + 281 => "varui4fromui2", + 282 => "varui4fromdec", + 283 => "bstr_usersize", + 284 => "bstr_usermarshal", + 285 => "bstr_userunmarshal", + 286 => "bstr_userfree", + 287 => "variant_usersize", + 288 => "variant_usermarshal", + 289 => "variant_userunmarshal", + 290 => "variant_userfree", + 291 => "lpsafearray_usersize", + 292 => "lpsafearray_usermarshal", + 293 => "lpsafearray_userunmarshal", + 294 => "lpsafearray_userfree", + 295 => "lpsafearray_size", + 296 => "lpsafearray_marshal", + 297 => "lpsafearray_unmarshal", + 298 => "vardeccmpr8", + 299 => "varcyadd", + 300 => "bstr_userunmarshal64", + 301 => "dllcanunloadnow", + 302 => "dllgetclassobject", + 303 => "varcymul", + 304 => "varcymuli4", + 305 => "varcysub", + 306 => "varcyabs", + 307 => "varcyfix", + 308 => "varcyint", + 309 => "varcyneg", + 310 => "varcyround", + 311 => "varcycmp", + 312 => "varcycmpr8", + 313 => "varbstrcat", + 314 => "varbstrcmp", + 315 => "varr8pow", + 316 => "varr4cmpr8", + 317 => "varr8round", + 318 => "varcat", + 319 => "vardatefromudateex", + 320 => "dllregisterserver", + 321 => "dllunregisterserver", + 322 => "getrecordinfofromguids", + 323 => "getrecordinfofromtypeinfo", + 324 => "lpsafearray_userfree64", + 325 => "setvarconversionlocalesetting", + 326 => "getvarconversionlocalesetting", + 327 => "setoanocache", + 328 => "lpsafearray_usermarshal64", + 329 => "varcymuli8", + 330 => "vardatefromudate", + 331 => "varudatefromdate", + 332 => "getaltmonthnames", + 333 => "vari8fromui1", + 334 => "vari8fromi2", + 335 => "vari8fromr4", + 336 => "vari8fromr8", + 337 => "vari8fromcy", + 338 => "vari8fromdate", + 339 => "vari8fromstr", + 340 => "vari8fromdisp", + 341 => "vari8frombool", + 342 => "vari8fromi1", + 343 => "vari8fromui2", + 344 => "vari8fromui4", + 345 => "vari8fromdec", + 346 => "vari2fromi8", + 347 => "vari2fromui8", + 348 => "vari4fromi8", + 349 => "vari4fromui8", + 350 => "lpsafearray_usersize64", + 351 => "lpsafearray_userunmarshal64", + 352 => "oacreatetypelib2", + 353 => "safearrayaddref", + 354 => "safearrayreleasedata", + 355 => "safearrayreleasedescriptor", + 356 => "sysaddrefstring", + 357 => "sysreleasestring", + 358 => "variant_userfree64", + 359 => "variant_usermarshal64", + 360 => "varr4fromi8", + 361 => "varr4fromui8", + 362 => "varr8fromi8", + 363 => "varr8fromui8", + 364 => "vardatefromi8", + 365 => "vardatefromui8", + 366 => "varcyfromi8", + 367 => "varcyfromui8", + 368 => "varbstrfromi8", + 369 => "varbstrfromui8", + 370 => "varboolfromi8", + 371 => "varboolfromui8", + 372 => "varui1fromi8", + 373 => "varui1fromui8", + 374 => "vardecfromi8", + 375 => "vardecfromui8", + 376 => "vari1fromi8", + 377 => "vari1fromui8", + 378 => "varui2fromi8", + 379 => "varui2fromui8", + 380 => "variant_usersize64", + 381 => "variant_userunmarshal64", + 401 => "oleloadpictureex", + 402 => "oleloadpicturefileex", + 411 => "safearraycreatevector", + 412 => "safearraycopydata", + 413 => "vectorfrombstr", + 414 => "bstrfromvector", + 415 => "oleicontocursor", + 416 => "olecreatepropertyframeindirect", + 417 => "olecreatepropertyframe", + 418 => "oleloadpicture", + 419 => "olecreatepictureindirect", + 420 => "olecreatefontindirect", + 421 => "oletranslatecolor", + 422 => "oleloadpicturefile", + 423 => "olesavepicturefile", + 424 => "oleloadpicturepath", + 425 => "varui4fromi8", + 426 => "varui4fromui8", + 427 => "vari8fromui8", + 428 => "varui8fromi8", + 429 => "varui8fromui1", + 430 => "varui8fromi2", + 431 => "varui8fromr4", + 432 => "varui8fromr8", + 433 => "varui8fromcy", + 434 => "varui8fromdate", + 435 => "varui8fromstr", + 436 => "varui8fromdisp", + 437 => "varui8frombool", + 438 => "varui8fromi1", + 439 => "varui8fromui2", + 440 => "varui8fromui4", + 441 => "varui8fromdec", + 442 => "registertypelibforuser", + 443 => "unregistertypelibforuser", + 444 => "oaenableperusertlibregistration", + 445 => "hwnd_userfree", + 446 => "hwnd_usermarshal", + 447 => "hwnd_usersize", + 448 => "hwnd_userunmarshal", + 449 => "hwnd_userfree64", + 450 => "hwnd_usermarshal64", + 451 => "hwnd_usersize64", + 452 => "hwnd_userunmarshal64", + 500 => "oacleanup", + _ => return None, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn resolves_special_library_ordinals_like_pefile() { + // Known pefile ordlookup values, lowercased for the imphash. + assert_eq!(special_ordinal_name("ws2_32.dll", 1), Some("accept")); + assert_eq!(special_ordinal_name("ws2_32.dll", 115), Some("wsastartup")); + assert_eq!( + special_ordinal_name("oleaut32.dll", 2), + Some("sysallocstring") + ); + } + + #[test] + fn lookup_key_is_case_insensitive_only_via_lowercased_input() { + // Caller passes an already-lowercased name; a non-lowercased key must miss. + assert_eq!(special_ordinal_name("WS2_32.DLL", 1), None); + } + + #[test] + fn non_special_libraries_and_unlisted_ordinals_return_none() { + assert_eq!(special_ordinal_name("kernel32.dll", 5), None); // not a special lib + assert_eq!(special_ordinal_name("ws2_32.dll", 60000), None); // ordinal not in table + } +} diff --git a/src/pe.rs b/src/pe.rs index bd63552..2a56280 100644 --- a/src/pe.rs +++ b/src/pe.rs @@ -61,18 +61,23 @@ pub fn parse_pe(data: &[u8], file_path: &str) -> Result { let dll_lower = import.dll.to_lowercase(); let lib_base = strip_lib_ext(&dll_lower).to_string(); // goblin represents an ordinal-only import with a SYNTHESIZED name - // ("ORDINAL "), not an empty one, so detect both forms. OffsetInspect - // renders these as "ord" (used verbatim in the imphash input). + // ("ORDINAL "), not an empty one, so detect both forms. For ordinals imported + // from ws2_32/wsock32/oleaut32, resolve to the real function name exactly as + // pefile/VirusTotal do (so the imphash correlates with threat intel); any other + // ordinal renders "ord". The resolved name is lowercased in the table, so the + // imphash and display strings are identical. let by_ordinal = import.name.is_empty() || import.name.starts_with("ORDINAL "); let func = if by_ordinal { - format!("ord{}", import.ordinal) + crate::ordinals::special_ordinal_name(&dll_lower, import.ordinal) + .map(str::to_string) + .unwrap_or_else(|| format!("ord{}", import.ordinal)) } else { import.name.to_lowercase() }; imphash_entries.push(format!("{lib_base}.{func}")); let func_display = if by_ordinal { - format!("ord{}", import.ordinal) + func.clone() } else { import.name.to_string() };