/Rainman2/win32pe.cpp

http://modstudio2.googlecode.com/ · C++ · 616 lines · 486 code · 91 blank · 39 comment · 78 complexity · 5d5d5604d69da8e9930110aab4b4a7b7 MD5 · raw file

  1. /*
  2. Copyright (c) 2008 Peter "Corsix" Cawley
  3. Permission is hereby granted, free of charge, to any person
  4. obtaining a copy of this software and associated documentation
  5. files (the "Software"), to deal in the Software without
  6. restriction, including without limitation the rights to use,
  7. copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the
  9. Software is furnished to do so, subject to the following
  10. conditions:
  11. The above copyright notice and this permission notice shall be
  12. included in all copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  14. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  15. OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  16. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  17. HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  18. WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20. OTHER DEALINGS IN THE SOFTWARE.
  21. */
  22. #include "win32pe.h"
  23. #include "exception.h"
  24. #ifndef DWORD
  25. #define DWORD unsigned long
  26. #endif
  27. #ifndef WORD
  28. #define WORD unsigned short
  29. #endif
  30. Win32PEFile::Win32PEFile()
  31. {
  32. m_pRootResourceNode = 0;
  33. }
  34. Win32PEFile::~Win32PEFile()
  35. {
  36. clear();
  37. }
  38. void Win32PEFile::clear()
  39. {
  40. delete m_pRootResourceNode;
  41. m_pRootResourceNode = 0;
  42. for(std::vector<IconGroup*>::iterator itr = m_vIconGroups.begin(); itr != m_vIconGroups.end(); ++itr)
  43. delete *itr;
  44. m_vIconGroups.clear();
  45. m_vSpecialTables.clear();
  46. m_vSections.clear();
  47. m_vImportedLibraries.clear();
  48. }
  49. unsigned long Win32PEFile::_mapToPhysicalAddress(unsigned long iVirtualAddress) throw(...)
  50. {
  51. for(std::vector<Section>::iterator itr = m_vSections.begin(); itr != m_vSections.end(); ++itr)
  52. {
  53. if(itr->iVirtualAddress <= iVirtualAddress && iVirtualAddress < (itr->iVirtualAddress + itr->iVirtualSize))
  54. {
  55. if(iVirtualAddress < (itr->iVirtualAddress + itr->iPhysicalSize))
  56. return iVirtualAddress - itr->iVirtualAddress + itr->iPhysicalAddress;
  57. else
  58. THROW_SIMPLE_(L"Cannot map to physical; %X is uninitialised virtual memory in section \'%s\'", iVirtualAddress, itr->sName.getCharacters());
  59. }
  60. }
  61. THROW_SIMPLE_(L"Cannot map to physical; %X lies outside of any section", iVirtualAddress);
  62. }
  63. unsigned long Win32PEFile::_mapToPhysicalAddressNoThrow(unsigned long iVirtualAddress) throw(...)
  64. {
  65. for(std::vector<Section>::iterator itr = m_vSections.begin(); itr != m_vSections.end(); ++itr)
  66. {
  67. if(itr->iVirtualAddress <= iVirtualAddress && iVirtualAddress < (itr->iVirtualAddress + itr->iVirtualSize))
  68. {
  69. if(iVirtualAddress < (itr->iVirtualAddress + itr->iPhysicalSize))
  70. return iVirtualAddress - itr->iVirtualAddress + itr->iPhysicalAddress;
  71. else
  72. return NOT_MAPPED;
  73. }
  74. }
  75. return NOT_MAPPED;
  76. }
  77. void Win32PEFile::load(IFile *pFile) throw(...)
  78. {
  79. clear();
  80. // Locate the COFF header, and assert that the file is PE
  81. try
  82. {
  83. // "At location 0x3c, the stub has the file offset to the PE signature"
  84. pFile->seek(0x3c, SR_Start);
  85. DWORD iHeadPosition;
  86. pFile->readOne(iHeadPosition);
  87. pFile->seek(iHeadPosition, SR_Start);
  88. char cSignature[4];
  89. pFile->readArray(cSignature, 4);
  90. if(memcmp(cSignature, "PE\0\0", 4) != 0)
  91. THROW_SIMPLE(L"PE signature does not match");
  92. }
  93. CATCH_THROW_SIMPLE({}, L"Cannot locate valid PE header; is this a PE file?");
  94. // Read the information we need from the COFF file header
  95. WORD wSectionCount;
  96. try
  97. {
  98. WORD wMachineType;
  99. pFile->readOne(wMachineType);
  100. pFile->readOne(wSectionCount);
  101. pFile->seek(16, SR_Current); // COFF file header is 20 bytes, the two previous fields are 4, 20-4=16
  102. }
  103. CATCH_THROW_SIMPLE({}, L"Cannot read COFF file header");
  104. // Read the information we need from the COFF 'optional' (not for PE files) header
  105. bool bIsPE32Plus = false;
  106. DWORD dwSpecialTableCount;
  107. try
  108. {
  109. WORD wMagicValue;
  110. pFile->readOne(wMagicValue);
  111. switch(wMagicValue)
  112. {
  113. case 0x10B: // PE32 format
  114. bIsPE32Plus = false;
  115. break;
  116. case 0x20B: // PE32+ format
  117. bIsPE32Plus = true;
  118. break;
  119. default:
  120. THROW_SIMPLE_(L"Unrecognised COFF optional header magic value (%i)", static_cast<int>(wMagicValue));
  121. break;
  122. }
  123. pFile->seek(bIsPE32Plus ? 106 : 90, SR_Current); // skip everything upto the NumberOfRvaAndSizes field
  124. pFile->readOne(dwSpecialTableCount);
  125. }
  126. CATCH_THROW_SIMPLE({}, L"Cannot read COFF optional header");
  127. // Read the special table locations and sizes
  128. m_vSpecialTables.reserve(dwSpecialTableCount);
  129. for(DWORD dwTable = 0; dwTable < dwSpecialTableCount; ++dwTable)
  130. {
  131. DWORD dwVirtualAddress;
  132. DWORD dwSize;
  133. try
  134. {
  135. pFile->readOne(dwVirtualAddress);
  136. pFile->readOne(dwSize);
  137. }
  138. CATCH_THROW_SIMPLE_({}, L"Cannot read optional header data directory #%i", static_cast<int>(dwTable));
  139. m_vSpecialTables.push_back(std::make_pair(dwVirtualAddress, dwSize));
  140. }
  141. // Read the sections
  142. m_vSections.reserve(wSectionCount);
  143. for(WORD wSection = 0; wSection < wSectionCount; ++wSection)
  144. {
  145. m_vSections.push_back(Section());
  146. Section &oSection = m_vSections[wSection];
  147. char aName[9] = {0};
  148. try
  149. {
  150. pFile->readArray(aName, 8);
  151. pFile->readOne(oSection.iVirtualSize);
  152. pFile->readOne(oSection.iVirtualAddress);
  153. pFile->readOne(oSection.iPhysicalSize);
  154. pFile->readOne(oSection.iPhysicalAddress);
  155. pFile->seek(12, SR_Current); // skip a few fields
  156. pFile->readOne(oSection.iFlags);
  157. }
  158. CATCH_THROW_SIMPLE_({}, L"Cannot read section #%i information", static_cast<int>(wSection));
  159. oSection.sName = aName;
  160. }
  161. // Map the special table addresses from virtual to physical
  162. for(DWORD dwTable = 0; dwTable < dwSpecialTableCount; ++dwTable)
  163. {
  164. try
  165. {
  166. if(m_vSpecialTables[dwTable].first != 0 && m_vSpecialTables[dwTable].second != 0)
  167. m_vSpecialTables[dwTable].first = _mapToPhysicalAddressNoThrow(m_vSpecialTables[dwTable].first);
  168. }
  169. CATCH_THROW_SIMPLE_({}, L"Cannot map special table #%i from virtual to physical", static_cast<int>(dwTable));
  170. }
  171. // Load all the special tables which we are interested in
  172. switch(dwSpecialTableCount)
  173. {
  174. default:
  175. // Other tables
  176. // (all statements fall-through)
  177. case 3:
  178. // Resource table
  179. try
  180. {
  181. _loadResources(pFile);
  182. }
  183. CATCH_THROW_SIMPLE({}, L"Cannot load resources");
  184. case 2:
  185. // Import table
  186. try
  187. {
  188. _loadImports(pFile);
  189. }
  190. CATCH_THROW_SIMPLE({}, L"Cannot load imports");
  191. case 1:
  192. // Export table
  193. // (this isn't loaded yet, but might be in the future)
  194. case 0:
  195. // 0 is provided so that default is hit for everything greater than the highest index we care about
  196. break;
  197. }
  198. }
  199. void Win32PEFile::load(RainString sFile) throw(...)
  200. {
  201. IFile* pFile = 0;
  202. try
  203. {
  204. pFile = RainOpenFile(sFile, FM_Read);
  205. load(pFile);
  206. }
  207. CATCH_THROW_SIMPLE_(delete pFile, L"Cannot load PE file \'%s\'", sFile.getCharacters());
  208. delete pFile;
  209. }
  210. const std::vector<Win32PEFile::IconGroup*>& Win32PEFile::getIconGroupArray() const throw()
  211. {
  212. return m_vIconGroups;
  213. }
  214. void Win32PEFile::_loadImports(IFile *pFile)
  215. {
  216. if(m_vSpecialTables.size() <= 1 || m_vSpecialTables[1].first == 0 || m_vSpecialTables[1].first == NOT_MAPPED)
  217. return;
  218. try
  219. {
  220. pFile->seek(m_vSpecialTables[1].first, SR_Start);
  221. }
  222. CATCH_THROW_SIMPLE({}, L"Cannot seek to imports table");
  223. std::vector<DWORD> vLibraryNameAddresses;
  224. for(int iLib = 0;; ++iLib)
  225. {
  226. DWORD dwNameOffset;
  227. try
  228. {
  229. pFile->seek(12, SR_Current);
  230. pFile->readOne(dwNameOffset);
  231. pFile->seek(4, SR_Current);
  232. }
  233. CATCH_THROW_SIMPLE_({}, L"Cannot load import details for library #%i", iLib);
  234. if(dwNameOffset == 0)
  235. break;
  236. else
  237. vLibraryNameAddresses.push_back(dwNameOffset);
  238. }
  239. for(std::vector<DWORD>::iterator itr = vLibraryNameAddresses.begin(); itr != vLibraryNameAddresses.end(); ++itr)
  240. {
  241. try
  242. {
  243. pFile->seek(_mapToPhysicalAddress(*itr), SR_Start);
  244. m_vImportedLibraries.push_back(pFile->readString<char>(0));
  245. }
  246. CATCH_THROW_SIMPLE({}, L"Cannot read library name");
  247. }
  248. }
  249. void Win32PEFile::_loadResources(IFile *pFile)
  250. {
  251. if(m_vSpecialTables.size() <= 2 || m_vSpecialTables[2].first == 0 || m_vSpecialTables[2].first == NOT_MAPPED)
  252. return;
  253. try
  254. {
  255. pFile->seek(m_vSpecialTables[2].first, SR_Start);
  256. }
  257. CATCH_THROW_SIMPLE({}, L"Cannot seek to resources table");
  258. m_pRootResourceNode = new ResourceNode;
  259. _loadResourceTable(pFile, m_pRootResourceNode->m_vChildren);
  260. const ResourceNode *pIconGroups = findResource(14);
  261. if(pIconGroups)
  262. {
  263. for(ResourceNode::Children::const_iterator itrGrp = pIconGroups->getChildren().begin(), endGrp = pIconGroups->getChildren().end(); itrGrp != endGrp; ++itrGrp)
  264. {
  265. IconGroup *pGroup = new IconGroup;
  266. m_vIconGroups.push_back(pGroup);
  267. const ResourceNode *pData = (*itrGrp)->findFirstChildWithData();
  268. if(pData)
  269. {
  270. pFile->seek(pData->getDataAddress(), SR_Start);
  271. _loadIconGroup(pFile, pGroup);
  272. }
  273. }
  274. }
  275. }
  276. void Win32PEFile::_loadResourceTable(IFile *pFile, Win32PEFile::ResourceNode::Children& vDestination)
  277. {
  278. WORD wNamedChildrenCount;
  279. WORD wIdentifiedChildrenCount;
  280. try
  281. {
  282. pFile->seek(12, SR_Current); // skip characteristics, timestamp and version
  283. pFile->readOne(wNamedChildrenCount);
  284. pFile->readOne(wIdentifiedChildrenCount);
  285. }
  286. CATCH_THROW_SIMPLE({}, L"Cannot read resource table header");
  287. DWORD dwChildrenCount, dwNamedCutoff;
  288. dwChildrenCount = static_cast<DWORD>(wNamedChildrenCount) + static_cast<DWORD>(wIdentifiedChildrenCount);
  289. dwNamedCutoff = wNamedChildrenCount;
  290. for(DWORD dwChildIndex = 0; dwChildIndex < dwChildrenCount; ++dwChildIndex)
  291. {
  292. union
  293. {
  294. unsigned long iNameOffset;
  295. unsigned long iIdentifier;
  296. };
  297. union
  298. {
  299. unsigned long iDataInfoAddress;
  300. unsigned long iChildrenAddress;
  301. };
  302. try
  303. {
  304. pFile->readOne(iNameOffset);
  305. pFile->readOne(iDataInfoAddress);
  306. }
  307. CATCH_THROW_SIMPLE_({}, L"Cannot read table entry #%lu", dwChildIndex);
  308. ResourceNode *pNewNode = CHECK_ALLOCATION(new (std::nothrow) ResourceNode);
  309. vDestination.push_back(pNewNode);
  310. if(dwChildIndex < dwNamedCutoff)
  311. {
  312. pNewNode->m_bHasName = true;
  313. pNewNode->m_iIdentifier = m_vSpecialTables[2].first + (iNameOffset & ~(1 << 31));
  314. }
  315. else
  316. {
  317. pNewNode->m_bHasIdentifer = true;
  318. pNewNode->m_iIdentifier = iIdentifier;
  319. }
  320. if(iChildrenAddress & (1 << 31))
  321. {
  322. pNewNode->m_bHasChildren = true;
  323. pNewNode->m_iDataOffset = iChildrenAddress & ~(1 << 31);
  324. }
  325. else
  326. {
  327. pNewNode->m_bHasData = true;
  328. pNewNode->m_iDataOffset = iDataInfoAddress;
  329. }
  330. pNewNode->m_iDataOffset += m_vSpecialTables[2].first;
  331. }
  332. for(DWORD dwChildIndex = 0; dwChildIndex < dwChildrenCount; ++dwChildIndex)
  333. {
  334. ResourceNode *pNode = vDestination[dwChildIndex];
  335. if(pNode->m_bHasName)
  336. {
  337. try
  338. {
  339. pFile->seek(pNode->m_iIdentifier, SR_Start);
  340. pNode->m_iIdentifier = 0;
  341. WORD wLength;
  342. pFile->readOne(wLength);
  343. pNode->m_sName = RainString(L"\0", 1).repeat(wLength);
  344. pFile->readArray(pNode->m_sName.begin(), wLength);
  345. }
  346. CATCH_THROW_SIMPLE_({}, L"Cannot read name of table entry #%lu", dwChildIndex);
  347. }
  348. if(pNode->m_bHasChildren)
  349. {
  350. try
  351. {
  352. pFile->seek(pNode->m_iDataOffset, SR_Start);
  353. pNode->m_iDataOffset = 0;
  354. _loadResourceTable(pFile, pNode->m_vChildren);
  355. }
  356. CATCH_THROW_SIMPLE_({}, L"Cannot read children of table entry #%lu", dwChildIndex);
  357. for(ResourceNode::Children::iterator itr = pNode->m_vChildren.begin(), end = pNode->m_vChildren.end(); itr != end; ++itr)
  358. {
  359. (**itr).m_pParent = pNode;
  360. }
  361. }
  362. if(pNode->m_bHasData)
  363. {
  364. try
  365. {
  366. pFile->seek(pNode->m_iDataOffset, SR_Start);
  367. pFile->readOne(pNode->m_iDataOffset);
  368. pFile->readOne(pNode->m_iDataLength);
  369. pFile->readOne(pNode->m_iDataCodepage);
  370. }
  371. CATCH_THROW_SIMPLE_({}, L"Cannot read data information of table entry #%lu", dwChildIndex);
  372. pNode->m_iDataOffset = _mapToPhysicalAddressNoThrow(pNode->m_iDataOffset);
  373. }
  374. }
  375. }
  376. void Win32PEFile::_loadIconGroup(IFile *pFile, Win32PEFile::IconGroup *pDestination)
  377. {
  378. WORD wIconCount;
  379. pFile->seek(4, SR_Current);
  380. pFile->readOne(wIconCount);
  381. for(WORD wIcon = 0; wIcon < wIconCount; ++wIcon)
  382. {
  383. Icon oIcon;
  384. pFile->readOne(oIcon.iWidth);
  385. pFile->readOne(oIcon.iHeight);
  386. pFile->readOne(oIcon.iColourCount);
  387. pFile->readOne(oIcon.iReserved);
  388. pFile->readOne(oIcon.iNumPlanes);
  389. pFile->readOne(oIcon.iBitsPerPixel);
  390. pFile->readOne(oIcon.iSize);
  391. pFile->readOne(oIcon.iIndex);
  392. const ResourceNode *pNode = findResource(3, oIcon.iIndex)->findFirstChildWithData();
  393. if(pNode)
  394. oIcon.iOffset = pNode->getDataAddress();
  395. pDestination->push_back(oIcon);
  396. }
  397. }
  398. const Win32PEFile::ResourceNode* Win32PEFile::getResources() const throw()
  399. {
  400. return m_pRootResourceNode;
  401. }
  402. Win32PEFile::ResourceNode::ResourceNode()
  403. {
  404. m_iIdentifier = 0;
  405. m_iDataOffset = 0;
  406. m_iDataLength = 0;
  407. m_iDataCodepage = 0;
  408. m_pParent = 0;
  409. m_bHasName = false;
  410. m_bHasIdentifer = false;
  411. m_bHasData = false;
  412. m_bHasChildren = false;
  413. }
  414. Win32PEFile::ResourceNode::~ResourceNode()
  415. {
  416. for(Children::iterator itr = m_vChildren.begin(), end = m_vChildren.end(); itr != end; ++itr)
  417. {
  418. delete *itr;
  419. }
  420. }
  421. const Win32PEFile::ResourceNode* Win32PEFile::ResourceNode::findFirstChildWithData() const
  422. {
  423. if(this == 0 || m_bHasData)
  424. return this;
  425. const ResourceNode *pReturnValue = 0;
  426. for(Children::const_iterator itr = m_vChildren.begin(), end = m_vChildren.end(); pReturnValue == 0 && itr != end; ++itr)
  427. {
  428. pReturnValue = (*itr)->findFirstChildWithData();
  429. }
  430. return pReturnValue;
  431. }
  432. const Win32PEFile::ResourceNode* Win32PEFile::ResourceNode::findChild(ResourceNodeName oName) const
  433. {
  434. if(this == 0)
  435. return 0;
  436. Children::const_iterator itr = std::find(m_vChildren.begin(), m_vChildren.end(), oName);
  437. if(itr == m_vChildren.end())
  438. return 0;
  439. else
  440. return *itr;
  441. }
  442. const Win32PEFile::ResourceNode* Win32PEFile::findResource(ResourceNodeName oType) const throw()
  443. {
  444. return getResources()->findChild(oType);
  445. }
  446. const Win32PEFile::ResourceNode* Win32PEFile::findResource(ResourceNodeName oType, ResourceNodeName oName) const throw()
  447. {
  448. return findResource(oType)->findChild(oName);
  449. }
  450. const Win32PEFile::ResourceNode* Win32PEFile::findResource(ResourceNodeName oType, ResourceNodeName oName, ResourceNodeName oLanguage) const throw()
  451. {
  452. return findResource(oType, oName)->findChild(oLanguage);
  453. }
  454. Win32PEFile::ResourceNodeName::ResourceNodeName(const Win32PEFile::ResourceNodeName& o)
  455. : m_sName(o.m_sName), m_iIdentifier(o.m_iIdentifier), m_bUsesName(o.m_bUsesName)
  456. {
  457. }
  458. Win32PEFile::ResourceNodeName::ResourceNodeName(RainString sName)
  459. : m_sName(sName), m_bUsesName(true)
  460. {
  461. }
  462. Win32PEFile::ResourceNodeName::ResourceNodeName(unsigned long iIdentifier)
  463. : m_iIdentifier(iIdentifier), m_bUsesName(false)
  464. {
  465. }
  466. Win32PEFile::Icon::Icon()
  467. {
  468. iWidth = 0;
  469. iHeight = 0;
  470. iColourCount = 0;
  471. iReserved = 0;
  472. iNumPlanes = 0;
  473. iBitsPerPixel = 0;
  474. iIndex = 0;
  475. iSize = 0;
  476. iOffset = Win32PEFile::NOT_MAPPED;
  477. }
  478. bool operator == (const Win32PEFile::ResourceNodeName& a, const Win32PEFile::ResourceNode& b)
  479. {
  480. if(a.hasName())
  481. return b.hasName() && a.getName() == b.getName();
  482. else
  483. return b.hasIdentifier() && a.getIdentifier() == b.getIdentifier();
  484. }
  485. bool operator == (const Win32PEFile::ResourceNodeName& a, const Win32PEFile::ResourceNode* b)
  486. {
  487. return a == *b;
  488. }
  489. bool operator == (const Win32PEFile::ResourceNode& a, const Win32PEFile::ResourceNodeName& b)
  490. {
  491. return b == a;
  492. }
  493. bool operator == (const Win32PEFile::ResourceNode* a, const Win32PEFile::ResourceNodeName& b)
  494. {
  495. return b == a;
  496. }
  497. size_t Win32PEFile::getSectionCount() const throw()
  498. {
  499. return getSectionArray().size();
  500. }
  501. const Win32PEFile::Section& Win32PEFile::getSection(size_t iIndex) const throw(...)
  502. {
  503. CHECK_RANGE_LTMAX(0, iIndex, getSectionCount());
  504. return getSectionArray()[iIndex];
  505. }
  506. const std::vector<Win32PEFile::Section>& Win32PEFile::getSectionArray() const throw()
  507. {
  508. return m_vSections;
  509. }
  510. size_t Win32PEFile::getImportedLibraryCount() const throw()
  511. {
  512. return getImportedLibraryArray().size();
  513. }
  514. const RainString& Win32PEFile::getImportedLibrary(size_t iIndex) const throw(...)
  515. {
  516. CHECK_RANGE_LTMAX(0, iIndex, getImportedLibraryCount());
  517. return getImportedLibraryArray()[iIndex];
  518. }
  519. const std::vector<RainString>& Win32PEFile::getImportedLibraryArray() const throw()
  520. {
  521. return m_vImportedLibraries;
  522. }