分享
 
 
 

根据PE文件格式获取LoadLibraryA()

王朝other·作者佚名  2006-04-18
窄屏简体版  字體: |||超大  

本节与PE文件格式相关的术语以<<微软PE/COFF规范>>为准([13]),不再强调该点。

winnt.h中定义了部分相关数据结构,后面如未单独注明来自哪个头文件,均隐指来

自winnt.h。执行vulnerable_0.exe,进入windbg调试状态。

> !list -t _LIST_ENTRY.Flink -x "dd" -a "+18 L1" 241ec0

00241ed8 00400000

00241f30 77f50000

00241fd8 77e60000

... ...

从上节可知,ntdll.dll基址是0x77f50000,kernel32.dll基址是0x77e60000。最开

始的64字节按如下数据结构解析:

--------------------------------------------------------------------------

#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ

typedef struct _IMAGE_DOS_HEADER // DOS .EXE header

&leftsign;

WORD e_magic; // +0x00 Magic number

WORD e_cblp; // +0x02 Bytes on last page of file

WORD e_cp; // +0x04 Pages in file

WORD e_crlc; // +0x06 Relocations

WORD e_cparhdr; // +0x08 Size of header in paragraphs

WORD e_minalloc; // +0x0a Minimum extra paragraphs needed

WORD e_maxalloc; // +0x0c Maximum extra paragraphs needed

WORD e_ss; // +0x0e Initial (relative) SS value

WORD e_sp; // +0x10 Initial SP value

WORD e_csum; // +0x12 Checksum

WORD e_ip; // +0x14 Initial IP value

WORD e_cs; // +0x16 Initial (relative) CS value

WORD e_lfarlc; // +0x18 File address of relocation table

WORD e_ovno; // +0x1a Overlay number

WORD e_res[4]; // +0x1c Reserved words

WORD e_oemid; // +0x24 OEM identifier (for e_oeminfo)

WORD e_oeminfo; // +0x26 OEM information; e_oemid specific

WORD e_res2[10]; // +0x28 Reserved words

LONG e_lfanew; // +0x3c File address of new exe header

// +0x40

&rightsign; IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

--------------------------------------------------------------------------

> db 77e60000 L0n64

77e60000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............

77e60010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 ........@.......

77e60020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

77e60030 00 00 00 00 00 00 00 00-00 00 00 00 f8 00 00 00 ................

> dd 77e60000+3c L1 (显示e_lfanew成员)

77e6003c 000000f8

其中e_lfanew成员用于定位PE头。从注释中理解,e_lfanew是File pointer,非RVA,

不过在这里当成RVA处理也没关系。e_lfanew值为0x000000f8,PE头在基址加0xf8的

位置。PE头最开始是标识"PE\0\0",占4字节,然后是20字节固定头。

--------------------------------------------------------------------------

#define IMAGE_NT_SIGNATURE 0x00004550 // PE00

#define IMAGE_SIZEOF_FILE_HEADER 20

typedef struct _IMAGE_FILE_HEADER

&leftsign;

WORD Machine; // +0x00

WORD NumberOfSections; // +0x02

DWORD TimeDateStamp; // +0x04

DWORD PointerToSymbolTable; // +0x08

DWORD NumberOfSymbols; // +0x0c

WORD SizeOfOptionalHeader; // +0x10

WORD Characteristics; // +0x12

// +0x14

&rightsign; IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

--------------------------------------------------------------------------

> db 77e60000+f8 L0n24

