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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
7 r/ u6 H3 {. O! t
9 ]5 E/ F% |+ {9 p0 R6 ?2 E, Z5 O  lWINDOWS 2K Dll 加载过程
" l% u% v2 B- z* S  J$ ^% Q4 Fjefong by 2005/03/30
7 P* @2 M. [! D' I这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
* K, J, P+ i: p: L在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”。
# P' h+ H. X) O1 P你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。- x+ e. a+ Z. I- I: d9 H
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。, f' T# Z+ c: L, n
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
* Z/ p/ A! e& a! u8 x  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
; M; ~1 M" L1 K! i% H  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):& o: J) ]# B8 L; J* u) k* \
//=============================================================================. e8 V) X; l, j5 H& Y, }2 h% R" ?' \2 U
// Matt Pietrek, September 1999 Microsoft Systems Journal
5 h4 n! ^/ {6 J( H" [! J3 R// 中文注释部分为jefong翻译
  ~/ e$ F+ m- ~! v//6 i3 k6 _* B  b- t4 F* j; ~
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
/ _9 I5 i5 E& L" J//' A- ~; T! {/ F" r6 V! b( w9 W$ _; ^
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;8 d0 J0 K2 x5 t) o, t5 \# ?6 x  i
//=============================================================================
  F7 M* n; a1 b9 G1 y
  c: ?6 b+ M& ?! E/ @8 L1 @. I; {#include <ntexapi.h>    // For HardError defines near the end
, F% M; S3 a1 X( z- \7 k4 f1 N1 o$ Z) i% u
// Global symbols (name is accurate, and comes from NTDLL.DBG), X; M' ]/ _% _5 C6 K! e8 s4 p
//  _NtdllBaseTag
# L: F4 Y# k, O7 e" b3 x//  _ShowSnaps
/ p5 d" N! F1 G/ M3 v7 Z6 g9 @//  _SaveSp( Y/ Z$ b% l  u6 u
//  _CurSp* G& V; Y4 x1 \9 D4 ~- g
//  _LdrpInLdrInit( X( A2 S2 Y* @6 V, A# Q8 K! g
//  _LdrpFatalHardErrorCount
, m5 G( w9 t$ G' y//  _LdrpImageHasTls6 ^; V+ H; @* G0 d& A
% w/ K& I4 }' H/ b
NTSTATUS
5 \( i) r; T% D, o8 v; [6 ?& CLdrpRunInitializeRoutines( DWORD bImplicitLoad )& e1 l( h/ D  q6 T9 a
{) s) p& u  c: E. U+ B- A+ k- _
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了5 F* Q  y6 \% F" c4 D* K
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
8 Q$ m' k8 P% Q# ~' A$ i5 Z
  b8 G- ^3 y3 S# R8 g5 }: ?+ K    if ( nRoutinesToRun )$ F7 }. A( n+ G
    {! o! B( r# f* V7 V* o
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。2 S# y" V% n2 A
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),  {$ H. j6 O& G4 H% w( T; S
                                            _NtdllBaseTag + 0x60000,
0 c* j$ i- n& ]! x* C                                            nRoutinesToRun * 4 );% m! z7 x: k8 a( d& X) C
                            3 `# r9 F, ]5 O' W" M9 f
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
$ ]1 G1 R; U) c( s            return STATUS_NO_MEMORY;4 o" G; Z2 c6 S4 ^2 z- t
    }
- h8 W7 A+ w3 @7 N. V, L! _* r    else
. G8 a0 N2 i( v, \- r, r- {3 C        pInitNodeArray = 0;- B0 b; n. O( P2 X; _2 E2 @7 l
- M2 O; |! ?, x
    //第二部分;
# I5 g+ C. v. v    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。+ X# f/ t$ U# O4 s2 a
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
8 S/ P% j$ o7 ]+ ~7 a! o    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
6 w- R7 r" {2 ?' w+ d$ D5 _        1 W& h! W3 L% f% y/ z
    if ( _ShowSnaps )$ Y$ R3 w# X5 v& u4 W5 ?/ Y8 l
    {5 i0 o, c( {% ?% m) E, _( ?* t
        _DbgPrint( "LDR: Real INIT LIST\n" );3 I& c& {$ u8 L2 t9 M; Z: r
    }
$ s( h# x! v0 X* _3 @( @6 |( R  J% B' H3 C+ E
    nModulesInitedSoFar = 0;
2 _  b+ R1 H$ g
" W9 R+ U/ A( V  O5 O9 c    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
7 q8 l4 V7 U/ x2 E. r    {& I1 T7 N4 I( y( ?7 d. o+ b: V
        
( n% m% j, O% u4 n! ?# R        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
: g$ d' l8 d/ ]- J  u# b        {+ F: m; q8 |4 V1 b, B# N7 t/ T
            ModuleLoaderInfo  pModuleLoaderInfo;
0 P) I9 I; Z+ _4 Q! w4 I. S            
% h0 s& R5 ~) Y! O0 w5 h            //! n' R2 d2 d& @- ?$ _/ s* J
            //一个ModuleLoaderInfo结构节点的大小为0X10字节1 A' u0 \- }1 s* [- T
            pModuleLoaderInfo = &NextNode - 0x10;5 e' X: o. o0 m
            3 H5 N9 L; i3 z0 E
            localVar3C = pModuleLoaderInfo;         ; j+ B) T2 D3 h, e* W9 v2 M  h3 s5 E
# j) Z) J2 q# _$ j+ U* R. b0 Q
            //( _+ U0 c7 m" K5 p3 z; v) Y6 ^2 ^
            // 如果模块已经被初始化,就忽略
2 Z8 J$ R: N4 v            // X_LOADER_SAW_MODULE = 0x40 已被初始化; X& K1 ?( Y) C$ q# x( F
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )2 f9 _# T$ u# y, Q6 _
            {+ z. k3 x9 p3 u$ M
                //
1 f7 B" O3 l( h2 [                // 模块没有被初始化,判断是否具有入口函数
! W. y; _2 E) s# H                //
' L- W8 ^7 j3 t& }- w# ?                if ( pModuleLoaderInfo->EntryPoint )# [' z0 o+ ]" W9 c& @3 o) h  x' |7 g
                {
. B" s, L* K1 g) c! \3 z                    //0 K$ h1 a2 r5 N6 t
                    // 具有初始化函数,添加到模块列表中,等待进行初始化6 o; q' U9 c9 z6 ?
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;: f8 h/ B* z; N/ b9 ?

' y* _$ ]! q1 U* ^# U; ]                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址- ^" Q7 r' R& A, ~
      // 例如:7 C: b0 Y0 \- Q0 }6 A/ z3 f
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
5 B0 [6 F  I. R, ]6 u! q3 ^                    if ( _ShowSnaps )5 x: l# O1 [* e
                    {
: }. j/ Q8 L# x: w, X! V9 T                        _DbgPrint(  "%wZ init routine %x\n",
) ], |. M' x& V! R: i3 ]                                    &pModuleLoaderInfo->24,
) @, J. O9 w- k/ Y7 H6 K                                    pModuleLoaderInfo->EntryPoint );
% z$ R/ N4 K+ R1 B6 _$ P5 w+ I7 j6 G                    }9 t, R, ^" n5 J6 U4 R
3 R1 L4 K# Z! f3 D' }$ P5 r5 i9 b
                    nModulesInitedSoFar++;
0 a+ c& Y5 W9 M2 M7 O) y+ ~                }, p" r+ Y+ b; }$ U
            }
6 c/ b8 s' K  E, ~5 ]$ z/ w9 G2 i# P" |& v* W. a( D
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。* V' f; Y: w' z3 A( }; q( q: P
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
, r# z( n. s9 M6 w3 D, J- Q5 ~- A) C
            // 处理下一个模块节点; G0 a  W, ?# B5 Q
            pCurrNode = pCurrNode->pNext
9 E( c* N, t/ p# r' y        }+ M- c2 o3 ^* ~& v) `# S
    }4 D/ E, b# @5 ^" J2 L6 u
    else
/ w4 U2 H  ^6 P% a    {% Q3 h' {2 y# Y  k+ A0 N' I
        pModuleLoaderInfo = localVar3C;     // May not be initialized???" o6 b. [4 u0 @* u" |6 T8 }0 L
    }
1 P$ m( v1 o, X9 A9 Y( U8 m% Q    " O- c& e0 y1 }/ N5 W2 S9 y  B, F
    if ( 0 == pInitNodeArray )+ Q) B2 n4 C' J; A
        return STATUS_SUCCESS;" T5 m! B$ C: V
6 D- C% B1 T5 ?$ |6 s' A9 S
    // ************************* MSJ Layout! *****************$ P* B4 o8 ^- l4 o9 g8 {5 j; q) ?
    // If you're going to split this code across pages, this is a great
" `" ]  ~3 P* p5 x    // spot to split the code.  Just be sure to remove this comment
  W+ A. s4 ?( O    // ************************* MSJ Layout! *****************& N7 k% }% I# I
    ( ~/ `) F8 {0 V9 G( c5 r* ?* l
    //
) H  w2 {. r( |  O    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
' _% D9 b/ D1 t! c: [" E- m7 }8 s    // 第三部分,调用初始化部分2 H4 e# |1 o3 B& t
    try     // Wrap all this in a try block, in case the init routine faults1 h6 U& Z; D0 H
    {
7 P9 e6 T! F, w2 S6 v- a        nModulesInitedSoFar = 0;  // Start at array element 0
- \0 ^* y5 O  |# b/ C: C7 ?/ I. I% f. ]3 o
        //
: U% K5 R7 N( B# y( u/ n        // 遍历模块队列+ Y6 F* l; N, c3 u8 H, ?. n
        //9 Q0 {8 E$ H/ {9 t
        while ( nModulesInitedSoFar < nRoutinesToRun )0 V' c9 U3 |) D  X. q$ w
        {
4 S' C7 m% |8 j1 l8 T% B. I8 [* z            // 获得模块指针3 q/ F% _8 i% [1 w7 f& B6 ]
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
. {* L2 m! N. w& Z2 ]
% u8 ?6 Z0 e& Q+ m( V( A6 F' {            // This doesn't seem to do anything...
3 M1 A1 t3 }- ^1 |  O            localVar3C = pModuleLoaderInfo;# r: l1 D, {* I3 f$ Y% t) ~
            - s* ^& N3 O; T) l
            nModulesInitedSoFar++;
& z: ^/ e% V+ g; B$ D               
9 v7 B# A- y8 J9 R  l2 M" U$ |: _9 s3 l            // 保存初始化程序入口指针9 A+ x) W7 U, B, w0 `6 B- p. ]
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;! E7 l8 e3 v+ f
            8 ]! i" j" H0 X+ `: u
            fBreakOnDllLoad = 0;    // Default is to not break on load* E3 m& c! l3 c/ X

! |/ k& Z8 ^, b8 a            // 调试用
0 P0 A" V0 d5 B" |( G7 P            // If this process is a debuggee, check to see if the loader% K& V& {8 h& |0 Z: |; [; h' E
            // should break into a debugger before calling the initialization.
; }8 \' q) e: N6 m' x; ~& k7 H5 z% j. {$ Z            //8 i8 r$ x  ]7 {+ F
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent(); e$ M  c. b. K) \
            // returns. IsDebuggerPresent is an NT only API.5 f- y9 M' t3 }6 i
            //
5 }: w! h& W- ?  J/ w            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
. E" y# t$ W/ m0 K            {+ H9 A2 N( T3 P' I4 ~! j; O3 ?$ ?/ e
                LONG retCode;
8 x1 i2 S$ c  Z- w3 P- @0 J4 X. j- h! s6 p$ {# i0 u: V% l
                //              
. v9 q/ }: h7 W& d                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\9 B3 V  t4 A+ b& U# j8 w+ [5 _
                // Windows NT\CurrentVersion\Image File Execution Options"' A- l) f" ~, ~/ s7 |$ r$ X
                // registry key.  If a a subkey entry with the name of
" I2 ?% a. p7 m                // the executable exists, check for the BreakOnDllLoad value.: u1 M3 w" v, f, [
                //1 N8 d8 A% V: s7 q3 N8 o( o
                retCode = + k. h  P9 I! R" q% r  n# J4 _
                    _LdrQueryImageFileExecutionOptions(6 w" v& h2 x7 F" K
                                pModuleLoaderInfo->pwszDllName,( \4 X- x0 p( B) s/ M, r7 t7 O$ M
                                "BreakOnDllLoad",pInitNodeArray
+ j% `/ D! o9 s1 a( i; I                                REG_DWORD,
3 v3 N: ^% H) Q% s5 I' c                                &fBreakOnDllLoad,4 u0 i8 t# i% U# w  m5 g
                                sizeof(DWORD),5 b; X# Z$ V& S7 ]
                                0 );
7 O' u/ L4 g+ g5 q4 X
1 W& I; y: }/ j* a                // If reg value not found (usually the case), then don't! `. V# x4 ~8 k- q1 W' S. O
                // break on this DLL init
! _( b; w! U/ B1 c/ f  _* c. D5 J                if ( retCode <= STATUS_SUCCESS )
2 h' e  b7 I' C: {: {- X                    fBreakOnDllLoad = 0;pInitNodeArray2 r' a; Z& s* g
            }" [$ f5 E+ ?$ ]' K9 p$ E2 ]$ h
            
2 M3 ?8 P" |- ~0 Z5 O  L            if ( fBreakOnDllLoad ), P* v* o2 F) k: D8 o* e9 _
            {           ) i. x. _2 K0 X  t* b9 I
                if ( _ShowSnaps )
5 }8 D* a( V" W, }: W                {! M! J: X, }/ `. C
                    // Inform the debug output stream of the module name
) h' ]6 u5 R/ M9 @5 V7 l                    // and the init routine address before actually breaking
+ D9 ]5 s' _8 `1 I& g# T& e6 J                    // into the debugger
1 z7 Y0 c" ~/ s4 d1 z8 ]
) D  H1 L( F+ o& i                    _DbgPrint(  "LDR: %wZ loaded.",* Q" O7 j" k; P6 x/ [
                                &pModuleLoaderInfo->pModuleLoaderInfo );0 W, C$ F* v6 s
                    * z, {* m, X* l" X8 s# f
                    _DbgPrint(  "- About to call init routine at %lx\n",
3 f8 b8 f" N/ K% \2 H                                pfnInitRoutine )2 Z3 {5 R: P7 A# k! X. j9 r, c9 C9 b
                }
: X/ S6 i5 J2 [4 D( {1 z                9 p' y- H* d! W
                // Break into the debugger                              
- X9 |$ v0 _1 ]; Q9 \; H/ z& o4 }                _DbgBreakPoint();   // An INT 3, followed by a RET! W! e, L! V- Q( c5 G
            }  \! m+ p  \5 l2 v4 M
            else if ( _ShowSnaps && pfnInitRoutine )
" _4 r- i) f3 |  M, r0 F) W: W            {: ~% f1 T" w5 H  x; a: y& r: M
                // Inform the debug output stream of the module name, @, k4 d# `5 ?% \& f$ q2 x
                // and the init routine address before calling it               
5 }# L9 ]+ P  f9 G  @. L; Y                _DbgPrint(  "LDR: %wZ loaded.",1 X5 j* r$ @' L* H" ?  S
                            pModuleLoaderInfo->pModuleLoaderInfo );
! p5 }! C/ d$ s: e+ f7 n* k! [+ O4 f
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
$ S& B3 C. V4 x4 s4 q9 A( t            }
3 X6 N# a, c7 K0 a                    5 i# w5 p% S, D0 x0 ]  W# I( P
            if ( pfnInitRoutine )
6 ~  N/ m0 w4 u  J            {
; J  ]' C+ y7 P( K& }7 f                // 设置DLL_PROCESS_ATTACH标志
3 j2 L# E5 D! P% G$ A+ `                //
1 ~/ a2 W8 d. [/ T2 m, [                // (Shouldn't this come *after* the actual call?)9 R' Y1 }+ _4 V7 h5 h# `0 A
                //9 @9 [. ]; r0 }; o: L
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
$ Z1 Q0 A# Q4 z, _7 |                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
) l5 e) i6 D1 X  B- }& O5 ^" \0 T/ H9 c
                //; X, L' {* j9 n8 u% U% T$ ^( ?
                // If there's Thread Local Storage (TLS) for this module,- h4 F' S. W7 Y9 r: Z0 S$ M8 |
                // call the TLS init functions.  *** NOTE *** This only" [; O. J: [& E0 f& J: j( B6 g
                // occurs during the first time this code is called (when0 V7 w. E  b7 e+ d& ?4 s
                // implicitly loaded DLLs are initialized).  Dynamically) q+ P0 u% T% ]
                // loaded DLLs shouldn't use TLS declared vars, as per the3 K8 `5 K% {) \
                // SDK documentation
% C; I+ a1 `3 J( y% i2 J: ]                // 如果模块需要分配TLS,调用TLS初始化函数
- D* `, y! U/ x" A# O0 \  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
$ g# X! \) `( J+ j* ~( j  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量, X: {$ X$ l: E; T" K; p  W
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )) |! f- `$ |/ x  d
                {
# Q( U5 J2 B! V                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,+ [2 m* t5 h& N4 U! @3 w
                                                DLL_PROCESS_ATTACH );
" q6 H. e/ C! h2 V                }
9 W8 R2 O5 z) @( X               
5 F8 e0 Z) U4 J2 Z
4 T& f' ^3 D: C4 W% i. E                hModDLL = pModuleLoaderInfo->hModDLL% j; a! M6 R9 q
: b* N+ H* }% i& i$ a2 h3 b
                MOV     ESI,ESP // Save off the ESP register into ESI  @# b# c: C+ e& T5 p
  / i' v6 O! m/ ?: X! Z
  // 设置入口函数指针               
% @7 ]; j" ~( n" b( a! |6 z- z                MOV     EDI,DWORD PTR [pfnInitRoutine]                     & b3 t8 \* a; ~. s# y
' y- g: _8 C; ]
                // In C++ code, the following ASM would look like:
* ~3 n7 Z& ~  s                //
% f9 r8 L6 T( Z: Q. Y! p                // initRetValue =0 q3 c8 _* K* d* m$ r% \; s
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
8 {7 f7 `+ {, Y                //& X) d9 e6 h2 x6 W
1 G! U/ v* h) [
                PUSH    DWORD PTR [bImplicitLoad]) D! T. p+ `- ~' n: k& q7 A
               
3 y2 M: M3 z' i2 X* T9 v( S                PUSH    DLL_PROCESS_ATTACH0 c3 E$ r; j3 I
               
1 n; x  C* y4 R. ?" p9 b                PUSH    DWORD PTR [hModDLL]
* u) a. E" c- o9 L                ( w3 y( ~* x, K+ Q- J" n) k3 _
                CALL    EDI     // 调用入口函数
7 E2 W# g* {4 O5 c                  i2 e" B7 J; P+ S/ A
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值2 ]6 ?1 ^5 T7 M$ m% a
3 E& t5 E! `4 C& p5 Z( F/ [
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
* {; Z2 ]* @3 x- U' A6 P                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
0 `1 r1 m& ]* {) w+ `
/ W+ f$ e- H  k4 ^: v                MOV     ESP,ESI     // Restore ESP to value before the call
. k- Y' P8 B) W# [" A/ F0 D
" r! j( R4 A# y, r- T+ X! u                //
  a7 @  N; m! ~                // 检查调用前后的ESP值是否一至& t, P5 b" S; ?9 M4 }$ R, d- |
  //
) g& _, p4 q) I" ^                if ( _CurSP != _SavSP )
  c; i0 y) Y4 B4 d; L' d                {
