找回密码
 加入计匠网
搜索
热搜: BIOS ACPI CPU Windows
查看: 14116|回复: 0

[转载]WINDOWS 2K Dll 加载过程

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]! N" \4 p- C: }, r
! n+ @$ X1 d" g$ M/ q1 C5 c
WINDOWS 2K Dll 加载过程
4 \7 H( d- c; G  ?jefong by 2005/03/30+ P0 Z% P, q* `5 c* b' x
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
, _3 }/ y3 @) @: L; a. Z5 T' `在windows中exe可执行程序运行时都会调用一些DLL,例如KERNEL32.DLL和USER32.DLL等系统的dll。但是dll是怎么被加载的呢?通常,大家都知道在编写dll时会有一个DLLMain的入口函数,但是实际上这个函数并不是调用dll时最先的工作。首先dll需要被加载,然后要进行初始化分配,再之后才进入DLLMain。还有可能你的一个dll中还会调用另一各dll。那么dll到底是怎样加载和初始化的呢,我们来参考一下Platform SDK中的“Dynamic-Link Library Entry-Point Function”。- ?6 L  p/ S$ ^- A
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。% x' ]/ A6 f, a2 H: f5 T
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。3 k  P6 h& x  O+ N
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
9 u' [2 {( U5 \% B% J* s  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。2 o  G, l& F) Y& ?" u4 W& f, S
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
! m/ U* c1 E, o' n/ |//=============================================================================' Z% h7 p/ O0 ]6 v- e
// Matt Pietrek, September 1999 Microsoft Systems Journal& Z( b; \; u8 [2 a
// 中文注释部分为jefong翻译
3 G- _! @, w, T, I//) _- K* e& ]/ ~  s* s
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3); \- _: M+ h( O0 @$ j7 T* |
//
3 x+ r3 _$ S( o$ O( Z) g$ q// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
# Q* s7 K+ b, s& H% o6 ^/ x; ~//=============================================================================! k, F6 ?1 s9 I' Z" Y$ Q6 l' z
1 r. ]6 |) r8 _- i
#include <ntexapi.h>    // For HardError defines near the end! c* {( `: D8 ?9 d/ l' m

