Fonctions importées/exportées d'un executable (win32)

Description

Voici un petit programme qui permet de voir les fonctions importées et exportées d'un executable.
Pour chaque fonction on ajoute un item dans un treeview, le texte de l'item contient le nom de la fonction plus d'autres infos, comme l'addresse virtuelle relative de la fonction par raport a l'addresse basse du module ou son Ordinal.

Un item d'une fonction importée est présenté sous cette forme (exemple):
053 0x000261e8 NtReadFile
- 053 est un nombre décimal qui sert juste a compter le nombre de fonction importées par la dll,
- 0x000261e8 est l'addresse virtuelle relative de la fonction (hexadecimal),
- NtReadFile est le nom public de la fonction.

Un item d'une fonction exportée est présenté sous l'une des deux formes suivantes:
- Si la fonction est exportée (exemple):
023 0x00003c22 socket
- 023 est l'Ordinal de la fonction (décimal),
- 0x00003c22 est l'addresse virtuelle relative de la fonction,
- socket est le nom public de la fonction exportée.

- Si la fonction est forwardée (exemple):
508 HeapAlloc ---> NTDLL.RtlAllocateHeap
- 508 est l'Ordinal de la fonction exportée,
- HeapAlloc est le nom de la fonction,
- NTDLL.RtlAllocateHeap est le nom de la fonction et de sa dll vers laquelle HeapAlloc est forwardée.

Un lien avec la doc de MS sur le format PE:
http://betouchi.free.fr/doc_et_ebook/prog_win32/pe_coff_file_format.doc

Source / Exemple :


#define _WIN32_WINNT 0x0500 
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include "resource.h"
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "comdlg32.lib")

HWND hMain, hTree, hPath;
char szImagePath[260], szCurrentDirectory[260];
char szMsgError[64] = "Erreur: ";

//------------------------------------------------------------------------------------------
// DisplayError: affiche un message d'erreur dans une messagebox
//------------------------------------------------------------------------------------------
int DisplayError(char * pszText, DWORD dwError)
{
	ultoa(dwError, szMsgError + 8, 10);
	MessageBox(hMain, pszText, szMsgError, MB_ICONERROR);
return 1;
}

