Skip to content

Instantly share code, notes, and snippets.

@mesaleh
Created April 18, 2015 02:48
Show Gist options
  • Save mesaleh/076871f4fb80e7530feb to your computer and use it in GitHub Desktop.
Save mesaleh/076871f4fb80e7530feb to your computer and use it in GitHub Desktop.
template <class T> // T: PIMAGE_THUNK_DATA64 or PIMAGE_THUNK_DATA32
vector<string> PE::getModuleAPIs(T pThunk, PIMAGE_SECTION_HEADER IT)
{
vector<string> APIs;
// check if IMAGE_THUNK_DATA is within the section of Import directory, otherwise, most likely the file is packed or manualy manipulated.
if (((DWORD)pThunk < ((DWORD)LoadAddr + IT->PointerToRawData)) || ((DWORD)pThunk >((DWORD)LoadAddr + IT->PointerToRawData + IT->SizeOfRawData))) {
Suspicious |= SUSPICIOUS_IMPORTS;
}
// check if IMAGE_THUNK_DATA points out of file boundaries.
if (((DWORD)pThunk < ((DWORD)LoadAddr)) || ( ((DWORD)pThunk + sizeof(*pThunk)) > ((DWORD)LoadAddr + FileSize)) ) {
Suspicious |= CORRUPTED_IMPORTS;
return APIs;
}
ULONGLONG iIMAGE_ORDINAL_FLAG;
if(isPE64())
iIMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG64;
else
iIMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG32;
if(pThunk->u1.Ordinal & iIMAGE_ORDINAL_FLAG) fImportByOrdinal = true;
while(pThunk->u1.Ordinal)
{
string API;
// if import by name
if(!(pThunk->u1.Ordinal & iIMAGE_ORDINAL_FLAG)) {
// Yup, ApiNameOffset is DWORD, 32bit, for both 32bit and 64bit executables, assuming we've not yet seen an 64bit executable > 4GB.
DWORD ApiNameOffset = getOffsetFromRva(pThunk->u1.AddressOfData) + FIELD_OFFSET(IMAGE_IMPORT_BY_NAME, Name);
// within file boundaries ?
if (ApiNameOffset > FileSize) {
Suspicious |= CORRUPTED_IMPORTS;
}
else {
DWORD i = ApiNameOffset;
while (i < FileSize && LoadAddr[i] != 0 && (i - ApiNameOffset < MAX_API_NAME)) i++; // There is no unallowed chars for API name.
/*
* There are three cases here:
*
* 1- If the size = MAX_API_NAME, Win loader's RtlInitString() will take the first MAX_API_NAME name regardless of the "real" size. That would be the API that will be looked for.
* 2- If the Name was shorter that MAX_API_NAME but passes the file size, most likely the memory location at the offset "file size"
* will be 0, so the loader will read the zero and terminates the string.
* Unless in very rare condition that the file ends exactly at the boundary of a memory page and accessing next page will fire an exception.
* For those two cases, we'll get the string up until the boundary, MAX_API_NAME or FileSize.
* 3- The file we're scanning is a good file that respects itself and has a normal API name, which is a case we don't usually encounter when dealing with malware :)
*/
if ((i >= FileSize) || (i - ApiNameOffset >= MAX_API_NAME))
Suspicious |= SUSPICIOUS_IMPORTS;
API = string((char*) &LoadAddr[ApiNameOffset], i - ApiNameOffset);
}
}
// else if import by ordinal
else {
int n = pThunk->u1.Ordinal & 0x00FF; // get ordinal number
API = "Ord(" + numToStr(n) + ")";
}
APIs.push_back(API);
pThunk++;
}
return APIs;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment