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

花了一天时间做出来.效果还可以.

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

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

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

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

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

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

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

{
内存加载DLL
wr960204
}
unit PELoader; interface
uses
Windows; function LoadPE(Buf: Pointer; Len: Integer): Cardinal;
procedure FreePE(Handle: Cardinal);
function GetProcAddress(Module: Cardinal; ProcessName: PChar): Pointer; implementation const
IMAGE_ORDINAL_FLAG = DWORD($); function GetProcAddress(Module: Cardinal; ProcessName: PChar): Pointer;
function strcmp(p1, p2: PChar): boolean;
begin
Result := False;
while (p1^ = p2^) do
begin
if (P1^ = #) or (P2^ = #) 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 := ;
Address := ;
repeat
ExportName := Pointer(Cardinal(Pointer(Cardinal(ImageExportDirectory.AddressOfNames) + Module + J * )^) + Module);
if strcmp(ExportName, ProcessName) then
Address := Cardinal(Pointer(Word(Pointer(J shl + Cardinal(
ImageExportDirectory.AddressOfNameOrdinals) + Module)^) and
$0000FFFF shl + Cardinal(ImageExportDirectory.AddressOfFunctions)
+ Module)^) + Module;
Inc(J);
until (Address <> ) or (J = ImageExportDirectory.NumberOfNames);
Result := Pointer(Address);
end; type
TImageSectionHeaders = array[..] of TImageSectionHeader;
PImageSectionHeaders = ^TImageSectionHeaders; TIIDUnion = record
case Integer of
: (Characteristics: DWORD);
: (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
case Integer of
: (ForwarderString: DWORD);
: (Function_: DWORD);
: (Ordinal: DWORD);
: (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[..] of Byte;
end;
IMAGE_IMPORT_BY_NAME = _IMAGE_IMPORT_BY_NAME;
TImageImportByName = IMAGE_IMPORT_BY_NAME;
PImageImportByName = PIMAGE_IMPORT_BY_NAME;
{ 计算对齐后的大小 } function GetAlignedSize(Origin, Alignment: Cardinal): Cardinal;
begin
result := (Origin + Alignment - ) div Alignment * Alignment;
end; { 计算加载pe并对齐需要占用多少内存,未直接使用OptionalHeader.SizeOfImage作为结果是因为据说有的编译器生成的exe这个值会填0 } function CalcTotalImageSize(MzH: PImageDosHeader; FileLen: Cardinal; peH: PImageNtHeaders;
peSecH: PImageSectionHeaders): Cardinal;
var
i : Integer;
begin
{计算pe头的大小}
result := GetAlignedSize(PeH.OptionalHeader.SizeOfHeaders, PeH.OptionalHeader.SectionAlignment); {计算所有节的大小}
for i := to peH.FileHeader.NumberOfSections - do
if peSecH[i].PointerToRawData + peSecH[i].SizeOfRawData > FileLen then // 超出文件范围
begin
result := ;
exit;
end
else if peSecH[i].VirtualAddress <> then //计算对齐后某节的大小
if peSecH[i].Misc.VirtualSize <> then
result := GetAlignedSize(peSecH[i].VirtualAddress + peSecH[i].Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment)
else
result := GetAlignedSize(peSecH[i].VirtualAddress + peSecH[i].SizeOfRawData, PeH.OptionalHeader.SectionAlignment)
else if peSecH[i].Misc.VirtualSize < peSecH[i].SizeOfRawData then
result := result + GetAlignedSize(peSecH[i].SizeOfRawData, peH.OptionalHeader.SectionAlignment)
else
result := result + GetAlignedSize(peSecH[i].Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment); end; { 加载pe到内存并对齐所有节 } function AlignPEToMem(const Buf: Pointer; Len: Integer; var PeH: PImageNtHeaders;
var PeSecH: PImageSectionHeaders; var Mem: Pointer; var ImageSize: Cardinal): Boolean;
var
SrcMz : PImageDosHeader; // DOS头
SrcPeH : PImageNtHeaders; // PE头
SrcPeSecH : PImageSectionHeaders; // 节表
i : Integer;
l : Cardinal;
Pt : Pointer;
begin
result := false;
SrcMz := Buf;
if Len < sizeof(TImageDosHeader) then exit;
if SrcMz.e_magic <> IMAGE_DOS_SIGNATURE then exit;
if Len < SrcMz._lfanew + Sizeof(TImageNtHeaders) then exit;
SrcPeH := pointer(Integer(SrcMz) + SrcMz._lfanew);
if (SrcPeH.Signature <> IMAGE_NT_SIGNATURE) then exit;
if (SrcPeH.FileHeader.Characteristics and IMAGE_FILE_DLL = ) //不是DLL,
or (SrcPeH.FileHeader.Characteristics and IMAGE_FILE_EXECUTABLE_IMAGE = ) //不可执行
or (SrcPeH.FileHeader.SizeOfOptionalHeader <> SizeOf(TImageOptionalHeader)) then exit;
SrcPeSecH := Pointer(Integer(SrcPeH) + SizeOf(TImageNtHeaders));
ImageSize := CalcTotalImageSize(SrcMz, Len, SrcPeH, SrcPeSecH);
if ImageSize = then
exit;
Mem := VirtualAlloc(nil, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配一块可以执行,可以读写的内存
if Mem <> nil then
begin
// 计算需要复制的PE头
l := SrcPeH.OptionalHeader.SizeOfHeaders;
for i := to SrcPeH.FileHeader.NumberOfSections - do
if (SrcPeSecH[i].PointerToRawData <> ) and (SrcPeSecH[i].PointerToRawData < l) then
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));
for i := to PeH.FileHeader.NumberOfSections - do
begin
// 定位该节在内存中的位置
if PeSecH[i].VirtualAddress <> then
Pt := Pointer(Cardinal(Mem) + PeSecH[i].VirtualAddress); if PeSecH[i].SizeOfRawData <> then
begin
// 复制数据到内存
Move(Pointer(Cardinal(SrcMz) + PeSecH[i].PointerToRawData)^, pt^, PeSecH[i].SizeOfRawData);
if peSecH[i].Misc.VirtualSize < peSecH[i].SizeOfRawData then
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; { 是否包含可重定向列表 } function HasRelocationTable(peH: PImageNtHeaders): Boolean;
begin
result := (peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> )
and (peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size <> );
end; type
PImageBaseRelocation = ^TImageBaseRelocation;
TImageBaseRelocation = packed record
VirtualAddress: cardinal;
SizeOfBlock: cardinal;
end; { 重定向PE用到的地址 } procedure DoRelocation(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 <> ) do
begin
pw := pointer(Integer(p) + Sizeof(TImageBaseRelocation));
for i := to (p.SizeOfBlock - Sizeof(TImageBaseRelocation)) div sizeof(WORD) do
begin
if pw^ and $F000 = $ then
Inc(PCardinal(Cardinal(NewBase) + p.VirtualAddress + (pw^ and $0FFF))^, Delta);
inc(pw);
end;
p := PImageBaseRelocation(pw);
end;
end; {填充引入地址表} function FillImports(peH: PImageNtHeaders; pImageBase: Pointer): BOOL;
type
TIMAGE_THUNK_DATAs = array[..] of IMAGE_THUNK_DATA;
PIMAGE_THUNK_DATAs = ^TIMAGE_THUNK_DATAs;
var
Offset : Cardinal;
pID : PIMAGE_IMPORT_DESCRIPTOR;
pRealIAT, pOriginalIAT: PIMAGE_THUNK_DATAs;
buf : array[..$FF] of char;
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;
if Offset = then //无导入表
Exit;
pID := PIMAGE_IMPORT_DESCRIPTOR(Cardinal(pImageBase) + Offset);
while (pID^.Union.Characteristics <> ) 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, );
for i := to $FF do
begin
if (pName[i] = #) then
break;
buf[i] := pName[i];
end;
//判断是否加载过,没加载过就加载
hDLL := GetModuleHandle(Buf);
if hDLL = then
hDLL := LoadLibrary(Buf);
I := ;
while True do
begin
if (pOriginalIAT[i].Function_ = ) then break;
lpFunction := nil;
if (pOriginalIAT[i].Ordinal and IMAGE_ORDINAL_FLAG <> ) then //按序号
begin
lpFunction := Windows.GetProcAddress(hDll, PChar(pOriginalIAT[i].Ordinal and $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; function LoadPE(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
//分配可执行的内存块
if alignPEToMem(Buf, Len, peH, peSecH, P, peSz) then
begin
if HasRelocationTable(peH) then //如果有重定位表就进行重定位
DoRelocation(peH, P);
FillImports(peH, P); //填写导入表
//获取并执行动态链接库的入口函数
DLLMain := Pointer(peH^.OptionalHeader.AddressOfEntryPoint + DWORD(P));
DLLMain(DWORD(P), DLL_PROCESS_ATTACH, );
Result := Cardinal(P);
end
else
Result := ; end; procedure FreePE(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, ); //反初始化DLL
VirtualFreeEx(GetCurrentProcess(),
P,
,
MEM_RELEASE);
end; end. 使用的方式如这个例子. {
测试DLL
}
library DLL; uses
Windows; {$R *.res}
var
vMsg : String = 'abc';//用一个全局变量可以检查重定位是否正确.重定位不正确一定访问不到这个变量. procedure A(Msg : PChar);
begin
MessageBox(, 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);
end;

在内存中加载DLL的更多相关文章

  1. 从内存中加载DLL Delphi版(转)

    源:从内存中加载DLL DELPHI版 原文 : http://www.2ccc.com/article.asp?articleid=5784 MemLibrary.pas //从内存中加载DLL D ...

  2. 从内存中加载DLL DELPHI版

    //从内存中加载DLL DELPHI版 unit MemLibrary; interface uses Windows; function memLoadLibrary(pLib: Pointer): ...

  3. 内存中加载DLL DELPHI版

    //从内存中加载DLL DELPHI版 unit MemLibrary; interface uses Windows; function memLoadLibrary(pLib: Pointer): ...

  4. 从内存中加载并启动一个exe

    windows似乎只提供了一种启动进程的方法:即必须从一个可执行文件中加载并启动.而下面这段代码就是提供一种可以直接从内存中启动一个exe的变通办法.用途嘛, 也许可以用来保护你的exe,你可以对要保 ...

  5. 如何在uboot上实现从网络下载版本镜像并直接在内存中加载之?

    这是作者近期项目上遇到的一个需求,描述如下: 一块MT7620N的路由器单板,Flash中已存放一个版本并可以通过uboot正常加载并启动.现在需要:在uboot上电启动过程中,通过外部按键触发干涉, ...

  6. 内存加载DLL

    1.前言 目前很多敏感和重要的DLL(Dynamic-link library) 都没有提供静态版本供编译器进行静态连接(.lib文件),即使提供了静态版本也因为兼容性问题导致无法使用,而只提供DLL ...

  7. C# 实现动态加载DLL插件 及HRESULT:0x80131047处理

    本代码实现DLL的动态加载, 类似PS里的滤镜插件! 1. 建立一个接口项目类库,此处名称为:Test.IPlugin using System; namespace Test.IPlugin { p ...

  8. java 加载dll介绍(转)

    最近在做的工作要用到本地方法,需要在Java中加载不少动态链接库(以下为方便延用Windows平台下的简写dll,但并不局限于Windows).刚刚把程序跑通,赶紧把一些心得写出来,mark.也希望对 ...

  9. c#实现动态加载Dll(转)

    c#实现动态加载Dll 分类: .net2009-12-28 13:54 3652人阅读 评论(1) 收藏 举报 dllc#assemblynullexceptionclass 原理如下: 1.利用反 ...

随机推荐

  1. RabbitMQ(2) 一般介绍

    RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用. 消息中间件在互联网公司的使用中越来越多,刚才还看到新闻阿里将RocketMQ捐献给了apa ...

  2. vue 日历组件只显示本月和下个月 -- 多选日期

    效果就是上图 代码贴出 1.在components > calander > index.vue <template> <div class="page&quo ...

  3. HTML 点击图片放大

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. python学习(七)

  5. 『Python CoolBook』数据结构和算法_多变量赋值&“*”的两种用法

    多变量赋值 a = [1,2,(3,4)] b,c,d = a print(b,c,d) b,c,(d,e) = a print(b,c,d,e) 1 2 (3, 4) 1 2 3 4 a = &qu ...

  6. Oracle 监听器日志配置与管理

    十一假期间,某客户因为监听日志问题导致系统登录挂起,当时在返京的路上,因客户业务不允许中断,无奈之下,借了个本子帮客户做了紧急处理,今天恰好有空,在网上搜了下有关监听日志的内容,发现一个不错的帖子,内 ...

  7. STL 小白学习(2) string

    #include <iostream> using namespace std; #include <string> //初始化操作 void test01() { //初始化 ...

  8. Vue(五) 购物车案例

    这一篇把之前所学的内容做一个总结,实现一个购物车样例,可以增加或者减少购买数量,可移除商品,并且购物车总价随着你的操作实时变化. 实现效果如图: 代码: <!DOCTYPE html> & ...

  9. Vim 安装、配置及复制粘贴操作

    1.安装:sudo apt-get install vim 2.配置:cd ~ #进入用户主目录 touch .vimrc #.后缀文件不可见 vi .vimrc #打开文件 输入: set cind ...

  10. spoj periodni

    题解: dp 方程弄出来就好做了 代码: #include<bits/stdc++.h> ,M=; typedef int arr[N]; typedef long long ll; in ...