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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
1 d4 w" w" p; a* J+ L4 R$ d
5 K" o$ ?' t' Y9 [WINDOWS 2K Dll 加载过程* A, n; f9 i3 V- H
jefong by 2005/03/302 Y  r! R. M' v+ ]6 ]. K
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。. a, t. e* r; a: b+ j) H9 I
在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”。& ]# v3 [: l  e: [
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。( L$ Y  x, D5 j1 Z
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。6 M! J! h; Q+ {# [1 M+ ]7 x+ e
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
. I& A* `8 _( ^7 C4 K  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。% n+ q/ w$ i) w" w- B
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
2 a; g& A, v, h8 x! {//=============================================================================
4 T& P- p& U8 S& F/ W// Matt Pietrek, September 1999 Microsoft Systems Journal
) z! W. n! X+ v1 A( y1 c7 X// 中文注释部分为jefong翻译
8 ?# U1 P3 J+ I/ S  b' c% h//
& i& l. K$ x! W5 ~# i2 m// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
3 ~7 o+ N  E* I1 ^" _) h//
* r% _# H0 @/ I, ^; Q0 O3 x8 ?// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
% q8 O6 R) {2 w2 s/ R. @, r( L. @& E//=============================================================================( {- C6 c- V7 q9 J1 _
  s9 c# |  z$ B1 k2 X1 v3 ?
#include <ntexapi.h>    // For HardError defines near the end
; D( Y/ h# r8 T' n! r4 l6 P
. M9 Y, w9 ]2 L2 v// Global symbols (name is accurate, and comes from NTDLL.DBG)" v. M  D0 X9 c, T9 K
//  _NtdllBaseTag
4 S  j/ x$ Z: X) a& v% r//  _ShowSnaps- r3 c% X, M3 }7 C4 j' Z" ^
//  _SaveSp
) }1 u, _* D; L; g4 S, n  S* S% J//  _CurSp
: o  Z& f; P8 K$ O' L* m7 f//  _LdrpInLdrInit& @3 _$ Y6 u- m  s* @% O$ M
//  _LdrpFatalHardErrorCount+ |! f9 I5 \% x+ `) [, t$ v
//  _LdrpImageHasTls
' }: Z  B, d- j1 l8 B* ?1 s' x' N$ I
NTSTATUS
% J# H6 S4 V) O: T8 LLdrpRunInitializeRoutines( DWORD bImplicitLoad )! r3 O" n6 U. L: G& _- T
{( n( q5 S. z1 v3 y# p- x" ~% B; w
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
5 d( r* y5 ]. E- Z    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
2 w& _# `. m/ ?* B0 Z5 }
# Y* l& ?4 k  z9 ]6 T! A    if ( nRoutinesToRun )& w4 A) p4 N% v+ ~- g
    {
/ P4 @1 ^- |2 q- O1 C% e        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
% g) `. Y/ P: S# e2 E% W$ v        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),% T& F# p% c9 i0 [; d7 `
                                            _NtdllBaseTag + 0x60000,3 ^/ [. I6 C& A' e' c5 n
                                            nRoutinesToRun * 4 );$ q* k5 n! ]( u! H" C- |2 O
                           
9 @9 K% O$ O3 U        if ( 0 == pInitNodeArray )    // Make sure allocation worked* V, ^, o: H) Q$ a
            return STATUS_NO_MEMORY;' y: k+ I" k7 Z. Y' g) v7 u; Z
    }
9 }/ S, [- W1 ?3 C$ A7 r    else' V6 r1 J* p5 q+ Z, n) g& n  i! M
        pInitNodeArray = 0;
# w, ^$ J$ J8 z8 `3 O, I: T1 z* V) ~% s3 u2 X' q( j% h
    //第二部分;
3 ]9 C/ `6 k/ a2 X3 j1 y% ~    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。0 g% A  p- u$ R" u& ~6 w6 P/ ]
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);$ \5 |* r# G+ y1 }# ]0 n& f
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;0 @# {& O8 u+ a, y( ~# g3 S% t
          b1 ^% S4 M  }9 |
    if ( _ShowSnaps )
7 F  O9 X  y: I* b7 A3 w    {
# }% H' ]& r- D% v3 e; j7 y3 e        _DbgPrint( "LDR: Real INIT LIST\n" );0 A7 O) h4 Q9 s) a; z  y$ r. x
    }0 A8 R! _' r7 R
$ P7 u8 Q( z7 `: ]" A+ b
    nModulesInitedSoFar = 0;
* P5 v# ^- C" O2 j% k2 i/ G$ b
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
- l# O; W( [2 F" d3 \    {  [% }  F0 B: ~; s
        
7 y- e% E+ b$ _' ~( n! M% g8 z8 Z1 J        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
& m& {7 U# ^. V        {
4 C" Z8 T& B9 s, p& R9 b" V            ModuleLoaderInfo  pModuleLoaderInfo;' `+ c8 t3 X4 O. G3 _, G% B
            ' R, b: `4 Z, p' R# F; c( O
            //+ e9 T* z5 J' P+ ?8 X( H. _
            //一个ModuleLoaderInfo结构节点的大小为0X10字节0 h0 }. O- s5 c0 G- y5 ~7 u) ?3 Q
            pModuleLoaderInfo = &NextNode - 0x10;! L% h& W6 j  k2 Q& i
            & P- [/ p- o! G( |$ w
            localVar3C = pModuleLoaderInfo;         
2 E3 `3 p5 R8 g0 z- `. I( r9 a9 d) `$ W* _4 M8 n( T
            //
% x4 w/ q+ m6 K) e            // 如果模块已经被初始化,就忽略
1 C# x# {+ a5 G! [" b$ x/ l$ V: g            // X_LOADER_SAW_MODULE = 0x40 已被初始化: E! {. i  a! h; G9 W* \) }* z
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) ), b" S: A) i& ~  r) A
            {
* s" Y7 d) K. x                //( f0 k+ E( y0 J" m( x2 o
                // 模块没有被初始化,判断是否具有入口函数; p5 c0 Y" d8 j
                //
/ I/ p! B+ H# f# k' o# y. w; ~                if ( pModuleLoaderInfo->EntryPoint )
( ]2 B' @% k. p+ m! k" `! }8 d                {5 i$ g3 E, Y2 G; L& [& ?  K
                    //
2 @$ _$ v6 Y; p: [6 C+ @                    // 具有初始化函数,添加到模块列表中,等待进行初始化
+ D3 P2 z* L+ s' K                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
1 b  `! F+ O3 ]+ `% v+ ^( l
4 k* Y  \0 O$ A3 R6 Q" s( h5 y: `0 ?6 O                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
- N% A- h5 ]7 {' S" t5 U$ U$ M      // 例如:7 E. i2 M4 G  t( _. E
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
" j' ~/ C* j4 D4 v3 `3 x+ q                    if ( _ShowSnaps )( D; ~" J3 x" p. J8 A) g
                    {
# f3 ?8 D  @+ o# @- a$ M                        _DbgPrint(  "%wZ init routine %x\n",$ v5 W8 N4 L  D/ [1 a& J
                                    &pModuleLoaderInfo->24," B0 U7 w% {3 j$ C; [: x9 n
                                    pModuleLoaderInfo->EntryPoint );2 z! r2 v/ O. u+ O( u
                    }
; x3 I) v5 M* A9 u( \5 r% |  K8 _2 }: t% |
                    nModulesInitedSoFar++;
  d8 v4 v, b6 `5 ~( @9 b0 r8 {                }  B4 }+ O  I6 v
            }  g& I3 G6 \' J* }  u
9 f" @1 b7 t! ]4 _/ P& u& Z
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。3 k! e: {+ v0 z
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;& p' M& h% h% R; N7 l8 i6 ~
/ H$ X) _9 i0 A- \/ G
            // 处理下一个模块节点
! X  Z5 z* c' P3 d8 _            pCurrNode = pCurrNode->pNext4 K0 T: L& h' H# F8 ]
        }
' f' E7 d8 ~: @    }+ G# l' [( X  C( f- Y
    else3 c: V. m! k+ `, a' z/ I- `5 |! a
    {
2 V2 \& c8 X  E! W7 E        pModuleLoaderInfo = localVar3C;     // May not be initialized???
  F9 E" s% H1 P% p5 H" ]+ [4 m    }- ?% S- a" O6 l$ N0 {$ n
    ! V2 c# \4 i  m, E5 q2 I+ x
    if ( 0 == pInitNodeArray )
) R- s* `8 S  K! p# a* I8 K0 J+ M6 G        return STATUS_SUCCESS;
$ B9 [: |. z2 A" ^7 M9 o# K7 ^
4 w% Q# E: [3 [0 _" V: l$ I    // ************************* MSJ Layout! *****************( H1 L' T4 m3 A) X: X0 d" ~! w
    // If you're going to split this code across pages, this is a great3 h; `  X9 N3 E- K5 K7 q( {
    // spot to split the code.  Just be sure to remove this comment
/ r" K  m: m! _) ]/ X3 O, A* B! h    // ************************* MSJ Layout! *****************, ?- A$ b8 F1 f8 `0 E7 s
   
0 B5 f/ W2 k) _. s    //
( I; T) _. M2 s! ~+ s' \2 f    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
4 e2 q6 B$ v# H. Q7 F0 w6 ?' ?    // 第三部分,调用初始化部分/ Q* S/ q, h! i1 x2 p
    try     // Wrap all this in a try block, in case the init routine faults
% N" h& x4 Z; y2 D4 U* k    {
6 j: q( o# r5 ~1 r" `8 ]( j2 E0 B        nModulesInitedSoFar = 0;  // Start at array element 0* X* Z) A8 F- P
4 f+ f" A# e5 Q8 z9 T) M! P% D
        //4 I4 K$ ~# z0 a' `$ R" G( m
        // 遍历模块队列
& [6 A2 y8 i' z. Y5 @8 Q        //
% G! c# \; ^0 z        while ( nModulesInitedSoFar < nRoutinesToRun )1 o9 z! W# m- o) F7 ~
        {
  ?# ^+ P+ c3 [9 c% V7 @            // 获得模块指针" k  D2 S4 K' ?3 M& Y) D
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];1 [9 N2 I1 ^# Y' {8 Z+ x' E
# @( r) D; {' A- D- _7 _: S- R
            // This doesn't seem to do anything...
2 ?6 o+ `  L4 Q4 B1 G            localVar3C = pModuleLoaderInfo;
8 T- ^( S8 }. n0 C- t            
/ |6 n; C  |8 H3 E) J1 S            nModulesInitedSoFar++;% A0 \, v2 {% b9 l3 M( Z
                . S3 L+ K% E, {( L
            // 保存初始化程序入口指针) C" f: B; P8 b- u' ~' k( m3 A
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
* a" `, x4 B  x4 b4 c: g: |            # N, ?5 \0 `* f3 n
            fBreakOnDllLoad = 0;    // Default is to not break on load* v; o# W. E1 X' z" a* S# K3 j

& c6 H$ Y/ E1 u            // 调试用
: K7 c, G; p2 X1 d, h1 o            // If this process is a debuggee, check to see if the loader) ?5 b8 V& d6 [4 ^% V
            // should break into a debugger before calling the initialization.. {, P7 b' B' @6 R% j/ z/ ~( ~
            /// J* q3 ?7 {' z1 U8 ?
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()8 @3 s5 L( ?4 M5 h% w& s
            // returns. IsDebuggerPresent is an NT only API.
- T# l# c2 }+ s6 W, E1 |            //2 A  k+ K9 F/ M; G' _& l
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
' I: t& C- P0 |$ M; D" b            {5 c1 z, Q9 T8 ?: {
                LONG retCode;0 H0 N: x4 O/ U8 z% z

& W* [- H* `$ G$ F; j, s# G                //              
& ^& Q6 ?/ V" i* ^9 C% {& {                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
$ `- F) X5 X2 H( m( Q                // Windows NT\CurrentVersion\Image File Execution Options"
' A+ |' U1 S3 G$ F5 b% P& U" z                // registry key.  If a a subkey entry with the name of: E7 i, b( }: t' p3 E5 F
                // the executable exists, check for the BreakOnDllLoad value.5 k' P+ b, h  ^1 s+ x
                //
6 h! b- W0 K, a: S" A& _* _                retCode = " h+ a" h/ a! i9 V+ Q) }! L/ q
                    _LdrQueryImageFileExecutionOptions(5 T, ^$ Y4 I8 f  r9 e- F
                                pModuleLoaderInfo->pwszDllName,
# u) \6 N/ c& |* T& Y                                "BreakOnDllLoad",pInitNodeArray
, k4 }" q7 j6 i$ E4 q                                REG_DWORD,
: u2 M* H7 J  S' }: T+ T, a3 r                                &fBreakOnDllLoad,3 T1 g: ~* \; x& D( R6 I
                                sizeof(DWORD),
) P% X4 k5 a( y7 P                                0 );8 Z0 b8 l9 d7 }5 X% L- s; A
6 [6 W5 Z( G! M. B
                // If reg value not found (usually the case), then don't! C# s  A9 j+ m, Y1 p6 R4 f
                // break on this DLL init% H) k/ i5 ^: t3 @
                if ( retCode <= STATUS_SUCCESS )
  f3 @: R6 P+ B! A. q% \                    fBreakOnDllLoad = 0;pInitNodeArray
  i* O( f  J2 W1 V            }4 c3 w  o2 Z* f  u2 U, y; ^
            7 u9 m% d) A" @4 T6 |
            if ( fBreakOnDllLoad )9 `1 Q, i* `5 u1 H- N: }
            {           * Z$ D$ ^' o5 s* I0 }  M
                if ( _ShowSnaps )
  H) j0 p5 Q1 R                {
7 F: U7 q4 B+ x% `4 b                    // Inform the debug output stream of the module name  j; F8 p! X: B# w0 B, I
                    // and the init routine address before actually breaking
3 L* W# a6 g' m$ w; B                    // into the debugger
" _  @2 M& n+ M  u/ k1 Q1 C
( N6 D, j( r$ Q* k5 M% L                    _DbgPrint(  "LDR: %wZ loaded.",0 m/ w) V* _* c7 x' [3 o
                                &pModuleLoaderInfo->pModuleLoaderInfo );" U  e0 ^5 a7 X
                    
9 Q- Y/ T2 m( D$ r                    _DbgPrint(  "- About to call init routine at %lx\n",
1 y) u4 D$ d5 W5 d1 @7 e) L  ~                                pfnInitRoutine )/ J6 {8 k! y: B  p  u( x
                }
; Y& b" t0 s& U3 h               
9 w$ {1 }3 V5 ^0 s, E3 ^                // Break into the debugger                              6 I! [5 K0 q# |
                _DbgBreakPoint();   // An INT 3, followed by a RET
$ c7 ~' d1 u- y# m. R4 ~2 ?            }; G' ?8 W" P( Z. Q! {" j& P/ e. d
            else if ( _ShowSnaps && pfnInitRoutine )+ Y" o& z$ E7 P* K3 X
            {4 H, k7 y9 [4 c4 u; }" _- B5 A5 b
                // Inform the debug output stream of the module name
. n. G/ g% @" O0 Y: c7 ?7 b                // and the init routine address before calling it               
* [. X; f" w' n' }9 ]                _DbgPrint(  "LDR: %wZ loaded.",
" g. A- S! z$ e                            pModuleLoaderInfo->pModuleLoaderInfo );' C" `! f" l) T4 K2 ^& A

* {4 v# I) e2 M/ O                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
( U4 r: g- P" ]" H& k2 j            }6 a+ Z5 m; C& u# _# F4 C# E
                    
+ R1 f# R; c2 G# \: j. L            if ( pfnInitRoutine )
7 E6 |& s  w4 k% Y' c# P            {' `( K* z8 V' c3 {
                // 设置DLL_PROCESS_ATTACH标志
1 B$ M0 F. K( c                //
5 s" E; z7 u7 d9 j                // (Shouldn't this come *after* the actual call?)
& B- v$ h* ]0 E( L3 x8 v1 l                //
' v$ e8 K$ U8 @; m                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             " [- I4 e; u! m2 G
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;$ K7 ^6 t; p9 u  K6 N( X
+ @% K" b+ e; w8 V
                //
- V& K: J! H6 W/ z5 h2 W                // If there's Thread Local Storage (TLS) for this module,: k( Z: v/ b5 w' A! D" V
                // call the TLS init functions.  *** NOTE *** This only
  Y' I, d) t% h0 a/ s" r0 D                // occurs during the first time this code is called (when9 J" ^* L7 J+ @: A4 `0 N6 C
                // implicitly loaded DLLs are initialized).  Dynamically
* r" c5 L0 p' B                // loaded DLLs shouldn't use TLS declared vars, as per the7 l. Y' f6 B; {2 |. L
                // SDK documentation2 i0 L$ {1 Y# M& p3 b6 m
                // 如果模块需要分配TLS,调用TLS初始化函数0 e( i5 ~) v4 x$ T4 U/ R8 [
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
9 W( j0 \3 ]; ^+ c  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量5 h# H8 S; o3 `; a: n# L6 a
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
( ^$ k9 b' R# N, K                {6 S- ~' v' ?, S) |# y
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,& y. P' x9 ]" A) o
                                                DLL_PROCESS_ATTACH );+ x; V! m4 T; a+ U" G' w
                }
+ V+ ~" C7 K# E& v- [/ P/ C                9 `+ C& G& ^9 E

  @2 |2 f! K' H7 e( y                hModDLL = pModuleLoaderInfo->hModDLL" l- o0 l/ e, t
" P9 s4 P: [* @! }! U+ ~
                MOV     ESI,ESP // Save off the ESP register into ESI
+ B* i1 S/ ~, i3 l. t  
1 `/ `! E* F) @5 r; [" u  // 设置入口函数指针                $ _$ n8 U  T$ }2 y" d2 f6 G7 X
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
4 }& g( w# F) b4 ?7 r( J( Y2 E& z
+ ^" a: Q! n) ^  B) N                // In C++ code, the following ASM would look like:. @' `  K- R/ r7 K9 v' @1 o9 B
                //" Q' @0 Z1 C" D$ n. j/ p
                // initRetValue =* l6 J+ L  S: |! M  y6 m$ ~( P5 F) ~
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);9 H" j# I: C! z7 y
                //$ O! a) l, X, ^
* T7 H4 i4 w! h& _% V
                PUSH    DWORD PTR [bImplicitLoad]
/ q+ v+ D) H0 j0 C! I* o9 v                : H+ }: d+ h  T
                PUSH    DLL_PROCESS_ATTACH  H7 J% c4 [- [
                1 q5 D3 T' i6 I
                PUSH    DWORD PTR [hModDLL]+ T, I5 q# b+ C8 Y. G+ u
               
, H1 X7 b; D) V, J. s                CALL    EDI     // 调用入口函数; z% O- }/ @# ~
                ; Z/ n6 k0 L+ c' e( L6 t; I1 }5 J
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
: f  W& J' F/ A/ j; x4 b$ i; v1 X% Z+ }! p4 j
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
8 l4 D' f$ J0 |" n0 f# B                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns! h' K$ v6 k0 g$ s- K7 g
2 W& o9 _1 x0 Z4 u% c
                MOV     ESP,ESI     // Restore ESP to value before the call5 K! o/ H, i( q' k$ I9 V. I8 p
% \3 v- ?3 U( C  B
                //) ?8 R4 j% k: a" c1 C5 C; O
                // 检查调用前后的ESP值是否一至
0 Q3 ^0 Q4 s1 j9 `0 i! n7 [  //
1 }2 m6 C- `  ~. |# ?! m  z                if ( _CurSP != _SavSP )
& z9 y9 {) P7 r                {
: p+ k: S+ w4 K                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
* P6 B. a( }8 K2 o- F- U7 f
% H0 t/ R( C, j                    hardErrorRetCode =   N( C# D+ r* }+ w
                        _NtRaiseHardError(. a2 w3 F1 V  U2 I2 v. i
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
$ `1 e. Q+ T7 V2 s+ X5 _  D+ r                            1,  // Number of parameters
" p4 Q& b& o, ]6 }% T* N+ C7 ^                            1,  // UnicodeStringParametersMask,8 N; F, f! m, y( l: ~" U/ {
                            &hardErrorParam,
9 H7 y8 H# n6 o, l( l- i                            OptionYesNo,    // Let user decide
! v# `2 T1 ?8 x: E( M2 v; D2 A                            &hardErrorResponse );* y9 U) P+ t9 P3 G8 O8 U$ i
                                            
* R% k% [3 k- c- U; q* o6 w                    if ( _LdrpInLdrInit ); ~; W/ W6 r; v+ Y* a
                        _LdrpFatalHardErrorCount++;
: z' g, f: d. d# W6 x, B: O& }$ I: ]) P
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)7 s2 |* x' X" W5 n
                        &&  (ResponseYes == hardErrorResponse) )8 ^  `6 [/ s# k/ O
                    {  Q' y: c# H6 P- y1 \$ I; v
                        return STATUS_DLL_INIT_FAILED;
$ O5 X/ m! q( V7 O                    }1 T: X5 S4 c$ o( s
                }0 i8 Y7 G* f9 Y# C' T: B* h' d4 U
( p0 Z5 _4 M4 o. z
                //
" v# a' M2 @, g5 i0 N                // 入口函数返回0,错误* H+ Q4 V5 X7 [+ D" r
                //9 r0 I/ s3 R: }) D4 [
                if ( 0 == initRetValue )7 J6 p' B1 g9 u
                {6 H( }3 g5 y9 m2 L) y3 K
                    DWORD hardErrorParam2;
  m8 I8 l0 r+ K1 B& S# r9 m  L, S                    DWORD hardErrorResponse2;' X/ D& j! u( R" @
                                        ) U) g  x8 [4 V, L" s8 c% m: t( e6 ^3 Z
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;6 T3 `* k% B: N
                    # ~& s. k* q7 G
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
) g' G  Q; x- r: `1 _6 e  g0 Y                                        1,  // Number of parameters# L2 j) e% N7 {. _4 z- Q
                                        1,  // UnicodeStringParametersMask/ s# J4 F0 N  M. E7 `
                                        &hardErrorParam2,# q: T2 }, m# E( B3 t' f* d3 D
                                        OptionOk,   // OK is only response
' v/ T% b% w3 e                                        &hardErrorResponse2 );* C" V. B6 R' y' o  @# ]
                                                            4 T# p: ?# C% ^
                    if ( _LdrpInLdrInit )4 Z9 W+ l' m" O1 s/ ~3 p' `1 q
                        _LdrpFatalHardErrorCount++;
2 O( B1 k( X' g5 X) n' F* p8 ~  [7 p+ L5 n- [5 B  t' n
                    return STATUS_DLL_INIT_FAILED;: _4 L' S/ z: [) Y( ^2 G: W) ], s
                }
& \: K; x. L4 n  F' X( ]            }
8 x; C8 B, G# X  N0 Q' j        }) [. H) b2 u1 h- D7 {9 a
9 k% k* M# j3 U. s- m( m# G
        //
$ P7 M% M$ }% N9 b5 T* Z6 d) q        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
% x$ W+ I& c' r4 Z# F        //      
8 [  Z% i7 d+ A9 U7 Y- b0 k8 U        if ( _LdrpImageHasTls && bImplicitLoad )
) b! Z# V% F/ x# z2 L% z. `7 t        {
# t( Y7 c# T% Y1 v5 d  m) a            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
0 Y7 y/ h3 j2 }7 ]) K                                        DLL_PROCESS_ATTACH );
; e, M; N! K9 Z7 G7 w+ p        }
& H9 J5 q$ s7 |1 Y, u    }
( A8 O3 Y3 ?1 j" _    __finally
+ c2 o9 A$ X1 O/ m' Z. T    {' i$ j6 Y1 N" n* e$ K5 J% c
        //
) d) f% q+ N5 f; L3 E. s        // 第四部分;$ A$ C/ P" T. ~# [( [9 S' a+ \, D3 C
        // 清除分配的内存
" [$ b; h/ v2 h% X" U' o8 s- ^        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
2 P4 U- i, y. S% q; [6 t, i    }
3 o* B+ U: v2 T$ D% S2 k: c, g, \: W. v; y( o: n9 b
    return STATUS_SUCCESS;
1 x' `3 W( b& \/ V}   
5 O2 T0 ~& Y  i3 R5 O
! F- O6 @6 r6 y6 j6 Q2 f9 O5 Q5 Q这个函数分为四个主要部分:
# ^3 s' s, `  X4 }5 S- e一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。: z  p5 r/ K4 O
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。! U: j; r: F8 m: j
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。) S$ e$ F6 \; U( h7 C
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。- d+ h6 \2 _3 t3 d* M( L
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。% Z4 m, F% U- Q  _; x/ X
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-6-4 10:28 , Processed in 4.712725 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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