自力で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();
}