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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
& r; T. s7 t) T$ S3 N( P% s0 @8 \5 h) ~2 o' A2 E  {
WINDOWS 2K Dll 加载过程
" r2 e( J4 v1 t  V* n- Sjefong by 2005/03/30
  q. r, \) u; h这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
/ x! a& M: [9 ^- k在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”。
- B- u  [, l6 y你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。- x# c0 [; k, Q5 p0 \
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
* U  L1 a. Q6 ~8 A8 |* k# @  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
3 X1 h  X0 l7 Y( G% K% p1 ^  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
% X( V* B0 C  X# v8 J$ B5 @  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):% I- y" F9 N, _6 V; W% [$ `. @
//=============================================================================, |' X  E& D" H2 i
// Matt Pietrek, September 1999 Microsoft Systems Journal! q# i: y9 x( @& s/ S
// 中文注释部分为jefong翻译1 O. L% p9 q8 W2 I( `" {
//
4 \$ A8 @8 D! ]& r2 p2 u// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
, q) A" @; k5 C0 h* D" V* H7 E//
( K- H* n! M7 t: S# F# O// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;1 V8 Q: D1 `$ W- ]  S) d
//=============================================================================+ ~( s9 B6 p9 @7 v
( r+ X2 e, \  f
#include <ntexapi.h>    // For HardError defines near the end& H; c8 P$ i; E; s0 M! X9 v

3 o6 F! f  u* B  v+ T  O// Global symbols (name is accurate, and comes from NTDLL.DBG)
9 `# k/ H, ~0 O5 N, o' M+ o//  _NtdllBaseTag
. j% G" R+ w  O4 {/ a7 d" `- ?//  _ShowSnaps
% K. n1 E; u" Z: b$ t& M/ v$ ~# ~$ h3 T//  _SaveSp
; u+ u+ u. L" T% F( v$ O7 X: }2 ^//  _CurSp9 s1 G& `: M. n1 C; H! o, f$ u0 Y( {
//  _LdrpInLdrInit
* d& ~4 p/ r. i  ^/ b8 I//  _LdrpFatalHardErrorCount
2 @  [5 {# `( a5 y* c  W4 K! q//  _LdrpImageHasTls
2 v( k. i( @* H2 T1 p3 l# ^3 }9 W( F* q/ t2 s
NTSTATUS9 A: H' S7 w( \% x, }% c
LdrpRunInitializeRoutines( DWORD bImplicitLoad )
% z& {9 M$ r% \# |; E% c# V{. G, _6 U/ o% k3 n% `& d
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
0 Y% ~1 `8 O2 C* y4 J    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();6 r2 @5 m% R# @: H4 q/ E. J: w

# q+ [. z5 }# m2 E    if ( nRoutinesToRun )+ ]; H: C- r) t3 D( N; t
    {) E8 b: X, i5 E9 x1 ?. g" P  _
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
' G# c' v' T. S( l9 ]        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
5 y6 _3 O- m8 ?0 V# N                                            _NtdllBaseTag + 0x60000,
4 c9 s- J& s4 h                                            nRoutinesToRun * 4 );7 ]* ?. ^/ a2 k% w  U/ D- i
                           
8 M" ~5 q* y  d6 |; b  b/ g5 z& d! }        if ( 0 == pInitNodeArray )    // Make sure allocation worked7 c( l! ?) w+ ^( u' F$ M. L7 @
            return STATUS_NO_MEMORY;9 y7 i" t3 w7 x1 `$ D0 |5 k
    }
- I1 t3 v0 j' p1 K: K9 j    else" h- G5 j# S# E9 B3 w
        pInitNodeArray = 0;
- w" P7 q* s$ t2 u
" @6 A/ g; s1 C3 ^0 w. o+ K: E    //第二部分;7 h. k  `  Q4 j" D( {
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。$ i& U3 A- O/ U& q# U2 e) f! x* \9 @
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);7 T1 J) x7 `2 [+ X
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;- R/ u. D6 G5 n7 c4 V% t( T
        1 c" B* C' R6 `7 i$ R! N
    if ( _ShowSnaps )# \$ {# S$ F& l! J
    {
) ]# V" L& Z5 @! D9 g        _DbgPrint( "LDR: Real INIT LIST\n" );" O5 v7 s7 W$ r6 g. k% |
    }
% d5 i) [) }) M& `: v9 K- P3 t& ^% y* \; _, U, Q
    nModulesInitedSoFar = 0;: C& W4 T/ q! F" F) P6 N4 m

  e* [* M  Z% i! O    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块& I, q' @/ y4 t7 v4 r
    {
" g6 N% c! {7 P  `        
2 o  ^& k+ C/ ^9 C  {* Z3 J        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块! X: {0 |. B+ ^& j2 k2 [
        {
3 D+ L; f9 Y$ P! `' W/ E+ h, _            ModuleLoaderInfo  pModuleLoaderInfo;
9 b0 [) _* g$ l$ a            
) a/ d3 n9 [% |) X            //
! A  i( O* ^2 [7 Z, j( n, h            //一个ModuleLoaderInfo结构节点的大小为0X10字节
1 F  P, h2 m$ _3 Z/ H4 C4 E* Y            pModuleLoaderInfo = &NextNode - 0x10;
  R: w6 u0 }5 C9 V5 u# z            1 C5 E) _- M9 G% W! p
            localVar3C = pModuleLoaderInfo;         0 W, J0 O: v4 K2 X; U# I: H

# Q0 v: N4 c; s6 v% Y* D* W4 W            //4 n* G' T3 d4 b' s
            // 如果模块已经被初始化,就忽略
/ D* Q/ s9 ~" M/ S6 {+ N            // X_LOADER_SAW_MODULE = 0x40 已被初始化0 H! _! e; M1 b. N2 X' b
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )5 n& G; b: s' v3 w/ v6 x
            {3 |7 T: k; _; U# E% K
                //' y- p% N5 O+ {9 |6 q. v; k$ v
                // 模块没有被初始化,判断是否具有入口函数
# u) n8 B: K  C! o                //
( l6 Z( R3 c* M  Y                if ( pModuleLoaderInfo->EntryPoint )' i) ?. k* ]5 o3 \
                {
) g  b/ e! M1 g# h3 ]9 s                    //
) J0 N9 V) E* x) J! C                    // 具有初始化函数,添加到模块列表中,等待进行初始化
% K6 m; P+ D$ T  }% |% ^+ N( S                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;0 k4 `6 U' y+ r, J! h" `  C

7 i; r% e" c7 U3 }, g5 E+ T0 z2 `( k                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址$ \5 C7 R( a, \9 }
      // 例如:
' T- y' s, D) ^1 I0 P  c                    // C:\WINNT\system32\KERNEL32.dll init routine 77f010005 W4 s7 R3 K2 D
                    if ( _ShowSnaps )
8 H8 |7 t+ k. T/ p. G                    {, C5 a  J+ f5 K6 _; s# ?& Z4 K
                        _DbgPrint(  "%wZ init routine %x\n",% ~0 @$ ]3 |, O1 `* f
                                    &pModuleLoaderInfo->24,
' ?4 ]# l8 I0 e- b  B                                    pModuleLoaderInfo->EntryPoint );
2 ?  F, G- F, r4 D' d                    }
& q( \- a# N' e$ c, {$ ~$ y- u* K; F% r/ C* k* g0 }
                    nModulesInitedSoFar++;5 Z0 j9 J# l$ u5 Z- B( o
                }- \) K, M3 M* W- T: `7 `: R
            }# v) e, S4 M! }, x7 C1 x+ l

$ m; f$ N+ W1 r            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
. r% C+ n' `8 F' {0 Z, J: V# W            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;# O6 h3 X8 |! R5 d& x- E

! q1 N$ c& y$ x4 z# ~$ v; g1 ?            // 处理下一个模块节点
) R$ t9 M3 d. x$ A- p( @3 g            pCurrNode = pCurrNode->pNext) m4 j, T- c4 x& `( F. Z& L* O% {
        }
, L! d* D7 d" G0 w    }
6 t! w4 q( ~, }' w6 O    else, J0 f. A7 i: Y0 j8 {) N0 l& o
    {
' e0 k* Z/ E% G6 _. ^- h        pModuleLoaderInfo = localVar3C;     // May not be initialized???
6 _, r4 b" j8 ?( _8 @    }
) P7 f$ I0 s. r    , U9 I$ o' b# g- H6 {3 O! T& C; i( h
    if ( 0 == pInitNodeArray )
7 }3 j4 g+ E; I* k: A6 A$ f        return STATUS_SUCCESS;4 c$ b; J) y' p& R7 E% o

. K0 O- b/ i, K2 N0 Y    // ************************* MSJ Layout! *****************+ D  c) L2 u, e7 k1 J3 V
    // If you're going to split this code across pages, this is a great4 h1 D0 k5 h; \6 ^# D
    // spot to split the code.  Just be sure to remove this comment2 M3 ~, m! j7 o& U2 b# h+ r; o
    // ************************* MSJ Layout! *****************! x% b4 J5 N. w% F) @% L
    $ M" ?& M9 D* `* H- v# K) j# c
    //: O! ~4 a" @8 L7 V; Q
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH0 R* a' q( P3 G6 i7 A" Y( `
    // 第三部分,调用初始化部分0 x9 ]$ h3 K7 h; C3 ^0 R6 w- P
    try     // Wrap all this in a try block, in case the init routine faults
; Q1 R6 I2 Q* ?' v) g    {
( o6 f6 B# B" u1 x3 G) Y        nModulesInitedSoFar = 0;  // Start at array element 0
7 H. E- M5 j$ |+ L5 @$ Z6 f! |7 Y/ }: s7 f( u+ J
        //
/ O7 O8 t3 ~) n: z( |        // 遍历模块队列: `; W$ Y8 Z& l/ e: S& S# I
        /// ]9 g# n' i( V  g3 ]
        while ( nModulesInitedSoFar < nRoutinesToRun )
3 h+ r! J, y7 Y, w        {# u+ p: c, P1 H& X
            // 获得模块指针
/ P1 k0 a' O( j            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
! I- b( Q8 n7 V7 u& }$ {& w/ n) e5 g: k7 A3 C" K
            // This doesn't seem to do anything...
( q1 D: N$ I, m, \/ @            localVar3C = pModuleLoaderInfo;5 k( |8 S. m1 `7 {. T8 C
            # {" T. U$ L0 _) @7 u
            nModulesInitedSoFar++;
+ j. p1 v1 \: m  q" O- @6 s               
& M# Z, _1 L4 `            // 保存初始化程序入口指针4 c0 }4 ?5 b, e& q0 A6 T# R
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
; U0 k3 y' b- E+ u2 ^- U8 y            ) r% e" o* k  u  G- e: `7 f# l
            fBreakOnDllLoad = 0;    // Default is to not break on load4 f' O6 i4 Y# r

$ y3 P1 I8 k6 Z" ?0 A4 |            // 调试用
" R3 w" m  @9 r. u6 c            // If this process is a debuggee, check to see if the loader8 a* S& m0 z' A7 q# C. ^9 P
            // should break into a debugger before calling the initialization.( v8 L8 L: y1 J3 a1 X, D
            //9 F$ \/ h' c& h% z
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
$ g3 X  A0 n5 a9 u, g* F3 a( K            // returns. IsDebuggerPresent is an NT only API.
& n; x+ W3 o2 p& ~  f            //
3 j# [3 b6 [0 `9 [4 Z3 E            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )# U/ X# Z3 I% V- T2 u: F7 m
            {9 }8 K% l' e' U& K. L: e' I0 m1 [
                LONG retCode;/ R( i2 j7 l& t) q( b1 `
/ [+ t$ L8 U% f+ J$ f
                //              3 C& e, r& V* y: ]
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\7 L7 E. m- f4 h0 c: S9 x2 [
                // Windows NT\CurrentVersion\Image File Execution Options"
) Q9 D! O3 M' Z1 L/ v7 w                // registry key.  If a a subkey entry with the name of
9 r/ s' t9 L+ G                // the executable exists, check for the BreakOnDllLoad value.
7 }  k; O" v" p                //
7 T# P" G8 D7 \/ h                retCode = + H0 Q2 {) O, u, |0 N* D4 k
                    _LdrQueryImageFileExecutionOptions(( e/ C9 @" Z" W- N; t4 ~% ?3 w: Z7 B
                                pModuleLoaderInfo->pwszDllName,  y$ d7 |. j% k4 G+ c
                                "BreakOnDllLoad",pInitNodeArray
- |7 ^2 l% [! x9 T" @% U: ]                                REG_DWORD,
; S2 X  {' D7 K: A9 n; d2 u- {                                &fBreakOnDllLoad,0 C+ b! S' @- I
                                sizeof(DWORD),
  l8 g) ]' r$ _                                0 );
4 H, P+ U+ X0 }
$ N) `* R' l4 \# F9 ~: Y                // If reg value not found (usually the case), then don't
( l9 ], g3 \+ ?                // break on this DLL init
# T4 d) W+ F; h( G$ X3 S+ ^                if ( retCode <= STATUS_SUCCESS )2 n& i8 {1 ^, o- r5 b
                    fBreakOnDllLoad = 0;pInitNodeArray
, \+ y* N4 C, Q7 a1 s8 c            }* ?7 _6 Q  M* r! y
            
, J( B( S# L0 Y. F2 d! D6 G3 v+ `- a            if ( fBreakOnDllLoad )3 g7 v9 T1 N/ w. i. T
            {           
8 ^2 L$ n5 w" Y                if ( _ShowSnaps )( W) c% K0 [+ v2 I( N8 N, @" e- b
                {1 M0 g/ H: O- u, ~/ h
                    // Inform the debug output stream of the module name
, c( n  y# l5 O* _" C8 w* j                    // and the init routine address before actually breaking
  ~/ P* T/ q! S& z" n9 k+ M                    // into the debugger
: W, s! M3 s2 v$ G3 C) m: f
( s' y7 c5 K* h% C# l. y% h1 Y                    _DbgPrint(  "LDR: %wZ loaded.",
5 c- Y- X9 q& F3 b6 G$ R2 y. v                                &pModuleLoaderInfo->pModuleLoaderInfo );
- T7 p# @' n5 G; m! |                    6 r3 k& @' c' `1 }, l
                    _DbgPrint(  "- About to call init routine at %lx\n",1 S3 |9 i  r" h
                                pfnInitRoutine )
6 B+ P5 `# P  ]                }
# p0 q7 a* p7 ]( @3 s2 l6 {- d               
4 j$ W# a+ M  d' E) j8 f                // Break into the debugger                              
8 [& U5 Q1 k# ^: F                _DbgBreakPoint();   // An INT 3, followed by a RET4 g9 [7 G, Y& e8 q- _. K
            }" e+ ~' m; L. c# Y6 e
            else if ( _ShowSnaps && pfnInitRoutine )
) k8 |1 a5 g/ Y' A) k: A) V            {
8 I' p( [; I9 ~0 M                // Inform the debug output stream of the module name! _7 t/ F# B  e* M  s2 W! O  h6 [
                // and the init routine address before calling it               / I. x& `7 f+ A& r
                _DbgPrint(  "LDR: %wZ loaded.",8 Q! u" j  G; H% S6 ^' M7 q' }7 \7 C
                            pModuleLoaderInfo->pModuleLoaderInfo );
! A; [* z% M* x( R% k2 Z3 r: P* v
* |3 P' n) Z& `                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
7 M: D( l  C/ K            }' ]' c4 a/ B/ {. t0 d& W; _
                    
' n- k2 p8 ^" {            if ( pfnInitRoutine )1 \1 Q0 n- y, s8 e; m; a
            {1 d0 h8 L/ f( H' f4 [# O
                // 设置DLL_PROCESS_ATTACH标志
4 T8 D/ b* V: R# Q                //
+ t+ g% I4 y. Q6 V/ t- H                // (Shouldn't this come *after* the actual call?)
) L! w+ Q9 r7 ~% A8 t" C                //, }( G' A! L6 M  ^
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             % G/ x3 l- T1 F- j* ?+ d
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;# h* g! p7 ]& R- S

8 S. R" T: g) ^) [/ i0 P# @                //0 s1 x) ]9 R  c& V( t5 ^
                // If there's Thread Local Storage (TLS) for this module,; x/ }2 p! t+ ]& p
                // call the TLS init functions.  *** NOTE *** This only+ t0 f( y( Z/ o% }( g1 G
                // occurs during the first time this code is called (when5 N5 t2 f2 n/ r# j$ j# s& Z$ c
                // implicitly loaded DLLs are initialized).  Dynamically. u8 p/ A; t2 i! ^/ x3 K
                // loaded DLLs shouldn't use TLS declared vars, as per the7 }) V" K( m/ F0 y/ M* H& b: O
                // SDK documentation
) ~6 o# I# P( d1 C, p                // 如果模块需要分配TLS,调用TLS初始化函数
4 p# B# e7 A% Y# c6 T! R. x  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
; B% O4 ?& f+ @  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
8 T6 p. M  K* S1 b, R( H6 [                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
4 h& Y% L7 Q# F( ^# l) }                {# v/ Q; c6 w4 O" W9 }
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
- x3 M8 ]6 ^" S/ b9 z9 v                                                DLL_PROCESS_ATTACH );
& g$ z" y' a, n" i- m                }
: @. p3 a/ _# e# i& `               
  U( B3 O0 D' p5 t  w  i! c! h, Z, |6 p3 U0 j% T) g: r
                hModDLL = pModuleLoaderInfo->hModDLL
# R+ F6 V8 `3 \% ^
8 }# ?4 J$ d% K3 N' }" l# }- l2 r                MOV     ESI,ESP // Save off the ESP register into ESI6 ]5 ?& }4 T) I
  
+ E, }6 `; ?% r" P: _' d# K. R# H  // 设置入口函数指针                : a/ S4 J' V3 y1 }  s1 J4 W
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     ! A( L/ Q4 s# e) c

8 j4 C& F0 {8 N                // In C++ code, the following ASM would look like:2 J8 r; D8 L! Z1 u4 v
                //3 H- @" B8 B+ W
                // initRetValue =
, q& \* U# r8 `* W0 N! [                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);- W/ }3 K$ u" t6 S
                //
1 I4 a+ m8 y6 w" @1 L0 \/ F9 V
, ]; @6 ~9 n9 y& Y$ g5 C# J                PUSH    DWORD PTR [bImplicitLoad]* G  f# R' U( g  d) w  n0 C
                - S) }5 X, U9 l7 e
                PUSH    DLL_PROCESS_ATTACH" _+ k. g. m3 Z/ I! Q
               
2 M* Y3 R$ j0 V0 q; ?9 S                PUSH    DWORD PTR [hModDLL]/ {0 z1 f+ Y, q9 {; B- U
               
3 o3 K2 S. Z" b2 @4 [9 L$ b  V0 J                CALL    EDI     // 调用入口函数) V3 ^9 q' i+ W3 |) @' ?# c3 C
                3 |5 @4 X: ]# a1 y, f
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值! R7 K7 X7 V8 W* b
, D! c, F: j8 _/ P0 D% A
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
7 S# J- m* k* N' s; S                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns: C6 Y! V8 X4 @' c( y$ A, l7 m4 u

* R9 \+ ~) w" m* ^# M8 V                MOV     ESP,ESI     // Restore ESP to value before the call7 H+ P, [2 s, k9 t/ B

; I$ G% u8 d" ?                //) I9 B7 Q! h% Q
                // 检查调用前后的ESP值是否一至0 H+ ?; \3 B0 R0 g$ E& _2 ?# ?
  // " j% d% P! ]5 H! l( g. b
                if ( _CurSP != _SavSP )- A5 H& U1 L& R4 ~) P* T* c# G! }
                {* W7 @4 Y( f3 E7 `1 m0 X  v
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;' I# y- p2 G% P

4 _4 A: H2 a- d9 E                    hardErrorRetCode = 5 E8 h+ L! K# n" o
                        _NtRaiseHardError(! Z1 y6 C6 q3 l) T' }: o, {9 j
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
0 D( F/ T) ^/ ]3 _) i" @, u4 `                            1,  // Number of parameters; i0 w1 F9 U/ U
                            1,  // UnicodeStringParametersMask,
' o6 T9 N( p3 o( ~                            &hardErrorParam,& z, Z/ w4 N9 H: |
                            OptionYesNo,    // Let user decide* O- h3 Y# X. H. g" f  Q; d1 C4 r
                            &hardErrorResponse );
9 [3 Y1 e- Y! m' M                                            9 B) p3 v# F+ J) \1 W
                    if ( _LdrpInLdrInit )
1 ^  x+ q0 F9 F# M9 Z6 T2 y                        _LdrpFatalHardErrorCount++;& n$ r# f5 Y' F

9 a( t$ p) H7 {7 E: n7 I+ u, U: D                    if (    (hardErrorRetCode >= STATUS_SUCCESS)8 m9 p! n- {; c# h
                        &&  (ResponseYes == hardErrorResponse) )2 U  F+ W7 [0 ]; z
                    {
; x1 |& T8 L5 F                        return STATUS_DLL_INIT_FAILED;
4 s( z3 v1 R8 Q( K* F$ x                    }
* q. z  x% Q9 w1 X* _% a0 H                }
7 @( ]8 l) b; G1 e6 B
! J2 g: x+ @% e8 C: F, D                //
1 n5 `9 b' C8 f% v& S                // 入口函数返回0,错误
/ b; M7 s: y5 _/ w8 G                //( U/ d; _. B- t) g* ?
                if ( 0 == initRetValue )0 E) B6 S9 D1 h1 K" j
                {
% j$ Z& v( T/ H+ @! N" m                    DWORD hardErrorParam2;
" [3 U9 i9 a# e! m                    DWORD hardErrorResponse2;4 {7 Z* K7 b  j* P
                                        . i( V! L% X5 v! k% H
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
- ~) `7 F- V* v( u/ f; c+ f* b/ }" q                    5 |( z% @2 Z: B1 A, ~+ q2 l$ h
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,% ~* g, ^  U# v
                                        1,  // Number of parameters$ z+ [7 c* F; U
                                        1,  // UnicodeStringParametersMask
( j$ ~& b- r* c9 M- n9 }, b                                        &hardErrorParam2,! _5 A7 F4 g( q/ A1 l3 L: f
                                        OptionOk,   // OK is only response
* L% p- \; F# T5 k3 l                                        &hardErrorResponse2 );6 b  {+ v# w, Z7 G) V
                                                            & o) i0 s) Q" P0 B1 O6 J
                    if ( _LdrpInLdrInit )
; O- T, ^1 `1 {                        _LdrpFatalHardErrorCount++;
8 z' L+ v2 E% B
6 N0 q* ]4 S2 C, Z; c' I, L                    return STATUS_DLL_INIT_FAILED;
9 W: U2 s' @* g; G# j. N/ d, \                }
4 h1 y! F) Z$ q" u9 Y5 ?            }
; H% l6 t, E( f+ i3 U0 c        }
$ F0 }  Z6 N, y
2 P6 Z8 e) e, G' m        //
3 J+ x! n4 H0 i- H- E* Q* R        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
. X& ^% |8 N; ?( ]6 k! D, f: a        //      ' `6 ]% ?6 `3 r) e/ O4 V* M1 Q
        if ( _LdrpImageHasTls && bImplicitLoad )9 R; A! {8 K) d7 Y
        {
% i8 I: \6 {) G) v: q1 ^9 B8 R            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,# Q5 ?" H5 u: S8 o0 k* ~
                                        DLL_PROCESS_ATTACH );- i3 T. G& c3 o9 H
        }; Y1 F7 l1 N9 `# @
    }* Z$ B" _( D* n& o9 E; {3 q) E7 E
    __finally
$ N6 W+ m1 d) o5 }0 f: X    {
8 S1 g& I1 y0 ^  {2 a: ~4 E2 x        //+ U) B" s% b& j+ o% R5 ?9 D1 \( R
        // 第四部分;4 L' \, k3 \% g7 Z, E1 j: H
        // 清除分配的内存" J6 {% l) P5 E' V1 k7 p
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );% }) v. n; l. a9 b1 S
    }
! ]; S4 b* v4 N  Y0 ], \6 d! s, D& p7 }- k! L
    return STATUS_SUCCESS;! B0 V# _6 ~9 o2 p6 u( W
}   ( k+ s3 p9 c2 ~9 I

" _; r) W6 r% [1 J. V这个函数分为四个主要部分:( G9 B$ D' O% S. D5 Y% o/ \
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。) o' v7 w! O( V  w
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。9 E) F; K" a5 y2 ?3 Y+ M
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
8 h* n& x  X0 y7 Z2 \4 w另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
+ i$ H/ {( ~1 g4 t+ l4 F$ Y( y" r- c在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
, c5 w( }( t: Z四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-4-19 22:01 , Processed in 0.074223 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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