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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]' O1 Q& g' J) D5 k2 L4 g) f% f

! d) ^6 u1 y' }/ s, b6 `WINDOWS 2K Dll 加载过程
  G% }% K8 L. {; q0 K" q# \; rjefong by 2005/03/30
% J2 G5 V6 u2 P# ~" q这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。8 g" J( v: p& Y: F* n3 M5 X' w
在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”。
1 B1 p1 b2 {( l- W你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
8 p) d( }2 c  f初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
' Q( S7 x) [; ?  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。; @' R2 M+ B  [/ z; Z, W" j) y6 D2 z
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
' [5 y1 Z) l" L2 J" A  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):3 A; u# l. {( N2 [6 [8 G
//=============================================================================$ T" X, v* ~8 W
// Matt Pietrek, September 1999 Microsoft Systems Journal
8 t$ ~3 s8 f0 u8 N- s# Z* Y// 中文注释部分为jefong翻译  Q; T4 ~4 t7 w, B
//' _8 \, K5 t/ z+ Z
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)9 x( ~/ K6 Y+ }  u) f. Z% P
//
. J  a+ D* g$ B1 n) a& h// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
% M0 Q- u, }2 O5 D' \2 p7 J! I0 w//=============================================================================. R- G: c0 t5 o  J" e4 `9 U% f
1 y6 f% X3 m1 D' ?. x
#include <ntexapi.h>    // For HardError defines near the end
( p3 C) x- o$ |
( y4 S+ v: \: @( P) P% S9 u2 b// Global symbols (name is accurate, and comes from NTDLL.DBG)) C3 J( v# I$ ?
//  _NtdllBaseTag5 f5 ^6 N7 W. n9 P2 h: G" \. d
//  _ShowSnaps7 ^. ~! @- w2 @7 v' q" l" `
//  _SaveSp
, h/ v. J/ y- I, [$ Q6 r* U* X//  _CurSp' e4 ]7 F& X( m4 V& k
//  _LdrpInLdrInit
2 }* F2 {" t# u# c9 o//  _LdrpFatalHardErrorCount6 m  v( R. e; M+ p1 C" a- J/ O4 O
//  _LdrpImageHasTls* F0 S2 P% F, Q% k! {/ H; J2 w7 G

5 M. p2 x6 I8 B) T7 mNTSTATUS& D: _0 I7 Q' g! d4 m7 s8 _
LdrpRunInitializeRoutines( DWORD bImplicitLoad )
3 d( k) i, R' a7 e4 u8 }3 i{( d$ r5 k- G# s7 l' p
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了: l: R( ^& }2 ]6 x
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();5 O! d( N  O* v- R$ l: {; I

5 z$ J+ _: M( E( D% U. g9 L    if ( nRoutinesToRun )
! C  ~5 q3 i* M9 l8 n2 L$ }/ C    {, I9 B) j  \( V
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。- M& c+ H; V* ?6 p! Y
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),4 a5 G3 p9 @/ v7 U( Y+ p
                                            _NtdllBaseTag + 0x60000,
% {; x& U  d& g                                            nRoutinesToRun * 4 );
- c* U- Q# |% o: B                           
3 g  d: |+ u1 ^4 {        if ( 0 == pInitNodeArray )    // Make sure allocation worked( y- y3 v5 H: _* m9 I3 k! c
            return STATUS_NO_MEMORY;8 p) k; L' P, }6 p/ }7 l
    }" f/ A1 {, Q# |( E
    else
; N  T9 @5 [  j* o8 z        pInitNodeArray = 0;4 m  ?' m, P/ ?8 Q7 [5 |

) ?/ w9 n) v+ g) v/ p, u9 L    //第二部分;
! N4 Y$ }; i0 X/ Z& C( B6 O& V7 S$ L    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
# M0 X4 ?8 T9 C, z& z; Y    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);* |4 D, w/ {& t# o+ m
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
. W6 |7 m6 V- K0 f) X        ( \% h( h% y3 \6 m$ i
    if ( _ShowSnaps ); B, q& R6 [- r3 u/ }
    {* i: I* i0 V2 I' T* r) M9 Y* s8 i
        _DbgPrint( "LDR: Real INIT LIST\n" );
+ L+ A+ L9 R/ Q" B# t0 k) S    }
# l! u) e( w9 ]5 W- ]1 s5 w5 U
) r$ X; \' n+ }; j& G    nModulesInitedSoFar = 0;
* }2 q8 Z9 r8 |" s2 Z& n9 {8 v2 s8 A, u
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块. n$ R; x$ U, ]  q6 H
    {
) t" E3 x. i: l; ^" {$ U        ! @- B2 j! v: Y6 L# s# e
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
+ k" j( F. c) S7 ]        {% c, [- t( F: K# O% a
            ModuleLoaderInfo  pModuleLoaderInfo;! Q2 c  _: X0 X5 Z0 R* ]
            5 \1 D/ ]1 X  ]2 C
            //9 N1 x2 L$ _# `1 M
            //一个ModuleLoaderInfo结构节点的大小为0X10字节/ ^" N& _# U  |3 P% U
            pModuleLoaderInfo = &NextNode - 0x10;
