来自:wblib > 馆藏分类
配色: 字号:
从资源文件中的DLL调到内存,然后加载DLL (Delphi)
2013-10-07 | 阅:  转:  |  分享 
  
从资源文件中的DLL调到内存,然后加载DLL(Delphi)

前提要求:

有个需求是把一个DLL作为数据打包到EXE中,运行的时候动态加载.但要求不是释放出来生成DLL文件加载.

不过由于是直接分配内存加载DLL的.有一些小缺陷.例如遍历进程中加载的模块的时候是找不到这个DLL的.GetModuleXXXX之类的API也就不能用了.当然也可以Hook这些函数做处理.不过便利不到这个模块也未必不是一个优点.例如写木马黑客之类的代码的时候,可以作为隐藏模块的手段.

先分析一下Windows系统加载PE文件时候的步骤吧.可以简单的理解为如下步骤:

1.读入文件(利用文件镜像)

2.如果是加载的位置和PE头规定的镜像基址不一致(通常是DLL),并且有重定位节就进行重定位.

3.RVA地址填写.如果有导入函数就加载DLL,把函数地址付给导入表项

4.执行入口代码.DLL的话就是DLLMain.

好了.条理清晰了,代码如下

{

内存加载DLL

wr960204

}

unitPELoader;

interface

uses

Windows;



functionLoadPE(Buf:Pointer;Len:Integer):Cardinal;

procedureFreePE(Handle:Cardinal);

functionGetProcAddress(Module:Cardinal;ProcessName:PChar):Pointer;

implementation

const

IMAGE_ORDINAL_FLAG=DWORD($80000000);

functionGetProcAddress(Module:Cardinal;ProcessName:PChar):Pointer;

functionstrcmp(p1,p2:PChar):boolean;

begin

Result:=False;

while(p1^=p2^)do

begin