//------------------------------------------------------------------------------------------
// DisplayImportExport: affiche dans le treeview les fonctions importées/exportées de 
// l'executable szImagePath
//------------------------------------------------------------------------------------------
int DisplayImportExport(void)
{
	char buffer[512], *b;
	TVITEMEX tvitem;                         // structure utilisée pour modifier des items
	TVINSERTSTRUCT tvi;                      // structure utilisée pour ajouter des items
	HTREEITEM hImportItem, hExportItem;      // handle des 2 items a la racine du treeview
	HTREEITEM hFunctionItem, hForwardItem;   // handle des 2 child items de l'item hExportItem

	// on charge le fichier
	BYTE *pBase = (BYTE*) LoadLibraryEx(szImagePath, 0, DONT_RESOLVE_DLL_REFERENCES);
	if(!pBase)
	{
		DisplayError("Erreur lors du chargement du module.", GetLastError());
		return 0;
	}

	// trouve et verifie la signature du fichier
	IMAGE_NT_HEADERS32 *pNTHeader = (IMAGE_NT_HEADERS32*)(pBase + ((IMAGE_DOS_HEADER*)pBase)->e_lfanew);
	if(pNTHeader->Signature != IMAGE_NT_SIGNATURE)
	{
		DisplayError("Signature PE invalide.", GetLastError());
		FreeLibrary((HMODULE)pBase);
		return 0;
	}

	// trouve les addresses des header COFF file et Optionnal/PE
	IMAGE_FILE_HEADER *pFileHeader = (IMAGE_FILE_HEADER*) &pNTHeader->FileHeader;
	IMAGE_OPTIONAL_HEADER32 *pPEHeader = (IMAGE_OPTIONAL_HEADER32*) &pNTHeader->OptionalHeader;

	// on trouve les RVA de l'Import Directory Table et de l'Export Directory Table
	// ainsi que leurs addresses virtuelles & leurs tailles
	IMAGE_IMPORT_DESCRIPTOR *pImportDirectoryTable = (IMAGE_IMPORT_DESCRIPTOR*)(pBase + pPEHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
	DWORD dwImportDirectorySize = (DWORD) pPEHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
	DWORD dwExportRVA = pPEHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
	IMAGE_EXPORT_DIRECTORY  *pExportDirectoryTable = (IMAGE_EXPORT_DIRECTORY*)(pBase + dwExportRVA);
	DWORD dwExportDirectorySize = (DWORD) pPEHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
	DWORD dwExportEndRVA = dwExportRVA + dwExportDirectorySize;

	// vide le treeview et desactive le redraw
	SendMessage(hTree, TVM_DELETEITEM, 0, 0);
	SendMessage(hTree, WM_SETREDRAW, 0, 0);

	// ajout des deux items a la racine du treeview
	buffer[0] = 0;
	tvi.hParent = 0;
	tvi.hInsertAfter = TVI_ROOT;
	tvi.item.mask = TVIF_TEXT;
	tvi.item.pszText = buffer;
	hImportItem = (HTREEITEM) SendMessage(hTree, TVM_INSERTITEM, 0, (long) &tvi);
	hExportItem = (HTREEITEM) SendMessage(hTree, TVM_INSERTITEM, 0, (long) &tvi);

	// ajoute les items enfants de l'item hImport 
	DWORD dwTotalCount = 0;
	if(dwImportDirectorySize)
	{
		// une itération par dll qui contient des fonctions importées
		for(; pImportDirectoryTable->Name; pImportDirectoryTable++)
		{
			// ajoute un item enfant a hImport, contenant le nom d'une dll
			tvi.hParent = hImportItem;
			tvi.hInsertAfter = TVI_SORT;
			tvi.item.pszText = (char*)(pBase + pImportDirectoryTable->Name);
			tvi.hParent = (HTREEITEM) SendMessage(hTree, TVM_INSERTITEM, 0, (long) &tvi);

			// addresse base du module courant
			HMODULE hModDLL = GetModuleHandle(tvi.item.pszText);
			tvi.item.pszText = buffer;

			// CORRECTION BUG: certains linker mettent OriginalFirstThunk à zéro, et ne produisent pas
			// d'Import Lookup Table les noms/ordinaux des fonctions se trouvent donc dans le tableau 
			// pointé par FirstThunk, et il n'est pas possible d'obtenir l'adresse des fonctions importées
			if(!pImportDirectoryTable->OriginalFirstThunk) // bug du linker
			{
				IMAGE_THUNK_DATA32 *pImportLookup = (IMAGE_THUNK_DATA32*)(pBase + pImportDirectoryTable->FirstThunk);
				for(DWORD i=0; pImportLookup->u1.Function; pImportLookup++, i++, dwTotalCount++)
				{
					if(pImportLookup->u1.Function & 0x80000000)
					{
						wsprintf(buffer, "%03u  Import par Ordinal: 0x%08x", i, pImportLookup->u1.Ordinal & 0x7FFFFFFF);
					}
					else
					{
						wsprintf(buffer, "%03u  <not bound>  %s", i, pBase + pImportLookup->u1.AddressOfData+2);
					}

					// on ajoute l'item
					SendMessage(hTree, TVM_INSERTITEM, 0, (long) &tvi);
				}
			}
			else
			{
				// trouve les addresses de l'Import Lookup Table (nom des fonctions) et de l'Import
				// Address Table (addresse des fonctions importées)
				IMAGE_THUNK_DATA32 *pImportLookup = (IMAGE_THUNK_DATA32*)(pBase + pImportDirectoryTable->OriginalFirstThunk);
				IMAGE_THUNK_DATA32 *pImportAddress = (IMAGE_THUNK_DATA32*)(pBase + pImportDirectoryTable->FirstThunk);

				for(DWORD i=0; pImportLookup->u1.Function; pImportLookup++, pImportAddress++, i++, dwTotalCount++)
				{
					// si le bit de poid fort de l'import lookup est a 1, la fonction est importée par
					// ordinal, sinon elle est importée par nom
					if(pImportLookup->u1.Function & 0x80000000)
					{
						wsprintf(buffer, "%03u  Import par Ordinal: 0x%08x", i, pImportLookup->u1.Ordinal & 0x7FFFFFFF);
					}
					else
					{
						wsprintf(buffer, "%03u  0x%08x  %s", i, pImportAddress->u1.Function - (DWORD)hModDLL, pBase + pImportLookup->u1.AddressOfData+2);
					}

					// on ajoute l'item
					SendMessage(hTree, TVM_INSERTITEM, 0, (long) &tvi);
				}
			}
		}	
	}

	// met a jour le texte de l'item "Import"
	strcpy(buffer, "Imports ["); b = buffer + 9;
	ultoa(dwTotalCount, b, 10); while(*b) b++;

  • b++ = ']'; *b = 0;
tvitem.mask = TVIF_HANDLE | TVIF_TEXT; tvitem.pszText = buffer; tvitem.hItem = hImportItem; SendMessage(hTree, TVM_SETITEM, 0, (long) &tvitem); // ajoute les items enfants de l'item hExport dwTotalCount = 0; if(dwExportDirectorySize) { DWORD dwFuncsCount = 0, dwForwardsCount = 0, dwOrdinal = 0, dwFuncRVA = 0; // on trouve le nombre de symbole exportés, ainsi que les addresses de diverses choses: // Export Function Table: contient les RVA des fonctions, // Export Name Table: contient les RVA des noms des fonctions au format asciiz // Export Ordinal Table: index des RVA des fonctions DWORD dwExportNamesCount = (DWORD) pExportDirectoryTable->NumberOfNames; DWORD *pAddressTable = (DWORD*)(pBase + pExportDirectoryTable->AddressOfFunctions); DWORD *pNameTable = (DWORD*)(pBase + pExportDirectoryTable->AddressOfNames); WORD *pOrdinalTable = (WORD*)(pBase + pExportDirectoryTable->AddressOfNameOrdinals); // ajoute deux child item a hExport: hFunctionItem et hForwardItem buffer[0] = 0; tvi.hParent = hExportItem; hFunctionItem = (HTREEITEM) SendMessage(hTree, TVM_INSERTITEM, 0, (long) &tvi); hForwardItem = (HTREEITEM) SendMessage(hTree, TVM_INSERTITEM, 0, (long) &tvi); // on cherche le type du symbole (forwarder ou fonction/variable) et on ajoute un child item // a hFunctionItem ou hForwardItem for(; dwExportNamesCount; pOrdinalTable++, pNameTable++, dwExportNamesCount--) { dwOrdinal = (DWORD) *pOrdinalTable; dwFuncRVA = pAddressTable[dwOrdinal]; dwOrdinal += pExportDirectoryTable->Base; // si dwFuncRVA est dans l'Export Table, c'est un forwarder, sinon c'est une fonction if(dwExportRVA < dwFuncRVA && dwFuncRVA < dwExportEndRVA) { dwForwardsCount++; wsprintf(buffer, "%03u %s ---> %s", dwOrdinal, pBase + *pNameTable, pBase + dwFuncRVA); tvi.hParent = hForwardItem; SendMessage(hTree, TVM_INSERTITEM, 0, (long) &tvi); } else { dwFuncsCount++; wsprintf(buffer, "%03u 0x%08x %s", dwOrdinal, dwFuncRVA, pBase + *pNameTable); tvi.hParent = hFunctionItem; SendMessage(hTree, TVM_INSERTITEM, 0, (long) &tvi); } } dwTotalCount = dwFuncsCount + dwForwardsCount; // met a jour le texte des items functions et forward // si l'un des items a 0 child item, on le suprime tvitem.hItem = hFunctionItem; if(dwFuncsCount) { strcpy(buffer, "Functions ["); b = buffer + 11; ultoa(dwFuncsCount, b, 10); while(*b) b++;
  • b++ = ']'; *b = 0;
SendMessage(hTree, TVM_SETITEM, 0, (long) &tvitem); } else SendMessage(hTree, TVM_DELETEITEM, 0, (long) hFunctionItem); tvitem.hItem = hForwardItem; if(dwForwardsCount) { strcpy(buffer, "Forwarders ["); b = buffer + 12; ultoa(dwForwardsCount, b, 10); while(*b) b++;
  • b++ = ']'; *b = 0;
SendMessage(hTree, TVM_SETITEM, 0, (long) &tvitem); } else SendMessage(hTree, TVM_DELETEITEM, 0, (long) hForwardItem); } // met a jour le texte de l'item "Export" strcpy(buffer, "Exports ["); b = buffer + 9; ultoa(dwTotalCount, b, 10); while(*b) b++;
  • b++ = ']'; *b = 0;