; m- u) T& P6 t7 l2 y& v  W: t9 F                    hardErrorParam = pModuleLoaderInfo->FullDllPath;: R% G. @" w7 I1 j6 p

3 t3 B5 y9 t! \' b- L# e& U: D2 F                    hardErrorRetCode =
% }9 X% x' Z% Z4 F! k* {                        _NtRaiseHardError(
3 s% g* n6 o6 Y                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,  U" c. r- ?7 l1 x6 f) o" Z
                            1,  // Number of parameters
- r# \: p) P4 t% D8 |; u/ _, V                            1,  // UnicodeStringParametersMask,+ R+ q4 c3 a; s
                            &hardErrorParam,
/ W2 A( u) e3 U" l% k                            OptionYesNo,    // Let user decide
/ }- l: x% ], b+ u# ]" N3 k" W0 z9 Q: q                            &hardErrorResponse );% V" l' ?6 m+ A1 l& ~1 [
                                            
0 H/ y, e( y$ I4 S4 e                    if ( _LdrpInLdrInit )
& `9 w. J( L0 j2 E/ L2 H                        _LdrpFatalHardErrorCount++;$ z1 m7 e9 W# Y- A; \! Q3 c' ^
2 `. x$ o2 `3 a% p
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)# ]8 F5 l5 V& d% G1 I5 Y
                        &&  (ResponseYes == hardErrorResponse) )/ J3 E# M: W  c, L* x9 P6 e
                    {, k! _8 e1 t( E( Q
                        return STATUS_DLL_INIT_FAILED;2 k+ k6 @8 D7 E1 t0 C/ Y& N7 f
                    }! `0 X1 K  M" e8 K; @2 o0 E
                }
