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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]# ?) P- T7 T% ~! c0 ]& l( |4 Y5 D
8 B' j9 ]7 N* c
WINDOWS 2K Dll 加载过程
4 x6 |6 |" v% B- ]- |/ Rjefong by 2005/03/30+ }# f* a4 T2 ?' e. b
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
9 |3 C. ?' m1 j% p1 Y9 ^在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! {3 o( y6 R+ K  K0 W, A) f7 m
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
; \# a: i& u# \2 X3 ?初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
# c& _5 I3 P! W" Z  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
0 b- Q! \9 _8 p" w4 X& o5 }) h; l) c  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。- R; S, `: h9 ?+ v
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):/ k  |+ y: f8 t* a- Y5 a0 x
//=============================================================================, j  {: k1 i+ y+ {( f
// Matt Pietrek, September 1999 Microsoft Systems Journal' V7 F; \, t6 x# i, P4 L
// 中文注释部分为jefong翻译
3 i% A' Y7 _4 p//3 x  P" ?, T$ I( M
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
+ [. b5 L, b& U5 f6 Q( w* c; n//3 `& u" r3 F* o5 J0 J- ~3 {
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;7 u% o* I) U& y) x7 X4 W2 _! B  Y) D
//=============================================================================
: M2 B$ e+ Y; |6 H% f$ g: ]& p1 e1 L& i0 d) o
#include <ntexapi.h>    // For HardError defines near the end# i. s4 S" a' _9 {: k
; k  x2 M! `, o9 h
// Global symbols (name is accurate, and comes from NTDLL.DBG)
/ m, W' q* k1 D//  _NtdllBaseTag
- @5 x- g& q# {9 Z//  _ShowSnaps! N: N: B" N* L& x
//  _SaveSp* O, L7 G4 a2 j% [4 L* G9 T( ^( Y
//  _CurSp
& o+ |. s0 T' [- C' X1 X+ @# B//  _LdrpInLdrInit, e' y, X3 y: U' R. V) @
//  _LdrpFatalHardErrorCount
# x7 y4 n$ I/ u* ?  S2 S//  _LdrpImageHasTls' f, G, |0 Z$ h* x; y
  ~9 x' U2 `' T3 P& r; `( L3 E