$ k9 f( s1 i' v// Global symbols (name is accurate, and comes from NTDLL.DBG)
6 k* U) a$ H6 B' k& i' C% `) \3 d( Z//  _NtdllBaseTag
' P% I* Y% t' w! h//  _ShowSnaps" C  @" C) z2 w: r# U) `
//  _SaveSp% y7 Q. |4 T$ u* U' s9 h3 H; C* K+ i
//  _CurSp6 P' n7 K6 E% S. m
//  _LdrpInLdrInit
1 @2 L* s+ B- J, L; b//  _LdrpFatalHardErrorCount
, h9 O& }$ E( F7 o! {' a//  _LdrpImageHasTls
1 x- q! q+ T2 F7 G5 [' k; N7 F" Z/ c- }
NTSTATUS4 s/ e( x" @  M8 d( E$ U3 f
LdrpRunInitializeRoutines( DWORD bImplicitLoad )8 L% b3 b9 x! |* y
{+ [; o7 H  \2 Z) s
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了7 d4 j8 v  F1 e/ ]: d
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();* l$ ?  r7 v' A* h' E
4 f& Q( b9 B8 m0 x7 T7 Y0 A9 P
    if ( nRoutinesToRun )% Z5 Q" k7 W( v9 c& R! D! `5 i
    {
) r# c+ w0 `. G& A; y% z        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
: t0 N0 [- \0 Q- G) t        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),7 P7 ]! _( Q# h7 G; f
                                            _NtdllBaseTag + 0x60000,0 J$ ~  {& t/ \! e/ p
                                            nRoutinesToRun * 4 );
0 k% c+ x- C: {% O2 `                            5 {4 m# }; n; _; O! E
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
+ D/ Q6 S0 R" N0 D) }            return STATUS_NO_MEMORY;& b" C% \- T* Z4 e  I. i( y
    }
* G7 e" a) V( d% A* q    else
% S" @& a2 C2 G2 u        pInitNodeArray = 0;
' \+ k9 }$ {. S/ e: C4 v0 v
) k/ `- d- [' b' T    //第二部分;) C$ ]5 j2 J' `2 r; V' ~
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
  B% l; W. n5 ^6 \    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
' W# [+ F% ?% u4 O' u7 A    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;) Y5 i, ^; t& L  q
        
$ Z, B( ~' [' I! j* K    if ( _ShowSnaps )3 `9 h2 F5 f+ ~8 V0 _
    {
9 T1 b% {3 j6 y' A3 V: Y        _DbgPrint( "LDR: Real INIT LIST\n" );3 V  V! k5 p2 ~7 p+ E) Q* o
    }
% X/ s, n. D* i0 D8 s6 @1 F
$ k6 ~$ S! {) O5 F$ p    nModulesInitedSoFar = 0;
0 g( V4 N5 r$ B0 O( V) O( a/ m+ c$ x( V1 ]8 u
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
9 D6 a* Y* S; b6 D' _    {
9 ~8 W( `# m" b, B* q4 z4 n        
7 q% s( p: D' O1 l5 U        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
9 u7 m4 m" V2 S4 v+ P* R. |        {! |4 a, b2 P3 j- a4 t. W8 T9 m
            ModuleLoaderInfo  pModuleLoaderInfo;0 l2 K6 Y% Z/ y4 v1 H; r7 K' u! m/ h
            ( l! m0 V2 w  i$ M' I7 Y9 h
            //4 D4 O: }3 l0 s1 c4 l9 \0 ~$ e- H/ q4 w
            //一个ModuleLoaderInfo结构节点的大小为0X10字节& ?! b% w$ J$ @9 ^
            pModuleLoaderInfo = &NextNode - 0x10;
& I1 x; S7 r0 U+ m6 X            
/ a8 p& Z2 _9 p7 x            localVar3C = pModuleLoaderInfo;         / G7 O, K9 j6 w# I& L5 B

# F: {5 C  ]% p3 C8 Z0 c            //
# v. @" Q% L; z+ N            // 如果模块已经被初始化,就忽略
% Q' f4 s6 O4 V            // X_LOADER_SAW_MODULE = 0x40 已被初始化0 S: M) U3 B' V1 k8 `! n0 k/ b$ L1 E
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
- V) g6 ^4 [  F            {& M6 o/ p  P% L3 e8 N+ {
                //. @, b* B! y7 m/ \
                // 模块没有被初始化,判断是否具有入口函数" x! \5 \; f4 {( C1 ]
                //
$ q: L+ c% }" o) X                if ( pModuleLoaderInfo->EntryPoint )
  ], |3 ]4 t  }! P* x/ N, S                {
# f% h3 o. x+ K                    //( x$ e- A- X$ {1 e
                    // 具有初始化函数,添加到模块列表中,等待进行初始化% c% {& S& u# l' Q  E/ D
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
+ m5 ]  r. {- }7 ^- e$ d
* s! l/ o: |5 Z1 `- u                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
' G* ^& N3 z0 u* w" Z" g      // 例如:% I7 }5 ~+ X" C1 J4 q* x1 [
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f010000 Z4 W* d: `- O/ `! ]
                    if ( _ShowSnaps )
& T" K$ }; M7 f8 `7 r( l% H. U1 |                    {
  k$ k  Y* ]+ ^" E2 p: {                        _DbgPrint(  "%wZ init routine %x\n",
# {: \- [! ^8 x8 m# U; U3 W9 `                                    &pModuleLoaderInfo->24,3 ~$ T6 [  i, J2 B: ?( z+ r; Z1 c
                                    pModuleLoaderInfo->EntryPoint );
( t  d& h. Y0 C5 u! U  z' B- I  _                    }
. W. x" L- ~. |) x/ W" G$ p! o! m
% q6 p' n! G! c) _7 p$ w& |- d: [/ w6 j                    nModulesInitedSoFar++;# K& l. k- }* \, f" z9 l
                }, |9 n) [: L% ~/ s0 k
            }: C7 k% B2 O7 _8 Z

( f: k! z# S6 l; j3 o  c            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
4 m! Z0 c0 h6 k3 z2 u0 m            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;3 \) q3 b5 `- t$ C3 b

4 i/ l/ ~- j  S) Y3 t0 a. \            // 处理下一个模块节点
) |* [8 L% M3 \6 g            pCurrNode = pCurrNode->pNext6 R; x' P& G3 I
        }
3 f) \0 ]- V' |: G4 }' K    }
! B( r' |: I7 @9 D! k    else
8 ~& {) \& a0 W* [6 _1 Z  v    {
) N8 F0 x' I  b9 E! u  W2 N5 N        pModuleLoaderInfo = localVar3C;     // May not be initialized???
5 X4 X3 E6 {* s6 j    }
; `" K5 |' P4 A* l   
6 _0 d3 T1 i) s% R4 Z    if ( 0 == pInitNodeArray )' ?4 X* h' A+ k* H
        return STATUS_SUCCESS;8 }1 l5 p) V& \) y, d+ T