4 e9 R( h; }; b2 Y2 F7 k
/ q/ W  n4 h' n1 m                //
3 y# I* Y- K& r* h8 [                // 入口函数返回0,错误
7 f2 L7 [1 S" b( l                //( E0 u4 p# H' u, [( G8 ?  [
                if ( 0 == initRetValue )
" c! r3 Z1 G) F& |4 Z; t                {1 f) F4 Z& E( j$ m+ e& w8 q% _
                    DWORD hardErrorParam2;" k2 y9 h- o( i9 A2 n, b
                    DWORD hardErrorResponse2;
! S7 P- R) E/ c                                       
4 o+ m8 ~0 N# _, S' l7 o                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;8 t% T6 ^- |3 D  p
                      q: ^9 p5 l, Q3 J8 m
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
) s! ^' P! h' g( P4 V                                        1,  // Number of parameters; T8 G3 p$ e" m# a  G$ |0 I+ d- k; c
                                        1,  // UnicodeStringParametersMask- w4 a! O, }: Q7 J* ?: E
                                        &hardErrorParam2,3 j* {3 B# K0 T3 Z" z; O/ y( N
                                        OptionOk,   // OK is only response8 x% Q& K, ]; |* |$ B4 @! B
                                        &hardErrorResponse2 );
: S0 b7 r  P3 h$ m  B                                                            
. o/ z4 t9 I  B8 L. c                    if ( _LdrpInLdrInit )
% u, T3 f/ F) a4 A8 q; t                        _LdrpFatalHardErrorCount++;0 M5 G% U% G. L! c1 D/ Q
- Z! I( _- W, b
                    return STATUS_DLL_INIT_FAILED;) W: U8 \4 N+ B: G- \& `4 |
                }9 R- J$ ?; x3 H
            }, E5 c( c$ U% c
        }% _3 l( F+ Y) Y& O; B4 M

" C2 n  V- P4 u        //
7 Z/ `8 E* |: G+ [$ U7 o$ D        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
/ i2 \: U! \/ r        //      
. _( J, B' J% `+ P8 ]$ y# ?" K$ O        if ( _LdrpImageHasTls && bImplicitLoad )8 `8 }4 t2 P( \) `* u3 x3 t3 j  X
        {
% y& M9 W+ J' Z/ c2 n5 d  j  M            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
9 q9 V2 c; X# t( U  v8 z3 D5 |! N                                        DLL_PROCESS_ATTACH );
! h' @$ a/ l- p* L        }
( \; G  i# v5 [% F$ y7 m    }% H: l5 H; u0 B) }5 B8 y* n9 _
    __finally1 W8 l: L) F+ U6 k8 g# F
    {" T7 A0 u  r+ O) y
        //
0 }9 u0 b0 o+ j3 ^! a0 m  v* L5 {; ]        // 第四部分;; \& R2 c4 B8 f/ S! T" O
        // 清除分配的内存
( f8 v0 x. D% _/ m6 f- ]: \% @" y- P        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
* n( e  H$ e8 J$ y5 [$ @% m" z    }' f9 f. S2 O. k+ {8 D7 z# \' [
9 @+ B4 e7 h: ~! C
    return STATUS_SUCCESS;. [: B! R* z7 c7 E: ?& d
}   % w, y; C  b% R) b

/ H: b7 E2 {! w& l这个函数分为四个主要部分:4 y- l$ R* |  Y
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
6 n7 k2 h9 b/ n二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。" [( Z9 D1 `# i1 W1 S( U
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
6 S/ g4 ]* a) m0 w6 U- |7 Y另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
4 ~- k3 V# Z: u  c# W在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。/ @0 I& x- P" q7 }3 L
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-3-5 12:44 , Processed in 0.576377 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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