if(P1^=#0)or(P2^=#0)then

begin

Result:=True;

Exit;

end;

Inc(P1);

Inc(P2);

end;

end;

var

ExportName:pChar;

Address:Cardinal;

J:Cardinal;

ImageDosHeader:PImageDosHeader;

ImageNTHeaders:PImageNTHeaders;

ImageExportDirectory:PImageExportDirectory;

begin

ImageDosHeader:=Pointer(Module);

ImageNTHeaders:=Pointer(Module+ImageDosHeader._lfanew);

ImageExportDirectory:=Pointer(ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress+Module);

J:=0;

Address:=0;

repeat

ExportName:=Pointer(Cardinal(Pointer(Cardinal(ImageExportDirectory.AddressOfNames)+Module+J4)^)+Module);

ifstrcmp(ExportName,ProcessName)then

Address:=Cardinal(Pointer(Word(Pointer(Jshl1+Cardinal(

ImageExportDirectory.AddressOfNameOrdinals)+Module)^)and

$0000FFFFshl2+Cardinal(ImageExportDirectory.AddressOfFunctions)

+Module)^)+Module;

Inc(J);

until(Address<>0)or(J=ImageExportDirectory.NumberOfNames);

Result:=Pointer(Address);

end;

type

TImageSectionHeaders=array[0..0]ofTImageSectionHeader;

PImageSectionHeaders=^TImageSectionHeaders;

TIIDUnion=record

caseIntegerof

0:(Characteristics:DWORD);

1:(OriginalFirstThunk:DWORD);

end;

PIMAGE_IMPORT_DESCRIPTOR=^IMAGE_IMPORT_DESCRIPTOR;

_IMAGE_IMPORT_DESCRIPTOR=record

Union:TIIDUnion;

TimeDateStamp:DWORD;

ForwarderChain:DWORD;

Name:DWORD;

FirstThunk:DWORD;

end;

IMAGE_IMPORT_DESCRIPTOR=_IMAGE_IMPORT_DESCRIPTOR;

TImageImportDecriptor=IMAGE_IMPORT_DESCRIPTOR;

PImageImportDecriptor=PIMAGE_IMPORT_DESCRIPTOR;

PIMAGE_THUNK_DATA32=^IMAGE_THUNK_DATA32;

_IMAGE_THUNK_DATA32=record

caseIntegerof

0:(ForwarderString:DWORD);

1:(Function_:DWORD);

2:(Ordinal:DWORD);

3:(AddressOfData:DWORD);

end;

IMAGE_THUNK_DATA32=_IMAGE_THUNK_DATA32;

TImageThunkData32=IMAGE_THUNK_DATA32;

PImageThunkData32=PIMAGE_THUNK_DATA32;

IMAGE_THUNK_DATA=IMAGE_THUNK_DATA32;

PIMAGE_THUNK_DATA=PIMAGE_THUNK_DATA32;

PIMAGE_IMPORT_BY_NAME=^IMAGE_IMPORT_BY_NAME;

_IMAGE_IMPORT_BY_NAME=record

Hint:Word;

Name:array[0..0]ofByte;

end;

IMAGE_IMPORT_BY_NAME=_IMAGE_IMPORT_BY_NAME;

TImageImportByName=IMAGE_IMPORT_BY_NAME;

PImageImportByName=PIMAGE_IMPORT_BY_NAME;

{计算对齐后的大小}

functionGetAlignedSize(Origin,Alignment:Cardinal):Cardinal;

begin

result:=(Origin+Alignment-1)divAlignmentAlignment;

end;

{计算加载pe并对齐需要占用多少内存,未直接使用OptionalHeader.SizeOfImage作为结果是因为据说有的编译器生成的exe这个值会填0}

functionCalcTotalImageSize(MzH:PImageDosHeader;FileLen:Cardinal;peH:PImageNtHeaders;

peSecH:PImageSectionHeaders):Cardinal;

var

i:Integer;

begin

{计算pe头的大小}

result:=GetAlignedSize(PeH.OptionalHeader.SizeOfHeaders,PeH.OptionalHeader.SectionAlignment);

{计算所有节的大小}

fori:=0topeH.FileHeader.NumberOfSections-1do

ifpeSecH[i].PointerToRawData+peSecH[i].SizeOfRawData>FileLenthen//超出文件范围

begin

result:=0;

exit;

end

elseifpeSecH[i].VirtualAddress<>0then//计算对齐后某节的大小

ifpeSecH[i].Misc.VirtualSize<>0then

result:=GetAlignedSize(peSecH[i].VirtualAddress+peSecH[i].Misc.VirtualSize,PeH.OptionalHeader.SectionAlignment)

else

result:=GetAlignedSize(peSecH[i].VirtualAddress+peSecH[i].SizeOfRawData,PeH.OptionalHeader.SectionAlignment)

elseifpeSecH[i].Misc.VirtualSize
result:=result+GetAlignedSize(peSecH[i].SizeOfRawData,peH.OptionalHeader.SectionAlignment)

else

result:=result+GetAlignedSize(peSecH[i].Misc.VirtualSize,PeH.OptionalHeader.SectionAlignment);

end;

{加载pe到内存并对齐所有节}

functionAlignPEToMem(constBuf:Pointer;Len:Integer;varPeH:PImageNtHeaders;

varPeSecH:PImageSectionHeaders;varMem:Pointer;varImageSize:Cardinal):Boolean;

var

SrcMz:PImageDosHeader;//DOS头

SrcPeH:PImageNtHeaders;//PE头

SrcPeSecH:PImageSectionHeaders;//节表

i:Integer;

l:Cardinal;

Pt:Pointer;

begin

result:=false;

SrcMz:=Buf;

ifLen
ifSrcMz.e_magic<>IMAGE_DOS_SIGNATUREthenexit;

ifLen
SrcPeH:=pointer(Integer(SrcMz)+SrcMz._lfanew);

if(SrcPeH.Signature<>IMAGE_NT_SIGNATURE)thenexit;

if(SrcPeH.FileHeader.CharacteristicsandIMAGE_FILE_DLL=0)//不是DLL,

or(SrcPeH.FileHeader.CharacteristicsandIMAGE_FILE_EXECUTABLE_IMAGE=0)//不可执行

or(SrcPeH.FileHeader.SizeOfOptionalHeader<>SizeOf(TImageOptionalHeader))thenexit;

SrcPeSecH:=Pointer(Integer(SrcPeH)+SizeOf(TImageNtHeaders));

ImageSize:=CalcTotalImageSize(SrcMz,Len,SrcPeH,SrcPeSecH);

ifImageSize=0then

exit;

Mem:=VirtualAlloc(nil,ImageSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);//分配一块可以执行,可以读写的内存

ifMem<>nilthen

begin

//计算需要复制的PE头

l:=SrcPeH.OptionalHeader.SizeOfHeaders;

fori:=0toSrcPeH.FileHeader.NumberOfSections-1do

if(SrcPeSecH[i].PointerToRawData<>0)and(SrcPeSecH[i].PointerToRawData
l:=SrcPeSecH[i].PointerToRawData;

Move(SrcMz^,Mem^,l);

PeH:=Pointer(Integer(Mem)+PImageDosHeader(Mem)._lfanew);

PeSecH:=Pointer(Integer(PeH)+sizeof(TImageNtHeaders));

Pt:=Pointer(Cardinal(Mem)+GetAlignedSize(PeH.OptionalHeader.SizeOfHeaders,PeH.OptionalHeader.SectionAlignment));

fori:=0toPeH.FileHeader.NumberOfSections-1do

begin

//定位该节在内存中的位置

ifPeSecH[i].VirtualAddress<>0then

Pt:=Pointer(Cardinal(Mem)+PeSecH[i].VirtualAddress);

ifPeSecH[i].SizeOfRawData<>0then

begin

//复制数据到内存

Move(Pointer(Cardinal(SrcMz)+PeSecH[i].PointerToRawData)^,pt^,PeSecH[i].SizeOfRawData);

ifpeSecH[i].Misc.VirtualSize
pt:=pointer(Cardinal(pt)+GetAlignedSize(PeSecH[i].SizeOfRawData,PeH.OptionalHeader.SectionAlignment))

else

pt:=pointer(Cardinal(pt)+GetAlignedSize(peSecH[i].Misc.VirtualSize,peH.OptionalHeader.SectionAlignment));

//pt定位到下一节开始位置

end

else

pt:=pointer(Cardinal(pt)+GetAlignedSize(PeSecH[i].Misc.VirtualSize,PeH.OptionalHeader.SectionAlignment));

end;

result:=True;

end;

end;

{是否包含可重定向列表}

functionHasRelocationTable(peH:PImageNtHeaders):Boolean;

begin

result:=(peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress<>0)

and(peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size<>0);

end;

type

PImageBaseRelocation=^TImageBaseRelocation;

TImageBaseRelocation=packedrecord

VirtualAddress:cardinal;

SizeOfBlock:cardinal;

end;

{重定向PE用到的地址}

procedureDoRelocation(peH:PImageNtHeaders;NewBase:Pointer);

var

Delta:Cardinal;

p:PImageBaseRelocation;

pw:PWord;

i:Integer;

begin

Delta:=Cardinal(NewBase)-peH.OptionalHeader.ImageBase;

p:=pointer(cardinal(NewBase)+peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

while(p.VirtualAddress+p.SizeOfBlock<>0)do

begin

pw:=pointer(Integer(p)+Sizeof(TImageBaseRelocation));

fori:=1to(p.SizeOfBlock-Sizeof(TImageBaseRelocation))divsizeof(WORD)do

begin

ifpw^and$F000=$3000then

Inc(PCardinal(Cardinal(NewBase)+p.VirtualAddress+(pw^and$0FFF))^,Delta);

inc(pw);

end;

p:=PImageBaseRelocation(pw);

end;

end;

{填充引入地址表}

functionFillImports(peH:PImageNtHeaders;pImageBase:Pointer):BOOL;

type

TIMAGE_THUNK_DATAs=array[0..0]ofIMAGE_THUNK_DATA;

PIMAGE_THUNK_DATAs=^TIMAGE_THUNK_DATAs;

var

Offset:Cardinal;

pID:PIMAGE_IMPORT_DESCRIPTOR;

pRealIAT,pOriginalIAT:PIMAGE_THUNK_DATAs;

buf:array[0..$FF]ofchar;

pName:PChar;

I:Integer;

hDll:HMODULE;

lpFunction:Pointer;

pByName:PIMAGE_IMPORT_BY_NAME;

begin

Result:=True;

Offset:=peH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

ifOffset=0then//无导入表

Exit;

pID:=PIMAGE_IMPORT_DESCRIPTOR(Cardinal(pImageBase)+Offset);

while(pID^.Union.Characteristics<>0)do

begin

pRealIAT:=PIMAGE_THUNK_DATAs(Cardinal(pImageBase)+pID^.FirstThunk);

pOriginalIAT:=PIMAGE_THUNK_DATAs(Cardinal(pImageBase)+pID^.Union.OriginalFirstThunk);

//获取DLL名字

PName:=PChar(Cardinal(pImageBase)+pID^.Name);

FillMemory(@Buf,$FF,0);

fori:=0to$FFdo

begin

if(pName[i]=#0)then

break;

buf[i]:=pName[i];

end;

//判断是否加载过,没加载过就加载

hDLL:=GetModuleHandle(Buf);

ifhDLL=0then

hDLL:=LoadLibrary(Buf);

I:=0;

whileTruedo

begin

if(pOriginalIAT[i].Function_=0)thenbreak;

lpFunction:=nil;

if(pOriginalIAT[i].OrdinalandIMAGE_ORDINAL_FLAG<>0)then//按序号

begin

lpFunction:=Windows.GetProcAddress(hDll,PChar(pOriginalIAT[i].Ordinaland$0000FFFF));

end

else//按名字

begin

//获取此IAT项所描述的函数名称

pByName:=PIMAGE_IMPORT_BY_NAME

(DWORD(pImageBase)+DWORD(pOriginalIAT[i].AddressOfData));

lpFunction:=Windows.GetProcAddress(hDll,PChar(@pByName^.Name));

if(lpFunction<>nil)then//找到了!

pRealIAT[i].Function_:=DWORD(lpFunction)

else

begin

Result:=False;

Exit;

end;

end;

Inc(I);

end;

pID:=PIMAGE_IMPORT_DESCRIPTOR(DWORD(pID)+sizeof(IMAGE_IMPORT_DESCRIPTOR));

end;

end;



functionLoadPE(Buf:Pointer;Len:Integer):Cardinal;

var

peH:PImageNtHeaders;//PE头

peSecH:PImageSectionHeaders;

peSz:Cardinal;

P:Pointer;

DLLMain:function(hinstDLL:Cardinal;fdwReason,lpvReserved:DWORD):BOOL;stdcall;

begin

//分配可执行的内存块

ifalignPEToMem(Buf,Len,peH,peSecH,P,peSz)then

begin

ifHasRelocationTable(peH)then//如果有重定位表就进行重定位

DoRelocation(peH,P);

FillImports(peH,P);//填写导入表

//获取并执行动态链接库的入口函数

DLLMain:=Pointer(peH^.OptionalHeader.AddressOfEntryPoint+DWORD(P));

DLLMain(DWORD(P),DLL_PROCESS_ATTACH,0);

Result:=Cardinal(P);

end

else

Result:=0;

end;

procedureFreePE(Handle:Cardinal);

var

dosH:PImageDosHeader;

peH:PImageNtHeaders;//PE头

DLLMain:function(hinstDLL:Cardinal;fdwReason,lpvReserved:DWORD):BOOL;stdcall;

P:Pointer;

begin

P:=Pointer(Handle);

dosH:=PImageDosHeader(P);

peH:=PImageNtHeaders(DWORD(P)+dosH^._lfanew);

DLLMain:=Pointer(peH^.OptionalHeader.AddressOfEntryPoint+DWORD(P));

DLLMain(DWORD(P),DLL_PROCESS_DETACH,0);//反初始化DLL

VirtualFreeEx(GetCurrentProcess(),

P,

0,

MEM_RELEASE);

end;

end.

使用的方式如这个例子.

{

测试DLL

}

libraryDLL;

uses

Windows;

{$R.res}

var

vMsg:String=''abc'';//用一个全局变量可以检查重定位是否正确.重定位不正确一定访问不到这个变量.

procedureA(Msg:PChar);

begin

MessageBox(0,PChar(vMsg+Msg),'''',MB_OK);

end;

exports

A;

begin

end.

在EXE中就可以直接在内存中加载这个DLL了.

var

hDLL:Cardinal;

begin



hDLL:=LoadPE(DLL的数据,DLL数据的大小);

A:=PELoader.GetProcAddress(DWORD(hDLL),''A'');

A(''aa'');

FreePE(hDLL);
献花(0)
+1
(本文系wblib首藏)