自力でAPIを呼び出す
kernerl32.dllのエクスポートセクションからAPIのアドレスを取得して、自力でAPIを呼び出すプログラム。
参考にしたのは、muffinさんの「APIの取得方法」というドキュメント。
Webページにソース・make用バッチファイル・コンパイル済みバイナリがおいてあります。
@echo off cl -c main.c link /SUBSYSTEM:WINDOWS /NODEFAULTLIB main.obj /OUT:main.exe
// ---------------------------------------------------------------------------- // * ヘッダファイルの定義 // winnt.hで定義されているものと同じ。winnt.hをインクルードするためには、使わ // ないLPCSTRなどの定義が必要で、面倒。なので、必要な構造体だけを抜き出してきた。 // // MIZUNO Hiroki(hiroki1124@gmail.com) // ---------------------------------------------------------------------------- typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; typedef unsigned long LONG; #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory typedef struct _IMAGE_DOS_HEADER { WORD e_magic; WORD e_cblp; WORD e_cp; WORD e_crlc; WORD e_cparhdr; WORD e_minalloc; WORD e_maxalloc; WORD e_ss; WORD e_sp; WORD e_csum; WORD e_ip; WORD e_cs; WORD e_lfarlc; WORD e_ovno; WORD e_res[4]; WORD e_oemid; WORD e_oeminfo; WORD e_res2[10]; LONG e_lfanew; } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; // // NT additional fields. // DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[16]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; // RVA from base of image DWORD AddressOfNames; // RVA from base of image DWORD AddressOfNameOrdinals; // RVA from base of image } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
// ---------------------------------------------------------------------------- // * 自力でAPIを呼び出すプログラム // ライブラリを使わずにAPIを呼び出す。自力でkernel32.dllのエクスポートテーブル // から、APIのアドレスを取得している。 // // MIZUNO Hiroki(hiroki1124@gmail.com) // ---------------------------------------------------------------------------- #include "header.h" typedef unsigned char byte; typedef unsigned short word; typedef unsigned long dword; typedef unsigned int uint; // おなじみのstrcmp。 // Cの標準ライブラリは使えないので、自前で用意する int strcmp(const char *s1, const char *s2) { while(*s1 && *s2) { if((*s1) != (*s2)) { return ((*s1) > (*s2) ? 1 : -1); } s1++; s2++; } if(*s1) return 1; if(*s2) return -1; return 0; } // メモリ上に展開されたKernel32.dllの先頭のアドレスを取得する uint get_kernel_base(int return_address) { // Kernel32.dllもPEファイルの一種。 // なので、先頭に書かれた署名'MZ'を探すことで、先頭にたどりつける uint address = return_address & 0xFFFF0000; int i; for(i = 0 ; i < 5 ; i++) { IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)address; if(dosHeader->e_magic == 'ZM') { IMAGE_NT_HEADERS32* ntHeader = (IMAGE_NT_HEADERS32*)(address+dosHeader->e_lfanew); if(ntHeader->Signature == 'EP') { return address; } } // 10ページさかのぼる address -= 0x10000; } return 0; } // メモリ上に展開されたPEファイルから、エクスポートされた関数の情報を取得する。 void get_export_table(uint base, uint* name_count,dword* functions[], char** names[],word* ordinals[]) { // ヘッダに格納されているアドレスは、すべて先頭からの相対アドレス。 // なので、補正をかけてから格納する。 IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)base; IMAGE_NT_HEADERS32* ntHeader = (IMAGE_NT_HEADERS32*)(base + dosHeader->e_lfanew); uint offset = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; IMAGE_EXPORT_DIRECTORY* export = (IMAGE_EXPORT_DIRECTORY*)(base +offset); *name_count= export->NumberOfNames; *functions = (dword*)(base + export->AddressOfFunctions); *names = (char**)(base + export->AddressOfNames); *ordinals = (word*) (base + export->AddressOfNameOrdinals); } typedef void* (*GET_PROC_ADDRESS)(void*,char*); // kernel32.dllの先頭アドレスを元に、GetProcAddressへのポインタを取得する。 GET_PROC_ADDRESS searchGetProcAddress(kernel_base) { uint max; word *ordinals; dword* functions; char** names; uint i; get_export_table(kernel_base,&max,&functions,&names,&ordinals); for(i = 0 ; i < max ; i++) { if(strcmp((dword)kernel_base+names[i],"GetProcAddress")==0) { return (GET_PROC_ADDRESS)((byte*)kernel_base + functions[ordinals[i]]); } } return 0; } void WinMainCRTStartup(){ // staticにしないと落ちる。だれか助けてー static void* (*GetProcAddress)(void*,char*); static int (*MessageBox)(void*,const char*,const char*,uint); static void* (*LoadLibrary)(const char*); static void (*ExitProcess)(); void* user; uint return_address; uint kernel_base; __asm{ mov eax,[ebp+4] mov return_address,eax } kernel_base = get_kernel_base(return_address); GetProcAddress = searchGetProcAddress(kernel_base); LoadLibrary = GetProcAddress((void*)kernel_base,"LoadLibraryA"); ExitProcess = GetProcAddress((void*)kernel_base,"ExitProcess"); user = LoadLibrary("user32.dll"); MessageBox = GetProcAddress(user ,"MessageBoxA"); MessageBox(0,"Hello,new world!!","Hello,world",0); ExitProcess(); }