tvitem.hItem = hExportItem; SendMessage(hTree, TVM_SETITEM, 0, (long) &tvitem); // ré-active le dessin du treeview et expand les deux branches principales SendMessage(hTree, WM_SETREDRAW, 1, 0); SendMessage(hTree, TVM_EXPAND, TVE_EXPAND, (long)hImportItem); SendMessage(hTree, TVM_EXPAND, TVE_EXPAND, (long)hExportItem); FreeLibrary((HMODULE)pBase); return 1; } //------------------------------------------------------------------------------------------ // AskForPath: func qui ouvre la common dialog "openfilename" //------------------------------------------------------------------------------------------ int AskForPath(void) { OPENFILENAME ofn; szImagePath[0] = 0; ZeroMemory(&ofn, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hMain; ofn.lpstrFile = szImagePath; ofn.nMaxFile = 256; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_LONGNAMES; ofn.lpstrFilter = "Executables (*.EXE, *.DLL)\0*.exe;*.dll\0"; ofn.nFilterIndex = 1; ofn.lpstrInitialDir = szCurrentDirectory; // l'utilisateur n'a pas selectionné de fichier if(!GetOpenFileName(&ofn)) return 0; GetCurrentDirectory(260, szCurrentDirectory); SetWindowText(hPath, szImagePath); return 1; } //--------------------------------------------------------------------------------------------- // DialogProc: callback de la fenetre principale //--------------------------------------------------------------------------------------------- BOOL __stdcall DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_INITDIALOG: hMain = hDlg; hPath = GetDlgItem(hDlg, IDC_PATH); hTree = GetDlgItem(hDlg, IDC_TREE); GetSystemDirectory(szCurrentDirectory, 260); PostMessage(hDlg, WM_COMMAND, IDOK, 0); return 1; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: if(AskForPath()) DisplayImportExport(); return 0; case IDCANCEL: EndDialog(hDlg, 0); } } return 0; } //--------------------------------------------------------------------------------------------- // WinMain //--------------------------------------------------------------------------------------------- int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { INITCOMMONCONTROLSEX ictrl; ictrl.dwSize = sizeof(INITCOMMONCONTROLSEX); ictrl.dwICC = ICC_TREEVIEW_CLASSES; InitCommonControlsEx(&ictrl); DialogBoxParam(hInstance, "MainDialog", 0, DialogProc, 0); return 0; }

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.