; k% M5 Z* g* S            
( e$ B. j4 ]9 f- o+ a: H- M0 A" M            localVar3C = pModuleLoaderInfo;         
5 ~1 r0 V& S5 {9 r# V* c* x, R! d; @2 ^" W8 _) H, l" h  N+ i- }
            //$ r- j. f( a, Q/ M+ g+ s3 P
            // 如果模块已经被初始化,就忽略
% O% v2 A  D9 b0 m            // X_LOADER_SAW_MODULE = 0x40 已被初始化
" ]" U- F$ {: A  m5 G            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )$ a' w  z8 ~' c
            {
3 T8 D& m7 `6 C6 b                //
1 O/ P* y5 D- [' X6 \; h& [                // 模块没有被初始化,判断是否具有入口函数) s9 O4 v& ]/ X. _- U
                //- G, K) W: |  L& H1 ?" k. I1 B: q- N
                if ( pModuleLoaderInfo->EntryPoint )
$ S7 `. n% s9 j5 G$ A5 W; H. ^                {# ^5 l1 v1 U! m- _  ~/ n+ w2 H
                    //
* A/ x+ E6 m8 H! R/ @0 I  E8 e                    // 具有初始化函数,添加到模块列表中,等待进行初始化
0 g" U3 ]: a( q( f                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
. m0 b: v! Y9 r/ ^* M4 F( f& n8 d0 S
& p7 l4 K$ Z, A0 X# n7 L" F8 m) j                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址+ o4 p8 w) _1 {7 d
      // 例如:
9 o7 @$ a8 _5 V; u/ L                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000$ K8 \1 d9 P5 O: M
                    if ( _ShowSnaps )! i8 O6 H! O4 I
                    {4 V1 _% ~/ l( f5 H( {% T
                        _DbgPrint(  "%wZ init routine %x\n",
+ C  ]3 V3 W9 U8 o+ W  _5 }                                    &pModuleLoaderInfo->24,
; m( I5 j6 U; j, J+ y                                    pModuleLoaderInfo->EntryPoint );% x" J8 w5 I, @. _
                    }0 A, M$ p, K% b8 P0 O
% A& x1 I9 }9 `; f! d  v
                    nModulesInitedSoFar++;
  Q5 N' u1 l" o: O( `                }
; B" m. G# p* s" w            }9 o: O  B0 O2 u

) I0 i) l4 K4 ?            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
% C) ^/ o# G* `( m# ?" t( r            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;0 }  E  @" ]; Z( v% U2 p

) p4 c; n" p0 X7 K            // 处理下一个模块节点
/ U$ u7 B$ I" \, a3 d            pCurrNode = pCurrNode->pNext
6 E6 C; j1 z, o* i* y        }. w8 {: b  \$ k/ |6 W6 q
    }
