PageRenderTime 92ms CodeModel.GetById 18ms app.highlight 67ms RepoModel.GetById 2ms app.codeStats 0ms

/Rainman2/win32pe.cpp

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