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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]% Y/ S( L0 A) r: i& G2 v: ^

7 N4 X! `$ r. J/ w/ `WINDOWS 2K Dll 加载过程4 @8 L9 o6 E2 U3 m  z: l
jefong by 2005/03/30  F1 P8 |% M- q: e3 U, s. w0 {
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。. P; s$ n+ N. f5 Q: ?  {. 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”。
  Z, G- E; B! A" s: X* {) G0 k你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。$ a: d/ R: O7 Z* b
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。9 q% s- l* \! h; C' z7 |+ ?, u* c
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。& E8 @& J  |7 f' x
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。3 V7 z# S" F* G! \3 K' m# c+ Q
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
6 l8 C; n; G, z5 D$ c% X, X) C' U( X! e//=============================================================================8 n/ x$ x6 I* ]% }' D/ ~
// Matt Pietrek, September 1999 Microsoft Systems Journal
# I3 a  l) R5 b// 中文注释部分为jefong翻译
" \: S3 e* L+ G4 T  W8 D% h- T( Q//
' L! G$ p. [' E) F7 v// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
1 h! m6 I1 N9 b2 S//
4 R( \/ g7 o/ R2 h// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
8 Y( L7 K/ V4 [//=============================================================================8 ]  w1 N0 Z/ i7 ?1 w* A
& m/ b. T- E; C( K& |0 D8 b  }
#include <ntexapi.h>    // For HardError defines near the end
: z: O8 R, F, z$ k0 b
' r" d1 n" Y9 |// Global symbols (name is accurate, and comes from NTDLL.DBG)
% ?2 s- A4 f- B2 A9 P//  _NtdllBaseTag+ o2 c$ T* ~, @- _" P5 ?
//  _ShowSnaps1 ]+ O$ G( v0 j* ?: n3 |$ i
//  _SaveSp4 N5 d# E# N- G4 L% `0 }7 \* _$ ]
//  _CurSp; F! O8 y# M6 Y, c3 [) V2 K$ s
//  _LdrpInLdrInit
* Z; D1 d7 w1 ^; O. r7 w6 I; t" H) _//  _LdrpFatalHardErrorCount! W! }& F' {7 M) p0 E+ S1 P
//  _LdrpImageHasTls
9 K0 J% {  j* t6 t) X. z% a. W/ @) l: d3 O/ S6 ~  R# [
NTSTATUS
! G; x; Q4 |. p% M! qLdrpRunInitializeRoutines( DWORD bImplicitLoad )1 q# N& v3 M% v0 Y! E! ~
{6 [: S8 p& U1 L0 f4 _. z$ ~
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
# L2 l3 {+ J, L3 N) J7 E% Y    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
, y( g7 I2 P! t5 Z" c# W% R
" x" D' d( M( Y7 I1 G( K0 c    if ( nRoutinesToRun )
2 A  b0 A* ?2 x6 |" ^  B    {- @- i3 K6 N9 E0 C/ ~% W( W
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
- o' e+ c$ R+ J/ X; ?0 w        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
5 R  b! e8 x9 ?( w7 T* q                                            _NtdllBaseTag + 0x60000,: G- x8 V1 N' i% T
                                            nRoutinesToRun * 4 );# f/ Q3 B5 [8 G1 B
                           
6 g+ D5 v1 z4 M7 D        if ( 0 == pInitNodeArray )    // Make sure allocation worked
, X- u$ O+ e5 G( r7 D$ F            return STATUS_NO_MEMORY;
2 ~, g# L( Y" B    }
7 U; \4 D4 ]5 }- z    else# J1 Z& D& i( L: Z! n( j3 e
        pInitNodeArray = 0;& s6 o+ S0 m$ [& t: H# I

# r% D5 i$ E" |. t2 B' i    //第二部分;4 I6 [9 A3 U& y# X8 x
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
7 Q- }) }1 g( t1 }) n    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);8 h3 G  A" o' \  p
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
$ B6 z) D% E) l; U7 |. {& v* N        & \* k9 q/ n2 G' {, T
    if ( _ShowSnaps )* I3 H9 W; V7 R# C6 t6 ]
    {
5 B* _' V& j! P0 x, v        _DbgPrint( "LDR: Real INIT LIST\n" );
! X9 m+ Z# i6 O7 f3 w    }
3 D( Z6 f$ s" q1 x  Z# B6 _
" I, A6 _2 W4 }  {& ?. ?    nModulesInitedSoFar = 0;
+ [# H" ~6 [6 T2 p. U1 l# b2 h5 a) W( {; O8 F. E  }7 z1 M
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
9 z, R; q" s, a: P- T    {
2 ]' w7 U3 C) e        % H8 Q9 m  O3 B- M  _- N9 G: R
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
( @6 E3 a" n5 ~' h: H        {
8 G3 w0 p% t& g. v6 @            ModuleLoaderInfo  pModuleLoaderInfo;8 O2 R: `5 b" E4 P) c. K5 a* @7 R
            
# W( c. ~9 U! d6 C            //
* R% v, B9 ?2 Q  A            //一个ModuleLoaderInfo结构节点的大小为0X10字节
- L& M5 r+ N. D6 }: w' u            pModuleLoaderInfo = &NextNode - 0x10;) D5 V: l# \. g3 Z( n
            # f. _  x8 \, k+ S0 K8 D6 [
            localVar3C = pModuleLoaderInfo;         2 v: `3 J. a' h% G- D& `7 I: r

) `5 G8 A% e0 Z) b. L1 \& g- e7 u            //8 h7 i/ V2 u1 N" B$ d
            // 如果模块已经被初始化,就忽略
! \- n$ _% G, ?2 v            // X_LOADER_SAW_MODULE = 0x40 已被初始化
; I5 ~) K: C6 f; Q+ Y7 _5 Q- r- z            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
: V4 e" Z, J: ~% ^9 ~7 l            {
9 q0 A$ U, Y9 ]2 G9 ?0 z6 O5 \                //
  W( g! Q! q- [                // 模块没有被初始化,判断是否具有入口函数# M  v. i4 }) `7 s  z0 W( m: ^
                //
! w0 g9 S; M( k                if ( pModuleLoaderInfo->EntryPoint )" a; M5 w; H3 G1 ^* p7 R) z6 o
                {/ O/ T# L2 i# V$ S9 q7 Q
                    //* E& T' `6 {, m4 k, T
                    // 具有初始化函数,添加到模块列表中,等待进行初始化
# M0 i( ~: O  z1 q- L) z                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;4 I5 [" u6 P2 ^
9 q- y. d+ G6 M; R) G6 G+ J
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
+ M6 v% e" z* S0 M; r  W# o      // 例如:
4 `# W9 _( g% r  M$ ~                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
! ^# h* N$ D0 ^                    if ( _ShowSnaps )
. H2 S* f* O$ I* t3 G$ ^! I                    {
" ^' P, h/ j. h/ G                        _DbgPrint(  "%wZ init routine %x\n",
9 b  e6 r% u9 D/ o/ k& _  R5 V0 I5 H                                    &pModuleLoaderInfo->24,
! X; c) ^0 Y5 E( p- P. c                                    pModuleLoaderInfo->EntryPoint );7 P6 k. m- n* U# O' S4 c, E
                    }! W! q# p; G+ M5 I0 n

/ f. A% i" R9 c+ \4 k* j& P! E                    nModulesInitedSoFar++;5 S5 }. X: a$ a+ `8 p+ g
                }
1 {( S( {2 ^: m) V            }
( m2 R- h0 ]7 a% c  v7 S- {5 v$ Q! a
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。' O( s7 p- v! L1 Z& i: Y
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
, o+ s( N5 J8 z' e: H! S; N  H1 |) H" X7 ?
            // 处理下一个模块节点
: q# i6 B2 y" ?- |0 I1 ~% _" e% n            pCurrNode = pCurrNode->pNext% T' t4 I$ U  W" |9 [
        }
5 b1 B+ f* e, A) ~% s- ?- ?    }" Z: m$ I+ Z" V( x; J  l! D
    else# i% W" u/ c& N) k2 Q0 j/ }% T
    {
: ?0 s6 ~5 X* j2 c" j        pModuleLoaderInfo = localVar3C;     // May not be initialized???( q* \" r2 m, u! a- t+ Q
    }- X# d' @" l  n0 Y* P) L; Q
   
! g% m) }: p3 {: F2 f8 `    if ( 0 == pInitNodeArray )
% [7 j0 E1 U) ?2 y) U9 _. L# s        return STATUS_SUCCESS;' A& h1 m% ?% p% t. d# p3 N
% |/ j# h- |* d0 i
    // ************************* MSJ Layout! *****************
; k$ X% r$ u) z- v5 }    // If you're going to split this code across pages, this is a great
! W! m- x8 p+ A5 ]! N) v  \    // spot to split the code.  Just be sure to remove this comment
% w; T) Z# y# c0 ^# h- j    // ************************* MSJ Layout! *****************
; w, I* L2 e! W6 l) T8 y   
# N+ m7 N/ t! X2 X    //
- g0 z; B; s# J5 d& W6 n    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
% J5 X4 C" a2 W1 m8 h+ H7 h    // 第三部分,调用初始化部分4 t7 K4 E: t$ `. o0 y7 v
    try     // Wrap all this in a try block, in case the init routine faults- ^4 `. }6 D& L
    {9 y' P7 f: k. g! i( _7 s
        nModulesInitedSoFar = 0;  // Start at array element 09 J# \4 [4 j" \

% R. F: x4 h2 d2 l6 f9 n  j        //% X( h: `$ V+ Z
        // 遍历模块队列% w2 _* p0 s: K
        //
! s# Y+ I) V) ?! q. O        while ( nModulesInitedSoFar < nRoutinesToRun )
$ C2 a7 d' g" g# }" y        {) R3 y0 Z0 F4 a1 e: k; f: W8 s' I
            // 获得模块指针3 ]& j$ M: n! Q2 O. h
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
4 P0 w0 e7 Q; i/ l+ D3 f9 q
% n9 r$ @+ q% T# C$ T0 w# [+ E            // This doesn't seem to do anything..., P- X! s% w  {+ e7 ]& e7 S
            localVar3C = pModuleLoaderInfo;' y0 ~7 q# E# g3 D
            1 |4 T1 S7 a' h( f& \4 B( F: N
            nModulesInitedSoFar++;9 l6 [# N9 N5 Y' N# @  [- s
                ( ]) B; ]2 M: B+ s3 p
            // 保存初始化程序入口指针
6 k. d) q$ y5 q) _9 f; O; B            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;' y  k) g, y+ A( V4 R
            2 w% \( [  c; E6 K. r4 P
            fBreakOnDllLoad = 0;    // Default is to not break on load( R& w4 |0 n2 F
- m7 R6 P9 \9 ^3 k6 b
            // 调试用
" P; N# H/ }6 R/ t" ^. |            // If this process is a debuggee, check to see if the loader
5 K% [+ k' W, J            // should break into a debugger before calling the initialization.3 J( v. f+ L, }, |
            //
. b0 U) V7 |' b3 e) j4 q            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
) J' f( Q4 w2 e0 v7 F. e            // returns. IsDebuggerPresent is an NT only API.
5 H  u& V- D0 R& T7 f            //* ?7 I8 x8 e8 q3 A
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
# d/ D3 X, L3 O4 c            {! F4 Y7 q. n- k) O2 l* h3 S8 X% J
                LONG retCode;
4 u4 u: W: u, \4 ~( u
3 h  ^3 l& p: L/ k! b# e( M9 E                //              
  L2 I3 d/ Y% [6 S7 z                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\9 _. Y; U' N& M- ^( D" z4 E, W
                // Windows NT\CurrentVersion\Image File Execution Options"# w. h2 ?7 l' K3 n4 h$ Z
                // registry key.  If a a subkey entry with the name of
0 O4 y/ Z+ r# Z+ ?  c1 O. O+ J                // the executable exists, check for the BreakOnDllLoad value.
5 w4 a& D/ U% c6 B5 F                //
- _) I4 Q8 y( B. W4 r0 z0 R                retCode =
4 B7 V& T' s% _, @9 Y                    _LdrQueryImageFileExecutionOptions(
; C4 @) R# j$ q" f- ~0 A                                pModuleLoaderInfo->pwszDllName,
$ c9 F$ n( f! G# I( b                                "BreakOnDllLoad",pInitNodeArray" s5 \. h7 g3 O; ^
                                REG_DWORD,
! k& w% s; n4 y' M2 z. A- _                                &fBreakOnDllLoad,
5 `6 f. l7 i# I$ e' i  D                                sizeof(DWORD),
  ]5 p% o' l1 s$ P' a' M: V                                0 );
4 }' M% P3 c, H6 e/ P, [. _1 h0 h/ Y, \. \( P' L4 u3 o9 g
                // If reg value not found (usually the case), then don't
8 J/ O6 x0 L( b' A: w                // break on this DLL init7 b+ N$ E& Z' r; K# Q( D: ~4 j# M
                if ( retCode <= STATUS_SUCCESS )
  B) C$ G' A# E$ ~4 S                    fBreakOnDllLoad = 0;pInitNodeArray! I* w7 j4 E: T+ E& v( m' G
            }) [, N5 N$ f& z
            % Y  }  i* J3 u9 {
            if ( fBreakOnDllLoad )
: ~9 \. O; l2 N+ S            {           
. D6 T' C. A+ X# Q3 P# F  k8 E                if ( _ShowSnaps )
+ |" [$ U; E6 _& c4 o9 A* y                {
% r7 E/ |4 I  k1 A! F; R- H$ [& k                    // Inform the debug output stream of the module name8 u/ f: n; r5 x& O! Z6 @
                    // and the init routine address before actually breaking8 i6 R) t, Y; @
                    // into the debugger* f1 H0 H$ Y5 z% Z' u; k

3 L8 [5 S, }4 F2 `/ J6 q# h                    _DbgPrint(  "LDR: %wZ loaded.",, x  E3 u8 N8 _2 [9 {
                                &pModuleLoaderInfo->pModuleLoaderInfo );
; v, q4 |4 N" t7 t" f                    
5 G$ I6 k% O. p. c                    _DbgPrint(  "- About to call init routine at %lx\n",
' b* T2 P& |$ ?- S' a. s                                pfnInitRoutine )
8 g) H% V6 P/ Z* y: |7 X                }7 r; b5 G  }8 A- O) E9 B
                9 O( g( B9 u. r0 @' Y6 [5 {
                // Break into the debugger                              - y, }$ c6 u8 [. [$ i! ^6 i0 {* Q
                _DbgBreakPoint();   // An INT 3, followed by a RET
6 P2 `! [9 j6 v7 s* u8 d            }
# b- y, {' m- B9 L- ]6 i            else if ( _ShowSnaps && pfnInitRoutine )
, T8 x) v$ V7 A            {, Y+ Z) G! O7 s# H2 f8 v
                // Inform the debug output stream of the module name/ s9 R2 m3 B! ]  @
                // and the init routine address before calling it               , x6 G8 m' f5 b. I
                _DbgPrint(  "LDR: %wZ loaded.",
2 n( [( L; {+ N) Z: N  X( r                            pModuleLoaderInfo->pModuleLoaderInfo );7 [" R8 ]2 s. F# _4 R

! D* \, X9 s, M. j4 P* U0 b                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);: x% x8 s9 N0 u
            }7 `8 a3 T) g1 p5 O0 r9 u# E
                    
. [% A9 g/ e$ o" o            if ( pfnInitRoutine )
2 i0 H5 t$ _. F) |- D            {
; g3 S; Z: V, b, I3 D* G9 }% s                // 设置DLL_PROCESS_ATTACH标志6 r! L6 [. @7 G$ T- h( c: \- a7 M5 e$ s
                //
) U# u! z- L6 z: F% s                // (Shouldn't this come *after* the actual call?)# [% H* R5 z$ b5 \, g2 k
                //
# h7 q+ O- d$ K                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             9 \  ]$ \) \$ b& F: b6 L- {
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;6 O' `3 Z; z7 n# ~7 y$ k
% w+ j  ^  f2 y3 \3 x4 f& u
                //) j5 W3 F8 h0 j5 H
                // If there's Thread Local Storage (TLS) for this module,
% }8 p! E' {  I6 R4 n  i; o                // call the TLS init functions.  *** NOTE *** This only
- j, Q0 U0 Y, V4 t# O                // occurs during the first time this code is called (when
5 ^: Q! T: ~$ L# a: V0 v8 q( ?                // implicitly loaded DLLs are initialized).  Dynamically- I  t" D) F! P4 Q1 ^9 S( Q
                // loaded DLLs shouldn't use TLS declared vars, as per the- R& O3 C$ o5 P! L1 }8 S
                // SDK documentation7 a! m; Q1 J) S' [) }4 h1 S
                // 如果模块需要分配TLS,调用TLS初始化函数