77e600f8 50 45 00 00 4c 01 04 00-28 fa 6d 3d 00 00 00 00 PE..L...(.m=....

77e60108 00 00 00 00 e0 00 0e 21

^^^^^

接下来是可选头,SizeOfOptionalHeader成员表明可选头占用了224字节(0x00e0)。

--------------------------------------------------------------------------

typedef struct _IMAGE_OPTIONAL_HEADER

&leftsign;

WORD Magic; // +0x00

BYTE MajorLinkerVersion; // +0x02

BYTE MinorLinkerVersion; // +0x03

DWORD SizeOfCode; // +0x04

DWORD SizeOfInitializedData; // +0x08

DWORD SizeOfUninitializedData; // +0x0c

DWORD AddressOfEntryPoint; // +0x10

DWORD BaseOfCode; // +0x14

DWORD BaseOfData; // +0x18

DWORD ImageBase; // +0x1c

DWORD SectionAlignment; // +0x20

DWORD FileAlignment; // +0x24

WORD MajorOperatingSystemVersion; // +0x28

WORD MinorOperatingSystemVersion; // +0x2a

WORD MajorImageVersion; // +0x2c

WORD MinorImageVersion; // +0x2e

WORD MajorSubsystemVersion; // +0x30

WORD MinorSubsystemVersion; // +0x32

DWORD Win32VersionValue; // +0x34

DWORD SizeOfImage; // +0x38

DWORD SizeOfHeaders; // +0x3c

DWORD CheckSum; // +0x40

WORD Subsystem; // +0x44

WORD DllCharacteristics; // +0x46

DWORD SizeOfStackReserve; // +0x48

DWORD SizeOfStackCommit; // +0x4c

DWORD SizeOfHeapReserve; // +0x50

DWORD SizeOfHeapCommit; // +0x54

DWORD LoaderFlags; // +0x58

DWORD NumberOfRvaAndSizes; // +0x5c Number of data-dictionary entries in the remainder of the Optional Header

// +0x60

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

// +0xe0

&rightsign; IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

typedef struct _IMAGE_DATA_DIRECTORY

&leftsign;

DWORD VirtualAddress; // +0x00 RVA

DWORD Size; // +0x04 The size in bytes

// +0x08

&rightsign; IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16

#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory

#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory

#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory

#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory

#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory

#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table

#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory

#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data

#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP

#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory

#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory

#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers

#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table

#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors

#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor

--------------------------------------------------------------------------

IMAGE_DATA_DIRECTORY.VirtualAddress是RVA。一般情况DataDirectory[]是含有16

个元素的结构数组。前两个元素分别对应Export Directory与Import Directory。但

是规范3.4.3小节指出元素个数不固定,需要检查NumberOfRvaAndSizes成员确定元素

个数。

此外,不要假设IMAGE_DATA_DIRECTORY.VirtualAddress指向所在section的起始位置,

比如Import Directory一般位于.idata section中,但不能假设RVA指向.idata的起

始位置。不要假设Import Directory所在section一定拥有".idata"这个名字。

> db 77e60000+f8+0n24 L0n224

77e60110 0b 01 07 00 00 56 07 00-00 dc 06 00 00 00 00 00 .....V..........

77e60120 60 ae 01 00 00 10 00 00-00 20 07 00 00 00 e6 77 `........ .....w

77e60130 00 10 00 00 00 02 00 00-05 00 01 00 05 00 01 00 ................

77e60140 04 00 00 00 00 00 00 00-00 60 0e 00 00 04 00 00 .........`......

77e60150 d3 7e 0e 00 03 00 00 00-00 00 04 00 00 10 00 00 .~..............

77e60160 00 00 10 00 00 10 00 00-00 00 00 00 10 00 00 00 ................

77e60170 40 d0 06 00 39 6b 00 00-7c 3b 07 00 28 00 00 00 @...9k..&line;;..(...

77e60180 00 a0 07 00 d8 5e 06 00-00 00 00 00 00 00 00 00 .....^..........

77e60190 00 00 00 00 00 00 00 00-00 00 0e 00 54 53 00 00 ............TS..

77e601a0 fc 63 07 00 38 00 00 00-00 00 00 00 00 00 00 00 .c..8...........

77e601b0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

77e601c0 a8 76 07 00 40 00 00 00-90 02 00 00 1c 00 00 00 .v..@...........

77e601d0 00 10 00 00 0c 06 00 00-00 00 00 00 00 00 00 00 ................

77e601e0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

> dd 77e60000+f8+0n24+5c L1 (检查NumberOfRvaAndSizes成员)

77e6016c 00000010

> dd 77e60000+f8+0n24+60 L2 (定位Export Directory,当前基是16)

77e60170 0006d040 00006b39

^^^^^^^^

> ? 77e60000+0006d040+00006b39

Evaluate expression: 2012035961 = 77ed3b79

> ? 77e60000+0006d040

Evaluate expression: 2012008512 = 77ecd040

现在我们知道Export Directory在"77e60000+0006d040",格式如下:

--------------------------------------------------------------------------

typedef struct _IMAGE_EXPORT_DIRECTORY

&leftsign;

DWORD Characteristics; // +0x00

DWORD TimeDateStamp; // +0x04

WORD MajorVersion; // +0x08

WORD MinorVersion; // +0x0a

DWORD Name; // +0x0c Name of the DLL

DWORD Base; // +0x10 Starting ordinal number for exports

DWORD NumberOfFunctions; // +0x14 Number of entries in the EAT

DWORD NumberOfNames; // +0x18 Number of entries in the ENPT/EOT

DWORD AddressOfFunctions; // +0x1c RVA from base of image

DWORD AddressOfNames; // +0x20 RVA from base of image

DWORD AddressOfNameOrdinals; // +0x24 RVA from base of image

// +0x28

&rightsign; IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

--------------------------------------------------------------------------

AddressOfFunctions

指向Export Address Table。

DWORD EAT[NumberOfFunctions];

EAT[i]

可选头中IMAGE_DATA_DIRECTORY结构决定了引出信息范围。如果EAT[i]的值不在

引出信息范围,则为函数地址(Export RVA)。否则,该值指向形如dllname.#27

或者dllname.exportfunc的ASCIZ串,用MS术语说,这是一个Forwarder RVA,表

示由另外一个dll实际引出某函数。

AddressOfNames

指向Export Name Pointer Table

DWORD ENPT[NumberOfNames];

ENPT[i]

指向Export Name Table中某个位置

ENT由ASCIZ串构成

AddressOfNameOrdinals

指向Export Ordinal Table

WORD EOT[NumberOfNames];

参看规范6.3.4小节,这几个数组之间的关系用伪C语言描述如下:

--------------------------------------------------------------------------

/*

* 怀疑该规范与MS最终实现有出入,规范中EOT[0]应该等于ordinal_base的,但MS

* 最终实现里EOT[0]等于0。这里的伪代码符合MS的最终实现。看来,理论->实践->

* 再理论的学习过程永远都要牢记,否则死菜了都不知道怎么死菜的。

*/

index = Search_ENPT( function_name );

index = EOT[ index ];

function_addr = EAT[ index ];

ordinal = ordinal_base + index;

--------------------------------------------------------------------------

为何多出个EOT,因为可能不同的函数名对应同一个函数地址,参[14]中文版的12.5

小节,英文版第19章"Building the DLL Module"。

假设源代码中有如下声明:

__declspec(dllexport) int __stdcall PublicFunc ( int a, int b, int c, int d );

假设没有使用DEF文件:

EXPORTS

PublicFunc

同时却又在源代码中出现:

#pragma comment( linker, "/EXPORT:PublicFunc=_PublicFunc@32" )

生成PE文件时会同时引出两个符号,"PublicFunc"与"_PublicFunc@32",这两个符号

对应同一个函数。反映在上述几个数组中,即EOT[i]等于EOT[j]。

NumberOfFunctions与NumberOfNames在这种情形下不等。除此之外,还有一种不等情

形。NumberOfNames为0,NumberOfFunctions不为0,表示模块仅通过ordinal引出函

数,这是相当极端却有可能出现的情形。

VC有个现成的工具dumpbin,可用于观察引出(export)信息:

> dumpbin X:\XP\system32\kernel32.dll /exports

Section contains the following exports for KERNEL32.dll

00000000 characteristics

3D6DE616 time date stamp Thu Aug 29 17:15:02 2002

0.00 version

1 ordinal base

942 number of functions

942 number of names

ordinal hint RVA name

1 0 000137E8 ActivateActCtx

2 1 000093FE AddAtomA

3 2 0000D496 AddAtomW

4 3 000607C5 AddConsoleAliasA

5 4 0006078E AddConsoleAliasW

6 5 0004E0A1 AddLocalAlternateComputerNameA

7 6 0004DF8C AddLocalAlternateComputerNameW

8 7 00035098 AddRefActCtx

9 8 AddVectoredExceptionHandler (forwarded to NTDLL.RtlAddVectoredExceptionHandler)

10 9 00036909 AllocConsole

... ...

401 190 0001B332 GetProcAddress

... ...

571 23A 0001D961 LoadLibraryA

572 23B 0001D941 LoadLibraryExA

573 23C 0001D839 LoadLibraryExW

574 23D 00013B38 LoadLibraryW

... ...

回windbg验证一下:

> db 77e60000+0006d040 L28

77ecd040 00 00 00 00 16 e6 6d 3d-00 00 00 00 34 f5 06 00 ......m=....4...

77ecd050 01 00 00 00 ae 03 00 00-ae 03 00 00 68 d0 06 00 ............h...

77ecd060 20 df 06 00 d8 ed 06 00

> da 77e60000+poi(0x77e60000+0x0006d040+0xc) (Name)

77ecf534 "KERNEL32.dll"

> dd 0x77e60000+0x0006d040+0x10 L1 (Base)

77ecd050 00000001

> dd 0x77e60000+0x0006d040+0x14 L1 (NumberOfFunctions)

77ecd054 000003ae

> dd 0x77e60000+0x0006d040+0x18 L1 (NumberOfNames)

77ecd058 000003ae

> dd 0x77e60000+0x0006d040+0x1c L3 (AddressOfFunctions、AddressOfNames、AddressOfNameOrdinals)

77ecd05c 0006d068 0006df20 0006edd8

套用这个公式:

--------------------------------------------------------------------------

0x190 = Search_ENPT( "GetProcAddress" );

0x190 = EOT[ 0x190 ];

0x77e7b332 = EAT[ 0x190 ];

0x191 = 1 + 0x190;

--------------------------------------------------------------------------

> da 77e60000+poi(77e60000+0006df20+0x190*4) (访问ENPT、ENT)

77ed1393 "GetProcAddress"

> dw 77e60000+0006edd8+0x190*2 L1 (访问EOT)

77ecf0f8 0190

> ? 77e60000+poi(77e60000+0006d068+0x190*4) (访问EAT)

Evaluate expression: 2011673394 = 77e7b332

> u 77e7b332 (这个地址不在[77ecd040, 77ed3b79)内)

kernel32!GetProcAddress:

77e7b332 55 push ebp

77e7b333 8bec mov ebp,esp

77e7b335 51 push ecx

77e7b336 51 push ecx

77e7b337 53 push ebx

77e7b338 57 push edi

77e7b339 8b7d0c mov edi,[ebp+0xc]

77e7b33c bbffff0000 mov ebx,0xffff

再来验证一下Forwarder RVA的情形:

--------------------------------------------------------------------------

8 = Search_ENPT( "AddVectoredExceptionHandler" );

8 = EOT[ 8 ];

0x77ed38ad = EAT[ 8 ];

9 = 1 + 8;

--------------------------------------------------------------------------

> da 77e60000+poi(77e60000+0006df20+8*4) (访问ENPT、ENT)

77ecf5cf "AddVectoredExceptionHandler"

> dw 77e60000+0006edd8+8*2 L1 (访问EOT)

77ecede8 0008

> ? 77e60000+poi(77e60000+0006d068+8*4) (访问EAT)

Evaluate expression: 2012035245 = 77ed38ad

> da 77e60000+poi(77e60000+0006d068+8*4) (这个地址在[77ecd040, 77ed3b79)内)

77ed38ad "NTDLL.RtlAddVectoredExceptionHan"

77ed38cd "dler"

现在总结一下"根据PE文件格式获取LoadLibraryA()/GetProcAddress()地址"全过程:

a. 通过TEB/PEB获取kernel32.dll基址

b. 在(基址+0x3c)处获取e_lfanew

c. 在(基址+e_lfanew+0x78)处获取Export Directory地址(后面为描述方便简称export)

d. 在(基址+export+0x1c)处获取AddressOfFunctions、AddressOfNames、AddressOfNameOrdinals

e. 搜索ENPT,确定"LoadLibraryA"、"GetProcAddress"所对应的index

f. index = EOT[ index ];

g. function_addr = EAT[ index ];

下面是完整的C语言演示程序,汇编化留到编写完整shellcode时进行。

--------------------------------------------------------------------------

/*

* -----------------------------------------------------------------------

* Compile : For x86/EWindows XP SP1 & VC 7

* : cl GetAddr.c /nologo /Os /G6 /W3 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE

* :

* Create : 2003-08-14 15:11

* Modify :

* -----------------------------------------------------------------------

*/

#include <stdio.h>

#include <stdlib.h>

#include <windows.h>

#pragma comment( linker, "/INCREMENTAL:NO" )

#pragma comment( linker, "/subsystem:console" )

static void * __stdcall SearchAPI ( char *BASE, DWORD *EAT, DWORD *ENPT, WORD *EOT, DWORD num, char *name )

&leftsign;

DWORD index = 0;

char *function_name = NULL;

void *function_addr = NULL;

for ( index = 0; index < num; index++ )

&leftsign;

function_name = ENPT[index] + BASE;

/*

* 大小写敏感比较

*/

if ( 0 == strcmp( function_name, name ) )

&leftsign;

index = EOT[index];

function_addr = EAT[index] + BASE;

return( function_addr );

&rightsign;

&rightsign; /* end of for */

return( NULL );

&rightsign; /* end of SearchAPI */

int __cdecl main ( int argc, char * argv[] )

&leftsign;

void *PEB = NULL,

*Ldr = NULL,

*Flink = NULL,

*kernel32_BaseAddress = NULL,

*kernel32_BaseDllName = NULL,

*ExportDirectory = NULL,

*PrivateLoadLibraryA = NULL,

*PrivateGetProcAddress = NULL;

DWORD NumberOfNames = 0,

*AddressOfFunctions = NULL,

*AddressOfNames = NULL;

WORD *AddressOfNameOrdinals = NULL;

LONG e_lfanew = 0;

__asm

&leftsign;

mov eax,fs:[0x30]

mov PEB,eax

&rightsign;

printf( "PEB = 0x%08X\n", PEB );

Ldr = *( ( void ** )( ( unsigned char * )PEB + 0x0c ) );

printf( "Ldr = 0x%08X\n", Ldr );

Flink = *( ( void ** )( ( unsigned char * )Ldr + 0x1c ) );

printf( "Flink = 0x%08X\n", Flink );

Flink = *( ( void ** )Flink );

kernel32_BaseAddress = *( ( void ** )( ( unsigned char * )Flink + 0x08 ) );

kernel32_BaseDllName = *( ( void ** )( ( unsigned char * )Flink + 0x20 ) );

printf( "kernel32_BaseAddress = 0x%08X\n", kernel32_BaseAddress );

wprintf( L"kernel32_BaseDllName = %s\n", kernel32_BaseDllName );

/*

* 根据PE文件格式进行解析

*/

e_lfanew = *( ( LONG * )( ( unsigned char * )kernel32_BaseAddress + 0x3c ) );

printf( "e_lfanew = 0x%08X\n", e_lfanew );

ExportDirectory = *( ( DWORD * )( ( unsigned char * )kernel32_BaseAddress + e_lfanew + 0x78 ) ) +

( unsigned char * )kernel32_BaseAddress;

printf( "ExportDirectory = 0x%08X\n", ExportDirectory );

NumberOfNames = *( ( DWORD * )( ( unsigned char * )ExportDirectory + 0x18 ) );

printf( "NumberOfNames = %u\n", NumberOfNames );

AddressOfFunctions = ( DWORD * )

(

*( ( DWORD * )( ( unsigned char * )ExportDirectory + 0x1c ) ) +

( unsigned char * )kernel32_BaseAddress

);

printf( "AddressOfFunctions = 0x%08X\n", AddressOfFunctions );

AddressOfNames = ( DWORD * )

(

*( ( DWORD * )( ( unsigned char * )ExportDirectory + 0x20 ) ) +

( unsigned char * )kernel32_BaseAddress

);

printf( "AddressOfNames = 0x%08X\n", AddressOfNames );

AddressOfNameOrdinals = ( WORD * )

(

*( ( DWORD * )( ( unsigned char * )ExportDirectory + 0x24 ) ) +

( unsigned char * )kernel32_BaseAddress

);

printf( "AddressOfNameOrdinals = 0x%08X\n", AddressOfNameOrdinals );

PrivateLoadLibraryA = SearchAPI

(

kernel32_BaseAddress,

AddressOfFunctions,

AddressOfNames,

AddressOfNameOrdinals,

NumberOfNames,

"LoadLibraryA"

);

printf( "PrivateLoadLibraryA = 0x%08X\n", PrivateLoadLibraryA );

printf( "LoadLibraryA = 0x%08X\n", LoadLibraryA );

PrivateGetProcAddress = SearchAPI

(

kernel32_BaseAddress,

AddressOfFunctions,

AddressOfNames,

AddressOfNameOrdinals,

NumberOfNames,

"GetProcAddress"

);

printf( "PrivateGetProcAddress = 0x%08X\n", PrivateGetProcAddress );

printf( "GetProcAddress = 0x%08X\n", GetProcAddress );

return( EXIT_SUCCESS );

&rightsign; /* end of main */

#if 0

/*

* 按初始化顺序前向遍历链表,第二个节点对应kernel32.dll。

*/

#include <stdio.h>

#include <stdlib.h>

#pragma comment( linker, "/INCREMENTAL:NO" )

#pragma comment( linker, "/subsystem:console" )

int __cdecl main ( int argc, char * argv[] )

&leftsign;

void *PEB = NULL,

*Ldr = NULL,

*Flink = NULL,

*p = NULL,

*BaseAddress = NULL,

*BaseDllName = NULL;

__asm

&leftsign;

mov eax,fs:[0x30]

mov PEB,eax

&rightsign;

printf( "PEB = 0x%08X\n", PEB );

Ldr = *( ( void ** )( ( unsigned char * )PEB + 0x0c ) );

printf( "Ldr = 0x%08X\n", Ldr );

Flink = *( ( void ** )( ( unsigned char * )Ldr + 0x1c ) );

printf( "Flink = 0x%08X\n", Flink );

p = Flink;

do

&leftsign;

BaseAddress = *( ( void ** )( ( unsigned char * )p + 0x08 ) );

BaseDllName = *( ( void ** )( ( unsigned char * )p + 0x20 ) );

printf( "p = 0x%08X 0x%08X ", p, BaseAddress );

wprintf( L"%s\n", BaseDllName );

p = *( ( void ** )p );

&rightsign;

while ( Flink != p );

return( EXIT_SUCCESS );

&rightsign; /* end of main */

#endif

--------------------------------------------------------------------------

执行效果如下:

> GetAddr

PEB = 0x7FFDF000

Ldr = 0x00241E90

Flink = 0x00241F28

kernel32_BaseAddress = 0x77E60000

kernel32_BaseDllName = kernel32.dll

e_lfanew = 0x000000F8

ExportDirectory = 0x77ECD040

NumberOfNames = 942

AddressOfFunctions = 0x77ECD068

AddressOfNames = 0x77ECDF20

AddressOfNameOrdinals = 0x77ECEDD8

PrivateLoadLibraryA = 0x77E7D961

LoadLibraryA = 0x77E7D961

PrivateGetProcAddress = 0x77E7B332

GetProcAddress = 0x77E7B332

这里演示的技巧不只用于exploit、shellcode、virus,还大量用于特殊Driver编程

中。根据PE文件格式解析内存中的模块映像是很基础的知识,我今天才接触了一下,

惭愧。

SearchAPI()用到了被搜索函数名,直接在shellcode中保存完整函数名会占用不少空

间。可以考虑在shellcode中保存函数名的某种哈希值,同时SearchAPI()比较哈希值

而非函数名。任何一种哈希算法都会丢失部分原有信息,以致不同函数名产生的哈希

值相同,所谓"碰撞"。virus编程中为此常常使用标准CRC32算法([8]),参29A-4.227。

但是LSD认为CRC32的汇编算法太长了([15]),他们用了一个相当简单的算法:

while ( *c )

&leftsign;

h = ( ( h << 5 ) &line; ( h >> 27 ) ) + *c++;

&rightsign;

不超过10行汇编代码。据LSD的报告称,对超过5000个不同的dll测试,覆盖50000个

不同的函数名,该哈希算法未产生一次碰撞。若真是如此,对于编写shellcode来讲,

完全足够了。

下面是完整的C语言演示程序,真正汇编化后应提前计算函数名的哈希值。

--------------------------------------------------------------------------

/*

* -----------------------------------------------------------------------

* Compile : For x86/EWindows XP SP1 & VC 7

* : cl GetAddr_0.c /nologo /Os /G6 /W3 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE

* :

* Create : 2003-08-14 17:24

* Modify :

* -----------------------------------------------------------------------

*/

#include <stdio.h>

#include <stdlib.h>

#include <windows.h>

#pragma comment( linker, "/INCREMENTAL:NO" )

#pragma comment( linker, "/subsystem:console" )

static DWORD __stdcall GetHash ( unsigned char *c )

&leftsign;

DWORD h = 0;

while ( *c )

&leftsign;

h = ( ( h << 5 ) &line; ( h >> 27 ) ) + *c++;

&rightsign;

return( h );

&rightsign; /* end of GetHash */

static void * __stdcall SearchAPI ( char *BASE, DWORD *EAT, DWORD *ENPT, WORD *EOT, DWORD num, DWORD NameHash )

&leftsign;

DWORD index = 0,

h = 0;

char *function_name = NULL;

void *function_addr = NULL;

for ( index = 0; index < num; index++ )

&leftsign;

function_name = ENPT[index] + BASE;

h = GetHash( function_name );

if ( h == NameHash )

&leftsign;

index = EOT[index];

function_addr = EAT[index] + BASE;

return( function_addr );

&rightsign;

&rightsign; /* end of for */

return( NULL );

&rightsign; /* end of SearchAPI */

int __cdecl main ( int argc, char * argv[] )

&leftsign;

void *PEB = NULL,

*Ldr = NULL,

*Flink = NULL,

*kernel32_BaseAddress = NULL,

*kernel32_BaseDllName = NULL,

*ExportDirectory = NULL,

*PrivateLoadLibraryA = NULL,

*PrivateGetProcAddress = NULL;

DWORD NumberOfNames = 0,

*AddressOfFunctions = NULL,

*AddressOfNames = NULL,

NameHash = 0;

WORD *AddressOfNameOrdinals = NULL;

LONG e_lfanew = 0;

__asm

&leftsign;

mov eax,fs:[0x30]

mov PEB,eax

&rightsign;

printf( "PEB = 0x%08X\n", PEB );

Ldr = *( ( void ** )( ( unsigned char * )PEB + 0x0c ) );

printf( "Ldr = 0x%08X\n", Ldr );

Flink = *( ( void ** )( ( unsigned char * )Ldr + 0x1c ) );

printf( "Flink = 0x%08X\n", Flink );

Flink = *( ( void ** )Flink );

kernel32_BaseAddress = *( ( void ** )( ( unsigned char * )Flink + 0x08 ) );

kernel32_BaseDllName = *( ( void ** )( ( unsigned char * )Flink + 0x20 ) );

printf( "kernel32_BaseAddress = 0x%08X\n", kernel32_BaseAddress );

wprintf( L"kernel32_BaseDllName = %s\n", kernel32_BaseDllName );

/*

* 根据PE文件格式进行解析

*/

e_lfanew = *( ( LONG * )( ( unsigned char * )kernel32_BaseAddress + 0x3c ) );

printf( "e_lfanew = 0x%08X\n", e_lfanew );

ExportDirectory = *( ( DWORD * )( ( unsigned char * )kernel32_BaseAddress + e_lfanew + 0x78 ) ) +

( unsigned char * )kernel32_BaseAddress;

printf( "ExportDirectory = 0x%08X\n", ExportDirectory );

NumberOfNames = *( ( DWORD * )( ( unsigned char * )ExportDirectory + 0x18 ) );

printf( "NumberOfNames = %u\n", NumberOfNames );

AddressOfFunctions = ( DWORD * )

(

*( ( DWORD * )( ( unsigned char * )ExportDirectory + 0x1c ) ) +

( unsigned char * )kernel32_BaseAddress

);

printf( "AddressOfFunctions = 0x%08X\n", AddressOfFunctions );

AddressOfNames = ( DWORD * )

(

*( ( DWORD * )( ( unsigned char * )ExportDirectory + 0x20 ) ) +

( unsigned char * )kernel32_BaseAddress

);

printf( "AddressOfNames = 0x%08X\n", AddressOfNames );

AddressOfNameOrdinals = ( WORD * )

(

*( ( DWORD * )( ( unsigned char * )ExportDirectory + 0x24 ) ) +

( unsigned char * )kernel32_BaseAddress

);

printf( "AddressOfNameOrdinals = 0x%08X\n", AddressOfNameOrdinals );

NameHash = GetHash( "LoadLibraryA" );

printf( "NameHash = 0x%08X\n", NameHash );

PrivateLoadLibraryA = SearchAPI

(

kernel32_BaseAddress,

AddressOfFunctions,

AddressOfNames,

AddressOfNameOrdinals,

NumberOfNames,

NameHash

);

printf( "PrivateLoadLibraryA = 0x%08X\n", PrivateLoadLibraryA );

printf( "LoadLibraryA = 0x%08X\n", LoadLibraryA );

NameHash = GetHash( "GetProcAddress" );

printf( "NameHash = 0x%08X\n", NameHash );

PrivateGetProcAddress = SearchAPI

(

kernel32_BaseAddress,

AddressOfFunctions,

AddressOfNames,

AddressOfNameOrdinals,

NumberOfNames,

NameHash

);

printf( "PrivateGetProcAddress = 0x%08X\n", PrivateGetProcAddress );

printf( "GetProcAddress = 0x%08X\n", GetProcAddress );

return( EXIT_SUCCESS );

&rightsign; /* end of main */

--------------------------------------------------------------------------

> GetAddr_0

PEB = 0x7FFDF000

Ldr = 0x00241E90

Flink = 0x00241F28

kernel32_BaseAddress = 0x77E60000

kernel32_BaseDllName = kernel32.dll

e_lfanew = 0x000000F8

ExportDirectory = 0x77ECD040

NumberOfNames = 942

AddressOfFunctions = 0x77ECD068

AddressOfNames = 0x77ECDF20

AddressOfNameOrdinals = 0x77ECEDD8

NameHash = 0x331ADDDC <- "LoadLibraryA"的哈希值

PrivateLoadLibraryA = 0x77E7D961

LoadLibraryA = 0x77E7D961

NameHash = 0x99C95590 <- "GetProcAddress"的哈希值

PrivateGetProcAddress = 0x77E7B332

GetProcAddress = 0x77E7B332

☆ 参考资源

[13] Microsoft Portable Executable and Common Object File Format Specification

http://www.microsoft.com/whdc/hwdev/download/hardware/pecoff.doc

http://www.microsoft.com/whdc/hwdev/download/hardware/pecoff.pdf

[15] http://www.lsd-pl.net/documents/winasm-1.0.1.pdf

http://www.lsd-pl.net/documents/winasm.ppt

http://lsd-pl.net/projects/winasm-1.0.1.tar.gz

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有