来源: http://blog.vckbase.com/windowssky
对于加壳软件的开发者,掌握PE Loader的实现是最基本的技术;因为壳运行结束后,你要仿照PE加载器去Load映象体,我曾看过UPX的开源代码,全手工打造PE,实现的也是极其复杂,是学习加壳,脱壳和开发加壳软件的上乘资料.
在ReatOs和Nt4.0上找到了其实现,前者实现的比较清晰,不过还是看看Nt4.0的实现吧
//KiThreadStartup->向Kernel APC插入LdrInitializeThunk->向User APC插入
//LdrInitializeThunk;详请参见PspCreateThread的实现
//
//注:Kernel APC与User APC的区别:
// 1)前者是在APC_LEVEL上运行,后者是在PASSIVE_LEVEL上运行
// 2)只有KTHREAD中的AlertAble或KTHREAD-ApcState-UserApcPending
// 为1的情况下,User APC中的Routine才会被执行
//
//ring0下启动ring3下的程序就是通过kernel下操作User APC实现的(向Explorer.exe中插入一个User APC)
//详请参考: http://www.codeproject.com/useritems/KernelExec.asp
//扯远了,还是来看看LdrInitializeThunk和LdrInitialize的实现吧
;++
;
; VOID
; LdrInitializeThunk(
; IN PVOID NormalContext,
; IN PVOID SystemArgument1,
; IN PVOID SystemArgument2
; )
;
; Routine Description:
;
; This function computes a pointer to the context record on the stack
; and jumps to the LdrpInitialize function with that pointer as its
; parameter.
;
; Arguments:
;
; NormalContext - User Mode APC context parameter (ignored).
;
; SystemArgument1 - User Mode APC system argument 1 (ignored).
;
; SystemArgument2 - User Mode APC system argument 2 (ignored).
;
; Return Value:
;
; None.
;
;--
cPublicProc _LdrInitializeThunk , 4
NormalContext equ [esp + 4]
SystemArgument1 equ [esp + 8]
SystemArgument2 equ [esp + 12]
Context equ [esp + 16]
lea eax,Context ; Calculate address of context record
mov NormalContext,eax ; Pass as first parameter to
if DEVL
xor ebp,ebp ; Mark end of frame pointer list
endif
IFDEF STD_CALL
jmp _LdrpInitialize@12 ; LdrpInitialize
ELSE
jmp _LdrpInitialize ; LdrpInitialize
ENDIF
stdENDP _LdrInitializeThunk
_TEXT ends
end
VOID
LdrpInitialize (
IN PCONTEXT Context,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This function is called as a User-Mode APC routine as the first
user-mode code executed by a new thread. It's function is to initialize
loader context, perform module initialization callouts...
Arguments:
Context - Supplies an optional context buffer that will be restore
after all DLL initialization has been completed. If this
parameter is NULL then this is a dynamic snap of this module.
Otherwise this is a static snap prior to the user process
gaining control.
SystemArgument1 - Supplies the base address of the System Dll.
SystemArgument2 - not used.
Return Value:
None.
--*/
{
NTSTATUS st, InitStatus;
PPEB Peb;
PTEB Teb;
UNICODE_STRING UnicodeImageName;
MEMORY_BASIC_INFORMATION MemInfo;
BOOLEAN AlreadyFailed;
LARGE_INTEGER DelayValue;
SystemArgument2;
AlreadyFailed = FALSE;
Peb = NtCurrentPeb();
Teb = NtCurrentTeb();
if (!Peb->Ldr) {
#if defined(MIPS) || defined(_ALPHA_)
ULONG temp;
#if defined(MIPS)
Peb->ProcessStarterHelper = (PVOID)LdrProcessStarterHelper;
#endif
//
// Set GP register
//
LdrpGpValue =(ULONG)RtlImageDirectoryEntryToData(
Peb->ImageBaseAddress,
TRUE,
IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
&temp
);
if (Context != NULL) {
LdrpSetGp( LdrpGpValue );
#if defined(_MIPS_)
Context->XIntGp = (LONG)LdrpGpValue;
#else
Context->IntGp = LdrpGpValue;
#endif
}
#endif // MIPS || ALPHA
NtGlobalFlag = Peb->NtGlobalFlag;
#if DBG
if (TRUE)
#else
if (Peb->BeingDebugged || Peb->ReadImageFileExecOptions)
#endif
{
PWSTR pw;
pw = (PWSTR)Peb->ProcessParameters->ImagePathName.Buffer;
if (!(Peb->ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) {
pw = (PWSTR)((PCHAR)pw + (ULONG)(Peb->ProcessParameters));
}
UnicodeImageName.Buffer = pw;
UnicodeImageName.Length = Peb->ProcessParameters->ImagePathName.Length;
UnicodeImageName.MaximumLength = UnicodeImageName.Length;
st = LdrQueryImageFileExecutionOptions( &UnicodeImageName,
L"GlobalFlag",
REG_DWORD,
&NtGlobalFlag,
sizeof( NtGlobalFlag ),
NULL
);
if (!NT_SUCCESS( st )) {
UnicodeImageName.Length = 0;
if (Peb->BeingDebugged) {
NtGlobalFlag |= FLG_HEAP_ENABLE_FREE_CHECK |
FLG_HEAP_ENABLE_TAIL_CHECK |
FLG_HEAP_VALIDATE_PARAMETERS;
}
}
}
#if DBG && FLG_HEAP_PAGE_ALLOCS
if ( NtGlobalFlag & FLG_HEAP_PAGE_ALLOCS ) {
//
// Turn on BOOLEAN RtlpDebugPageHeap to indicate that
// new heaps should be created with debug page heap manager
// when possible. Also force off other heap debug flags
// that can cause conflicts with the debug page heap
// manager.
//
RtlpDebugPageHeap = TRUE;
NtGlobalFlag &= ~( FLG_HEAP_ENABLE_TAGGING |
FLG_HEAP_ENABLE_TAG_BY_DLL
);
}
#endif // DBG && FLG_HEAP_PAGE_ALLOCS
}
#if defined(MIPS) || defined(_ALPHA_)
else
if (Context != NULL) {
LdrpSetGp( LdrpGpValue );
#if defined(_MIPS_)
Context->XIntGp = (LONG)LdrpGpValue;
#else
Context->IntGp = LdrpGpValue;
#endif
}
#endif // MIPS || ALPHA
ShowSnaps = (BOOLEAN)(FLG_SHOW_LDR_SNAPS & NtGlobalFlag);
//
// Serialize for here on out
//
Peb->LoaderLock = (PVOID)&LoaderLock;
if ( !RtlTryEnterCriticalSection(&LoaderLock) ) {
if ( LoaderLockInitialized ) {
RtlEnterCriticalSection(&LoaderLock);
}
else {
//
// drop into a 30ms delay loop
//
DelayValue.QuadPart = Int32x32To64( 30, -10000 );
while ( !LoaderLockInitialized ) {
NtDelayExecution(FALSE,&DelayValue);
}
RtlEnterCriticalSection(&LoaderLock);
}
}
if (Teb->DeallocationStack == NULL) {
st = NtQueryVirtualMemory(
NtCurrentProcess(),
Teb->NtTib.StackLimit,
MemoryBasicInformation,
(PVOID)&MemInfo,
sizeof(MemInfo),
NULL
);
if ( !NT_SUCCESS(st) ) {
LdrpInitializationFailure(st);
RtlRaiseStatus(st);
return;
}
else {
Teb->DeallocationStack = MemInfo.AllocationBase;
}
}
InitStatus = STATUS_SUCCESS;
try {
if (!Peb->Ldr) {
LdrpInLdrInit = TRUE;
#if DBG
//
// Time the load.
//
if (LdrpDisplayLoadTime) {
NtQueryPerformanceCounter(&BeginTime, NULL);
}
#endif // DBG
try {
//
//This function initializes the loader for the process.
//This includes:
// - Initializing the loader data table
// - Connecting to the loader subsystem
// - Initializing all staticly linked DLLs
InitStatus = LdrpInitializeProcess( Context,
SystemArgument1,
UnicodeImageName.Length ? &UnicodeImageName : NULL
);
}
except ( EXCEPTION_EXECUTE_HANDLER ) {
InitStatus = GetExceptionCode();
AlreadyFailed = TRUE;
LdrpInitializationFailure(GetExceptionCode());
}
#if DBG
if (LdrpDisplayLoadTime) {
NtQueryPerformanceCounter(&EndTime, NULL);
NtQueryPerformanceCounter(&ElapsedTime, &Interval);
ElapsedTime.QuadPart = EndTime.QuadPart - BeginTime.QuadPart;
DbgPrint("/nLoadTime %ld In units of %ld cycles/second /n",
ElapsedTime.LowPart,
Interval.LowPart
);
ElapsedTime.QuadPart = EndTime.QuadPart - InitbTime.QuadPart;
DbgPrint("InitTime %ld/n",
ElapsedTime.LowPart
);
DbgPrint("Compares %d Bypasses %d Normal Snaps %d/nSecOpens %d SecCreates %d Maps %d Relocates %d/n",
LdrpCompareCount,
LdrpSnapBypass,
LdrpNormalSnap,
LdrpSectionOpens,
LdrpSectionCreates,
LdrpSectionMaps,
LdrpSectionRelocates
);
}
#endif // DBG
if (!NT_SUCCESS(InitStatus)) {
#if DBG
DbgPrint("LDR: LdrpInitializeProcess failed - %X/n", InitStatus);
#endif // DBG
}
} else {
if ( Peb->InheritedAddressSpace ) {
InitStatus = LdrpForkProcess();
}
else {
#if defined (WX86)
if (Teb->Vdm) {
InitStatus = LdrpInitWx86(Teb->Vdm, Context, TRUE);
}
#endif
// This function is called by a thread that is terminating cleanly.
//It's purpose is to call all of the processes DLLs to notify them
//that the thread is detaching.
LdrpInitializeThread();
}
}
} finally {
LdrpInLdrInit = FALSE;
RtlLeaveCriticalSection(&LoaderLock);
}
NtTestAlert();
if (!NT_SUCCESS(InitStatus)) {
if ( AlreadyFailed == FALSE ) {
LdrpInitializationFailure(InitStatus);
}
RtlRaiseStatus(InitStatus);
}
}
详细实现参见:
nt4/private/ntos/dll/ldrinit.c
nt4/private/ntos/dll/ldrapi.c
nt4/private/ntos/dll/ldrsnap.c
reatos实现参见:
ReactOS030/dll/ntdll/ldr/startup.c
ReactOS030/dll/ntdll/ldr/utils.c
ReactOS030/dll/ntdll/ldr/elf.c