├── .gitattributes ├── .gitignore ├── MalwareResourceScanner_Console.sln ├── MalwareResourceScanner_Console ├── MalwareResourceScanner_Console.vcxproj ├── MalwareResourceScanner_Console.vcxproj.filters ├── MalwareResourceScanner_Console.vcxproj.user ├── ResourceParser.cpp ├── ResourceParser.h └── main.cpp ├── README.md ├── tests ├── notepad32.exe_ ├── notepad64.exe_ ├── xored1 ├── xored2 ├── xored3 ├── xored4 └── xored5 └── tool └── MalwareResourceScanner_Console.exe /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # ========================= 18 | # Operating System Files 19 | # ========================= 20 | 21 | # OSX 22 | # ========================= 23 | 24 | .DS_Store 25 | .AppleDouble 26 | .LSOverride 27 | 28 | # Icon must ends with two \r. 29 | Icon 30 | 31 | # Visual Studio 32 | Debug 33 | *.sdf 34 | *.opensdf 35 | *.tlog 36 | *.obj 37 | *.idb 38 | *.pdb 39 | *.log 40 | *.lastbuildstate 41 | *.*ilk 42 | *.manifest 43 | *.ipch 44 | 45 | 46 | # Thumbnails 47 | ._* 48 | 49 | # Files that might appear on external disk 50 | .Spotlight-V100 51 | .Trashes 52 | -------------------------------------------------------------------------------- /MalwareResourceScanner_Console.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MalwareResourceScanner_Console", "MalwareResourceScanner_Console\MalwareResourceScanner_Console.vcxproj", "{744FE857-7506-4EBE-8E6A-0FED2D119C78}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {744FE857-7506-4EBE-8E6A-0FED2D119C78}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {744FE857-7506-4EBE-8E6A-0FED2D119C78}.Debug|Win32.Build.0 = Debug|Win32 14 | {744FE857-7506-4EBE-8E6A-0FED2D119C78}.Release|Win32.ActiveCfg = Release|Win32 15 | {744FE857-7506-4EBE-8E6A-0FED2D119C78}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /MalwareResourceScanner_Console/MalwareResourceScanner_Console.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {744FE857-7506-4EBE-8E6A-0FED2D119C78} 23 | Win32Proj 24 | MalwareResourceScanner_Console 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | v100 32 | 33 | 34 | Application 35 | true 36 | Unicode 37 | v110 38 | 39 | 40 | Application 41 | false 42 | true 43 | Unicode 44 | v100 45 | 46 | 47 | Application 48 | false 49 | true 50 | Unicode 51 | v110 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | 72 | 73 | true 74 | 75 | 76 | false 77 | 78 | 79 | false 80 | 81 | 82 | 83 | 84 | 85 | Level3 86 | Disabled 87 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 88 | MultiThreadedDebug 89 | 90 | 91 | Console 92 | true 93 | 94 | 95 | 96 | 97 | 98 | 99 | Level3 100 | Disabled 101 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 102 | 103 | 104 | Console 105 | true 106 | 107 | 108 | 109 | 110 | 111 | 112 | Level3 113 | 114 | 115 | MaxSpeed 116 | true 117 | true 118 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 119 | MultiThreaded 120 | 121 | 122 | Console 123 | true 124 | true 125 | true 126 | 127 | 128 | 129 | 130 | Level3 131 | 132 | 133 | MaxSpeed 134 | true 135 | true 136 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 137 | 138 | 139 | Console 140 | true 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /MalwareResourceScanner_Console/MalwareResourceScanner_Console.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | -------------------------------------------------------------------------------- /MalwareResourceScanner_Console/MalwareResourceScanner_Console.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -d d:\hello\ 5 | WindowsLocalDebugger 6 | 7 | 8 | -unpack -scannonpefiles -d c:\samples\ 9 | WindowsLocalDebugger 10 | 11 | 12 | -v d:\xored1 13 | WindowsLocalDebugger 14 | 15 | -------------------------------------------------------------------------------- /MalwareResourceScanner_Console/ResourceParser.cpp: -------------------------------------------------------------------------------- 1 | #include "ResourceParser.h" 2 | 3 | BOOL CResourceParser::LoadFile( LPCWSTR szFileName ) 4 | { 5 | DWORD dwNumberOfBytesRead; 6 | HANDLE hFile; 7 | BOOL fResult; 8 | 9 | if (!szFileName) 10 | return FALSE; 11 | 12 | // 13 | // remove old buffer 14 | // 15 | if (m_pBuffer) 16 | { 17 | delete[] m_pBuffer; 18 | m_pBuffer = NULL; 19 | } 20 | 21 | fResult = FALSE; 22 | 23 | // 24 | // open file for read 25 | // 26 | hFile = CreateFileW(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 27 | if ( hFile != INVALID_HANDLE_VALUE ) 28 | { 29 | m_dwFileSize = GetFileSize(hFile, NULL); 30 | if ( m_dwFileSize != INVALID_FILE_SIZE ) 31 | { 32 | // 33 | // allocate new buffer and read the file into our buffer 34 | // 35 | m_pBuffer = new BYTE[ m_dwFileSize ]; 36 | SetFilePointer(hFile, 0, NULL, FILE_BEGIN); 37 | if ( !ReadFile(hFile, m_pBuffer, m_dwFileSize, &dwNumberOfBytesRead, NULL) || m_dwFileSize != dwNumberOfBytesRead ) 38 | { 39 | // 40 | // something failed, remove the buffer and set it to NULL 41 | // 42 | delete[] m_pBuffer; 43 | m_pBuffer = NULL; 44 | } 45 | else 46 | { 47 | // 48 | // file got successful read into the buffer 49 | // now check if its a valid PE and then read it into the buffer 50 | // 51 | wcscpy_s( m_szFileName, MAX_PATH, szFileName ); 52 | if ( IsValidPEInBuffer(m_pBuffer, m_dwFileSize) ) 53 | { 54 | fResult = ReadHeaders(); 55 | } 56 | } 57 | } 58 | CloseHandle( hFile ); 59 | } 60 | return fResult; 61 | } 62 | 63 | BOOL CResourceParser::ReadHeaders() 64 | { 65 | m_DosHeader = (PIMAGE_DOS_HEADER)m_pBuffer; 66 | if ( m_DosHeader && m_DosHeader->e_magic == IMAGE_DOS_SIGNATURE ) 67 | { 68 | m_NtHeader = (PIMAGE_NT_HEADERS32)( (char*)m_pBuffer + m_DosHeader->e_lfanew ); 69 | m_NtHeader64 = (PIMAGE_NT_HEADERS64)m_NtHeader; 70 | if ( m_NtHeader && m_NtHeader->Signature == IMAGE_NT_SIGNATURE ) 71 | { 72 | m_fPePlus = m_NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC; 73 | return TRUE; 74 | } 75 | } 76 | return FALSE; 77 | } 78 | 79 | BOOL CResourceParser::IsValidPEInBuffer( const BYTE* pBuffer, DWORD dwSize ) 80 | { 81 | PIMAGE_DOS_HEADER DosHeader; 82 | PIMAGE_NT_HEADERS32 NtHeader; 83 | PIMAGE_NT_HEADERS64 NtHeader64; 84 | DWORD64 dwTemp; 85 | 86 | if ( pBuffer == NULL || dwSize == 0 ) 87 | return FALSE; 88 | 89 | if ( dwSize < (sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER)) ) // test if there are enough bytes for the check 90 | return FALSE; 91 | 92 | __try 93 | { 94 | DosHeader = (PIMAGE_DOS_HEADER)pBuffer; 95 | if ( DosHeader->e_magic == IMAGE_DOS_SIGNATURE ) 96 | { 97 | if ( (DWORD)DosHeader->e_lfanew >= dwSize ) 98 | return FALSE; 99 | 100 | NtHeader = (PIMAGE_NT_HEADERS) ( (char*)pBuffer + DosHeader->e_lfanew ); 101 | NtHeader64 = (PIMAGE_NT_HEADERS64)(NtHeader); 102 | if ( NtHeader->Signature == IMAGE_NT_SIGNATURE ) 103 | { 104 | BOOL fPePlus = NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC; 105 | 106 | if ( fPePlus ) 107 | { 108 | // 109 | // 64 bit PE file 110 | // 111 | dwTemp = RvaToFileOffset( NtHeader64->OptionalHeader.AddressOfEntryPoint, pBuffer, NtHeader64->FileHeader.NumberOfSections, TRUE ); 112 | if ( dwTemp == (DWORD64)-1 ) 113 | return FALSE; 114 | 115 | dwTemp = RvaToFileOffset( NtHeader64->OptionalHeader.BaseOfCode, pBuffer, NtHeader64->FileHeader.NumberOfSections, TRUE ); 116 | if ( dwTemp == (DWORD64)-1 ) 117 | return FALSE; 118 | } 119 | else 120 | { 121 | // 122 | // 32 bit PE file 123 | // 124 | dwTemp = RvaToFileOffset( NtHeader->OptionalHeader.AddressOfEntryPoint, pBuffer, NtHeader->FileHeader.NumberOfSections, FALSE ); 125 | if ( dwTemp == (DWORD64)-1 ) 126 | return FALSE; 127 | 128 | dwTemp = RvaToFileOffset( NtHeader->OptionalHeader.BaseOfCode, pBuffer, NtHeader->FileHeader.NumberOfSections, FALSE ); 129 | if ( dwTemp == (DWORD64)-1 ) 130 | return FALSE; 131 | } 132 | 133 | return TRUE; 134 | } 135 | } 136 | } __except( 1 ) 137 | { 138 | // 139 | // exception 140 | // 141 | } 142 | return FALSE; 143 | } 144 | 145 | DWORD64 CResourceParser::RvaToFileOffset( DWORD64 dwRVA ) 146 | { 147 | if ( m_fPePlus ) 148 | return RvaToFileOffset(dwRVA, m_pBuffer, m_NtHeader64->FileHeader.NumberOfSections, TRUE ); // 64bit PE 149 | return RvaToFileOffset( dwRVA, m_pBuffer, m_NtHeader->FileHeader.NumberOfSections, FALSE ); // 32bit 150 | } 151 | 152 | DWORD64 CResourceParser::RvaToFileOffset( DWORD64 dwRVA, const BYTE* pBuffer, WORD wNumberOfSections, BOOL fPePlus ) 153 | { 154 | DWORD64 dwFileOffset; 155 | PIMAGE_SECTION_HEADER pSection; 156 | 157 | dwFileOffset = (DWORD64)-1; 158 | 159 | if ( pBuffer == NULL || wNumberOfSections == 0 ) 160 | return dwFileOffset; 161 | 162 | pSection = (PIMAGE_SECTION_HEADER)( (char*)pBuffer + ((IMAGE_DOS_HEADER*)(pBuffer))->e_lfanew + (fPePlus ? sizeof(IMAGE_NT_HEADERS64) : sizeof(IMAGE_NT_HEADERS32)) ); 163 | 164 | for ( WORD n = 0; n < wNumberOfSections; n++, pSection++ ) 165 | { 166 | if ( pSection && dwRVA >= pSection->VirtualAddress && dwRVA < (pSection->VirtualAddress + pSection->Misc.VirtualSize) ) 167 | { 168 | dwFileOffset = dwRVA - pSection->VirtualAddress + pSection->PointerToRawData; 169 | break; 170 | } 171 | } 172 | 173 | return dwFileOffset; 174 | } 175 | 176 | BOOL CResourceParser::ParseResources() 177 | { 178 | DWORD64 dwResourceFileOffset = 0, dwDataFileOffset = 0, dwResourceVA = 0, dwResourceSize = 0; 179 | 180 | ResourceDirectoryTable *lpResRoot, *lpResType, *lpResName; 181 | ResourceDirectoryEntry *lpEntry1, *lpEntry2, *lpEntry3; 182 | ResourceDataEntry *pData; 183 | BOOL fFound = FALSE; 184 | XRayInformation XRayInfo; 185 | 186 | UINT64 ui64Key = 0; 187 | 188 | if ( m_pBuffer == NULL || m_NtHeader == NULL || (m_fPePlus && !m_NtHeader64) ) 189 | return FALSE; 190 | 191 | // 192 | // check if we have a valid PE file with a resource section 193 | // 194 | 195 | if ( m_fPePlus ) 196 | { 197 | dwResourceVA = m_NtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; 198 | dwResourceSize = m_NtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size; 199 | } 200 | else 201 | { 202 | dwResourceVA = m_NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; 203 | dwResourceSize = m_NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size; 204 | } 205 | 206 | if ( dwResourceSize == 0 || dwResourceVA == 0 ) 207 | return FALSE; 208 | 209 | // 210 | // get resource section 211 | // 212 | dwResourceFileOffset = RvaToFileOffset( dwResourceVA ); 213 | 214 | if ( dwResourceFileOffset == (DWORD64)-1 ) 215 | return FALSE; 216 | 217 | lpResRoot = reinterpret_cast( m_pBuffer + dwResourceFileOffset ); 218 | if ( lpResRoot->dwCharacteristics != 0 ) 219 | return FALSE; 220 | 221 | // 222 | // enumerate all types 223 | // 224 | for ( WORD i = 0; i < lpResRoot->dwNumberOfIDEntries + lpResRoot->dwNumberOfNameEntries; i++) 225 | { 226 | lpEntry1 = reinterpret_cast(lpResRoot + 1) + i; 227 | if ( (lpEntry1->dwDataRva & 0x7FFFFFFF) >= dwResourceSize ) 228 | return FALSE; 229 | 230 | lpResType = reinterpret_cast(reinterpret_cast(lpResRoot) + (lpEntry1->dwDataRva & 0x7FFFFFFF)); 231 | 232 | // 233 | // call callback function 234 | // 235 | if ( m_lpCallback ) ((ResourceCallbackFunction)m_lpCallback)( static_cast(this), RM_ENUM_TYPE, (LPVOID)lpEntry1, NULL ); 236 | 237 | // 238 | // enumerate all names 239 | // 240 | for ( WORD j = 0; j < lpResType->dwNumberOfIDEntries + lpResType->dwNumberOfNameEntries; j++ ) 241 | { 242 | lpEntry2 = reinterpret_cast(lpResType + 1) + j; 243 | if ( (lpEntry2->dwDataRva & 0x7FFFFFFF) >= dwResourceSize ) 244 | return FALSE; 245 | 246 | lpResName = reinterpret_cast(reinterpret_cast(lpResRoot) + (lpEntry2->dwDataRva & 0x7FFFFFFF)); 247 | 248 | // 249 | // call callback function 250 | // 251 | if ( m_lpCallback ) ((ResourceCallbackFunction)m_lpCallback)( static_cast(this), RM_ENUM_NAME, (LPVOID)lpEntry2, NULL ); 252 | 253 | // 254 | // enumerate all languages 255 | // now we have access to the offsets of the data 256 | // 257 | for ( WORD k = 0; k < lpResName->dwNumberOfIDEntries + lpResName->dwNumberOfNameEntries; k++ ) 258 | { 259 | lpEntry3 = reinterpret_cast(lpResName +1) + k; 260 | if ( (lpEntry3->dwDataRva & 0x7FFFFFFF) >= dwResourceSize ) 261 | return FALSE; 262 | 263 | pData = reinterpret_cast(reinterpret_cast(lpResRoot) + (lpEntry3->dwDataRva & 0x7FFFFFFF)); 264 | 265 | dwDataFileOffset = RvaToFileOffset( pData->dwDataRva ); 266 | if ( dwDataFileOffset == (DWORD64)-1 ) 267 | continue; // encrypted resource? 268 | 269 | // 270 | // call callback function 271 | // 272 | if ( m_lpCallback ) ((ResourceCallbackFunction)m_lpCallback)( static_cast(this), RM_ENUM_LANG, (LPVOID)lpEntry3, &dwDataFileOffset ); 273 | 274 | // 275 | // check if data is encrypted (invalid offset) and if file size is larger than 0 bytes 276 | // 277 | if ( dwDataFileOffset && dwDataFileOffset < m_dwFileSize && pData->dwSize > 0 ) 278 | { 279 | // 280 | // try X Ray on that buffer 281 | // 282 | ui64Key = 0; 283 | 284 | if ( XRayBuffer(static_cast(m_pBuffer + dwDataFileOffset), pData->dwSize, ui64Key) ) 285 | { 286 | // 287 | // call callback function 288 | // 289 | memset( &XRayInfo, 0, sizeof(XRayInformation) ); 290 | 291 | XRayInfo.lpType = lpEntry1; 292 | XRayInfo.lpName = lpEntry2; 293 | XRayInfo.lpLanguage = lpEntry3; 294 | XRayInfo.lpDataEntry = pData; 295 | XRayInfo.dwFileOffset = dwDataFileOffset; 296 | XRayInfo.ui64EncryptionKey = ui64Key; 297 | 298 | if ( m_lpCallback ) ((ResourceCallbackFunction)m_lpCallback)( static_cast(this), RM_XRAY, (LPVOID)lpEntry3, &XRayInfo ); 299 | 300 | fFound = TRUE; 301 | } 302 | } 303 | } 304 | } 305 | } 306 | return fFound ? TRUE : FALSE; 307 | } 308 | 309 | BOOL CResourceParser::SearchEncryptionKey( BYTE* pBuffer, DWORD dwSize, UINT64 ui64SearchMask, UINT64 &ui64Key, UINT8 unKeySize ) 310 | { 311 | UINT64 ui64DecryptKey = 0; 312 | DWORD dwRevertBytes = 0; 313 | BOOL fResult = FALSE; 314 | 315 | ui64Key = 0; 316 | 317 | // 318 | // get decryption key 319 | // 320 | if (unKeySize == 4) 321 | { 322 | ui64DecryptKey = *(DWORD*)(pBuffer) ^ (DWORD)ui64SearchMask; 323 | } 324 | else if (unKeySize == 8) 325 | { 326 | ui64DecryptKey = *(UINT64*)(pBuffer) ^ (UINT64)ui64SearchMask; 327 | } 328 | 329 | // 330 | // decrypt the whole buffer 331 | // 332 | __try 333 | { 334 | for ( DWORD n = 0; n < dwSize; n += unKeySize ) 335 | { 336 | if (unKeySize == 4) 337 | { 338 | *(DWORD*)(pBuffer + n) ^= (DWORD)ui64DecryptKey; 339 | } 340 | else if ( unKeySize == 8) 341 | { 342 | *(UINT64*)(pBuffer + n) ^= (UINT64)ui64DecryptKey; 343 | } 344 | dwRevertBytes += unKeySize; 345 | } 346 | } 347 | __except( 1 ) 348 | { 349 | // error while writing in buffer 350 | } 351 | 352 | // 353 | // check if its a valid PE file ( DOS + NT Headers + offset calculation from EP ) 354 | // 355 | if ( IsValidPEInBuffer(pBuffer, dwRevertBytes) ) 356 | { 357 | ui64Key = ui64DecryptKey; 358 | fResult = TRUE; 359 | } 360 | else 361 | { 362 | // 363 | // revert 364 | // 365 | for ( DWORD n = 0; n < dwRevertBytes; n += unKeySize ) 366 | { 367 | switch ( unKeySize ) 368 | { 369 | case 4: 370 | *(DWORD*)(pBuffer + n) ^= (DWORD)ui64DecryptKey; 371 | break; 372 | case 8: 373 | *(UINT64*)(pBuffer + n) ^= (UINT64)ui64DecryptKey; 374 | break; 375 | default: 376 | break; 377 | } 378 | } 379 | } 380 | return fResult; 381 | } 382 | 383 | BOOL CResourceParser::XRayBuffer( BYTE* pBuffer, DWORD dwSize, UINT64& ui64Key ) 384 | { 385 | if ( pBuffer == NULL || dwSize == 0 ) 386 | return FALSE; 387 | 388 | // 389 | // check if its a valid PE file 390 | // 391 | if ( IsValidPEInBuffer(pBuffer, dwSize) ) 392 | return TRUE; 393 | 394 | // 395 | // Encrypt buffer 396 | // 397 | for ( unsigned int n = 0; n < sizeof(g_SearchInfoArray) / sizeof(g_SearchInfoArray[0]); n++ ) 398 | { 399 | if ( SearchEncryptionKey(pBuffer, dwSize, g_SearchInfoArray[n].ui64SearchMask, ui64Key, g_SearchInfoArray[n].unKeySize) ) 400 | return TRUE; 401 | } 402 | 403 | return FALSE; 404 | } 405 | 406 | 407 | //// 408 | //// search for entries 409 | //// lpResRoot = Root Resource Directory 410 | //// lpEntry = Current Entry 411 | //// lpSearch = Search Name or ID 412 | //// 413 | //BOOL CResourceParser::FindEntry( ResourceDirectoryTable *lpResRoot, ResourceDirectoryEntry *lpEntry, LPWSTR lpSearch ) 414 | //{ 415 | // WCHAR *lpName; 416 | // WORD nLength; 417 | // BOOL fResult = FALSE; 418 | // 419 | // // 420 | // // search by ID 421 | // // 422 | // if ( IS_INTRESOURCE( lpSearch ) ) 423 | // { 424 | // if ( IS_INTRESOURCE( lpEntry->nIntegerID ) ) 425 | // { 426 | // if ( (INT32)lpSearch == lpEntry->nIntegerID ) 427 | // { 428 | // fResult = TRUE; 429 | // } 430 | // } 431 | // } 432 | // else 433 | // { 434 | // // 435 | // // search by Name 436 | // // 437 | // if ( !IS_INTRESOURCE( lpEntry->nIntegerID ) ) 438 | // { 439 | // nLength = *(WORD*)((const char*)lpResRoot + (lpEntry->dwNameRva & 0x7FFFFFFF)); 440 | // lpName = (WCHAR*)((const char*)lpResRoot + (lpEntry->dwNameRva & 0x7FFFFFFF) + sizeof(WORD)); 441 | // 442 | // if ( _wcsnicmp( lpName, lpSearch, nLength ) == 0 ) 443 | // { 444 | // fResult = TRUE; 445 | // } 446 | // } 447 | // } 448 | // return fResult; 449 | //} 450 | // 451 | // 452 | ///* 453 | // lpType = Type of resource 454 | // lpName = Name of resource 455 | // nLanguage = Language ID, use -1 if you want to have the first language entry. 456 | //*/ 457 | //BOOL CResourceParser::FindResource( LPWSTR lpType, LPWSTR lpName, WORD wLanguage, DWORD64& dwOffset, DWORD& dwSize ) 458 | //{ 459 | // DWORD64 dwResourceFileOffset = 0, dwResourceVA = 0, dwResourceSize = 0; 460 | // 461 | // ResourceDirectoryTable *lpResRoot, *lpResType, *lpResName; 462 | // ResourceDirectoryEntry *lpEntry1, *lpEntry2, *lpEntry3; 463 | // ResourceDataEntry *pData; 464 | // 465 | // BOOL fFoundDirectory = FALSE; 466 | // 467 | // if ( !m_pBuffer || !m_NtHeader ) 468 | // return FALSE; 469 | // 470 | // // 471 | // // check if we have a valid PE file with a resource section 472 | // // 473 | // dwResourceVA = m_NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; 474 | // dwResourceSize = m_NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size; 475 | // 476 | // if ( dwResourceSize == 0 || dwResourceVA == 0 ) 477 | // return FALSE; 478 | // 479 | // // 480 | // // get resource section 481 | // // 482 | // dwResourceFileOffset = RvaToFileOffset( dwResourceVA ); 483 | // if ( dwResourceFileOffset == (DWORD64)-1 ) 484 | // return FALSE; 485 | // 486 | // lpResRoot = (ResourceDirectoryTable *)( m_pBuffer + dwResourceFileOffset ); 487 | // if ( lpResRoot->dwCharacteristics != 0 ) 488 | // return FALSE; 489 | // 490 | // // 491 | // // enumerate all types 492 | // // 493 | // for ( WORD i = 0; i < lpResRoot->dwNumberOfIDEntries + lpResRoot->dwNumberOfNameEntries; i++) 494 | // { 495 | // lpEntry1 = (ResourceDirectoryEntry *) (lpResRoot + 1) + i; 496 | // if ( (lpEntry1->dwDataRva & 0x7FFFFFFF) >= dwResourceSize ) 497 | // return FALSE; 498 | // 499 | // lpResType = (ResourceDirectoryTable *) ((const char*)lpResRoot + (lpEntry1->dwDataRva & 0x7FFFFFFF)); 500 | // if ( !lpResType ) 501 | // return FALSE; 502 | // 503 | // if ( !FindEntry( lpResRoot, lpEntry1, lpType ) ) 504 | // continue; 505 | // 506 | // // 507 | // // enumerate all names 508 | // // 509 | // for ( WORD j = 0; j < lpResType->dwNumberOfIDEntries + lpResType->dwNumberOfNameEntries; j++ ) 510 | // { 511 | // lpEntry2 = (ResourceDirectoryEntry *) (lpResType + 1) + j; 512 | // if ( !lpEntry2 || (lpEntry2->dwDataRva & 0x7FFFFFFF) >= dwResourceSize ) 513 | // return FALSE; 514 | // 515 | // lpResName = (ResourceDirectoryTable *) ((const char*)lpResRoot + (lpEntry2->dwDataRva & 0x7FFFFFFF)); 516 | // if ( !lpResName ) 517 | // return FALSE; 518 | // 519 | // if ( !FindEntry( lpResRoot, lpEntry2, lpName ) ) 520 | // continue; 521 | // 522 | // // 523 | // // enumerate all languages 524 | // // now we have access to the offsets of the data 525 | // // 526 | // for ( WORD k = 0; k < lpResName->dwNumberOfIDEntries + lpResName->dwNumberOfNameEntries; k++ ) 527 | // { 528 | // lpEntry3 = (ResourceDirectoryEntry *) (lpResName +1) + k; 529 | // if ( (lpEntry3->dwDataRva & 0x7FFFFFFF) >= dwResourceSize ) 530 | // return FALSE; 531 | // 532 | // if ( wLanguage != (WORD)-1 ) 533 | // { 534 | // if ( !FindEntry( lpResRoot, lpEntry3, (LPWSTR)wLanguage ) ) 535 | // continue; 536 | // } 537 | // 538 | // pData = (ResourceDataEntry *) ((const char*)lpResRoot + (lpEntry3->dwDataRva & 0x7FFFFFFF)); 539 | // dwOffset = RvaToFileOffset( pData->dwDataRva ); 540 | // if ( dwOffset == (DWORD64)-1 || pData->dwSize == 0 ) 541 | // return FALSE; 542 | // 543 | // dwSize = pData->dwSize; 544 | // 545 | // return TRUE; 546 | // } 547 | // } 548 | // } 549 | // return FALSE; 550 | //} 551 | // 552 | -------------------------------------------------------------------------------- /MalwareResourceScanner_Console/ResourceParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "windows.h" 3 | 4 | enum ResourceCallbackNames{ RM_ENUM_TYPE = 0, RM_ENUM_NAME, RM_ENUM_LANG, RM_XRAY, RM_ENUM_RESOURCE}; 5 | 6 | struct ResourceDirectoryTable 7 | { 8 | DWORD dwCharacteristics; 9 | DWORD dwTimeDateStamp; 10 | WORD dwMajorVersion; 11 | WORD dwMinorVersion; 12 | WORD dwNumberOfNameEntries; 13 | WORD dwNumberOfIDEntries; 14 | }; 15 | 16 | struct ResourceDirectoryEntry 17 | { 18 | union 19 | { 20 | DWORD dwNameRva; // address of the string that gives the Type, Name or Language ID entry (table level dependent) 21 | INT32 nIntegerID; // the integer that identifies the Type, Name or Language ID entry 22 | }; 23 | union 24 | { 25 | DWORD dwDataRva; // high bit = 0, address of the resource data within the resource section(leaf) 26 | DWORD dwSubDirectoryRva; // high bit = 1, the lower 31 bits are the address of another resource directory table (one level down) 27 | }; 28 | }; 29 | 30 | struct ResourceDataEntry 31 | { 32 | DWORD dwDataRva; 33 | DWORD dwSize; 34 | DWORD dwCodePage; 35 | DWORD dwReserved; 36 | }; 37 | 38 | struct XRayInformation 39 | { 40 | ResourceDataEntry *lpDataEntry; 41 | ResourceDirectoryEntry *lpType; 42 | ResourceDirectoryEntry *lpName; 43 | ResourceDirectoryEntry *lpLanguage; 44 | DWORD64 dwFileOffset; 45 | UINT64 ui64EncryptionKey; 46 | }; 47 | 48 | #define LODWORD(l) ((DWORD)(l & 0x00000000ffffffff)) 49 | #define HIDWORD(l) ((DWORD)(( l >> 32) & 0x00000000ffffffff)) 50 | 51 | typedef DWORD (WINAPI * ResourceCallbackFunction) ( LPVOID lpObject, DWORD dwMessage, LPVOID lpEntry, LPVOID lpParam1 ); 52 | 53 | 54 | struct SearchInfo 55 | { 56 | UINT64 ui64SearchMask; 57 | UINT8 unKeySize; 58 | }; 59 | 60 | const SearchInfo g_SearchInfoArray[] = 61 | { 62 | {0x00505a4d, 4}, // Delphi 63 | {0x00905a4d, 4}, // C / Assembler 64 | {0x00005a4d, 4}, 65 | {0x0000000200505a4d, 8}, 66 | {0x0000000300505a4d, 8}, 67 | {0x0000000000505a4d, 8} 68 | }; 69 | 70 | 71 | class CResourceParser 72 | { 73 | BYTE* m_pBuffer; 74 | DWORD m_dwFileSize; 75 | 76 | WCHAR m_szFileName[ MAX_PATH + 1 ]; 77 | 78 | PIMAGE_DOS_HEADER m_DosHeader; 79 | PIMAGE_NT_HEADERS32 m_NtHeader; 80 | PIMAGE_NT_HEADERS64 m_NtHeader64; 81 | BOOL m_fPePlus; 82 | BOOL m_fDumpFile; 83 | DWORD m_dwDumpNumber; 84 | 85 | ResourceCallbackFunction *m_lpCallback; 86 | 87 | private: 88 | BOOL ReadHeaders(); 89 | BOOL SearchEncryptionKey( BYTE* pBuffer, DWORD dwSize, UINT64 unSearchMask, UINT64& unKey, UINT8 unKeySize ); 90 | BOOL IsValidPEInBuffer( const BYTE* pBuffer, DWORD dwSize ); 91 | //BOOL FindEntry( ResourceDirectoryTable* lpResRoot, ResourceDirectoryEntry* pEntry, LPWSTR lpSearch ); 92 | 93 | public: 94 | CResourceParser() 95 | { 96 | m_pBuffer = NULL; 97 | m_lpCallback = NULL; 98 | m_fDumpFile = FALSE; 99 | memset( m_szFileName, 0, sizeof(m_szFileName) ); 100 | m_dwDumpNumber = 0; 101 | m_DosHeader = NULL; 102 | m_NtHeader = NULL; 103 | m_NtHeader64 = NULL; 104 | m_dwFileSize = 0; 105 | m_fPePlus = FALSE; 106 | } 107 | ~CResourceParser() { delete[] m_pBuffer; }; 108 | BOOL LoadFile( LPCWSTR szFileName ); 109 | DWORD64 RvaToFileOffset( DWORD64 dwRVA ); 110 | DWORD64 RvaToFileOffset( DWORD64 dwRVA, const BYTE* pBuffer, WORD wNumberOfSections, BOOL fPePlus ); 111 | BOOL ParseResources( ); 112 | //BOOL FindResource( LPWSTR lpType, LPWSTR lpName, WORD nLanguage, DWORD64& wOffset, DWORD& dwSize ); 113 | BOOL XRayBuffer( BYTE* pBuffer, DWORD dwSize, UINT64& unKey ); 114 | 115 | // 116 | // class 117 | // 118 | void SetCallback( ResourceCallbackFunction* lpCallback ) { m_lpCallback = lpCallback; }; 119 | BYTE* GetBuffer() { return m_pBuffer; }; 120 | DWORD GetSize() { return m_dwFileSize; }; 121 | void SetDumpFlag( BOOL fDumpFile ) { m_fDumpFile = fDumpFile; }; 122 | BOOL GetDumpFlag() const { return m_fDumpFile; }; 123 | WCHAR* GetFileName() { return m_szFileName; }; 124 | DWORD GetDumpNumber() const { return m_dwDumpNumber; }; 125 | void IncrementDumpNumber() { m_dwDumpNumber++; }; 126 | }; 127 | 128 | -------------------------------------------------------------------------------- /MalwareResourceScanner_Console/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "ResourceParser.h" 6 | 7 | //void ShowLastError() 8 | //{ 9 | // DWORD dwLastError; 10 | // LPWSTR szMsgBuffer = NULL; 11 | // 12 | // dwLastError = GetLastError(); 13 | // 14 | // FormatMessageW( 15 | // FORMAT_MESSAGE_ALLOCATE_BUFFER | 16 | // FORMAT_MESSAGE_FROM_SYSTEM | 17 | // FORMAT_MESSAGE_IGNORE_INSERTS, 18 | // NULL, 19 | // dwLastError, 20 | // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 21 | // szMsgBuffer, 22 | // 0, NULL 23 | // ); 24 | // 25 | // printf( "ERROR: %08x ( %08d )\nMESSAGE: %S\n", dwLastError, dwLastError, szMsgBuffer ); 26 | //} 27 | 28 | struct RegistryScanInfo 29 | { 30 | HKEY hKey; 31 | WCHAR *szPath; 32 | }; 33 | 34 | BOOL DumpBufferToFile( LPWSTR szName, BYTE* lpBuffer, DWORD dwSize ) 35 | { 36 | HANDLE hFile; 37 | DWORD dwNumberOfBytesWritten = 0; 38 | BOOL fResult = FALSE; 39 | 40 | hFile = CreateFileW( szName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); 41 | if ( hFile != INVALID_HANDLE_VALUE ) 42 | { 43 | if ( WriteFile( hFile, lpBuffer, dwSize, &dwNumberOfBytesWritten, NULL ) && dwNumberOfBytesWritten == dwSize ) 44 | { 45 | fResult = TRUE; 46 | } 47 | CloseHandle( hFile ); 48 | } 49 | return fResult; 50 | } 51 | 52 | DWORD WINAPI MyResourcesCallback ( LPVOID lpObject, DWORD dwMessage, LPVOID lpEntry, LPVOID lpParam1 ) 53 | { 54 | CResourceParser *pFile = static_cast(lpObject); 55 | //ResourceDirectoryEntry *pEntry = (ResourceDirectoryEntry*)lpEntry; 56 | XRayInformation *lpInformation = NULL; 57 | BYTE* pAddress = NULL; 58 | WCHAR szName[ MAX_PATH ] = { 0 }; 59 | 60 | switch ( dwMessage ) 61 | { 62 | case RM_XRAY: 63 | // 64 | // Message 3 is that we have found a PE file in the resource directory. 65 | // 66 | lpInformation = static_cast(lpParam1); 67 | 68 | pAddress = (BYTE*)(pFile->GetBuffer() + lpInformation->dwFileOffset); 69 | 70 | printf( "Filename: %S\n" 71 | " Type: 0x%08x, Name: 0x%08x, Language: 0x%08x\n" 72 | " Offset: 0x%08llx, Size: 0x%08x, Encryption Key: 0x%08llx\n\n", 73 | pFile->GetFileName(), 74 | lpInformation->lpType ? lpInformation->lpType->nIntegerID : 0, 75 | lpInformation->lpName ? lpInformation->lpName->nIntegerID : 0, 76 | lpInformation->lpLanguage ? lpInformation->lpLanguage->nIntegerID : 0, 77 | lpInformation->dwFileOffset, 78 | lpInformation->lpDataEntry ? lpInformation->lpDataEntry->dwSize : 0, 79 | lpInformation->ui64EncryptionKey); 80 | 81 | // 82 | // dump file 83 | // 84 | if ( pFile->GetDumpFlag() && lpInformation && lpInformation->lpDataEntry ) 85 | { 86 | swprintf_s( szName, MAX_PATH, L"%s_%lu.bin", pFile->GetFileName(), pFile->GetDumpNumber() ); 87 | 88 | if ( !DumpBufferToFile( szName, pAddress, lpInformation->lpDataEntry->dwSize ) ) 89 | { 90 | printf( " Can't dump to file! (Last Error : %08d d)\n\n", GetLastError() ); 91 | } 92 | else 93 | { 94 | printf( " Resource saved to: %S\n\n", szName ); 95 | pFile->IncrementDumpNumber(); 96 | } 97 | } 98 | break; 99 | } 100 | return 0; 101 | } 102 | 103 | 104 | BOOL ScanFile( CResourceParser &pFile, LPWSTR lpszFileName, BOOL fScanNonPeFiles ) 105 | { 106 | BOOL fResult = FALSE; 107 | if ( pFile.LoadFile(lpszFileName) ) 108 | { 109 | fResult = pFile.ParseResources(); 110 | } 111 | else if ( fScanNonPeFiles ) 112 | { 113 | UINT64 ui64Key = 0; 114 | pFile.LoadFile(lpszFileName); 115 | fResult = pFile.XRayBuffer( pFile.GetBuffer(), pFile.GetSize(), ui64Key ); 116 | if ( fResult ) 117 | { 118 | XRayInformation info = { 0 }; 119 | ResourceDataEntry entry = { 0 }; 120 | info.ui64EncryptionKey = ui64Key; 121 | entry.dwSize = pFile.GetSize(); 122 | info.lpDataEntry = &entry; 123 | MyResourcesCallback( (LPVOID)&pFile, RM_XRAY, NULL, (LPVOID)&info ); 124 | } 125 | } 126 | 127 | return fResult; 128 | } 129 | 130 | BOOL ScanDirectory( CResourceParser &pFile, LPWSTR lpszDirectory, BOOL fScanNonPeFiles ) 131 | { 132 | WCHAR szSearchFilter[ MAX_PATH + 1]; 133 | WCHAR szFullPath[ MAX_PATH + 1]; 134 | WIN32_FIND_DATAW fdFileData = { 0 }; 135 | HANDLE hSearch; 136 | BOOL fResult = FALSE; 137 | DWORD dwAttributes; 138 | 139 | StringCchPrintf( szSearchFilter, MAX_PATH, L"%s\\*.*", lpszDirectory, fdFileData.cFileName); 140 | 141 | hSearch = FindFirstFileW( szSearchFilter, &fdFileData ); 142 | 143 | if ( hSearch != INVALID_HANDLE_VALUE ) 144 | { 145 | do 146 | { 147 | if ( wcscmp( fdFileData.cFileName, L"." ) == 0 || wcscmp( fdFileData.cFileName, L".." ) == 0 ) 148 | continue; 149 | 150 | StringCchPrintf( szFullPath, MAX_PATH, L"%s\\%s", lpszDirectory, fdFileData.cFileName); 151 | 152 | dwAttributes = GetFileAttributesW(szFullPath); 153 | if ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ) 154 | { 155 | ScanDirectory(pFile, szFullPath, fScanNonPeFiles ); 156 | } 157 | else 158 | { 159 | ScanFile( pFile, szFullPath, fScanNonPeFiles ); 160 | } 161 | } while ( FindNextFileW( hSearch, &fdFileData ) ); 162 | } 163 | 164 | return fResult; 165 | } 166 | 167 | BOOL FileExists( LPWSTR lpszFileName ) 168 | { 169 | HANDLE hFile; 170 | 171 | hFile = CreateFileW( lpszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); 172 | if ( hFile != INVALID_HANDLE_VALUE ) 173 | { 174 | CloseHandle( hFile ); 175 | return TRUE; 176 | } 177 | return FALSE; 178 | } 179 | 180 | 181 | LPWSTR ExtractFileNameFromRegValue( LPCWSTR lpszSrc, DWORD dwSize, LPWSTR lpszDestinationPath ) 182 | { 183 | WCHAR *szPos = NULL; 184 | WCHAR *szSpace = NULL; 185 | LPWSTR lpszDestination; 186 | 187 | lpszDestination = lpszDestinationPath; 188 | 189 | if ( !lpszSrc || !lpszDestination ) 190 | return NULL; 191 | 192 | wcsncpy_s( lpszDestination, dwSize, lpszSrc, dwSize ); 193 | 194 | // 195 | // If first character is a " we will ignore it 196 | // 197 | if ( *lpszDestination == '"' ) 198 | { 199 | lpszDestination += 1; 200 | szPos = wcschr( lpszDestination, '"' ); 201 | if ( szPos ) 202 | { 203 | *szPos = '\0'; 204 | } 205 | } 206 | 207 | // 208 | // Check if path starts with rundll32 209 | // 210 | if ( _wcsnicmp( lpszDestination, L"rundll32", 8 ) == 0 ) 211 | { 212 | szPos = wcschr( lpszDestination, ' ' ); 213 | if ( szPos ) 214 | { 215 | lpszDestination = (szPos + 1); 216 | szPos = wcschr( lpszDestination, ',' ); 217 | if ( szPos ) 218 | { 219 | *szPos = '\0'; 220 | } 221 | } 222 | } 223 | 224 | // 225 | // Remove arguments after path 226 | // 227 | szPos = wcsrchr( lpszDestination, L'.' ); 228 | szSpace = wcsrchr( lpszDestination, L' ' ); 229 | if ( szSpace > szPos ) 230 | *szSpace = '\0'; 231 | 232 | // 233 | // lets see if it starts with an c:\ or something, else we will attach a C:\WINDOWS\ or C:\WINDOWS\SYSTEM32\ 234 | // 235 | if ( FileExists(lpszDestination) ) 236 | return lpszDestination; 237 | 238 | wchar_t szTemp[ MAX_PATH +1 ]; 239 | wchar_t szTempDir[ MAX_PATH +1 ]; 240 | 241 | GetWindowsDirectoryW(szTempDir, MAX_PATH ); 242 | StringCchPrintf(szTemp, MAX_PATH, L"%s\\%s", szTempDir, lpszDestination); 243 | 244 | if ( FileExists(szTemp) ) 245 | { 246 | wcsncpy_s( lpszDestinationPath, MAX_PATH, szTemp, MAX_PATH ); 247 | return lpszDestinationPath; 248 | } 249 | 250 | GetSystemDirectoryW(szTempDir, MAX_PATH ); 251 | StringCchPrintf(szTemp, MAX_PATH, L"%s\\%s", szTempDir, lpszDestination); 252 | 253 | if ( FileExists(szTemp) ) 254 | { 255 | wcsncpy_s(lpszDestinationPath, dwSize, szTemp, MAX_PATH ); 256 | return lpszDestinationPath; 257 | } 258 | 259 | return NULL; 260 | 261 | } 262 | 263 | BOOL ScanQuickRegistry( CResourceParser &pFile ) 264 | { 265 | BOOL fResult = FALSE; 266 | HKEY hReg = NULL; 267 | DWORD dwDisposion = 0; 268 | WCHAR szValueName[ MAX_PATH +1 ] = { 0 }; 269 | DWORD dwIndex = 0; 270 | DWORD dwValueNameSize = 0; 271 | DWORD dwType = 0; 272 | BYTE lpData[ 0x800 ] = { 0 }; 273 | WCHAR szDestinationFilePath[ MAX_PATH + 1 ] = { 0 }; 274 | WCHAR *lpszScanPath = NULL; 275 | DWORD dwFilePathSize = 0; 276 | DWORD dwDataSize = 0; 277 | DWORD dwResult = 0; 278 | 279 | RegistryScanInfo RegistryKeys[] = { 280 | { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run" }, 281 | { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce" }, 282 | { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices" }, 283 | { HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run" }, 284 | { HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce" }, 285 | { HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices" }, 286 | { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Userinit" }, 287 | { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Shell" }, 288 | { HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Known DLLs" }, 289 | { HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SharedTaskScheduler" } 290 | }; 291 | 292 | printf( "* Registry Quick Scan\n" ); 293 | 294 | dwDisposion = REG_OPENED_EXISTING_KEY; 295 | for ( int n = 0; n < sizeof(RegistryKeys) / sizeof(RegistryKeys[0]); n++ ) 296 | { 297 | if ( RegCreateKeyExW( RegistryKeys[n].hKey, RegistryKeys[n].szPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, NULL, &hReg, &dwDisposion ) == ERROR_SUCCESS ) 298 | { 299 | dwIndex = 0; 300 | while ( TRUE ) 301 | { 302 | dwDataSize = sizeof(lpData); 303 | dwValueNameSize = MAX_PATH; 304 | dwType = REG_SZ; 305 | dwResult = RegEnumValueW( hReg, dwIndex, szValueName, &dwValueNameSize, NULL, &dwType, lpData, &dwDataSize ); 306 | if ( dwResult == ERROR_SUCCESS ) 307 | { 308 | if ( lpData && *lpData ) 309 | { 310 | dwFilePathSize = MAX_PATH; 311 | lpszScanPath = ExtractFileNameFromRegValue( (LPCWSTR)lpData, dwFilePathSize, szDestinationFilePath ); 312 | if ( lpszScanPath ) 313 | { 314 | ScanFile( pFile, (LPWSTR)lpszScanPath, FALSE ); 315 | } 316 | } 317 | dwIndex++; 318 | } 319 | else 320 | { 321 | // 322 | // some error occurred or we have scanned all items 323 | // 324 | if ( dwResult != ERROR_NO_MORE_ITEMS ) 325 | { 326 | continue; 327 | } 328 | break; 329 | } 330 | } 331 | RegCloseKey( hReg ); 332 | } 333 | } 334 | 335 | //hRegistry = RegCreateKeyExW( HKEY_LOCAL_MACHINE, 336 | 337 | return fResult; 338 | } 339 | 340 | void ShowInfo() 341 | { 342 | printf( "Usage:\n" ); 343 | printf( "mrs.exe \n"); 344 | printf( " -d Scans directory\n"); 345 | printf( " -unpack Auto unpack found files\n" ); 346 | printf( " -quickscan Quick-Scan Windows start up programs\n" ); 347 | printf( " -scannonpefiles Try to unpack also non-PE files (like unknown file formats)\n" ); 348 | printf( "\n\n" ); 349 | } 350 | 351 | int wmain(int argc, wchar_t * argv[] ) 352 | { 353 | CResourceParser ResourceParser; 354 | BOOL fDirectory = FALSE; 355 | BOOL fInvalidParameter = FALSE; 356 | BOOL fQuickScan = FALSE; 357 | LPWSTR szScanPath = NULL; 358 | BOOL fScanNonPeFiles = FALSE; 359 | 360 | printf( "Malware Resource Scanner v0.1 by Esmid Idrizovic\n" ); 361 | printf( "27. October 2011\n\n" ); 362 | 363 | if ( argc < 2 ) 364 | { 365 | ShowInfo(); 366 | return 0; 367 | } 368 | 369 | ResourceParser.SetCallback( (ResourceCallbackFunction*)&MyResourcesCallback ); 370 | 371 | for ( int n = 1; n < argc; n++ ) 372 | { 373 | if ( _wcsicmp(argv[n], L"-unpack") == 0 ) 374 | { 375 | ResourceParser.SetDumpFlag( TRUE ); 376 | } 377 | else if ( _wcsicmp(argv[n], L"-d") == 0 ) 378 | { 379 | if ( (n + 1) < argc ) 380 | { 381 | fDirectory = TRUE; 382 | } 383 | else 384 | { 385 | fInvalidParameter = TRUE; 386 | break; 387 | } 388 | } 389 | else if ( _wcsicmp(argv[n], L"-scannonpefiles") == 0 ) 390 | { 391 | fScanNonPeFiles = TRUE; 392 | } 393 | else if ( _wcsicmp(argv[n], L"-quickscan") == 0 ) 394 | { 395 | fQuickScan = TRUE; 396 | } 397 | else 398 | { 399 | if ( argv[n] ) 400 | szScanPath = argv[n]; 401 | } 402 | } 403 | 404 | if ( fInvalidParameter || (!fQuickScan && !szScanPath) ) 405 | { 406 | ShowInfo(); 407 | } 408 | else if ( fQuickScan ) 409 | { 410 | ScanQuickRegistry( ResourceParser ); 411 | } 412 | else if ( szScanPath) 413 | { 414 | if ( fDirectory ) 415 | { 416 | ScanDirectory( ResourceParser, szScanPath, fScanNonPeFiles ); 417 | } 418 | else 419 | { 420 | ScanFile( ResourceParser, szScanPath, fScanNonPeFiles ); 421 | } 422 | } 423 | 424 | return 0; 425 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Malware Resource Scanner 2 | ======================= 3 | Malware Resource Scanner can identify PE files which are hidden in other PE file resources which are encrypted by an XOR key (up to an 8 Byte key). You can scan single files and you can also scan a directory and automatically unpack found hidden PE files. If MSR can't detect your hidden PE file then you must update the g_SearchInfoArray in ResourceParser.h. 4 | 5 | Usage 6 | ===== 7 | Malware Resource Scanner v0.1 by Esmid Idrizovic 8 | 27. October 2011 9 | 10 | Usage: 11 | mrs.exe 12 | -d Scans directory 13 | -unpack Auto unpack found files 14 | -quickscan Quick-Scan Windows start up programs 15 | -scannonpefiles Try to unpack also non-PE files (like unknown file formats) 16 | 17 | -------------------------------------------------------------------------------- /tests/notepad32.exe_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edix/MalwareResourceScanner/fa6e70e493e2a83a41a43101863513a33fd57ce3/tests/notepad32.exe_ -------------------------------------------------------------------------------- /tests/notepad64.exe_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edix/MalwareResourceScanner/fa6e70e493e2a83a41a43101863513a33fd57ce3/tests/notepad64.exe_ -------------------------------------------------------------------------------- /tests/xored1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edix/MalwareResourceScanner/fa6e70e493e2a83a41a43101863513a33fd57ce3/tests/xored1 -------------------------------------------------------------------------------- /tests/xored2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edix/MalwareResourceScanner/fa6e70e493e2a83a41a43101863513a33fd57ce3/tests/xored2 -------------------------------------------------------------------------------- /tests/xored3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edix/MalwareResourceScanner/fa6e70e493e2a83a41a43101863513a33fd57ce3/tests/xored3 -------------------------------------------------------------------------------- /tests/xored4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edix/MalwareResourceScanner/fa6e70e493e2a83a41a43101863513a33fd57ce3/tests/xored4 -------------------------------------------------------------------------------- /tests/xored5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edix/MalwareResourceScanner/fa6e70e493e2a83a41a43101863513a33fd57ce3/tests/xored5 -------------------------------------------------------------------------------- /tool/MalwareResourceScanner_Console.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edix/MalwareResourceScanner/fa6e70e493e2a83a41a43101863513a33fd57ce3/tool/MalwareResourceScanner_Console.exe --------------------------------------------------------------------------------