& X% [4 K* w; i' D  M; {5 r    else
, e( |8 ]% C4 t    {% U) x" N# P0 a
        pModuleLoaderInfo = localVar3C;     // May not be initialized???
8 U0 b" U: c' W7 N4 I: K    }
, J( |' S7 _" E+ @4 F* K# f* J& K5 T    4 K4 j% a) J7 c( y+ Y* A6 Y
    if ( 0 == pInitNodeArray )* T5 P  L3 E, T2 A
        return STATUS_SUCCESS;
/ |, L' `: m# {2 m, y4 B. o! k! _3 P4 J
    // ************************* MSJ Layout! *****************6 _4 K: }# U4 N1 b' {1 ~% S  n9 q
    // If you're going to split this code across pages, this is a great
: I1 _, b, H5 b% q    // spot to split the code.  Just be sure to remove this comment8 Q0 F' M* V/ x- g$ d
    // ************************* MSJ Layout! *****************
& L, n9 K& U. O9 `% C+ `    5 j% _; d8 w  k" b) w
    //" e- L  T% _) }; O+ A' S. @9 C
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH1 ^" j, Y$ S2 f
    // 第三部分,调用初始化部分4 `/ {4 _- B: G: H% D1 P0 v
    try     // Wrap all this in a try block, in case the init routine faults
6 Z: H  `' r1 P4 x( k  A* r    {
- h6 M; T' S2 i* d4 N# c8 _        nModulesInitedSoFar = 0;  // Start at array element 0. L/ S4 R/ z5 X" h
8 h/ n. @, a! s' E
        //
1 a3 P6 i  R; P* ~* {0 O        // 遍历模块队列
) \, U- f" I) ^8 q; ^3 v' A        //
: X. p7 V' I2 b7 X' n        while ( nModulesInitedSoFar < nRoutinesToRun )8 o9 H' r: K" T
        {
* j- S, M/ A& L, e, v( B9 B            // 获得模块指针2 N) Y7 f* Q! l. \# w
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
5 l, X3 t! h, k' {
! `* O0 K% ^6 u            // This doesn't seem to do anything...
9 |+ p6 E: |' m4 N            localVar3C = pModuleLoaderInfo;
4 M9 A) G4 U* b6 w( Y            
; n( F! b8 x. D0 j# }! b            nModulesInitedSoFar++;
3 T3 @0 I/ f3 Z' T               
# S  M! Y+ c& c( f& V; Q            // 保存初始化程序入口指针
6 I0 v- X# L0 Z" h- G- q& I            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
: X2 `2 c& T/ s& N. v. ]  a( ~& }            ' H, D; @/ A. ^* V1 A
            fBreakOnDllLoad = 0;    // Default is to not break on load* ], m* ^, ^8 D: n* P

. G2 i1 ~' p; ?            // 调试用
+ n. x0 ?. u2 p- k4 E/ v            // If this process is a debuggee, check to see if the loader. v: N% N( r4 I) L$ h( C
            // should break into a debugger before calling the initialization.
/ H6 k4 C/ H# h$ J& z            //. U5 x" a6 v% \& t
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
  ^  C* \& }2 c            // returns. IsDebuggerPresent is an NT only API.
$ ?7 @# r2 F! o: }7 \            //
; B' o% p2 [7 L, I" y            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )# A; m) d7 u# j
            {
9 g5 Y& Y5 x, T, g/ i+ V5 N& p                LONG retCode;* {' I) ~+ c& r
, `; ^# P2 h0 \( [8 Z$ E3 D( F
                //              # c/ V& B+ Q8 k9 y  {4 q4 O1 s
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
/ V- S/ \) d3 F3 B3 s; J: l                // Windows NT\CurrentVersion\Image File Execution Options"$ q4 c: Q# b* M" X' }& ]
                // registry key.  If a a subkey entry with the name of
8 G# }; ^6 }+ d% Y/ W3 P                // the executable exists, check for the BreakOnDllLoad value.- O( ~7 D: m0 f3 a9 a
                //
9 s1 O5 ]' U3 U1 v                retCode =
! |5 I3 e$ ^& j                    _LdrQueryImageFileExecutionOptions(/ A% z5 [$ k9 w0 ]/ n8 d
                                pModuleLoaderInfo->pwszDllName,
3 R0 F( b- a' ~$ {$ v                                "BreakOnDllLoad",pInitNodeArray  S1 z6 f: d$ t2 s* Z  @
                                REG_DWORD,
9 A6 h- r! @/ @* p' i* G                                &fBreakOnDllLoad,5 D" K) l" M( X
                                sizeof(DWORD),
# a, m+ H8 x3 z) R5 P9 Y                                0 );4 W5 A$ K( m/ ^7 [9 M
2 c# h. g, T5 O) ~, U+ [
                // If reg value not found (usually the case), then don't; u5 j) B7 @! J& i/ v7 O: F4 L
                // break on this DLL init# [% v/ E( d" d1 z4 S
                if ( retCode <= STATUS_SUCCESS )
$ x: D8 d; J4 `/ `4 J" k                    fBreakOnDllLoad = 0;pInitNodeArray8 Y" H) U0 a& G' J
            }
# C* P& Y6 D4 K3 G5 \. s            + I; I6 f* ]$ n$ \7 ~
            if ( fBreakOnDllLoad )
0 a  E9 s' J+ K' O            {           2 M0 b% E- S: n, X: r" ~. o
                if ( _ShowSnaps )
+ T& E9 g; h7 J9 r% m0 T                {2 F# O$ Y1 Z# t' e8 w" {; L
                    // Inform the debug output stream of the module name+ v9 F) Y  [2 h, N$ `( r
                    // and the init routine address before actually breaking) Y$ G6 ?" z' I- D
                    // into the debugger
' h; \" }- y$ V$ R; n8 P, ~- H" C* \7 b# {) O$ R
                    _DbgPrint(  "LDR: %wZ loaded.",+ @/ K7 f  f, t; Q- B3 G& u' y# N
                                &pModuleLoaderInfo->pModuleLoaderInfo );- {& D2 I" U4 Y% V, _) D$ U
                    / C" P7 B4 h7 X% W  d( X
                    _DbgPrint(  "- About to call init routine at %lx\n",: J; g7 P4 r+ p/ H" @5 }
                                pfnInitRoutine )4 ~5 u- \, t* P: @0 h
                }; \; s$ d7 P0 O
               
' K1 ?4 V, V: c' G                // Break into the debugger                              ) }! f/ O& u8 b3 o
                _DbgBreakPoint();   // An INT 3, followed by a RET
6 r. J0 T) n! f  a            }6 k7 a7 M# I' X6 D
            else if ( _ShowSnaps && pfnInitRoutine )
* e7 \! u; F0 I/ {$ X0 o            {6 L" F- Y* `  k  j( ]
                // Inform the debug output stream of the module name) j: A2 F6 D( `, d: Y1 U9 h" Y
                // and the init routine address before calling it               9 x3 @. d; `6 J3 {
                _DbgPrint(  "LDR: %wZ loaded.",
. X8 T6 x1 `2 [2 h, X: n; J; Q                            pModuleLoaderInfo->pModuleLoaderInfo );% N7 b$ Y0 l+ S5 y! [

& U) @5 P( B8 C; L+ F: U                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);3 C  T$ a! E/ ~. x7 h- J. n
            }
: n" b2 {: P5 ^8 H+ B2 D  C# s7 u( r                    & G& a8 s1 o9 B! B1 a$ N7 \
            if ( pfnInitRoutine )
* p) N; e( `. A% s7 c7 g" M            {6 p% p! ^2 [9 m0 T
                // 设置DLL_PROCESS_ATTACH标志
1 [8 Y% L* Q' r% B9 {# G                //3 O7 @2 ^0 _% g  B
                // (Shouldn't this come *after* the actual call?)
; y" K, w- E6 k; _                //2 o! R8 d9 l$ }& N  c' O
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
1 }3 C  H9 Y: J  K0 [( T8 @                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;$ Y! j* r6 V" q' F* a

: P2 k7 `; l8 b6 y4 t! ?8 N; Z                //# Q# }6 h6 q. c
                // If there's Thread Local Storage (TLS) for this module,
/ ~" \# j4 |( e; D8 Y: Y" p1 v                // call the TLS init functions.  *** NOTE *** This only
/ B, S: Z2 j; d. x8 h1 k( m; C                // occurs during the first time this code is called (when$ K! k8 j5 K+ ~: X
                // implicitly loaded DLLs are initialized).  Dynamically+ }' q2 v/ u$ x* ?( z  T0 }
                // loaded DLLs shouldn't use TLS declared vars, as per the
* j! E1 J7 c; ~; z! @# m5 u                // SDK documentation
) r& u0 g4 U( V# v# L& Z                // 如果模块需要分配TLS,调用TLS初始化函数+ c6 f6 Q1 m5 y, X
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
2 M; @% S: C* I: t2 M! A+ m" k+ @8 ?  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量8 p! L. X, l5 d1 }( U6 U
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
2 [/ q2 K0 i6 Y/ G$ k                {9 S9 Q8 [. J) K9 q( X1 Z  v% ?: Z
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,# ?- z" t3 i8 H. d
                                                DLL_PROCESS_ATTACH );% t3 L8 y6 r8 F  a+ u) t9 ?7 ^- W
                }
9 S* o6 z2 U4 n0 n                5 W; Z, p. z% y

, J( Y8 K2 B8 z" c                hModDLL = pModuleLoaderInfo->hModDLL& n* d) f5 M" f3 g  T

# F/ g  i. M; J5 {! m                MOV     ESI,ESP // Save off the ESP register into ESI
6 o5 u- j9 y- e  0 i/ O: p1 s3 Z7 k: {4 w( \
  // 设置入口函数指针                " |" l( v/ v0 b' U0 {+ b3 V+ i
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     9 c5 Y5 T- l2 u' r* l

) o( G& O9 h( @, V' \                // In C++ code, the following ASM would look like:  f7 z0 D4 r( [4 d) f
                //
) t  j+ g# M; R- Z; f; h9 O7 h                // initRetValue =
9 t( F( V+ h; U# S' C3 t                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
3 `$ K/ y8 r1 i* s6 ?. B: Q                //
( n) l6 r* U6 D5 u, B0 G9 t: ~/ i5 [( y0 L9 P7 Y9 d, \7 b/ D% Q
                PUSH    DWORD PTR [bImplicitLoad]% G4 e% I8 Z+ g) Z: I
               
5 D& d0 M7 m$ {; [- ~. a5 {) E" X                PUSH    DLL_PROCESS_ATTACH
7 M* d9 t4 w" L2 u                ' B) X! B' m5 ]& c7 P! \
                PUSH    DWORD PTR [hModDLL]7 D" T9 }/ R& l$ `6 x
               
8 b9 m, L) ]  e4 L                CALL    EDI     // 调用入口函数
* s2 b, n. ?3 Q0 c               
  I1 g3 |2 [, S+ F5 ^* y% B' P                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值( w! v% Z$ w; S2 v' z
+ q- n$ |- u/ E+ o6 {6 O* c
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
( h5 ?: ]# k( D! ~0 o                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns3 y+ M/ B) l2 R# v1 ]* ]
6 b% r; H2 ?; ?8 O
                MOV     ESP,ESI     // Restore ESP to value before the call
, F& l/ e4 U( Q( R% c  {  f2 @4 \6 f
                //
& `+ {/ {" ^) V/ R                // 检查调用前后的ESP值是否一至, h5 n6 Z: E: D2 I- B
  //
5 z' ~. {, X; d1 v, }                if ( _CurSP != _SavSP )
& g3 F: Y2 _: F5 F+ Y                {
2 Y' e& y- F' b! O% ]( F7 K                    hardErrorParam = pModuleLoaderInfo->FullDllPath;; k5 U9 r4 n5 h' }/ Y' o

1 r: X5 ~/ A, t. ?8 V! o& U  J+ ?                    hardErrorRetCode = : u$ K# Y- f+ n2 i- ^
                        _NtRaiseHardError(
. E0 V% z# v+ M  f                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,5 u2 |( P7 X" @3 Y
                            1,  // Number of parameters
7 }4 E7 P: _8 `" v' M6 I                            1,  // UnicodeStringParametersMask,
6 B: @5 C( G6 O7 v                            &hardErrorParam,/ {% a1 M8 Y) H, G. x+ Z
                            OptionYesNo,    // Let user decide
- ?/ v! V8 m4 R  {  ?9 ]$ ?                            &hardErrorResponse );
5 \6 ~5 y. H/ m' i0 m. L/ g                                            & h% K0 j6 e3 p# X! k
                    if ( _LdrpInLdrInit )
* ]* `9 Z2 m3 |- c                        _LdrpFatalHardErrorCount++;
" w# L; E: _3 Y7 \( |" f) M( G6 Z( B1 K) W) P' o; U! J
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)% ~' q& z, p' Y! M! ]
                        &&  (ResponseYes == hardErrorResponse) )
( W- L0 ]) ^. |                    {% P' Q* H. ^% Q% T" [' Q5 D
                        return STATUS_DLL_INIT_FAILED;; Z7 {6 K  v  C5 u
                    }6 m0 `1 e  m. ]  `
                }
' D4 A4 ?" z% H+ S0 h! C: t9 M; l7 U
                //$ l/ D0 D4 v" ^  b/ E# H
                // 入口函数返回0,错误
9 A& ?9 m+ h8 W$ A                //, [- g) p$ C/ c+ y' b/ x- {
                if ( 0 == initRetValue )
  b. z" X) @5 C& j# W* g4 [" H                {% k. `8 s7 i# O1 C5 t
                    DWORD hardErrorParam2;
* w3 G2 d+ k4 z9 |+ ?/ j                    DWORD hardErrorResponse2;$ Z3 \+ }9 o. Z6 v
                                        0 \) X9 |8 H( T9 M/ S7 w
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;# S3 a* \/ o8 O1 d; d% W: l
                    ( ?+ N- R( J2 L, h; M! P
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
' ?) O7 u! J, K1 H  D$ C/ g6 ]: U                                        1,  // Number of parameters
' I7 i  F9 v7 K7 D9 D7 C7 E5 y                                        1,  // UnicodeStringParametersMask
9 {- t+ v0 O) r4 W8 L                                        &hardErrorParam2,. ?; E+ J2 q0 r! }. ]  c* L
                                        OptionOk,   // OK is only response5 L; w2 r5 P# n0 j2 [
                                        &hardErrorResponse2 );) x4 \% z3 \' o! F" L
                                                            & k8 k9 f1 ~- O! D9 ~9 E
                    if ( _LdrpInLdrInit ). w. ~) Q) c" U8 i/ d
                        _LdrpFatalHardErrorCount++;
6 t$ g* @* R' U9 K4 J$ r3 u% V7 X, Q0 }, U- m* g4 H
                    return STATUS_DLL_INIT_FAILED;
+ O! y4 T+ t# X4 J8 r                }
. X. ?+ X: y: g6 X- D            }- r( @9 d& A  B& \* |+ W% S
        }
2 n+ d/ U4 w; f* P7 c2 h
+ B6 ~! E' Q/ U        //6 t5 q% u+ A9 o  f* x( w$ p
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
- p" T8 ~: @4 R9 H, s        //      
; I" T$ n7 j2 N# u: E! U        if ( _LdrpImageHasTls && bImplicitLoad )
/ r1 ^, C: j* _6 ^, x, r        {& G5 \  r; m% h
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,5 ^3 @  `; ?$ j) U3 X( F% O8 a
                                        DLL_PROCESS_ATTACH );
) U" V: L- N/ M2 D; k        }) X! y; o* u& Q
    }9 @0 i, e: {) ^0 w9 ^
    __finally! k$ i9 @- ^' ~1 g2 V6 U
    {4 L) r$ _8 I1 U) |: m; x- R; M
        //
0 a" @0 P0 b8 p6 N        // 第四部分;
. r+ r/ M7 U, D& o+ ?5 u        // 清除分配的内存7 p* X# O3 p% |2 V
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
( \& `5 s1 P% w3 H    }# ^& X/ y/ @1 T! C

: b9 I" J) E2 k% h/ v* j9 z  |    return STATUS_SUCCESS;. D& N8 L1 R1 p. u& }9 H
}   ) V- n3 \2 M* Y+ D/ e% a6 y% {) W; z

7 u4 o, F! B3 k6 K+ T  s这个函数分为四个主要部分:
8 x0 l; P# n- X0 o8 G+ G6 k3 ~; F一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
. X' G/ N" C9 q0 N/ k$ L& S/ _  v二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。- h' d; o$ I; X: \& c* B
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
+ S2 A" o; L$ ]5 T6 y另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
+ I  f% u. U% N$ a  }在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。7 I# v- I1 _5 P6 \6 S8 Z4 `+ Z
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-2-21 07:55 , Processed in 0.060856 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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