사용자 참여란
// 과니입니다.
// 이번엔 PE (Portable Executable) 구조체를 이용하여 API 함수인 GetProcAddress함수를 직접 제작해보겠습니다.
// 참고서적은 한빛미디어의 Windows 시스템 실행파일의 구조와 원리입니다.
// 포팅할수있는 부분은 포팅하고 나머진 구현하여 제작하였습니다. (필요없는 부분은 제거도 ㅋㅋㅋ)
// 첨부파일은 PE Format Unit으로 추가해주신 후 사용하시면 됩니다.
// 별로 글제주는 없어서 팁는 주석으로 보충설명하는 식으로 하겠습니다.
// PE 구조를 활용할 수 있는 부분은 DLL내의 Export함수를 모두 뽑아올수있는 것 (물론 프로그램있지만)
// 더 나아가서는 OS Kernel 프로그래밍에도 도움이 됩니다. (아주 열심히 쭉 더 나가면 -_ -a)
uses
..., uPEFormat;
procedure TForm1.Button1Click(Sender: TObject);
const
SAMPLE_DLL = 'C:\Windows\System32\ntdll.dll';
SAMPLE_NATIVE_API = 'ZwQueryInformationProcess';
var
ProcAddr : Pointer;
handleDll : THandle;
begin
// LoadLibrary로 Dll을 로드하여 프로세스 주소에 매핑
handleDll := LoadLibrary(SAMPLE_DLL);
ProcAddr := MyGetProcAddress(handleDll, SAMPLE_NATIVE_API);
if not Assigned(ProcAddr) then begin
ShowMessage('함수가 매핑된 위치가 없습니다.')
end else begin
ShowMessage('함수 매핑된 위치 : ' + Format('%p', [ProcAddr]));
end;
end;
function MyGetProcAddress(hModule : LongWord; pszSymbol : PChar) : Pointer;
type
TWordArr = array [0..0] of Word;
PWordArr = ^TWordArr;
TLongWordArr = array [0..0] of LongWord;
PLongWordArr = ^TLongWordArr;
var
// 각 파일 구조체 선언
pImageBase : PUCHAR;
pImageDosHeader : PIMAGE_DOS_HEADER; // IMAGE_DOS_HEADER
pImageNtHeaders : PIMAGE_NT_HEADERS; // IMAGE_NT_HEADERS
pImageDataDirectory : PIMAGE_DATA_DIRECTORY; // IMAGE_DATA_DIRECTORY
pImageExportDirectory : PIMAGE_EXPORT_DIRECTORY; // IMAGE_EXPORT_DIRECTORY
dwFuncIndex : LongWord;
pFuncName : PChar;
FnIndex: LongWord;
begin
Result := nil;
pImageBase := PUCHAR(hModule);
// 매핑되지 않았다면 종료
// 매핑되지 않았다는 것은 DLL의 주소가 잘못되었다는 것.
if not Assigned(pImageBase) then Exit;
pImageDosHeader := PIMAGE_DOS_HEADER(pImageBase);
// PE 포멧 파일인지 확인하여 PE파일이 아니라면 종료한다.
// PE (Portable Executable) 포멧 파일은 Exe, Dll에 해당한다.
// PE 구조는 IMAGE_DOS_HEADER, IMAGE_FILE_HEADERM, IMAGE_OPTIONAL_HEADER32 헤더로 구성된다.
// IMAGE_DOS_SIGNATURE 0x5A4D (23117 : MZ)
if pImageDosHeader.e_magic <> IMAGE_DOS_SIGNATURE then Exit;
// e_lfanew : PE 헤더의 시작점을 가리키는 RVA 값이다.
// 파일이 매핑된 주소의 시점부터 e_lfanew 값을 더하면 IMAGE_NT_HEADERS 가 나온다.
pImageNtHeaders := PIMAGE_NT_HEADERS(LongInt(pImageBase) + pImageDosHeader.e_lfanew);
// IMAGE_NT_SIGNATURE 를 확인한다.
// IMAGE_NT_HEADERS의 시작지점이 $00004550 값인지 확인한다.
// 0x00004550 (17744 : PE00)
if pImageNtHeaders.Signature <> IMAGE_NT_SIGNATURE then Exit;
pImageDataDirectory := @pImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
// IMAGE_EXPORT_DIRECTORY : export된 함수들을 담고 있는 구조체이다.
pImageExportDirectory := PIMAGE_EXPORT_DIRECTORY(Cardinal(pImageBase) + pImageDataDirectory.VirtualAddress);
dwFuncIndex := 0;
// pImageExportDirectory.NumberOfFunctions : DLL 파일 내의 export된 함수의 갯수를 의미한다.
// 처음부터 갯수만큼 돌면서 검색하려고 하는 함수가 존재하는 지 확인한다.
while (dwFuncIndex < pImageExportDirectory.NumberOfFunctions - 1) do begin
// 해당 주소의 함수명을 가져온다.
pFuncName := PChar(PLongWordArr(Cardinal(pImageExportDirectory.AddressOfNames) +
Cardinal(pImageBase))^[dwFuncIndex] + Cardinal(pImageBase));
// 검색하려고 하는 pszSymbol 함수와
// DLL에서 검색된 pFuncName 함수를 비교한다.
if StrLIComp(pszSymbol, pFuncName, Length(pszSymbol)) = 0 then begin
// AddressOfNameOrdinals 순서수 테이블
FnIndex := PWORDARR(Cardinal(pImageExportDirectory.AddressOfNameOrdinals) +
Cardinal(pImageBase))^[dwFuncIndex];
// 검색하려는 함수명과 DLL에서 Export된 함수명이 같다면 해당 함수의 주소를 리턴한다.
Result := Pointer(PLongWordArr(Cardinal(pImageExportDirectory.AddressOfFunctions) +
Cardinal(pImageBase))^[FnIndex] + Cardinal(pImageBase));
Break;
end;
// 두 함수가 같지 않으면 Index를 증가해 다음 함수를 검색한다.
inc(dwFuncIndex);
end;
end;






