Signature and first (and only?) argument: PEB
Further, this user suggests that the entry point has the following signature
ULONG __stdcall(*)(void* PEB)
The following example tries to demonstrate that the first argument (which is passed in
rcx
in Win-X64) does actually point to the
process environment block (PEB).
entry-point.c
The function
printAddress
prints the value of the first parameter of the entry-point function, the value of
[gs]:60h
, the same value as returned by the Microsoft C-Compiler intrinsic function
__readgsqword
(see also
peb.c
) and the value of the PEB as returned by
NtQueryInformationProcess
. These four values should all be equal when the program is run.
#include <windows.h>
#include <winternl.h>
void* get_gs_60();
HANDLE stdOut;
void* get_peb_address() {
typedef NTSTATUS (NTAPI *ptrNtQueryInformationProcess)
(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
ptrNtQueryInformationProcess qry = (ptrNtQueryInformationProcess) GetProcAddress(
GetModuleHandleA("ntdll.dll"),
"NtQueryInformationProcess"
);
HANDLE proc = GetCurrentProcess();
PROCESS_BASIC_INFORMATION pbi;
qry(proc, 0, &pbi, sizeof(pbi), NULL);
return pbi.PebBaseAddress;
}
void printAddress(const char* text, void* addr) {
char buf[200];
int len = wsprintfA(buf, "%-20s: %p\n", text, addr);
DWORD charsWritten;
WriteConsoleA(stdOut, buf, len, &charsWritten, 0);
}
int __stdcall entryPoint(void* first_arg) {
stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
//
// Use an compiler intrinsic function to get a pointer to
// the PEB:
//
DWORD64 intr = __readgsqword(0x60);
//
// Get the same pointer with «ordinary» assembly:
//
void* gs_60 = get_gs_60();
//
// Alternatively, use WinAPI functions:
//
void* PEB = get_peb_address();
printAddress("First argument", first_arg);
printAddress("gs:[60h]" , (void*) gs_60);
printAddress("intrinsic" , (void*) intr);
printAddress("PEB" , PEB);
return 42;
}
go_60.asm
go_60.asm
is an assembler source file whose only function get_gs_60
returns the value of [gs]:60h
so that this value can be printed with printAddress
.
_TEXT SEGMENT
PUBLIC get_gs_60
get_gs_60 PROC
mov rax, qword ptr gs:[60h]
ret
get_gs_60 ENDP
_TEXT ENDS
END
Compilation and linking
The two source files can be compiled and linked like so:
cl /nologo /GS- /c entry-point.c
ml64 /nologo /c gs_60.asm
link /nologo /entry:entryPoint /nodefaultlib /subsystem:console /machine:x64 entry-point.obj gs_60.obj kernel32.lib user32.lib /out:entry-point.exe
Executing the program
When the program is executed, it prints the same address four times, thus giving evidence that indeed the first parameter of the entry point function points to the PEB:
First argument : 000000975583D000
gs:[60h] : 000000975583D000
intrinsic: : 000000975583D000
PEB : 000000975583D000