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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
2 p# a  r% A% B1 B" W, |& o2 x
& X4 O/ ~4 o+ Q1 f& Z( \8 KWINDOWS 2K Dll 加载过程
: [. J, a; B) g& \jefong by 2005/03/30
& j' F5 c" A  L+ \" ~8 Q% K这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
, M8 A0 R  T+ }) b在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. l7 t+ A: i, T5 d9 e
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
& N$ Q4 V3 o9 F% D# _, u初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。6 ]* J% ?$ z( F' D. x4 J
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。$ @  g; x' }* a( E/ I9 A7 @; o9 L
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。6 x8 M! q* u$ v: ~
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
' I3 Q+ ?, V# a& ^//=============================================================================0 q; T7 F3 x1 h$ o% R/ w* @& \
// Matt Pietrek, September 1999 Microsoft Systems Journal
! m4 V) `" b1 O- F8 E! n& o1 a$ }$ ^( d// 中文注释部分为jefong翻译
, m) p, ~7 j$ M! ?% g4 A//
0 c) G7 M, l/ z6 @// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)& U9 U7 m$ s- G' a3 s3 B4 b; c( r' d
//
& a3 {  j, S* [: f( e3 b// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
- B+ t! R% G( F- Y& }8 ]4 \4 X; k* P# u//=============================================================================
3 M. b+ w8 t9 O9 O  @" H  T; X$ l' V2 P+ L+ q
#include <ntexapi.h>    // For HardError defines near the end& O+ h5 H' o# r9 j$ a
% h+ a, o6 Y( N. S0 L
// Global symbols (name is accurate, and comes from NTDLL.DBG)
+ E- e! t7 W# j+ L//  _NtdllBaseTag! N' c% i: v. R& p/ R. X% q  ^  U
//  _ShowSnaps) u/ H# y3 A& G2 z7 Q
//  _SaveSp, l' Y% H" T; Y4 L+ f/ ]
//  _CurSp8 ^8 H2 E' _$ o- N" @# J. m8 V
//  _LdrpInLdrInit, V8 C' a% y5 h- o2 V% }, o  b0 f
//  _LdrpFatalHardErrorCount
" W' ^% M3 F. H//  _LdrpImageHasTls
, U; W# y- Q( S, _  G- s7 ^( k7 J( y; |$ L% |. M' @! X! }
NTSTATUS" I: L2 G; F5 h* Y9 |" f+ l
LdrpRunInitializeRoutines( DWORD bImplicitLoad )
7 H3 K9 L) w1 ~+ K2 j+ R{
" z' ?+ L7 V2 P  a    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
" Q, G5 r  \3 v+ G3 a    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();( B/ u* E8 _0 i% V8 M! v% ?/ M

' C; S9 W5 u7 z" J5 T    if ( nRoutinesToRun )* G0 Y5 r/ T* v9 Y; r
    {
) T4 A% f9 E# _1 j% Y$ G, v        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
& e0 }' F8 j; }        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),1 X" Q7 @! @4 a; @
                                            _NtdllBaseTag + 0x60000,. g' m' D# h' l& N' D
                                            nRoutinesToRun * 4 );& A, `5 t' p( ?5 _; ^! y
                           
0 D3 T+ z3 X, M. @5 Y/ }        if ( 0 == pInitNodeArray )    // Make sure allocation worked) z% W/ @% v( e6 E* M
            return STATUS_NO_MEMORY;
; ^3 W7 z: y# a3 t. f# D    }
6 G! P6 h  r; n, B; G2 Y    else: N# u( R5 P* J, ~8 t0 G/ G
        pInitNodeArray = 0;: U- ]3 S# F% A* v6 f" W8 q
0 l/ c. _" V6 z( o
    //第二部分;
: g$ F- f& r( R    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。- L7 V% Q% o9 F" j, s( f. G
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);7 C; f6 J) Y0 V0 T& e0 |# m
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;) s, o7 y5 y! O* `+ E6 i/ P
        ' f5 G% ]4 W9 S; ]
    if ( _ShowSnaps )2 e6 D* l  c8 u: N
    {  F: o3 O- @6 v' `
        _DbgPrint( "LDR: Real INIT LIST\n" );