2 F- \( N+ `/ h# H' f    // ************************* MSJ Layout! ****************** g0 Z! A) k( i+ G, n
    // If you're going to split this code across pages, this is a great
5 m" J/ K1 `- E& S    // spot to split the code.  Just be sure to remove this comment, _  {& w+ h% I# A
    // ************************* MSJ Layout! *****************
, a* I6 a, w$ B4 u   
; n5 H2 m+ i) ]9 I) m  b    /// t2 x3 N: H! f1 ]' C6 r7 Y* i
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
' ^1 @2 `$ z( q" j    // 第三部分,调用初始化部分& j% }2 |/ f9 P
    try     // Wrap all this in a try block, in case the init routine faults
0 |, c8 G4 k* A1 X; {( p5 y% Z: ?2 l    {) ~' I3 l  i* ^0 L3 {$ Q+ Y
        nModulesInitedSoFar = 0;  // Start at array element 0
7 G$ Z) b# C8 e8 n+ h  M) R* g! q7 L' |+ q  ?# m  o- }$ W
        //
+ T' e0 u% U2 M% ~& b' b) v        // 遍历模块队列5 L; @/ J( x7 v' u) I
        //
$ Y  f8 @# b, z* Q        while ( nModulesInitedSoFar < nRoutinesToRun )
& n! g! y4 b; w* F& I( P        {6 o" ~8 {0 G$ `8 T5 X$ e7 y
            // 获得模块指针- s0 q6 K( T3 e. I; W1 k" d. y
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
* c. D9 C( K$ u/ `3 Y( O; r
& k: c6 {! r+ o2 x            // This doesn't seem to do anything...
# D; u7 v$ f! Q; O$ W            localVar3C = pModuleLoaderInfo;8 I' O/ U- k& E& n/ f; y
            0 t5 Y" s% X+ \  ^  B
            nModulesInitedSoFar++;5 D  S2 h8 c- h9 g9 o8 ?8 [
               
/ f9 B7 m1 C0 k( K" F            // 保存初始化程序入口指针+ d+ y) s  O6 R# f7 F, M& a
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;3 V/ l2 @  S9 y# o/ D
            / s7 H: R, s" l- v- k# n
            fBreakOnDllLoad = 0;    // Default is to not break on load, `( I8 H# @# ~0 U# w+ C

# m7 W7 I: L' N/ j$ X% ^/ @            // 调试用
: n3 B0 N  v8 {. n  |* w            // If this process is a debuggee, check to see if the loader
$ o, _5 t0 h9 r/ w& j            // should break into a debugger before calling the initialization.
9 R' Z4 c+ R# |, A, ^- l# h5 l( j            //( {8 Y( k0 R# V0 f( S& c
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
5 y; G& j! h; F% f% F% I            // returns. IsDebuggerPresent is an NT only API.' _$ }1 I  b* I- }' U+ m
            //) c( Y7 I2 |* {, y5 o( R( a
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )) M+ N# M5 e$ N/ V
            {
  Q+ g* j6 d6 z; o  r0 v  u                LONG retCode;% _2 f( _( r7 j! o
/ O# ?( A. Q& H. I7 @; k! }7 R% i
                //              
# s, ?3 ?0 O$ Y2 ?9 k                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
" k6 |8 ~- E% V& p                // Windows NT\CurrentVersion\Image File Execution Options"
  N" v& P6 J  x0 i/ b* O. Y4 ]                // registry key.  If a a subkey entry with the name of
+ }* x& R( J9 f; X                // the executable exists, check for the BreakOnDllLoad value.8 Q3 p4 V% F' j$ |
                //
! d" B4 A- _( w0 W. \                retCode =
5 Q, L. \, G+ E9 C$ d                    _LdrQueryImageFileExecutionOptions(
5 B/ n2 V9 `+ _" T7 e9 a" C                                pModuleLoaderInfo->pwszDllName,
7 x- A# D& T: b* n( }. B                                "BreakOnDllLoad",pInitNodeArray
: J" L! S2 g; r' z6 P9 g                                REG_DWORD,
# ~5 H& g6 f* v& J2 S0 V0 E                                &fBreakOnDllLoad,
' r0 q; N* Y5 ^                                sizeof(DWORD),. o5 i) i5 B# z6 \# u
                                0 );
' V  M  {( r7 ~! s$ t8 Q
* _; v2 e& ~: F$ N3 i' r6 d  U% Z                // If reg value not found (usually the case), then don't& A& W: Q. i% c# e7 @, [7 W
                // break on this DLL init  t$ y2 ]6 g2 A. \
                if ( retCode <= STATUS_SUCCESS )
4 M( ~' _, M3 w$ j4 O1 I                    fBreakOnDllLoad = 0;pInitNodeArray# w9 f  F2 y( V1 k7 i9 i* T
            }5 t1 d4 B! G% L; s# o  O
            
: b8 g8 c+ G4 r5 J/ w3 a9 v3 u" X            if ( fBreakOnDllLoad )
) o1 d" I- O# f" d( T            {           0 _! Z2 {* Y) F" Q# t. Z2 U0 _
                if ( _ShowSnaps )
9 H: |, d/ D6 L6 @! j                {- X) x0 B' y+ ~# A- N2 ]
                    // Inform the debug output stream of the module name. r" P" }9 _0 J% D# x
                    // and the init routine address before actually breaking5 d% P1 ?* M' Z3 u$ c
                    // into the debugger& {+ T9 v: N% i+ L+ p  X& J1 s
0 P0 V+ R% I- Q1 I* B0 Z
                    _DbgPrint(  "LDR: %wZ loaded.",1 G; P- k6 w. H# c# K, J
                                &pModuleLoaderInfo->pModuleLoaderInfo );
' W8 K! s8 J" @                    
0 B7 g$ |4 h6 r$ T* z2 f                    _DbgPrint(  "- About to call init routine at %lx\n",/ L5 m& y  {2 I* C
                                pfnInitRoutine )" S7 S* h1 y+ O5 C/ S8 o
                }
1 H/ A" R* Z* N+ e$ j' X4 [                $ ^# s, a. s3 D! e
                // Break into the debugger                              
& z8 J0 Q4 ~! ^/ e/ K                _DbgBreakPoint();   // An INT 3, followed by a RET
" R1 _* d' t' {4 O# I3 ~- t            }
0 l) ]+ B5 w' z            else if ( _ShowSnaps && pfnInitRoutine )
7 E: f( {* M, l- V/ k            {
8 `+ B5 S/ u* v1 @/ a                // Inform the debug output stream of the module name
0 x3 F& j: w4 G* F/ C* m                // and the init routine address before calling it               
1 W' x, ~- _6 J! j                _DbgPrint(  "LDR: %wZ loaded.",
! G9 W3 k  ]$ A/ r$ \( f; E0 k  C                            pModuleLoaderInfo->pModuleLoaderInfo );9 d/ @# N( l; s$ i/ N

9 w  \; E+ }; B$ P+ }( e4 g                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);$ Q8 g; L+ H* \% L  n5 h' {
            }9 m. `1 e, h) K- m
                    
: B1 m- b$ V# q$ F* J4 u            if ( pfnInitRoutine )
0 T  h8 ]: i; ]  K/ z. t6 O            {
* p6 I' b. [$ R2 V/ r  K2 T7 H                // 设置DLL_PROCESS_ATTACH标志
9 F+ Y$ x* l# j+ D" L9 Q3 {5 q                //
. `& i( \% {7 y' Z6 U! i                // (Shouldn't this come *after* the actual call?)
4 p! p0 M/ x4 c0 d$ p6 r                //
# j6 E8 \1 {2 r% |                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             $ I/ B0 B& {0 o/ E# ~6 R
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
4 v9 B5 R  w: I4 r) x4 T# O2 M
4 r( j/ |/ c% F& J: R/ }                //
: M: j) K7 v% w% {8 R8 ^3 X6 \                // If there's Thread Local Storage (TLS) for this module,2 V1 z3 d4 H& K: z4 z5 {
                // call the TLS init functions.  *** NOTE *** This only5 \' e# n4 b3 c$ R+ }
                // occurs during the first time this code is called (when8 L) G6 p+ u0 @% b  w
                // implicitly loaded DLLs are initialized).  Dynamically9 K7 L1 b' W+ K2 ~" l! o6 A
                // loaded DLLs shouldn't use TLS declared vars, as per the
( O! e9 a: V- d: V                // SDK documentation
, O4 A, U' K, B( ]# ]) `                // 如果模块需要分配TLS,调用TLS初始化函数
+ A9 N) u! |: v# a# A* K, S  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时9 x+ ~1 z7 T  K3 h
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
4 B& W) V' _$ K0 Z& Q/ v$ L                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
' m8 H3 h; Y' q3 v$ @% m3 @, W                {
+ L( W! E4 m9 J9 z  E+ Y                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,/ }2 T7 Z$ t- Z) ~  ^
                                                DLL_PROCESS_ATTACH );# `! |( ^/ N/ n1 s# w- k
                }
