home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / LordLucifer / win32asm / files / win32asm.exe / Win32ASM / Procs / Debug.asm next >
Encoding:
Assembly Source File  |  1997-11-11  |  22.0 KB  |  548 lines

  1.     Page 60,190
  2.     TITLE Debugging routines.
  3. ; ==========================================================================
  4. ; Debugging routines and helpers.
  5. ; ==========================================================================
  6. ; $Header: /ASMLib32/Debug.asm 3     10/11/97 19:40 Philippe $
  7. ; ============================================================================
  8. ; $Log: /ASMLib32/Debug.asm $
  9. ; 3     10/11/97 19:40 Philippe
  10. ; Made some tool routines PUBLIC.
  11. ;
  12. ; 2     18/09/97 14:49 Philippe
  13. ; Added FatalErrorInit routine.
  14. ; Caption for FatalError dialog box now set to calling .EXE path name.
  15. ;
  16. ; 1     4/09/97 4:48 Philippe
  17. ; Initial insertion in SourceSafe.
  18. ; ============================================================================
  19.  
  20.  
  21. ; ==========================================================================
  22. ; ToDo List:
  23. ; - ...
  24. ; ==========================================================================
  25.  
  26.                 .586
  27.                 .MODEL FLAT,STDCALL
  28.  
  29.                 .NOLISTMACRO
  30.                 .NOLIST
  31.  
  32. UniCode         = 0
  33.  
  34.                 INCLUDE Instr32.mac
  35.                 INCLUDE Win32Inc.equ
  36.  
  37.                 INCLUDE Kernel32.equ
  38.                 INCLUDE User32.equ
  39.                 INCLUDE PEImage.equ
  40.  
  41.                 INCLUDE ASMLib32.inc
  42.  
  43.                 .LIST
  44.  
  45.     PAGE
  46. ; =========================================================================
  47. ; FatalError:
  48. ; Called to abend the current process.
  49. ; The Call instruction is followed by:
  50. ; - a length byte (containing the length of the message that follows)
  51. ; - an ASCIIZ string, describing the cause for the abend.
  52. ; =========================================================================
  53.  
  54.                 .CONST
  55.  
  56. FatalFormat     BYTE 'FatalError at %1!#x! (offset 0x%3!.8X! in image section ''%4!s!%''):'
  57.                 BYTE '%n'
  58.                 BYTE '%2!s!.'
  59.                 BYTE '%n'
  60.                 BYTE 'Now about to attempt debugger call (INT 01)...'
  61. NullByte        BYTE 0
  62.  
  63. FatalFormatGLE  BYTE 'FatalError at %1!#x! (offset 0x%3!.8X! in image section ''%4!s!''):'
  64.                 BYTE '%n'
  65.                 BYTE '%2!s!.'
  66.                 BYTE '%n'
  67.                 BYTE 'GetLastError returned %5!i!: %6!s!'
  68.                 BYTE '%n'
  69.                 BYTE 'Now about to attempt debugger call (INT 01)...'
  70.                 BYTE 0
  71.  
  72. NoMessage       BYTE '(No FatalError message)'
  73. NullMsg         BYTE 0
  74.  
  75.     .DATA
  76.  
  77. FatalBoxTitle   BYTE 40 DUP (0)
  78.                 BYTE 0                  ;Terminating byte.
  79.  
  80.     ALIGN DWORD
  81.  
  82.  
  83. ; The following are not data definitions, but data masks that apply to the
  84. ; stack. This allows the following routines to be fully reentrant.
  85.  
  86. ; Define the various fields on the stack during a FatalError.
  87.  
  88. LocalData   STRUCT
  89. FatalCall   DWORD ?                     ;%1 Address of fatal error call.
  90. MessageAddr DWORD ?                     ;%2 FatalError message address,
  91. SectRelAddr DWORD ?                     ;%3 Section relative address,
  92. SectNamePtr DWORD ?                     ;%4 section name pointer.
  93. LastError   DWORD ?                     ;%5 GetLastError error code.
  94. lpErrorMsg  DWORD ?                     ;%6 GetLastError Message.
  95.                                         ; <-- Add other FormatMessage parms
  96.                                         ;above this point.
  97.                                         ;Add any further FormatMessage address
  98.                                         ;up there for display in the FatalFormat
  99.                                         ;message. FormatMessage parms start
  100.                                         ;at FatalCall downwards, as directed
  101.                                         ;by the format string.
  102. BufferAddr  DWORD 0                     ;System-returned buffer address,
  103. FatalFlags  BYTE 0                      ;Non-zero if FatalErrStd.
  104. SectName    BYTE 9 DUP (?)              ;Section name.
  105. LocalData   ENDS
  106.  
  107.  
  108. ; Defines the bits in FatalFlags.
  109.  
  110. FFErrASM        = 00000000b             ;ASM Proc non-flag (called from HLL)
  111. FFErrStd        = 00000001b             ;Std Proc flag (called from HLL)
  112. FFGetLastError  = 00000010b             ;GetLastError flag (must call GetLastError)
  113.  
  114.  
  115. StackSave   STRUCT
  116. FieldEDI    DWORD 0                     ;Result of PUSHAD
  117. FieldESI    DWORD 0
  118. FieldEBP    DWORD 0
  119. FieldESP    DWORD 0
  120. FieldEBX    DWORD 0
  121. FieldEDX    DWORD 0
  122. FieldECX    DWORD 0
  123. FieldEAX    DWORD 0
  124. FieldFlags  DWORD 0                     ;Result of PUSHFD
  125. StackSave   ENDS
  126.  
  127. StackImage STRUCT
  128.             LocalData <>                ;Local data (see LocalData struct)
  129.             StackSave <>                ;PUSHFD/PUSHAD
  130. FieldRetAd  DWORD 0                     ;"Return" address from FatalError.
  131. StdMessage  DWORD 0                     ;Message Address for FatalErrorStd call.
  132. StackImage ENDS
  133.  
  134.  
  135.                 .CODE
  136.  
  137. ; ============================================================================
  138. ; FatalInit:
  139. ; Initialize FatalError routine.
  140. ; This allows one to specify the ASCIIZ string that will appear in the
  141. ; title of the FatalError box if it ever pops up.
  142. ; ============================================================================
  143.  
  144. FatalErrorInit PROC PUBLIC USES ECX EDI ESI,
  145.                  lpNewString:DWORD
  146.  
  147.     LEA EDI,FatalBoxTitle
  148.     MOV ESI,lpNewString
  149.     MOV ECX,SIZEOF FatalBoxTitle
  150.  
  151.       .REPEAT
  152.       MOV AL,[ESI]                      ;Get byte from source,
  153.       INC ESI                           ;bump source cursor.
  154.       DEC ECX                           ;Decrement byte count,
  155.       MOV [EDI],AL                      ;copy byte.
  156.       .BREAK .IF ZERO?                  ;Exit loop if output field full,
  157.       INC EDI                           ;bump output cursor,
  158.       .UNTIL AL==0                      ;exit if end of source.
  159.  
  160.     INC EDI
  161.  
  162.     MOV [EDI],BYTE PTR 0                ;Force next character to null.
  163.     RET
  164. FatalErrorInit ENDP
  165.  
  166.  
  167. ; ============================================================================
  168. ; FatalError / FatalErrorGLE:
  169. ; Called with message address on stack to abend the current process.
  170. ;
  171. ; void FatalErrorStd(char *Message);
  172. ;
  173. ; We use EXTERNDEF C definitions rather PROTOs here because we DO NOT want
  174. ; to use the regular stack frame and MASM generated entry sequence here.
  175. ; The strange manoeuvers we need to play here preclude it, and we don't want
  176. ; MASM to mess with the stack nor with any of our registers either. We have
  177. ; to keep this under full control.
  178. ; ============================================================================
  179.  
  180.  
  181. EXTERNDEF C FatalErrorGLE@4:NEAR
  182. FatalErrorGLE@4 PROC C PUBLIC
  183.  
  184.     PUSHFD                              ;Save CPU flags,
  185.     PUSHAD                              ;save registers at time of abend.
  186.     SUB ESP,SIZEOF LocalData            ;Allocate local variables,
  187.     MOV EBP,ESP                         ;get EBP => first local var.
  188.  
  189.     ASSUME EBP:PTR StackImage
  190.                                         ;This is FatalError, not FatalErrorASM,
  191.                                         ;and we need a GetLastError call.
  192.     MOV [EBP].FatalFlags,FFErrStd or FFGetLastError
  193.     JMP FatalErrorCommon
  194. FatalErrorGLE@4 ENDP
  195.  
  196. ;; The following one is obsolete.
  197. ;; Embedding data straight inside the code segment is a total performance
  198. ;; killer on 486 and upper processors.
  199. ;
  200. ;EXTERNDEF C FatalErrorASM:NEAR
  201. ;FatalErrorASM PROC C
  202. ;
  203. ;    PUSHFD                              ;Save CPU flags,
  204. ;    PUSHAD                              ;save registers at time of abend.
  205. ;    SUB ESP,SIZEOF LocalData            ;Allocate local variables,
  206. ;    MOV EBP,ESP                         ;get EBP => first local var.
  207. ;
  208. ;    ASSUME EBP:PTR StackImage
  209. ;    MOV [EBP].FatalFlags,FFErrASM       ;This is FatalErrorASM, not FatalError.
  210. ;    JMP FatalErrorCommon
  211. ;FatalErrorASM ENDP
  212.  
  213.  
  214. EXTERNDEF C FatalError@4:NEAR
  215. FatalError@4 PROC C PUBLIC
  216.     PUSHFD                              ;Save CPU flags,
  217.     PUSHAD                              ;save registers at time of abend.
  218.     SUB ESP,SIZEOF LocalData            ;Allocate local variables,
  219.     MOV EBP,ESP                         ;get EBP => first local var.
  220.  
  221.     ASSUME EBP:PTR StackImage
  222.     MOV [EBP].FatalFlags,FFErrStd       ;This is FatalErr, not FatalErrorASM.
  223. ;   JMP FatalErrorCommon                ;Fall thru to common part.
  224.  
  225. FatalErrorCommon::
  226.  
  227.       .IF FatalBoxTitle == 0            ;If title not initialized,
  228.       INVOKE GetModuleFileName,         ;stuff module name there.
  229.                NULL,
  230.                OFFSET FatalBoxTitle,
  231.                SIZEOF FatalBoxTitle
  232.       .ENDIF
  233.  
  234.     MOV EAX,[EBP].FieldRetAd            ;Get 'return' address in EAX (actually
  235.                                         ;points to code-embedded error message).
  236.     MOV EDX,EAX                         ;Get a copy in EDX,
  237.     SUB EDX,5                           ;compute address of fatalerr call,
  238.     MOV [EBP].FatalCall,EDX             ;save on stack for later display.
  239.  
  240.     LEA EAX,[EBP].SectNamePtr           ;Compute EAX => pointer to SectionName,
  241.     LEA ECX,[EBP].SectName              ;        ECX => Section name storage,
  242.     MOV [EAX],ECX                       ;store SectName address in pointer.
  243.  
  244.     INVOKE GetSectionData,              ;Get section relative address
  245.              EDX,                       ;for this virtual address,
  246.              NULL,                      ;in this process module,
  247.              ECX                        ;dest address for section name.
  248.  
  249.     MOV [EBP].SectRelAddr,EAX           ;Save section relative address.
  250.                                         ;Assume FatalErr call supplied
  251.                                         ;no FatalErr message, so
  252.                                         ;set default fatalerror message.
  253.     MOV [EBP].MessageAddr,OFFSET NoMessage
  254.  
  255.       .IF EAX != -1                     ;If address belongs to our module,
  256.                                         ;(this could trigger an GP otherwise)
  257.       MOV AL,[EBP].FatalFlags           ;Get FatalError flags.
  258.       TEST AL,FFErrStd                  ;Is Std bit set?
  259.         .IF ZERO?                       ;No, assembly call:
  260.         MOV EAX,[EBP].FieldRetAd        ;Get FatalErr message addr back again:
  261.           .IF BYTE PTR [EAX] != 0       ;and if we seem to have a message,
  262.           INC EAX                       ;Bump past message length,
  263.           MOV [EBP].MessageAddr,EAX     ;store actual message address.
  264.           .ENDIF
  265.         .ELSE                           ;FatalErrorStd call.
  266.         MOV EAX,[EBP].StdMessage        ;Get message address from stack.
  267.         MOV [EBP].MessageAddr,EAX
  268.         .ENDIF
  269.  
  270. ;     DEBUG                             ;Debugging aid.
  271.  
  272.       MOV AL,[EBP].FatalFlags           ;Get FatalError flags,
  273.       TEST AL,FFGetLastError            ;Do we need to call GetLastError?
  274.       LEA EBX,[EBP].lpErrorMsg          ;(get EBX => GLE sysmsg pointer holder)
  275.       MOV EAX,0                         ;Zero EAX without changing flags anyhow,
  276.       MOV [EBX],EAX                     ;and zero GLE system msg pointer too.
  277.         .IF !ZERO?                      ;Yes, need to call GetLastError.
  278.         INVOKE GetLastError             ;Get LastError in EAX,
  279.         MOV [EBP].LastError,EAX         ;and save in stack for FormatMessage,
  280.         MOV ECX,EAX                     ;copy of LastError in ECX.
  281.         .ENDIF
  282.       .ENDIF
  283.  
  284.     OR EAX,EAX                          ;Did we need to call GetLastError?
  285.  
  286.       .IF !ZERO?                        ;Yes. Get system message
  287.       INVOKE FormatMessage,             ;message pointer in [EBX] (lpErrorMsg).
  288.              FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM,
  289.              0,ECX,0,EBX,0,0
  290.       MOV ECX,OFFSET FatalFormatGLE     ;and point to ad hoc format string.
  291.       .ELSE
  292.       MOV ECX,OFFSET FatalFormat        ;Otherwise, straight FatalErr msg.
  293.       .ENDIF
  294.  
  295.     LEA EDX,[EBP].FatalCall             ;Get EDX => our argument array,
  296.     LEA EBX,[EBP].lpErrorMsg            ;Get EBX => storage place for
  297.                                         ;FormatMessage allocated buffer.
  298.                                         ;Now format our full FatalError msg
  299.                                         ;(that might embed the GetLastError msg).
  300.     INVOKE FormatMessage,
  301.              FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_STRING or\
  302.              FORMAT_MESSAGE_ARGUMENT_ARRAY,
  303.              ECX,0,0,EBX,0,EDX
  304.  
  305.     MOV EBX,[EBX]                       ;Get EBX => FormatMessage allocated
  306.                                         ;buffer,
  307.     INVOKE MessageBox,                  ;display formatted message.
  308.              0,EBX,ADDR FatalBoxTitle,
  309.              MB_OK or MB_ICONSTOP or MB_APPLMODAL or \
  310.              MB_SETFOREGROUND or MB_TOPMOST or MB_SERVICE_NOTIFICATION
  311.  
  312.     INVOKE LocalFree,                   ;Free latest FormatMessage allocated buffer.
  313.              EBX
  314.  
  315.     MOV EAX,[EBP].lpErrorMsg            ;Did we use a formatted GetLastError msg?
  316.       .IF EAX != 0                      ;Yes.
  317.       INVOKE LocalFree,                 ;Then free GetLastError - FormatMessage
  318.                EAX                      ;allocated buffer too.
  319.       .ENDIF
  320.  
  321.     ADD EBP,SIZEOF LocalData            ;Release stack variables,
  322.     MOV ESP,EBP                         ;and restore stack pointer.
  323.     POPAD                               ;Restore registers,
  324.     POPFD                               ;restore flags like they were when
  325.                                         ;error happened, and then
  326.     DEBUG                               ;enter debugger if any.
  327.  
  328.     INVOKE ExitProcess,                 ;All done, exit process.
  329.              -1
  330.     RET
  331. FatalError@4 ENDP
  332.  
  333.  
  334. ; ============================================================================
  335. ; Given a virtual address and a module name, return a section name and
  336. ; compute the relative offset of the virtual address inside the section.
  337. ;
  338. ; On entry,
  339. ; 1st parm is a virtual address inside the process,
  340. ; 2nd parm is a pointer to an ASCIIZ module name (such as "FOO.EXE" or
  341. ;   "BAR.DLL"). Set to NULL if the address belongs to the .EXE file used to
  342. ;   create the calling process.
  343. ; 3rd parm is a pointer to a 9-byte field that will receive the ASCIIZ name
  344. ; of the section where the virtual address belongs.
  345. ;
  346. ; On exit,
  347. ;   If module or section not found, the relocated address returned will be
  348. ;   set to -1 (0FFFFFFFFh) and the SectionName field will be set to
  349. ;   the ASCIIZ string '>Unknown',0
  350. ;   Otherwise, EAX will contain the section-relative address of original
  351. ;   address and szSectionBuffer will contain the ASCIIZ section name (like
  352. ;   '_TEXT',0 or '.rdata',0 or...).
  353. ; ============================================================================
  354.  
  355.                 .CONST
  356.  
  357. Unknown         BYTE '>Unknown',0
  358.  
  359.                 .CODE
  360.  
  361.  
  362. GetSectionData PROC PUBLIC USES ECX EDX EDI ESI,
  363.                        Address:DWORD,
  364.                        lpszModuleName:DWORD,
  365.                        lpszSectionBuffer:DWORD
  366.  
  367.     MOV EAX,lpszModuleName              ;Get EAX => name of module looked for,
  368.     CALL LocateSectionTable             ;Get its section table address.
  369.  
  370.       .IF EAX != 0                      ;and if found it,
  371.       MOV EDI,EAX                       ;Get EDI => section table,
  372.       ASSUME EDI:PTR SectHeader         ;and tell MASM.
  373.  
  374.       MOV EAX,Address                   ;Get virtual address to locate (in EAX),
  375.       SUB EAX,EDX                       ;Compute corresponding RVA.
  376.  
  377.                                         ;ECX still contains number of entries in
  378.                                         ;section table.
  379.       CALL FindRelocAddress             ;Get section where address lives.
  380.                                         ;If we don't find it, EAX will be set
  381.                                         ;to -1 by FindRelocAddress.
  382.         .IF EDI != 0                    ;Found it! EAX = section relative addr,
  383.                                         ;EDI => section entry.
  384.         LEA ESI,[EDI].SectName          ;Get ESI => section name,
  385.         MOV EDI,lpszSectionBuffer       ;and EDI => user destination address.
  386.         MOV ECX,SIZEOF SectHeader.SectName ;Get ECX = max section name,
  387.         CLD                             ;make sure forward copy.
  388.         REP MOVSB                       ;Copy section name.
  389.         MOV BYTE PTR [EDI],0            ;Section names are zero padded, but
  390.         .ENDIF                          ;add zero in case 8-bytes names.
  391.  
  392.       .ELSE
  393.       MOV EAX,-1                        ;GetModuleAddress returned null addr.
  394.       .ENDIF
  395.  
  396.       .IF EAX == -1                     ;If address not solved,
  397.       LEA ESI,OFFSET Unknown            ;Assume we won't find section name.
  398.       MOV EDI,lpszSectionBuffer         ;"Unknown".
  399.       MOV ECX,SIZEOF Unknown
  400.       CLD
  401.       REP MOVSB
  402.       .ENDIF
  403.  
  404.     RET
  405. GetSectionData ENDP
  406.  
  407. ; ============================================================================
  408. ; On entry:
  409. ;   EAX = RVA to locate,
  410. ;   EDI = section table virtual address,
  411. ;   ECX = number of entries in section table.
  412. ; On exit,
  413. ;   EDI == 0 :
  414. ;     Address doesn't belong to a section in this module,
  415. ;     EAX = -1.
  416. ;   EDI != 0 :
  417. ;     EDI => section table entry this address belongs to,
  418. ;     EAX = relocatable (section relative) address.
  419. ; ============================================================================
  420.  
  421.  
  422. FindRelocAddress PROC USES EDX
  423.  
  424.       .REPEAT
  425.       MOV EDX,[EDI].SectRVA             ;Get low boundary RVA of section:
  426.       CMP EAX,EDX                       ;Set carry if lower sect boundary
  427.         .IF !CARRY?                     ;greater than address. If inside,
  428.         ADD EDX,[EDI].SectRawDataSize   ;compute section high boundary RVA:
  429.         CMP EDX,EAX                     ;Address above section?
  430.         .ENDIF                          ;(set carry if so.)
  431.       .BREAK .IF !CARRY?                ;Inside this section, exit loop.
  432.       SUB EDI,-(SIZEOF SectHeader)      ;No, bump to next section table entry
  433.                                         ;and set carry (in case loop is over),
  434.       .UNTILCXZ                         ;loop till all entries done.
  435.  
  436.       .IF !CARRY?                       ;Target belongs to this section.
  437.       SUB EAX,[EDI].SectRVA             ;Set EAX to section-relative address.
  438.       .ELSE
  439.       MOV EDI,0                         ;Target not found, return EDI == 0
  440.       MOV EAX,-1                        ;and RVA = 0.
  441.       .ENDIF
  442.  
  443.     RET
  444. FindRelocAddress ENDP
  445.  
  446. ; ============================================================================
  447. ; On entry:
  448. ;   EAX = pointer to module name,
  449. ; On exit:
  450. ;   EAX => section table,
  451. ;   ECX =  number of sections in module,
  452. ;   EDX => module load address.
  453. ; ============================================================================
  454.  
  455.  
  456. LocateSectionTable PROC USES ESI
  457.  
  458.     INVOKE GetModuleHandle,             ;Get module load address.
  459.              EAX
  460.  
  461.       .IF EAX != 0                      ;If module found:
  462.       MOV EDX,EAX                       ;save module address in EDX.
  463.       ASSUME EAX:PTR DOSHeader          ;EAX => load image (DOS header).
  464.  
  465.       ADD EAX,[EAX].RVAPEHeader         ;Get RVA of PE header,
  466.       ASSUME EAX:PTR ImageFileHeader    ;Now EAX => PE image header.
  467.  
  468.       XOR ECX,ECX                       ;zero high word of ECX for later.
  469.       MOV ESI,EAX                       ;Get ESI => ImageFileHeader.
  470.       MOV CX,[EAX].IFHNumberOfSections  ;get number of sections in ECX.
  471.       MOVZX EAX,[EAX].IFHOptionalHeaderSize    ;Get optional header size in EAX,
  472.       LEA EAX,[ESI][EAX]+SIZEOF ImageFileHeader;skip regular and optional header,
  473.       .ENDIF                                   ;EAX now points to section table.
  474.  
  475.     RET
  476. LocateSectionTable ENDP
  477.  
  478. ; ============================================================================
  479. ; Get the address of the PE header for a module.
  480. ; On entry,
  481. ;   1st parm => ASCIIZ module name (NULL if process main module),
  482. ; On exit,
  483. ;   Return value = PE Header address,
  484. ;   (EDX == module load address)
  485. ; ============================================================================
  486.  
  487.  
  488. GetPEHeader  PROC PUBLIC lpszModuleName:DWORD
  489.  
  490.     INVOKE GetModuleHandle,             ;Get our load address.
  491.              lpszModuleName
  492.  
  493.       .IF EAX != 0                      ;Got our load address. and if found:
  494.       MOV EDX,EAX                       ;keep a copy of module load address,
  495.       ASSUME EAX:PTR DOSHeader          ;EAX => load image (DOS header).
  496.       ADD EAX,[EAX].RVAPEHeader         ;Compute address of PE header.
  497.       .ENDIF
  498.     RET
  499. GetPEHeader  ENDP
  500.  
  501. ; ============================================================================
  502. ; Get the base address of the code of a module.
  503. ; On entry,
  504. ;   1st parm => ASCIIZ module name (NULL if process main module),
  505. ; On exit,
  506. ;   Return value = Code base address.
  507. ; ============================================================================
  508.  
  509.  
  510. GetCodeBase  PROC PUBLIC lpszModuleName:DWORD
  511.  
  512.     INVOKE GetPEHeader,                 ;Get EAX == PEHeader:
  513.              lpszModuleName
  514.  
  515.       .IF EAX !=0                       ;If if found,
  516.       ASSUME EAX:PTR PEHeader           ;tell MASM,
  517.       MOV EAX,[EAX].BaseOfCode          ;get RVA of code address,
  518.       ADD EAX,EDX                       ;Compute virtual address of code.
  519.       .ENDIF
  520.     RET
  521. GetCodeBase  ENDP
  522.  
  523. ; ============================================================================
  524. ; Get the base address of the data of a module.
  525. ; On entry,
  526. ;   1st parm => ASCIIZ module name (NULL if process main module),
  527. ; On exit,
  528. ;   Return value = Data base address.
  529. ; ============================================================================
  530.  
  531.  
  532. GetDataBase  PROC PUBLIC lpszModuleName:DWORD
  533.  
  534.     INVOKE GetPEHeader,                 ;Get EAX == PEHeader:
  535.              lpszModuleName
  536.  
  537.       .IF EAX !=0                       ;And if found,
  538.       ASSUME EAX:PTR PEHeader           ;tell MASM,
  539.       MOV EAX,[EAX].BaseOfData          ;get RVA of data address,
  540.       ADD EAX,EDX                       ;Compute virtual address of code.
  541.       .ENDIF
  542.     RET
  543. GetDataBase  ENDP
  544.  
  545.  
  546.     END
  547.