NTSTATUS; N( u5 T6 ~" g$ E! u4 Q
LdrpRunInitializeRoutines( DWORD bImplicitLoad )
$ Y  c7 p( Z0 X9 v5 x; X{+ a0 L! \( x  @# ?, l
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
+ Q9 N0 m( |! X. p; g    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
  {# a; h9 @$ f3 N1 s& H6 t7 {" \( O# G
! o* |& o3 e! B/ ~2 o0 b3 j    if ( nRoutinesToRun )% g, q$ |6 l+ ~% O1 |7 Z- M
    {0 d4 j# N* F. P1 J$ l  |1 Y9 Y& O
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。' y/ i& T& N& \  e6 U2 i
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),! W' N3 c# z" g' W3 N0 S+ d% n
                                            _NtdllBaseTag + 0x60000,$ _4 f8 d2 O# E
                                            nRoutinesToRun * 4 );
' t% A+ P1 b) z7 T+ o5 X7 l! X                            ; ], H# N- S5 u( v0 F! B4 g  U
        if ( 0 == pInitNodeArray )    // Make sure allocation worked3 ]+ G3 \4 l2 `2 F
            return STATUS_NO_MEMORY;+ U' f1 Y  V3 Q1 B2 e, B$ E6 v1 ?
    }
0 D& I, w+ Z/ t7 j+ E    else, u) S! z- z4 u% i" `7 Z
        pInitNodeArray = 0;" h* v, ~/ v# ], ]* K$ V. g$ K0 J
6 ?+ A# [2 D6 ^+ e* x  ~) z& U
    //第二部分;! l8 E- g2 m3 g' T5 ^: c
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。' x9 \0 D- S& x9 J5 m( Q% M: I7 A9 g
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
% f# n& W- Z$ H% H  Y0 T+ B    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
! m( T( D/ W, j) T' J( l8 U7 Y* i        9 w! n( n7 H3 |! {$ Q, x
    if ( _ShowSnaps )9 j! G3 e) \5 n6 Q# k0 G2 y
    {# c' [* ]: F6 M: @' M/ Y+ I' B6 g0 E
        _DbgPrint( "LDR: Real INIT LIST\n" );
0 E3 \& n8 w( o    }
7 L* B7 q' `& X7 E0 `9 C0 O+ ~1 Z+ k! J3 {9 I# |' K$ g0 P- Q* z
    nModulesInitedSoFar = 0;
* ^2 U( E. E; e6 s) ?0 g
% d$ ]2 k, }( n    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
) z; i7 @* b% `0 a+ h* \' l    {
) R9 s* p; ]* j  S        0 s4 P4 r* T8 n$ I
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
' ~& W7 I& r- ?0 g        {
3 U* }/ b; f8 F5 |            ModuleLoaderInfo  pModuleLoaderInfo;
( O/ K$ z6 F8 v: \! z, @            
% d2 X1 T" H* X8 j, f% J            //
& [8 R$ I% X$ b9 {3 Z! q            //一个ModuleLoaderInfo结构节点的大小为0X10字节
- V+ c- \$ L, ~            pModuleLoaderInfo = &NextNode - 0x10;9 r' e0 v* m$ C" m; W
            
" h* ^) U, i, }4 }# Y% L            localVar3C = pModuleLoaderInfo;         
3 A9 z$ `7 r2 s5 ]9 O9 J: b, R  \  t
, A9 P7 S& [# g- s            //; b2 F- A# ~& }) b' q" |) d! d. I
            // 如果模块已经被初始化,就忽略- B, m5 F2 b# ~7 U+ {9 b
            // X_LOADER_SAW_MODULE = 0x40 已被初始化
& D2 X  s/ \5 w1 Y. u1 Y0 B            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
2 M/ T3 u8 F; z2 O! {# T& {6 m            {3 b3 `/ D6 z; P( H- S8 j
                //
  s6 L) t8 X1 h7 h                // 模块没有被初始化,判断是否具有入口函数  q7 d( Z0 W# t) [- v
                //7 ^7 u7 h" w! n2 |
                if ( pModuleLoaderInfo->EntryPoint )
" j/ X/ {! S2 c! V( e( Q                {
* K" x0 z0 K( F* U3 a0 |                    //
5 L) q! h1 w8 |* f' v                    // 具有初始化函数,添加到模块列表中,等待进行初始化
; x( o- y$ s9 c# O4 \7 C                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;6 m& E" A! _" c! M1 v: L: {
8 P6 Y6 R9 ~0 G6 M, d' e& h
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址( i2 _0 [( C. P9 x0 |0 V
      // 例如:
- s- c% H, U+ V/ K+ b8 t                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
# y8 P! n! Z1 E# R- N9 T                    if ( _ShowSnaps )
5 n( ^9 c% b4 `% W0 F5 _                    {
& ~5 G: Q- D8 g  x! W                        _DbgPrint(  "%wZ init routine %x\n",. @1 N" a7 v' }% K* c- N3 |
                                    &pModuleLoaderInfo->24,) s) x; D4 U4 h' x. b
                                    pModuleLoaderInfo->EntryPoint );4 E5 Q7 G7 H  u
                    }  y7 N- k5 r- `$ o' Z4 U8 r
$ b% {3 o1 u; i' z# z% {0 E- I+ I
                    nModulesInitedSoFar++;
2 i# T1 G( X2 B) p% g" \8 q6 B                }. I0 m. b  g6 S  D; K, q
            }
" H! r; ?( W" L+ N9 T1 m3 B- Z( N( V; e% o* c9 e" X
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
0 E! P; ~, ?1 S3 N            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
- e8 ~# ?8 ?6 t% ?$ ?
: h8 k1 j% E: q3 y4 A) z8 y8 d            // 处理下一个模块节点1 a& s8 E: E; ?! E, E9 C8 }0 @
            pCurrNode = pCurrNode->pNext
0 E4 m( I) @' m        }0 M0 _$ r: o# c/ ?) x$ V
    }! y+ V6 v% y/ ]* J
    else
. j) K7 Q" e, _" c7 c4 S, J+ C5 @4 N  z    {% I4 k$ b( Z" R( P2 v0 L; I
        pModuleLoaderInfo = localVar3C;     // May not be initialized???% a3 g7 j  ^6 Q1 w  Q
    }( {5 X: o% V" [8 H% ~
    6 {, ]0 U- Q$ P' y5 V1 L& [
    if ( 0 == pInitNodeArray )3 Y: @( e$ j/ O' v+ F) B* v! ?
        return STATUS_SUCCESS;
' E' ^, n" l$ U7 z" I5 L9 M$ r
, B5 B! J9 i" @0 N    // ************************* MSJ Layout! *****************% a! K" L# {9 b* X5 w7 L( Z; K/ X
    // If you're going to split this code across pages, this is a great
  }9 K+ E) e, d; B% i    // spot to split the code.  Just be sure to remove this comment
9 D% G% N& ?; i$ O; A    // ************************* MSJ Layout! *****************5 w9 L. ]8 L4 [- B+ W! ]1 T
   
2 e( U6 j1 v; c& m6 ?    /// ]& G/ @! t& M0 ?" z" W9 m9 |
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH5 R; T! h* c2 {$ t4 n( i  V2 A
    // 第三部分,调用初始化部分
, o' A, W- |5 h$ P    try     // Wrap all this in a try block, in case the init routine faults. P  Y3 g2 Y0 \2 N6 g: f' Z9 [
    {
. [+ @) t+ E1 P& \4 b3 p        nModulesInitedSoFar = 0;  // Start at array element 0" u( ~. e4 u. E9 ]/ [" K' N$ F
: o% ~/ W3 I! Z4 v0 q2 V# o  e
        //
7 q9 k/ f; }, ~9 g$ ~+ d8 n* L        // 遍历模块队列: O; a# o: a, Y
        //
8 e/ H" ~3 k. I. l3 W+ S3 Y& N        while ( nModulesInitedSoFar < nRoutinesToRun )
1 s( [" S  k; I        {
. ?, f: n& t5 D6 w% c$ T8 i            // 获得模块指针! t( l5 V0 m5 e. ^) w' X8 j% h
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];4 v; _; S$ ~. {0 X. Q2 c* K
4 d  B5 f2 l+ N# T0 O
            // This doesn't seem to do anything...
$ g$ R* \0 w. b            localVar3C = pModuleLoaderInfo;
# g3 m1 B) Y, |; k9 [5 S            & s, {. F; Q( V/ V
            nModulesInitedSoFar++;
& x9 g$ A2 p! j9 Z, @               
4 ^' p" x' P" F2 }% r            // 保存初始化程序入口指针
& A! D  J* e3 r) w! `; Q1 ^            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;1 L9 \  w# S$ c' u* @  {. a
            
9 @. U9 M! O7 i6 o            fBreakOnDllLoad = 0;    // Default is to not break on load
' H9 \- |( Y0 H7 Y' i2 a
4 b8 M3 k0 n/ |% a, \9 U            // 调试用) J# R# Y5 D- \& {9 A* o
            // If this process is a debuggee, check to see if the loader
, |2 r. ^) g" W/ I0 ^; w1 D  w            // should break into a debugger before calling the initialization.! R  j: B( b' b5 C
            //, [1 x8 H9 s9 x4 [; i
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
0 }0 `" N0 N! O; X6 a3 l  {            // returns. IsDebuggerPresent is an NT only API.9 @5 P* ]3 I; T$ B" Q. {3 U
            //
3 k+ L& ], Q. s- {& I            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
: b6 d2 D$ r: m& w+ A            {, h: [) y; D8 J1 @1 ]) ^& p0 w
                LONG retCode;
  O1 J; r0 N0 k4 N; D: }# `0 t  g* O2 z! }8 Q9 @
                //              
2 F- Y. Y$ U9 p                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\: X' h+ j: a3 U* {( n$ t0 o5 R
                // Windows NT\CurrentVersion\Image File Execution Options"
3 B2 N0 K( Y9 q5 `3 y3 B                // registry key.  If a a subkey entry with the name of. c- R, g$ e4 }! i& Q; k0 M/ F: y
                // the executable exists, check for the BreakOnDllLoad value.* M' U! Q$ g. V1 Q; m5 r
                //" G( V6 n* [, e+ u/ y# l' ~$ A
                retCode = : h' X8 b  I8 a
                    _LdrQueryImageFileExecutionOptions(" U2 M/ ]' C6 s# ~- E; w
                                pModuleLoaderInfo->pwszDllName,
0 ?$ E3 _6 |3 c3 I0 i- v0 k7 u                                "BreakOnDllLoad",pInitNodeArray; Y, s% w1 U# Q. J) {
                                REG_DWORD,
4 M) b5 x0 s/ k$ R$ p9 }9 |                                &fBreakOnDllLoad,
4 O  u! a# M% c- X* R# Q                                sizeof(DWORD),
8 b: g; t& I8 |7 s) V! u1 i                                0 );! V2 T3 Y# ]" W2 D$ ], m* b
9 S& d+ J6 V8 a
                // If reg value not found (usually the case), then don't/ G% {* g2 y% X3 z; x5 z
                // break on this DLL init
6 @( h$ c5 _3 @$ f; L                if ( retCode <= STATUS_SUCCESS )2 V3 d6 n7 E. l$ |3 q0 t
                    fBreakOnDllLoad = 0;pInitNodeArray5 N* U: A7 N0 ^7 i
            }
0 k4 P# U  i8 a" y, y' c' Q            # p$ i) V+ y* K/ i0 U
            if ( fBreakOnDllLoad ). \2 D+ ^0 n8 o( u4 j* M
            {           
: k$ t# D: F8 t" L% P1 |$ M0 i                if ( _ShowSnaps )% C6 F! D* g1 e' Y* a( g. L
                {
6 x$ ^5 l8 X2 t2 d9 A% A- Q                    // Inform the debug output stream of the module name! q4 a: {( c+ _  T6 N
                    // and the init routine address before actually breaking, E6 ?2 h" p0 ]) ^8 o
                    // into the debugger
; R! ]" f* n* N# g% z) _) B8 X# o# S" p9 P
                    _DbgPrint(  "LDR: %wZ loaded.",, H/ ~8 W' ^2 A" X# l* I
                                &pModuleLoaderInfo->pModuleLoaderInfo );$ t( J0 w+ }  f9 y: ^
                    
. [8 y  E7 d6 [# L& n                    _DbgPrint(  "- About to call init routine at %lx\n",
  ~; R: b. `$ r                                pfnInitRoutine )
* Q5 x# M1 X9 u/ f3 Y  K$ N8 \                }' s' ~" [6 W6 N
                / l( X: v8 @' u: u
                // Break into the debugger                              
4 {# Z7 h3 }2 C4 P! Z- ]7 Z/ `                _DbgBreakPoint();   // An INT 3, followed by a RET( _4 f) `5 V5 f" E" x. z
            }
& n' k0 S3 B" g! W8 g% V; |' x& d            else if ( _ShowSnaps && pfnInitRoutine )8 U( Z3 l/ S& u3 T" f$ i4 [$ f
            {
/ U# E4 x4 p( y* h0 p                // Inform the debug output stream of the module name+ [9 Z8 H; T* h8 \
                // and the init routine address before calling it               % o3 E$ w- |& k
                _DbgPrint(  "LDR: %wZ loaded.",
0 U% g9 ~. ~/ l0 a) j                            pModuleLoaderInfo->pModuleLoaderInfo );
; |' C( A3 O# a
2 Q" L' C! b1 C! ]6 r2 G& L                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);$ c7 O/ {' f$ z/ }% W$ ]6 b
            }
  X+ ?' [# w. U/ x0 }3 v' C: A; I/ t                    3 b: }9 R: p9 ^/ u0 o, l
            if ( pfnInitRoutine )& p. O; Z0 {; ?; ?
            {
5 ^9 b5 p  H4 k% h+ c5 Z* g                // 设置DLL_PROCESS_ATTACH标志
' n; z% g* B& p+ H# o                //
) R- q& t7 y) Q4 e3 k! l                // (Shouldn't this come *after* the actual call?)4 }" g- d% R- n: Z
                //% A" z# `# ?' V8 a9 x0 _5 F$ L
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             # i! T& m3 A# `8 `
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
! ]5 ^" b, t3 U5 }$ a9 W5 r0 z; @+ M/ D: d- l& ]$ {  }+ O6 t
                //' {% x& b9 r) E. O* O7 G5 H& c
                // If there's Thread Local Storage (TLS) for this module,
! |' A+ t5 M8 o- q                // call the TLS init functions.  *** NOTE *** This only
8 g& U0 f8 q: g4 |2 @/ L! Y. h! e6 Y2 F                // occurs during the first time this code is called (when: n2 H( ^6 X5 L9 W$ P
                // implicitly loaded DLLs are initialized).  Dynamically2 q1 N, I$ ^% H) A9 J- a8 h) ~. E
                // loaded DLLs shouldn't use TLS declared vars, as per the
& D8 H! H" a$ p" d) z+ ^0 ]                // SDK documentation
2 h$ o( K, m% n; }7 g                // 如果模块需要分配TLS,调用TLS初始化函数4 F$ ]' b% c2 h
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
* a' P4 Y' e9 g! p7 K  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量4 [, ]! M( n' Y- [
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
' w9 C; n3 Y: g+ w! A+ q                {
- p; ^2 {8 i0 ]* ]9 z9 j                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
* J) f  d7 ]  P0 T1 U; @% x                                                DLL_PROCESS_ATTACH );& f/ v" _2 k2 d# c( E5 R
                }
% |, A8 u: z9 p) Y- u) g                % `# o# {  g% c$ c1 u* h7 \8 h

% B8 v$ m/ S" @- B. L1 y  u7 R                hModDLL = pModuleLoaderInfo->hModDLL
* J( C- S6 `: D- D' z; R$ M4 n3 H; x5 z! L% N& z
                MOV     ESI,ESP // Save off the ESP register into ESI) C! n) D/ c( Z! T
  
9 M) b# g  f0 a( H  // 设置入口函数指针                0 [' u1 v% _2 L, R! O( k
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
. j% m: n) \4 P
2 @/ R; [: I" J+ Y5 x                // In C++ code, the following ASM would look like:, ~4 y0 X+ i. h
                //
8 u5 `! X3 P2 w# _; I  v                // initRetValue =
. O* X' L, v5 G0 [                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
! q" c) b1 {' h& t* m! }8 B4 o$ x% _                //
- v# ?+ [7 f, v5 v- i$ v+ m4 C+ t/ M# G$ c+ q, P: K
                PUSH    DWORD PTR [bImplicitLoad]
; h5 K: F' j+ S, g% G. w5 m  a                / c4 V' M+ V) g: T
                PUSH    DLL_PROCESS_ATTACH- h# H0 m2 ], P2 U
               
3 {, |1 t- t# t5 P. s2 x5 U                PUSH    DWORD PTR [hModDLL]
0 j  w, q: {6 w7 f9 n                - L; ^$ R2 Q: h2 e1 P
                CALL    EDI     // 调用入口函数
% ]1 ]; y4 T+ m6 }  P                ) @1 l6 M) I  p2 Z& g8 y: s
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
2 f3 ?$ y/ a, H4 g* e/ S% ?/ [: Y& |+ W
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
% r. i3 g, P+ B- M6 O, y3 j+ O                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
/ @( J+ @* J8 x; W1 m! {! K( V* x7 k  `: g2 G& A9 R- X! H5 w
                MOV     ESP,ESI     // Restore ESP to value before the call
! ~1 G6 W9 @; i" a+ ^# k; h1 d4 {: o
. N7 w, o/ X/ p3 l5 ~3 |9 j                //) J, p* l8 p/ W+ ?! C' e- a) C8 m* t) l
                // 检查调用前后的ESP值是否一至
7 x+ `( |" F: k  // / h8 p; h* g" ^+ o* {
                if ( _CurSP != _SavSP )& ^  q0 r; @, h8 [4 \5 Q; H
                {
, n: V: Y$ N: j, t2 Z                    hardErrorParam = pModuleLoaderInfo->FullDllPath;. b9 r- R! C: o

" j# G' ^3 s/ a; h  D) h                    hardErrorRetCode =
. q4 N3 @3 o2 {8 i  x                        _NtRaiseHardError() C4 w& B# Q0 v* s2 }4 V
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
+ C/ n# d4 V( H+ {  Y                            1,  // Number of parameters, I8 _# H8 I6 _) w+ \! U0 @
                            1,  // UnicodeStringParametersMask,
. r; }/ b/ |. i0 r8 L- o8 I                            &hardErrorParam,& U. h5 [: |7 P* ~/ d; g
                            OptionYesNo,    // Let user decide
- H4 K$ g+ H: E2 k6 v                            &hardErrorResponse );
8 W$ A* @6 x7 r  \2 f: p3 a" P                                            ! T+ R" B& [; L' i4 V* n5 \
                    if ( _LdrpInLdrInit )
  W; m: Y8 D. e5 i  s  g" A                        _LdrpFatalHardErrorCount++;
/ R, G/ z: s% g/ L' [) r1 H, h5 `- Y: t( N, Y
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
/ \' m) [1 G# h; M, g# H( a                        &&  (ResponseYes == hardErrorResponse) )* k5 H& a# w8 z% ^$ ~! h+ @
                    {
1 I: B4 D8 J! ~+ h1 {: D8 b: I                        return STATUS_DLL_INIT_FAILED;
+ P4 N0 U7 @# d                    }
: J* D8 k9 m% L4 C, Q8 X                }$ P  z+ ]0 b4 G/ J; ~. J; S

" L# L: R. y8 G6 p                //
" @' k0 Q6 w( |2 e5 H                // 入口函数返回0,错误
! S' t& {& p) O3 m9 ~: G( }! A                //- S% z: B# T4 g& V( l) w
                if ( 0 == initRetValue )
& x% _. ?/ {6 j! l                {  {. m1 k( g) N- s/ N' @5 Z6 M1 p6 H$ `
                    DWORD hardErrorParam2;' \! ^: F' A7 N& C" {! p3 D* L4 Y
                    DWORD hardErrorResponse2;
7 Z; Q. B6 O' I' g                                        - r1 N) ?+ k2 R" Y+ n, F+ D
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
: _6 I0 n& H1 c1 V5 @4 W" g                    8 M! l( a  S" @4 P5 m" G# k
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
+ h% J8 b9 {, h                                        1,  // Number of parameters: C' n! ]9 p% o6 r
                                        1,  // UnicodeStringParametersMask# Y$ T( Q" m7 s$ V6 v
                                        &hardErrorParam2,
& V; A, v" L+ D; K8 ~2 T! A                                        OptionOk,   // OK is only response
7 R+ s% V9 T2 ]  l4 f                                        &hardErrorResponse2 );! W, r& p9 D3 k, w) V. x
                                                            
9 C. T5 q, O$ r/ z$ D5 ?) R+ a                    if ( _LdrpInLdrInit )
- z4 h5 s" x* O. t3 b! U                        _LdrpFatalHardErrorCount++;1 Y, W3 ^7 X# C- t0 w
( j1 ?% l! @9 f
                    return STATUS_DLL_INIT_FAILED;
$ }4 p+ I( l* j0 S                }; b& X" M7 o2 }% \/ X
            }+ ~( o' k3 f- G
        }) j% c/ l$ h4 l# v) {$ w; O