/ ~& A& @# ^5 W& q, S                , e* H* k( q- [* A- j0 |8 Z% ~

2 a6 @( V! a# {+ P                hModDLL = pModuleLoaderInfo->hModDLL
2 a7 ]7 a8 G" w; z% P0 P6 D
8 O$ c3 k8 ?7 ?9 A5 h% }                MOV     ESI,ESP // Save off the ESP register into ESI0 d8 s5 r- V9 a7 F
  ) p/ \) O5 w5 o% m7 v
  // 设置入口函数指针                2 |1 u% m$ P6 ]# i$ v! |2 s
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     $ s/ l, M; Y4 M3 l0 z

1 _/ v3 S$ D9 U3 y                // In C++ code, the following ASM would look like:
4 [- l, C4 [0 S0 Q; Z- R5 t& s                //
9 b7 n+ T( G" u5 p' a" b6 g                // initRetValue =: {9 _* j+ S8 F9 W6 k3 e5 H! J
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
' }( V- t, u( O                //
9 T# n$ x, \5 d- J4 ]' j; v. L
1 X  s9 m3 f/ W) n                PUSH    DWORD PTR [bImplicitLoad]) j* C* [. }( T! [
               
# Y7 h* G* k; b2 _, ^1 t0 \                PUSH    DLL_PROCESS_ATTACH- s6 M" L1 d6 t' E% m2 t: Z, ?* W
               
5 B; _' G/ I$ l0 q9 V                PUSH    DWORD PTR [hModDLL]6 a5 t# M; f$ v5 c6 S1 {3 P+ a
                % ]2 Y* T3 R' R7 t; b
                CALL    EDI     // 调用入口函数+ Z& _, u0 `) r# _
               
- _1 R4 w! }- `5 t                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
4 F" U& F. c. E) T# |9 o0 b) p) C* s9 H1 n% A! u! }" X) d' S
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
4 r+ p; ]/ n, ]                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns  C/ q' \) v6 o. m  b
  P1 w7 O' p. |% @% n
                MOV     ESP,ESI     // Restore ESP to value before the call; E6 V- ], P5 g4 W0 Q
; x# O; N6 }$ J; k# ]# |
                //
1 X" Q$ d; |7 N' A, j                // 检查调用前后的ESP值是否一至  J5 U" e* A/ e0 l) Q8 j
  // ' }. O) F/ `! Q. B4 K# o1 h
                if ( _CurSP != _SavSP )
/ S" b/ G; q! S                {
- g0 S; f' s! D# G8 b                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
/ X% W% G8 `# M2 y1 w
% j% E/ l' `2 i# D* O% y0 v                    hardErrorRetCode = # o  a. R: X8 I4 p6 K2 w* Y
                        _NtRaiseHardError(
4 q, ?" g/ O$ Z7 }' a8 J                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,# Y( b9 f1 c/ n
                            1,  // Number of parameters
2 ]% p/ \! ]! K* R" Q3 O                            1,  // UnicodeStringParametersMask,
( r- o9 v! c' z( d                            &hardErrorParam,
, ~9 t9 T6 T$ P& A9 \                            OptionYesNo,    // Let user decide
/ q$ J0 L" c4 M6 A# `$ {7 i- P5 h                            &hardErrorResponse );
9 x1 A" h/ N" P4 W                                            " e1 `7 T8 O" g# r: S, W2 |2 s: F
                    if ( _LdrpInLdrInit )
; M; v6 e. G. a! ~                        _LdrpFatalHardErrorCount++;/ V; C# ]0 K! h& P/ z! p
7 s: W9 h  [& _" O9 F$ G
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)$ T, R; ^) ^( B$ w  z+ O/ j* j
                        &&  (ResponseYes == hardErrorResponse) )