1 o! n) n4 s$ l    }
" \$ y) j5 V1 ^  j
( J6 O# m% K/ W/ Q1 T. m    nModulesInitedSoFar = 0;
/ y6 [! ~! _" l; `9 \7 C' l# d4 I/ k7 w
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
$ I( W# O6 C1 Z* Y4 ]6 ^    {3 _" u, d; a+ r# p) t
        
6 G  ^; \7 @& \8 P; O3 x& I        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
! W  [$ _& A/ [! K        {
$ M) R; ]6 o$ [! V( O  G            ModuleLoaderInfo  pModuleLoaderInfo;
" z/ R! C% q! j7 t/ a$ i2 T  E            
0 I# U8 Z% b8 T- D. B2 H7 R, V            //, W% ~" e4 W; }4 x. K
            //一个ModuleLoaderInfo结构节点的大小为0X10字节
8 \( f3 d& d/ T            pModuleLoaderInfo = &NextNode - 0x10;# s3 G% ~- r# S& l. n
            # U& G: h: v" u5 P. B' a
            localVar3C = pModuleLoaderInfo;         4 x) f& T% Z5 n( G

1 ?; [2 R2 u. _' _- ^9 N+ |            //
) F, R: N- A+ c4 F9 p3 K3 z            // 如果模块已经被初始化,就忽略
  ~. n: J' `  q  ?- O. z            // X_LOADER_SAW_MODULE = 0x40 已被初始化9 t. r# }6 ?1 R, E3 v
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
( X. f6 |2 d8 r8 F; [( n: @            {! o/ c' r% M! s, W
                //
" L, ~( ?6 n  t1 o4 a" b                // 模块没有被初始化,判断是否具有入口函数/ J' J2 d* [* I5 g' |3 X
                //
4 @/ ?, ^! J+ l6 C8 Y" c9 E                if ( pModuleLoaderInfo->EntryPoint )
$ I9 J$ T. b: g7 i                {
+ ?0 X. U' V* ]: @                    //
% r& @8 M' i& |5 M* Q                    // 具有初始化函数,添加到模块列表中,等待进行初始化7 G$ Z% R  X  N; F$ A
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
  ?: T# B# o/ Q2 [4 x3 ~+ S
) T1 E/ X. u! ^$ w                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
$ m6 |; |6 v  m/ U3 {1 b      // 例如:
; u* j, T; J1 `' k% M7 J& t                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
; [: E7 s, B9 n                    if ( _ShowSnaps )6 U1 {' U7 B# H# U$ D
                    {
$ u, _: G) b+ ~6 @                        _DbgPrint(  "%wZ init routine %x\n",
) {3 a$ @0 f6 K5 a% L7 M$ q$ Z                                    &pModuleLoaderInfo->24,
5 V, ^4 g; p. v9 m, H: V                                    pModuleLoaderInfo->EntryPoint );1 O  U' H" ]: K8 W
                    }
! h$ c- m+ o" ?8 \% {  J/ j  Q1 j% ~$ e7 ]1 @6 e" ]3 [
                    nModulesInitedSoFar++;8 ^; U) H  \2 e) \3 J% ^
                }
7 m+ J7 Q) S" v' \* ?, a            }7 K! g5 s8 l- _, d' o7 o! ?
6 Q% U& \& U+ ]+ ?, ?; I: z. c
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。8 A/ c9 H6 q4 a9 U1 G$ _2 d5 B2 T9 t+ ^
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;5 ?; ~& }3 s# l  W6 h

( d$ W; r% n* {9 g( e/ \& T+ f6 @: n            // 处理下一个模块节点( R4 ]: T' v/ v
            pCurrNode = pCurrNode->pNext' X1 I3 S- n9 q- `( x7 y. l
        }
& \& S+ v$ c; m% S$ P% F# P    }
. a( ~' ]" u% h    else
0 k) `( j  ~( Q0 B3 Z7 v    {
; Z- C! b+ Y: s! T2 g* k1 _        pModuleLoaderInfo = localVar3C;     // May not be initialized???
6 ]7 F' M! O. m( }+ R    }* R+ Y1 ?- [8 N0 X+ M+ S! z4 O; n
   
' j9 F( R* a$ Y1 W    if ( 0 == pInitNodeArray )& N: w. W8 Z! J) [1 j
        return STATUS_SUCCESS;
: C' e: m2 ~& D( |2 f1 Q( a/ S  Y! i
    // ************************* MSJ Layout! *****************' w* R- p4 p; ~' ^+ H3 J
    // If you're going to split this code across pages, this is a great
# K. b* C/ {0 }9 p9 r    // spot to split the code.  Just be sure to remove this comment1 m4 Z# J. b; C) P' f
    // ************************* MSJ Layout! *****************% U) r# i1 g, L& c" c9 G
    / ^, d/ t8 N6 @) x8 l6 Q7 N7 S
    //
3 u5 R9 y2 ^0 V: b7 O2 r1 ?    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH+ g" B8 H( u3 v& {5 Y2 q
    // 第三部分,调用初始化部分
9 l, J/ z4 L$ B/ _9 s2 L    try     // Wrap all this in a try block, in case the init routine faults
+ B1 D# m5 \5 u4 X6 q    {, ]0 H. J4 H. h+ U1 |! B
        nModulesInitedSoFar = 0;  // Start at array element 0
6 y! ]. N. U# a0 o; T; \6 }
* r( u& k5 u$ T        //3 c- X3 W  {# c
        // 遍历模块队列
- J7 f; L$ C* X8 |. z' a0 c8 M        //
$ y# n+ I, d& Z4 U2 i& n. r2 Z        while ( nModulesInitedSoFar < nRoutinesToRun )' p$ o% x9 z! E* u
        {
: H2 b* A4 N9 B, L! H$ `- k& Q7 c            // 获得模块指针5 R' y1 E7 _( R. d4 I# K
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
% v' u+ u" G5 h* h
& i  U: _9 u8 c3 _. S/ u( V% L4 I            // This doesn't seem to do anything...2 b, {! c8 U5 _/ @% A
            localVar3C = pModuleLoaderInfo;
- R% c' @7 w, S% x: G2 K7 G$ W& B6 ^/ G            
! U2 i' {( q' `9 o. U            nModulesInitedSoFar++;
! [3 V" g" ]" ^+ R               
: H# [2 r+ b" e% y* c/ p7 J, G            // 保存初始化程序入口指针! \2 M: _. B) n* G, ^8 N( T5 ]1 k
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;3 ^9 J* T' q' Z5 F4 t; M. g
            
% j. G& w; a% I/ d4 x7 z            fBreakOnDllLoad = 0;    // Default is to not break on load
+ I* q- ]) N2 [/ Q$ {7 t
. a4 U' }4 c+ C$ g7 q  p" `            // 调试用
5 m& a, X' W3 y2 c! J% o            // If this process is a debuggee, check to see if the loader
) K+ G0 a$ F' s( v( R* S, T  d. C/ \            // should break into a debugger before calling the initialization.
% S* E' a+ n+ \3 K9 G% M' q) M" U            /// W0 s, H+ Z% x" ]. W1 k. e
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
" H( c' l. y" p' _            // returns. IsDebuggerPresent is an NT only API.- ^# s1 s0 Z! x/ G
            //4 ^) e" P. T0 `" ?5 F  N# z- N
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
* w- {, J* T4 G& l" H/ w            {
3 t- ~/ s; h' t+ u; B                LONG retCode;/ U9 w* K+ d( S

, R# v4 i+ C" B! [                //              
' r7 d0 Z- ]5 y                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\+ @& c) z7 U5 y( q( u; e; |$ d
                // Windows NT\CurrentVersion\Image File Execution Options"
! ~0 ?4 I; j5 C                // registry key.  If a a subkey entry with the name of
5 C# i8 C' H: f$ d                // the executable exists, check for the BreakOnDllLoad value.9 K. J9 Y! N9 V- \. h7 M; r- }1 A
                //
) a5 f& M+ b( Z. O! I                retCode = - M. N- n; K1 y" _
                    _LdrQueryImageFileExecutionOptions(
2 z' R/ i# F% z* k, W                                pModuleLoaderInfo->pwszDllName,$ S% [# \, [4 y8 P
                                "BreakOnDllLoad",pInitNodeArray
% j3 \$ {9 ^, b" p9 |' d                                REG_DWORD,9 L' D( w0 w0 U* R$ M. Q& v" A
                                &fBreakOnDllLoad,
( T# R! D2 c; ?. R- S                                sizeof(DWORD),% q$ R0 B. `$ U* l& P; [
                                0 );( c6 u  w7 `* V0 y# T: p
+ E  J  n* q$ y2 `0 v$ P( i
                // If reg value not found (usually the case), then don't
, `9 `; Y: c4 j' `/ n4 c                // break on this DLL init
3 ?( k! t0 F. {# Z8 y                if ( retCode <= STATUS_SUCCESS )
  ~  i" l$ v( t) C6 [7 c                    fBreakOnDllLoad = 0;pInitNodeArray5 p6 z. {5 d; J" T; ]
            }/ q2 a1 z/ T  F1 e
            5 z2 x  O% p: U' S( k
            if ( fBreakOnDllLoad )5 S# a- R) b' q+ h+ r, V
            {           . U% S8 Q0 U. R- J
                if ( _ShowSnaps )
* |8 h" u+ q) _2 ]1 z) K                {
) {9 k! U/ [! M, @  J  v( r: h                    // Inform the debug output stream of the module name; y2 x9 M' S1 k! J* C" l
                    // and the init routine address before actually breaking( ^# e' S3 s' [9 \% T
                    // into the debugger
" u& _0 e3 {' c# a9 }8 F$ Q5 A
7 h) s0 S% r, F' E/ T                    _DbgPrint(  "LDR: %wZ loaded.",& y+ J4 v0 z& N: }" u2 c4 w& X
                                &pModuleLoaderInfo->pModuleLoaderInfo );6 b5 i$ g% O+ w+ z
                    
# l) Y+ D8 V2 ]8 a9 R                    _DbgPrint(  "- About to call init routine at %lx\n",
+ M7 ~1 J; o2 {& B. G( L                                pfnInitRoutine )
8 I3 C; M# O% K( o' x. |                }
9 L) x0 p, Q- R- V" ~5 y3 _4 x                8 i( f6 Z" i) |2 ?/ J2 l4 w% P8 u
                // Break into the debugger                              
2 h0 x2 Q" H7 H( W                _DbgBreakPoint();   // An INT 3, followed by a RET
  a! b$ y0 A, S7 J& U: G& D  f            }8 U# e$ B- [9 x/ R: `
            else if ( _ShowSnaps && pfnInitRoutine )
: }4 u+ W& v8 {( }  A# i            {' N& M1 w: a- d( e7 R! e  M4 A0 _) _/ V
                // Inform the debug output stream of the module name
: M6 G0 D$ F2 M7 f9 j                // and the init routine address before calling it               
3 Z6 [3 q2 C. {  ~2 e6 x1 |                _DbgPrint(  "LDR: %wZ loaded.",0 P  m- V: u8 _6 `
                            pModuleLoaderInfo->pModuleLoaderInfo );' m; R6 S0 @  Q& ^

! l: z6 U2 R2 j1 r1 K8 z                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);+ q; _5 ^" _4 L  W' v. z) ~
            }5 e( Y2 |+ Z, o3 m1 G
                    
- u/ n* m8 }. M            if ( pfnInitRoutine ); x3 Y0 R. A. u
            {. |( o1 v3 O5 d2 A+ ~. S' k
                // 设置DLL_PROCESS_ATTACH标志- |8 u. p; A5 \% D: h8 ]! v5 Q- Z
                //
7 e( m% m, c7 M0 M. k" M9 W                // (Shouldn't this come *after* the actual call?)9 M1 m& F+ ]8 B' y+ v
                //. v7 L6 y: [. e6 [2 r( D1 f) v- l6 s
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             7 i2 ~) _3 C3 A, |2 p
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;: x3 G" `5 c1 D" m# u8 g  ?' \

2 t4 t- V  q( g) [                //0 e7 X; e4 M( f! S
                // If there's Thread Local Storage (TLS) for this module,, T* \' V% z$ U2 {
                // call the TLS init functions.  *** NOTE *** This only
( w( ~) X& k, ?                // occurs during the first time this code is called (when7 ?. y) P3 W( f. z' V3 x  R' m; ~
                // implicitly loaded DLLs are initialized).  Dynamically
3 f2 j( J9 ~) w5 \$ \, S3 f* m8 D                // loaded DLLs shouldn't use TLS declared vars, as per the
- x# F& I! v9 C, W# Z8 S: w" Y6 Q                // SDK documentation
9 a1 F, w( ]) T, V7 U                // 如果模块需要分配TLS,调用TLS初始化函数
# L* y/ M8 d7 _9 }+ R  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
& I+ S/ {4 r5 D+ Z5 \5 q4 J3 P) {  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
1 ]0 x# s8 d6 p3 U3 @7 M                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
" k7 k, t) L% k                {! w) H9 A# ?, e$ W+ H1 F$ C+ |8 P
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
) G; n6 y) [* Z, h: V6 L$ L                                                DLL_PROCESS_ATTACH );& a+ w, g" Z% F7 |
                }
: l: `6 N& B  i% c1 }                9 C7 s: A3 j, K4 l

* i* |  V% ~# P  X8 |& d                hModDLL = pModuleLoaderInfo->hModDLL$ U  D# R' ]  U' I7 ?

4 |' |8 a- t0 N1 Q+ `7 b0 S6 H$ k                MOV     ESI,ESP // Save off the ESP register into ESI
5 v* m; f. G+ w0 K+ s, m  % a/ a* y# N8 `! |
  // 设置入口函数指针               
( u/ m: z6 a: R  a& m* [- K8 b                MOV     EDI,DWORD PTR [pfnInitRoutine]                     / L, ]+ U# C* l+ C

3 z" \/ f2 @! I/ _  p+ m                // In C++ code, the following ASM would look like:
# a8 g( v9 ^6 e                //
) P/ [. w7 z- ^2 ^, h                // initRetValue =6 @2 n1 o6 g; ~0 j% O2 E
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);* `- ^- q% A% [+ q& {! t
                //- Z# n2 B1 g$ L

$ U- p  j- ~# A# }                PUSH    DWORD PTR [bImplicitLoad]
4 D7 E* }0 j" j0 p8 F  ^2 X               
3 t; \% a, o6 y2 s: ]                PUSH    DLL_PROCESS_ATTACH
# Z0 b0 _, V! F6 A1 ?                  P% `1 V& k2 r$ K0 i3 x; \
                PUSH    DWORD PTR [hModDLL]# s, A! U. ]! c6 p) o: t& R
                2 S- u' H( e/ V4 v5 B3 ~
                CALL    EDI     // 调用入口函数
% \. ?: C2 w+ W9 ]$ x               
# f8 Z5 W* o2 c                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值6 p: K. g3 j* r9 k" q! P" a

5 C+ @  E/ i- k  {% M4 {; h                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the; d" z5 d4 Y4 Y% U1 D
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
) e1 K9 R. _" g1 T/ ?! Y3 t2 ^- P" B4 ^% q% f4 k; s
                MOV     ESP,ESI     // Restore ESP to value before the call5 f! u- ~5 @. u2 [

4 b. C% k3 c7 k- c" ]                //
5 k% h) Q6 {* t' J                // 检查调用前后的ESP值是否一至' c: q3 s& x9 f! j
  // 9 [3 b6 u& u: K5 Y
                if ( _CurSP != _SavSP )
# c8 p( O# S# j9 v5 f                {6 N6 b& C0 |+ n
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;9 n# l2 Y. G, z
) E/ ~- s" }% G2 S& q( w
                    hardErrorRetCode =
% p% X7 U3 V( z& r8 f. K+ b) l                        _NtRaiseHardError() ?7 D6 f1 J1 Z2 y; e! ~6 c* \8 ~' N
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,5 m1 Z: }' R! j/ Z
                            1,  // Number of parameters
" I: `' j, a9 n                            1,  // UnicodeStringParametersMask,: D8 S8 R( A! y1 ?% o
                            &hardErrorParam,
, k4 m: i- A. U                            OptionYesNo,    // Let user decide
. ~# r4 a0 D: s$ e                            &hardErrorResponse );$ ]& n% U* G! y3 G" V
                                            
4 `- G7 r7 w9 ]* J                    if ( _LdrpInLdrInit )
9 k; r! X. F2 l: B; \                        _LdrpFatalHardErrorCount++;  Z% P1 r+ X" d" B$ ~5 |
0 E' n0 d# z  o0 s& r8 ^
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)' A3 f# F0 Z9 u) C
                        &&  (ResponseYes == hardErrorResponse) )
: C* S4 @9 z, m+ K' P% h/ J                    {
' Q1 E0 g% J* l$ P- z* W                        return STATUS_DLL_INIT_FAILED;
% ?  J) L2 b; n+ A. g                    }: K' }. M! `3 Q+ \. G2 S6 o- E
                }
* E' H! {  W% p$ v
. l; {3 H' m& h- M                //
9 a, [2 Q$ X; r2 ~. O                // 入口函数返回0,错误* ~6 v, j9 r% a# C: E, c
                //
; x8 O* e+ B# a7 g                if ( 0 == initRetValue )
1 d. m. t7 I& u& r0 z4 T                {* ^! u$ H$ P: T0 W$ {
                    DWORD hardErrorParam2;
3 S, r) E5 A; U3 s                    DWORD hardErrorResponse2;1 ]' q2 f) v8 O6 o# B
                                       
$ z. B$ N; g- t; U  M5 M) K# q2 i                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
' s4 F3 Q8 M  M; d                    
5 X9 z' m' ~5 e0 V: c- R                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,- P) F( {; Q3 R8 K, K0 O
                                        1,  // Number of parameters3 ?, I' I. P  H5 d( T& z0 N( y
                                        1,  // UnicodeStringParametersMask) g9 @, {* O5 C2 S5 j  I) `0 G
                                        &hardErrorParam2,7 |6 w! `/ ]% a# j- T7 G/ `( p
                                        OptionOk,   // OK is only response
, y9 T( f9 }9 Y; r/ U                                        &hardErrorResponse2 );
3 s& S& P7 R# z: Y$ P( h                                                            4 ]. i& i5 N' b  V" C$ D
                    if ( _LdrpInLdrInit )/ U! o: C4 U; D8 ^
                        _LdrpFatalHardErrorCount++;
5 w: _+ o( B0 o
7 H9 O# f: d0 c" H, R                    return STATUS_DLL_INIT_FAILED;
& e) n: j& ~, o5 |9 J* I9 B                }
- ]# O1 {- Z- o2 p" o( \3 {            }1 s7 C9 G- `/ W  e
        }
; A) m, ]) _( C. g$ l  E( ~
$ a0 F) f) s) m2 W7 q8 ^7 A7 y        //
0 b' |9 s' R7 c4 l% N: B        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
4 z" g" N) j6 E& o# X; c" R        //      
; v( Q' v1 G3 [, h, m7 ]" M        if ( _LdrpImageHasTls && bImplicitLoad )
( c6 ~' M0 ?* R# j% Y        {4 N( @$ u- l7 z% S, V' s8 h5 B
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,8 E, {+ t5 q! N9 @
                                        DLL_PROCESS_ATTACH );: o: Q" T. X* Z1 \  J* y
        }4 _( m# c& l" u) g' m
    }
3 y/ W% j( ^* z7 w, w1 S- @    __finally: O, I9 O. ?# @4 i8 p  ]
    {; L" X  O- j% E5 h* z$ N
        //
6 r/ y) F# s5 J8 |* F        // 第四部分;
) X2 J% k4 c$ g) ]9 _4 q        // 清除分配的内存0 [: U$ X3 u6 x+ P! x1 `7 A
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );- R( g) w* G' g+ ?# l' n4 H
    }
; F1 ^! W" w. d  a/ J1 a. s  G2 z3 i2 q8 X& w! `7 N( L2 Q
    return STATUS_SUCCESS;# i# }: g5 r8 U& {3 n
}     F: D' J1 S6 _  f: @+ f- j
6 ?7 p/ D* g. g4 W4 u
这个函数分为四个主要部分:! o6 |( Q* A; Q
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
$ `- w0 N, l" i9 V二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。6 P, _& c4 B; Y& A) T0 ^. Z
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。, r9 ?. @- b- d" [# ~/ D2 S3 t
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
7 [  E$ `* u- p  W4 R7 E5 ]  r在最后的伪代码部分使用汇编语言来进行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 P& _3 c& v" E
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2024-5-20 06:33 , Processed in 0.018746 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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