! y  I6 M4 b2 |8 M1 [
        //
) P) S& n$ X/ H! z: a4 T$ z/ ]        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时5 x, O/ D; G3 w6 w$ m# M
        //      
  o: i6 d, E8 k- l2 X$ M2 F  a        if ( _LdrpImageHasTls && bImplicitLoad )
2 o4 V; f) v% V$ W% T' X6 [) Y        {
4 v' b# y+ D0 J0 j            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
. z2 j3 X4 w, Y0 H                                        DLL_PROCESS_ATTACH );
1 @, J( f: H9 X7 g3 E0 Q! s        }
7 Q8 I0 N2 V# b. v5 Y  y* A& W    }
! S: V$ d: s5 M7 Y    __finally$ g0 [# l- p3 P
    {4 m6 T. h" F5 g" i
        //% o; j$ r/ ]: j% f
        // 第四部分;
6 m0 m  p* t/ H        // 清除分配的内存* c$ ^0 N1 h2 z
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );( `' ^1 \  x1 A2 j" C( Y' V4 v
    }
" [+ G' P4 k8 S8 H( a  U' |- t1 o* Q
    return STATUS_SUCCESS;5 F' m4 h/ ?9 U. H/ {+ i' d) ~
}   
+ \& V& a' U" \
- R1 i% z! ^% F" x/ G. G/ e这个函数分为四个主要部分:
  A1 Z" l% c9 w; O* p$ x. h8 C% D一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。8 {0 w6 x$ e, T- J
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。0 Y7 u- ]( J( J" c, d
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。' X( B) N1 ~, u5 i# y3 r' y. S* x, W
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。. a5 |8 P: |+ t2 P
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。4 |- h0 ]! o" B5 v6 a9 A
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-10-12 06:50 , Processed in 1.121113 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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