/ A  b5 z* o; L% f                    {: ]3 v: K: [; y7 J% `* G
                        return STATUS_DLL_INIT_FAILED;
% i1 v* L, I) D2 `# x! i                    }5 e1 g8 f" K! F' ^$ O
                }
) X) O" G' C( f3 {" v  q7 h. W/ P- I# ^1 R/ I# G
                //
% Y  f- i; m% ^2 n1 R$ y# d                // 入口函数返回0,错误
/ R" d# l7 k9 Q2 _1 D2 W                //
, S) ~; b6 j/ X' J8 s8 @2 k8 t                if ( 0 == initRetValue )9 U; `9 V1 F6 e6 M. s
                {
( `. Z3 S6 q0 y& S6 q$ A                    DWORD hardErrorParam2;) e9 i$ L  Z& E  [# e8 @5 Y! b& F
                    DWORD hardErrorResponse2;2 ^6 W$ f. ]& C
                                       
% ?$ @* f( w* M) Q- s8 u& d* W                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
3 P" I" W! u/ [0 i' y+ \                    : }' ~' b, _/ \, a
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
' l$ W: t9 `0 k; j                                        1,  // Number of parameters& Y: T9 j6 p) X7 t4 `2 q4 {: M
                                        1,  // UnicodeStringParametersMask
0 o. Q! T9 J( q/ f; U+ I4 Z                                        &hardErrorParam2,# A& w& D) J8 T! B1 `
                                        OptionOk,   // OK is only response& j/ `! D7 \2 R1 |
                                        &hardErrorResponse2 );
- d9 R  z) H0 D                                                            4 E, t" l8 d  g) F% ]8 S) ]4 p- E# G
                    if ( _LdrpInLdrInit )
  z5 B6 b; S, X) ?6 ~                        _LdrpFatalHardErrorCount++;. b& B% {/ a+ c/ [& y) H

; z+ K2 U/ g& o0 e& L" F8 ]                    return STATUS_DLL_INIT_FAILED;* v; m7 C4 u& K5 B: r1 t
                }