, H! e3 |  G2 \8 S" H  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时% b; H) I3 `2 r5 X3 `% h+ a
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量) K3 q- x* [, O; k- b+ d& M/ V0 c
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad ), c3 j- Y5 M  r* _$ p
                {
4 D+ L3 \2 {/ f9 j                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,3 K# O6 p- e; p' p1 ]
                                                DLL_PROCESS_ATTACH );5 z- v2 b3 k8 F' L/ R- M# f" @
                }7 h$ [- e* d$ Q6 @, f
               
3 b5 Z( E) |2 d- ]1 a0 w0 @
( S' l5 o- D/ x                hModDLL = pModuleLoaderInfo->hModDLL
( X- M; v4 i; x: Q  a7 J$ Q! F4 ~* p& p7 a' _8 r/ b7 L& L' Z: I
                MOV     ESI,ESP // Save off the ESP register into ESI, _& l4 L, y' m) D4 V# c
  : v  S( z% D" t! V$ {% r
  // 设置入口函数指针                & h% a4 P$ K' }6 s0 M
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
( a7 o- x. U2 Y: b6 h3 t# N8 O1 f5 p7 ^& C
                // In C++ code, the following ASM would look like:
5 s2 m8 E9 S3 z3 }% _. S                //  ~# Y0 W  b* }6 d
                // initRetValue =3 Z+ n3 n5 ?0 `5 @( I8 k( m( l' ?
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);1 s2 u% ?* U& d& O# M- q
                //
6 _2 `8 R! m% E( ^1 ]0 E
) U1 p. j9 Y9 o6 H- I; j, |- U                PUSH    DWORD PTR [bImplicitLoad]
* R6 W! q& _$ p) I                ) K3 H* ]. e. t+ ^
                PUSH    DLL_PROCESS_ATTACH# I2 L( m1 p6 v% i) h) i+ ^
                " H9 k' b# l' X- d# D2 `) [! _+ D
                PUSH    DWORD PTR [hModDLL]7 o7 _/ O3 l5 m# ^1 Z+ q+ h" L
               
/ h3 r% W( O" k- U                CALL    EDI     // 调用入口函数" D, c9 J/ J8 {& P- x
                / x" s2 y0 |+ ?5 N1 k0 N' W& n
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
# F4 x# n( D- f! l$ u0 u' X! ]8 P- h% e/ z% U5 _' q( f# b& l# I' v; w
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
2 c9 k( [1 f/ R, H) T. l& Z1 n                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns# t; N- A4 v( H4 Z5 Z
/ V0 ~0 O, ~  T2 O* _( }2 N& |9 ]) x
                MOV     ESP,ESI     // Restore ESP to value before the call
" E- E  A7 x6 `" s0 F% C+ f
  }& Z5 X' h( L                //
9 W) e& n1 ]5 L; y- _, [( C% _% c9 \                // 检查调用前后的ESP值是否一至
6 R& I5 R6 M0 R8 w0 \% f  //
. Q+ d; v' Z: V( `* b; T                if ( _CurSP != _SavSP )0 p- V) v* W- V. g! `+ u5 d( D% @# R
                {
3 n6 [) J+ T" J+ I; T                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
$ v3 V& Q" A. T* |( C$ k2 I" W) j  x6 o7 g
                    hardErrorRetCode =
: y- h6 F# Y# V2 x8 d" e                        _NtRaiseHardError(7 I4 k; h- H: g; `
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
( u9 u: d. I- h5 {1 |9 ]$ k                            1,  // Number of parameters7 k. h1 d- P0 Q, @* K; ]7 R
                            1,  // UnicodeStringParametersMask,0 F& w' j2 ]7 G- c% W0 z
                            &hardErrorParam,6 a. B" N  L6 p) E" [" w0 J, G
                            OptionYesNo,    // Let user decide
9 @. ^0 i$ R$ @; v3 Q7 w                            &hardErrorResponse );: V( a; g/ _! v) e% i' z
                                            8 Y! F" x1 X# t( |! h4 e3 S0 T
                    if ( _LdrpInLdrInit )
6 y# U3 W* }- i" b3 S                        _LdrpFatalHardErrorCount++;; R; [6 z9 \% I9 X

, w* Y, x* k( U) q, k                    if (    (hardErrorRetCode >= STATUS_SUCCESS)8 t) I2 W+ U& ~; O8 _5 `
                        &&  (ResponseYes == hardErrorResponse) )
7 h! z: h- l- D1 L' o                    {
4 {2 h3 [' H2 p3 ?6 x, R                        return STATUS_DLL_INIT_FAILED;, C; {' @  {- c6 H/ h: B; o
                    }
5 j3 a; _4 S6 |! m" Z1 r                }/ @6 X/ `6 T- e2 i9 j

+ k. Z5 r7 i: S( n# G- B                //2 R' C: k! ~# e$ N
                // 入口函数返回0,错误; }  f! c' ~- E. v
                //
& j5 v4 G; ?  e1 }3 m! }5 l                if ( 0 == initRetValue )' l, o9 u2 u+ D# e/ V. Q$ R
                {2 C, C- i- p+ y* }
                    DWORD hardErrorParam2;
; ^& C: i2 `9 x# c                    DWORD hardErrorResponse2;
; [: W3 U8 d/ d) K2 Q                                       
7 C  ~2 {9 J* c0 f                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;) Q- s" I+ z& i1 A) Y
                    * t8 |* b( s( t1 K8 g
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
# v! H" i2 V8 x8 e: r- W% n# `3 p                                        1,  // Number of parameters# i0 ?) I; U3 E3 o( d( }# A
                                        1,  // UnicodeStringParametersMask) i1 W/ Q! S' U" X
                                        &hardErrorParam2,+ a+ C' _4 B& B% ]$ S7 |& p
                                        OptionOk,   // OK is only response5 ^& Z) A* C1 X+ c5 x
                                        &hardErrorResponse2 );
: v8 z+ ~7 p7 h0 w& H) j6 a# t4 [                                                            
' f+ q& s& j1 ?6 G5 S                    if ( _LdrpInLdrInit )
; `7 T: ]2 _& R  T4 i" C5 R                        _LdrpFatalHardErrorCount++;
; k9 j) a5 c% ]0 a2 T; B% \0 M% p2 F
                    return STATUS_DLL_INIT_FAILED;
) A3 |- t- Q0 D* ^4 e, \, J1 l* e/ r" Z                }
& l' u5 A; P1 x. n            }
+ I5 G0 K6 t' [6 Q& \4 w- Y        }
- A3 ^$ ^- ~# U" p7 h+ v0 s) L% N
        //! K' y9 ?2 O  {" f* F5 M/ r0 i
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时; _( A7 j3 q$ p! G( U0 B/ ^
        //      
' }! X4 F) |4 O8 B& u6 F9 R" X        if ( _LdrpImageHasTls && bImplicitLoad )
% F7 L, d8 y' j9 C9 E3 f& |3 {        {
2 L% Z) l2 ]/ q+ r  o# Y. a            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,8 E- [# R7 y. M- |& W  n% F& v" t
                                        DLL_PROCESS_ATTACH );
3 y6 B0 v4 {) G" d: h( D        }
) p8 ~1 P6 R4 Z2 e) ]    }
# u( R+ R& y0 P- j    __finally: D! U+ U/ o7 u6 _$ v
    {2 X, R0 y. e0 i) L- Q3 R
        //. I1 I2 h% U( ^4 k" [0 `# {# S5 n
        // 第四部分;3 U5 ~) y: t' K% ?
        // 清除分配的内存- @: O1 a  q( R1 R& e5 g
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
, j7 t- C+ @) z+ @# a4 K. e% J    }) G/ s* g* {- E6 Z) J

1 l# f# A6 p- h2 T    return STATUS_SUCCESS;- `" y7 V. k* W- [5 w
}   + n1 o4 ~" ^' [$ b. W
* m) Q4 h& [+ |# K* j
这个函数分为四个主要部分:  @7 j, {6 _9 \# ^
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。" k8 g6 K7 R1 }0 Y
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。
7 ^3 L- f; D0 c- u% W; \: r/ b三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
$ O7 A' q! R6 ]1 y6 T2 N9 f另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
- N* x# d; b2 Q3 L" @: {; t3 n( X在最后的伪代码部分使用汇编语言来进行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 x9 C& j- N' M9 q( H* s
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2025-3-5 00:29 , Processed in 0.022505 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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