I've been writing quite a bit of IDC script (http://www.datarescue.com/idabase/idaclike.htm) lately to help improve the efficiency of my malware analysis work. One of the scripts I was writing recently needed to know the Virtual Address of a section in the disassembled PE file. Naturally, I looked in IDA's help file for the IDC function for this. You can imagine my surprise when I discovered that there weren't any built-in IDC functions with the word "section" in their name.

"Perhaps IDA uses a different name for 'section'," I thought, and I got all excited when I saw the IDC function SegByBase. From IDA's help file:

Code:
SegByBase
// Get segment by segment base
//      base - segment base paragraph or selector
// returns: linear address of the start of the segment
//          BADADDR - no such segment
  
long    SegByBase       (long base);            // BADADDR - no such segment
Aha! Well, now it's clear that IDA must refer to sections as "segments".

So I try using SegByBase on a few samples, and everything's going fine. Then I try running it on a sample with a dozen or so sections, and IDA's Segments subview (View --> Open subviews --> Segments) only shows two segments, namely segments 7 and 9. I had thought that maybe the other ten segments contained resources and IDA purposely chose not to load them. However, this was not the case; they were not resource sections and IDA did load them into the disassembly view... they just weren't showing up in the Segment subview. And what's more, I wasn't able to get the base addresses of them with SegByBase.

Due to the unreliability of IDA's segments, I came to the conclusion that I would just use custom IDC script to convert section indices to Virtual Addresses. I knew of pe_sections.idc, written by Atli Gudmundsson of Symantec. However, this script actually loads the headers into the .idb file, whereas I didn't want to modify the .idb. The script also does a lot more "stuff", but I just wanted something nice and simple.

So I wrote my own IDC function to do it. It assumes that the original PE file is in the same directory as the .idb file.

Code:
static GetAddressFromSection(section)
{
 auto hFile;
 auto e_lfanew;
 auto imageBase;
 auto numberOfSections;
 auto sectionRva;
 
 hFile = fopen(GetInputFile(), "rb");
 
 if (0 == hFile)
 {
  Fatal("Cannot open \"" + GetInputFile() + "\"");
 }

 // Seek to the e_lfanew field 
 if (0 != fseek(hFile, 0x3C, 0))
 {
  Fatal("Cannot seek in \"" + GetInputFile() + "\"");
 }
 
 // Read the value of e_lfanew
 e_lfanew = readlong(hFile, 0);
 
 // Seek to IMAGE_NT_HEADERS
 if (0 != fseek(hFile, e_lfanew, 0))
 {
  Fatal("Cannot seek in \"" + GetInputFile() + "\"");
 }
 
 // Read the Signature
 if (0x00004550 != readlong(hFile, 0))
 {
  Fatal("Not a valid PE file");
 }
 
 // Seek to the IMAGE_NT_HEADERS.OptionalHeader.ImageBase field
 if (0 != fseek(hFile, e_lfanew + 0x18 + 0x1C, 0))
 {
  Fatal("Cannot seek in \"" + GetInputFile() + "\"");
 }
 imageBase = readlong(hFile, 0);
 
 // Seek to the IMAGE_FILE_HEADER.NumberOfSections field
 if (0 != fseek(hFile, e_lfanew + 0x06, 0))
 {
  Fatal("Cannot seek in \"" + GetInputFile() + "\"");
 }
 
 // Read the number of sections
 numberOfSections = readshort(hFile, 0);
 if (section >= numberOfSections)
 {
  Fatal("Invalid section");
 }
 
 // Seek to the desired section
 if (0 != fseek(hFile, e_lfanew + 0xF8 + section * 0x28 + 0x0C, 0))
 {
  Fatal("Cannot seek in \"" + GetInputFile() + "\"");
 }
 
 sectionRva = readlong(hFile, 0);
 
 fclose(hFile);
 
 return imageBase + sectionRva;
}

Of course, more error-checking could be done (such as checking for the magic 'MZ' bytes at the beginning of the file), but you get the gist. Notice that it doesn't do anything "magical"... it simply follows the basic PE header structures to find the Virtual Address of the section.

[Updated January 24, 2006 - fopen(...) should be called with mode "rb", not just "r". Thanks to my officemate Aaron for catching this.]

[Comment on March 6, 2006 - The code above assumes that IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader is 0xE0. I leave it as an exercise to the reader to make this code more robust ]


http://malwareanalysis.com/CommunityServer/blogs/geffner/archive/2005/09/13/6.aspx