( d2 N% e" t2 d) B            }1 |' T' M- y# c
        }0 z- z% M( B  @, c# e

. K* B% x5 R' N, g        //
5 o6 ?* o/ E# U3 z$ Z' w# [        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
3 t' W3 A0 V2 E) }        //      6 S7 h$ z5 B" t* o% ~" m
        if ( _LdrpImageHasTls && bImplicitLoad )" G, l( B; f3 b5 W* e+ w; E9 @
        {# @4 r, ~; c. ^, t8 |: q! e3 k( t
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
2 o+ X: x: \$ I2 E' v                                        DLL_PROCESS_ATTACH );( y4 ]' h7 U8 F2 Q0 L( G# X
        }* v) [2 L% l5 C/ K4 h
    }. v9 E7 l% E( ]% O1 T
    __finally
6 V% R2 Y* l6 t* e4 i  m1 S* A    {4 a, x6 ?6 f  ~+ a" e6 F
        //0 \. i- L% R& b% c
        // 第四部分;
( B' _2 v( v0 a9 m1 H& h        // 清除分配的内存9 S; T$ i1 }' P3 L, ~
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );9 S1 |9 t9 H1 R. H* B* l" }. B
    }0 b* d' d" d& ~
( S, S8 Q5 J0 ?0 E, e# m4 f
    return STATUS_SUCCESS;* [/ ?3 K# \- T: }- H
}   , `1 e% @/ u4 N0 v
1 w3 P" ?, ?& X5 L5 ^7 ~& X
这个函数分为四个主要部分:
8 o( p% R( I# U一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
# e! |, V) L/ y  ?- w7 j4 }( C二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。
0 z6 Y" p, A  P" U! g  j三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
9 V) q3 x4 L; ^! T4 P  `另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。' }5 k  G' C; y! y4 I
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
9 ]$ c0 H0 g% T$ I: Z3 X四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

Archiver|手机版|小黑屋|计匠网

GMT+8, 2025-12-1 07:14 , Processed in 0.401799 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表