从内存中加载并启动一个exe
生活随笔
收集整理的這篇文章主要介紹了
从内存中加载并启动一个exe
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
從內(nèi)存中加載并啟動一個exe
文章作者:Idle_ (阿呆)
信息來源:[url]http://cnxhacker.net/article/show/2821.html[/url]
windows似乎只提供了一種啟動進(jìn)程的方法:即必須從一個可執(zhí)行文件中加載并啟動。
而下面這段代碼就是提供一種可以直接從內(nèi)存中啟動一個exe的變通辦法。
用途嘛, 也許可以用來保護(hù)你的exe,你可以對要保護(hù)的 exe 進(jìn)行任意切分、加密、存儲,只要運行時能將exe的內(nèi)容正確拼接到一塊內(nèi)存中,就可以直接從內(nèi)存中啟動,而不必不安全地去生成一個臨時文件再從臨時文件啟動進(jìn)程。另外這段代碼也提供了一種自己寫exe外殼的簡單途徑,如果能配合其它各種外殼技術(shù)就更好地保護(hù)你的exe文件。
原理很簡單:就是“借尸還魂”,啟動一個僵尸進(jìn)程(NT下可以是自身程序啟動的另一個進(jìn)程),然后在它運行前將其整個替換成內(nèi)存中的exe內(nèi)容,待正式運行后執(zhí)行的就是你的目標(biāo)代碼了。
不過代碼中還有一些不盡人意的地方,比如在98下運行會留一個僵尸程序的殼在硬盤上(其實那個僵尸程序本身就是一個完整的可執(zhí)行程序,直接運行的話只顯示一條錯誤信息然后就退出了)。另外由于客觀條件限制,代碼沒有經(jīng)過充分測試,只在XP下進(jìn)行了一些初步測試:普通exe都能正常運行,upx壓縮過的exe絕大多數(shù)情況下都能運行,只有在不能卸載僵尸外殼時才有問題(upx壓縮過的exe沒有重定向表,無法加載到其它地址運行)。
如果有bug望告之,如果有更好的方法特別是能解決98下的遺留尾巴的話希望不吝賜教。
{ ******************************************************* }
{ * 從內(nèi)存中加載并運行exe * }
{ ******************************************************* }
{ * 參數(shù): }
{ * Buffer: 內(nèi)存中的exe地址 }
{ * Len: 內(nèi)存中exe占用長度 }
{ * CmdParam: 命令行參數(shù)(不包含exe文件名的剩余命令行參數(shù))}
{ * ProcessId: 返回的進(jìn)程Id }
{ * 返回值: 如果成功則返回進(jìn)程的Handle(ProcessHandle), }
{ 如果失敗則返回INVALID_HANDLE_VALUE }
{ ******************************************************* }
unit PEUnit;
interface
uses windows;
function MemExecute(const ABuffer; Len: Integer; CmdParam: string; var ProcessId: Cardinal): Cardinal;
implementation
{$R ExeShell.res} // 外殼程序模板(98下使用)
type
TImageSectionHeaders = array [0..0] of TImageSectionHeader;
PImageSectionHeaders = ^TImageSectionHeaders;
{ 計算對齊后的大小 }
function GetAlignedSize(Origin, Alignment: Cardinal): Cardinal;
begin
result := (Origin + Alignment - 1) div Alignment * Alignment;
end;
{ 計算加載pe并對齊需要占用多少內(nèi)存,未直接使用OptionalHeader.SizeOfImage作為結(jié)果是因為據(jù)說有的編譯器生成的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);
{計算所有節(jié)的大小}
for i := 0 to peH.FileHeader.NumberOfSections - 1 do
if peSecH[i].PointerToRawData + peSecH[i].SizeOfRawData > FileLen then // 超出文件范圍
begin
result := 0;
exit;
end
else if peSecH[i].VirtualAddress <> 0 then //計算對齊后某節(jié)的大小
if peSecH[i].Misc.VirtualSize <> 0 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到內(nèi)存并對齊所有節(jié) }
function AlignPEToMem(const Buf; Len: Integer; var PeH: PImageNtHeaders;
var PeSecH: PImageSectionHeaders; var Mem: Pointer; var ImageSize: Cardinal): Boolean;
var
SrcMz: PImageDosHeader; // DOS頭
SrcPeH: PImageNtHeaders; // PE頭
SrcPeSecH: PImageSectionHeaders; // 節(jié)表
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 <> 0) or
(SrcPeH.FileHeader.Characteristics and IMAGE_FILE_EXECUTABLE_IMAGE = 0)
or (SrcPeH.FileHeader.SizeOfOptionalHeader <> SizeOf(TImageOptionalHeader)) then exit;
SrcPeSecH := Pointer(Integer(SrcPeH)+SizeOf(TImageNtHeaders));
ImageSize := CalcTotalImageSize(SrcMz, Len, SrcPeH, SrcPeSecH);
if ImageSize = 0 then
exit;
Mem := VirtualAlloc(nil, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配內(nèi)存
if Mem <> nil then
begin
// 計算需要復(fù)制的PE頭字節(jié)數(shù)
l := SrcPeH.OptionalHeader.SizeOfHeaders;
for i := 0 to SrcPeH.FileHeader.NumberOfSections - 1 do
if (SrcPeSecH[i].PointerToRawData <> 0) 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 := 0 to PeH.FileHeader.NumberOfSections - 1 do
begin
// 定位該節(jié)在內(nèi)存中的位置
if PeSecH[i].VirtualAddress <> 0 then
Pt := Pointer(Cardinal(Mem) + PeSecH[i].VirtualAddress);
if PeSecH[i].SizeOfRawData <> 0 then
begin
// 復(fù)制數(shù)據(jù)到內(nèi)存
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 定位到下一節(jié)開始位置
end
else
pt := pointer(Cardinal(pt) + GetAlignedSize(PeSecH[i].Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment));
end;
result := True;
end;
end;
type
TVirtualAllocEx = function (hProcess: THandle; lpAddress: Pointer;
dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
var
MyVirtualAllocEx: TVirtualAllocEx = nil;
function IsNT: Boolean;
begin
result := Assigned(MyVirtualAllocEx);
end;
{ 生成外殼程序命令行 }
function PrepareShellExe(CmdParam: string; BaseAddr, ImageSize: Cardinal): string;
var
r, h, sz: Cardinal;
p: Pointer;
fid, l: Integer;
buf: Pointer;
peH: PImageNtHeaders;
peSecH: PImageSectionHeaders;
begin
if IsNT then
{ NT 系統(tǒng)下直接使用自身程序作為外殼進(jìn)程 }
result := ParamStr(0)+CmdParam
else begin
// 由于98系統(tǒng)下無法重新分配外殼進(jìn)程占用內(nèi)存,所以必須保證運行的外殼程序能容納目標(biāo)進(jìn)程并且加載地址一致
// 此處使用的方法是從資源中釋放出一個事先建立好的外殼程序,然后通過修改其PE頭使其運行時能加載到指定地址并至少能容納目標(biāo)進(jìn)程
r := FindResource(HInstance, 'SHELL_EXE', RT_RCDATA);
h := LoadResource(HInstance, r);
p := LockResource(h);
l := SizeOfResource(HInstance, r);
GetMem(Buf, l);
Move(p^, Buf^, l); // 讀到內(nèi)存
FreeResource(h);
peH := Pointer(Integer(Buf) + PImageDosHeader(Buf)._lfanew);
peSecH := Pointer(Integer(peH) + sizeof(TImageNtHeaders));
peH.OptionalHeader.ImageBase := BaseAddr; // 修改PE頭重的加載基址
if peH.OptionalHeader.SizeOfImage < ImageSize then // 目標(biāo)比外殼大,修改外殼程序運行時占用的內(nèi)存
begin
sz := Imagesize - peH.OptionalHeader.SizeOfImage;
Inc(peH.OptionalHeader.SizeOfImage, sz); // 調(diào)整總占用內(nèi)存數(shù)
Inc(peSecH[peH.FileHeader.NumberOfSections-1].Misc.VirtualSize, sz); // 調(diào)整最后一節(jié)占用內(nèi)存數(shù)
end;
// 生成外殼程序文件名, 為本程序改后綴名得到的
// 由于不想 uses SysUtils (一旦 use 了程序?qū)⒃龃?0K左右), 而且偷懶,所以只支持最多運行11個進(jìn)程,后綴名為.dat, .da0~.da9
result := ParamStr(0);
result := copy(result, 1, length(result) - 4) + '.dat';
r := 0;
while r < 10 do
begin
fid := CreateFile(pchar(result), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if fid < 0 then
begin
result := copy(result, 1, length(result)-3)+'da'+Char(r+Byte('0'));
inc(r);
end
else begin
//SetFilePointer(fid, Imagesize, nil, 0);
//SetEndOfFile(fid);
//SetFilePointer(fid, 0, nil, 0);
WriteFile(fid, Buf^, l, h, nil); // 寫入文件
CloseHandle(fid);
break;
end;
end;
result := result + CmdParam; // 生成命令行
FreeMem(Buf);
end;
end;
{ 是否包含可重定向列表 }
function HasRelocationTable(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 = packed record
VirtualAddress: cardinal;
SizeOfBlock: cardinal;
end;
{ 重定向PE用到的地址 }
procedure DoRelocation(peH: PImageNtHeaders; OldBase, NewBase: Pointer);
var
Delta: Cardinal;
p: PImageBaseRelocation;
pw: PWord;
i: Integer;
begin
Delta := Cardinal(NewBase) - peH.OptionalHeader.ImageBase;
p := pointer(cardinal(OldBase) + peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
while (p.VirtualAddress + p.SizeOfBlock <> 0) do
begin
pw := pointer(Integer(p) + Sizeof(p^));
for i := 1 to (p.SizeOfBlock - Sizeof(p^)) div 2 do
begin
if pw^ and $F000 = $3000 then
Inc(PCardinal(Cardinal(OldBase) + p.VirtualAddress + (pw^ and $0FFF))^, Delta);
inc(pw);
end;
p := Pointer(pw);
end;
end;
type
TZwUnmapViewOfSection = function (Handle, BaseAdr: Cardinal): Cardinal; stdcall;
{ 卸載原外殼占用內(nèi)存 }
function UnloadShell(ProcHnd, BaseAddr: Cardinal): Boolean;
var
M: HModule;
ZwUnmapViewOfSection: TZwUnmapViewOfSection;
begin
result := False;
m := LoadLibrary('ntdll.dll');
if m <> 0 then
begin
ZwUnmapViewOfSection := GetProcAddress(m, 'ZwUnmapViewOfSection');
if assigned(ZwUnmapViewOfSection) then
result := (ZwUnmapViewOfSection(ProcHnd, BaseAddr) = 0);
FreeLibrary(m);
end;
end;
{ 創(chuàng)建外殼進(jìn)程并獲取其基址、大小和當(dāng)前運行狀態(tài) }
function CreateChild(Cmd: string; var Ctx: TContext; var ProcHnd, ThrdHnd, ProcId, BaseAddr, ImageSize: Cardinal): Boolean;
var
si: TStartUpInfo;
pi: TProcessInformation;
Old: Cardinal;
MemInfo: TMemoryBasicInformation;
p: Pointer;
begin
FillChar(si, Sizeof(si), 0);
FillChar(pi, SizeOf(pi), 0);
si.cb := sizeof(si);
result := CreateProcess(nil, PChar(Cmd), nil, nil, False, CREATE_SUSPENDED, nil, nil, si, pi); // 以掛起方式運行進(jìn)程
if result then
begin
ProcHnd := pi.hProcess;
ThrdHnd := pi.hThread;
ProcId := pi.dwProcessId;
{ 獲取外殼進(jìn)程運行狀態(tài),[ctx.Ebx+8]內(nèi)存處存的是外殼進(jìn)程的加載基址,ctx.Eax存放有外殼進(jìn)程的入口地址 }
ctx.ContextFlags := CONTEXT_FULL;
GetThreadContext(ThrdHnd, ctx);
ReadProcessMemory(ProcHnd, Pointer(ctx.Ebx+8), @BaseAddr, SizeOf(Cardinal), Old); // 讀取加載基址
p := Pointer(BaseAddr);
{ 計算外殼進(jìn)程占有的內(nèi)存 }
while VirtualQueryEx(ProcHnd, p, MemInfo, Sizeof(MemInfo)) <> 0 do
begin
if MemInfo.State = MEM_FREE then
break;
p := Pointer(Cardinal(p) + MemInfo.RegionSize);
end;
ImageSize := Cardinal(p) - Cardinal(BaseAddr);
end;
end;
{ 創(chuàng)建外殼進(jìn)程并用目標(biāo)進(jìn)程替換它然后執(zhí)行 }
function AttachPE(CmdParam: string; peH: PImageNtHeaders; peSecH: PImageSectionHeaders;
Ptr: Pointer; ImageSize: Cardinal; var ProcId: Cardinal): Cardinal;
var
s: string;
Addr, Size: Cardinal;
ctx: TContext;
Old: Cardinal;
p: Pointer;
Thrd: Cardinal;
begin
result := INVALID_HANDLE_VALUE;
s := PrepareShellExe(CmdParam, peH.OptionalHeader.ImageBase, ImageSize);
if CreateChild(s, ctx, result, Thrd, ProcId, Addr, Size) then
begin
p := nil;
if (peH.OptionalHeader.ImageBase = Addr) and (Size >= ImageSize) then // 外殼進(jìn)程可以容納目標(biāo)進(jìn)程并且加載地址一致
begin
p := Pointer(Addr);
VirtualProtectEx(result, p, Size, PAGE_EXECUTE_READWRITE, Old);
end
else if IsNT then // 98 下失敗
begin
if UnloadShell(result, Addr) then // 卸載外殼進(jìn)程占有內(nèi)存
// 重新按目標(biāo)進(jìn)程加載基址和大小分配內(nèi)存
p := MyVirtualAllocEx(Result, Pointer(peH.OptionalHeader.ImageBase), ImageSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (p = nil) and hasRelocationTable(peH) then // 分配內(nèi)存失敗并且目標(biāo)進(jìn)程支持重定向
begin
// 按任意基址分配內(nèi)存
p := MyVirtualAllocEx(result, nil, ImageSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if p <> nil then
DoRelocation(peH, Ptr, p); // 重定向
end;
end;
if p <> nil then
begin
WriteProcessMemory(Result, Pointer(ctx.Ebx+8), @p, Sizeof(DWORD), Old); // 重置目標(biāo)進(jìn)程運行環(huán)境中的基址
peH.OptionalHeader.ImageBase := Cardinal(p);
if WriteProcessMemory(Result, p, Ptr, ImageSize, Old) then // 復(fù)制PE數(shù)據(jù)到目標(biāo)進(jìn)程
begin
ctx.ContextFlags := CONTEXT_FULL;
if Cardinal(p) = Addr then
ctx.Eax := peH.OptionalHeader.ImageBase + peH.OptionalHeader.AddressOfEntryPoint // 重置運行環(huán)境中的入口地址
else
ctx.Eax := Cardinal(p) + peH.OptionalHeader.AddressOfEntryPoint;
SetThreadContext(Thrd, ctx); // 更新運行環(huán)境
ResumeThread(Thrd); // 執(zhí)行
CloseHandle(Thrd);
end
else begin // 加載失敗,殺掉外殼進(jìn)程
TerminateProcess(Result, 0);
CloseHandle(Thrd);
CloseHandle(Result);
Result := INVALID_HANDLE_VALUE;
end;
end
else begin // 加載失敗,殺掉外殼進(jìn)程
TerminateProcess(Result, 0);
CloseHandle(Thrd);
CloseHandle(Result);
Result := INVALID_HANDLE_VALUE;
end;
end;
end;
function MemExecute(const ABuffer; Len: Integer; CmdParam: string; var ProcessId: Cardinal): Cardinal;
var
peH: PImageNtHeaders;
peSecH: PImageSectionHeaders;
Ptr: Pointer;
peSz: Cardinal;
begin
result := INVALID_HANDLE_VALUE;
if alignPEToMem(ABuffer, Len, peH, peSecH, Ptr, peSz) then
begin
result := AttachPE(CmdParam, peH, peSecH, Ptr, peSz, ProcessId);
VirtualFree(Ptr, peSz, MEM_DECOMMIT);
//VirtualFree(Ptr, 0, MEM_RELEASE);
end;
end;
initialization
MyVirtualAllocEx := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'VirtualAllocEx');
end.
寫了一個簡單程序測試通過:)
program Test;
//{$APPTYPE CONSOLE}
uses
SysUtils,
Classes,
PEUnit in 'PEUnit.pas';
var
ABuffer: array of byte;
Stream: TFileStream;
ProcessId: Cardinal;
begin
Stream:=TFileStream.Create('Target.exe', fmOpenRead);
try
SetLength(ABuffer, Stream.Size);
Stream.ReadBuffer(ABuffer[0], Stream.Size);
MemExecute(ABuffer[0], Stream.Size, '', ProcessId);
finally
Stream.Free;
end;
end.
文章作者:Idle_ (阿呆)
信息來源:[url]http://cnxhacker.net/article/show/2821.html[/url]
windows似乎只提供了一種啟動進(jìn)程的方法:即必須從一個可執(zhí)行文件中加載并啟動。
而下面這段代碼就是提供一種可以直接從內(nèi)存中啟動一個exe的變通辦法。
用途嘛, 也許可以用來保護(hù)你的exe,你可以對要保護(hù)的 exe 進(jìn)行任意切分、加密、存儲,只要運行時能將exe的內(nèi)容正確拼接到一塊內(nèi)存中,就可以直接從內(nèi)存中啟動,而不必不安全地去生成一個臨時文件再從臨時文件啟動進(jìn)程。另外這段代碼也提供了一種自己寫exe外殼的簡單途徑,如果能配合其它各種外殼技術(shù)就更好地保護(hù)你的exe文件。
原理很簡單:就是“借尸還魂”,啟動一個僵尸進(jìn)程(NT下可以是自身程序啟動的另一個進(jìn)程),然后在它運行前將其整個替換成內(nèi)存中的exe內(nèi)容,待正式運行后執(zhí)行的就是你的目標(biāo)代碼了。
不過代碼中還有一些不盡人意的地方,比如在98下運行會留一個僵尸程序的殼在硬盤上(其實那個僵尸程序本身就是一個完整的可執(zhí)行程序,直接運行的話只顯示一條錯誤信息然后就退出了)。另外由于客觀條件限制,代碼沒有經(jīng)過充分測試,只在XP下進(jìn)行了一些初步測試:普通exe都能正常運行,upx壓縮過的exe絕大多數(shù)情況下都能運行,只有在不能卸載僵尸外殼時才有問題(upx壓縮過的exe沒有重定向表,無法加載到其它地址運行)。
如果有bug望告之,如果有更好的方法特別是能解決98下的遺留尾巴的話希望不吝賜教。
{ ******************************************************* }
{ * 從內(nèi)存中加載并運行exe * }
{ ******************************************************* }
{ * 參數(shù): }
{ * Buffer: 內(nèi)存中的exe地址 }
{ * Len: 內(nèi)存中exe占用長度 }
{ * CmdParam: 命令行參數(shù)(不包含exe文件名的剩余命令行參數(shù))}
{ * ProcessId: 返回的進(jìn)程Id }
{ * 返回值: 如果成功則返回進(jìn)程的Handle(ProcessHandle), }
{ 如果失敗則返回INVALID_HANDLE_VALUE }
{ ******************************************************* }
unit PEUnit;
interface
uses windows;
function MemExecute(const ABuffer; Len: Integer; CmdParam: string; var ProcessId: Cardinal): Cardinal;
implementation
{$R ExeShell.res} // 外殼程序模板(98下使用)
type
TImageSectionHeaders = array [0..0] of TImageSectionHeader;
PImageSectionHeaders = ^TImageSectionHeaders;
{ 計算對齊后的大小 }
function GetAlignedSize(Origin, Alignment: Cardinal): Cardinal;
begin
result := (Origin + Alignment - 1) div Alignment * Alignment;
end;
{ 計算加載pe并對齊需要占用多少內(nèi)存,未直接使用OptionalHeader.SizeOfImage作為結(jié)果是因為據(jù)說有的編譯器生成的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);
{計算所有節(jié)的大小}
for i := 0 to peH.FileHeader.NumberOfSections - 1 do
if peSecH[i].PointerToRawData + peSecH[i].SizeOfRawData > FileLen then // 超出文件范圍
begin
result := 0;
exit;
end
else if peSecH[i].VirtualAddress <> 0 then //計算對齊后某節(jié)的大小
if peSecH[i].Misc.VirtualSize <> 0 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到內(nèi)存并對齊所有節(jié) }
function AlignPEToMem(const Buf; Len: Integer; var PeH: PImageNtHeaders;
var PeSecH: PImageSectionHeaders; var Mem: Pointer; var ImageSize: Cardinal): Boolean;
var
SrcMz: PImageDosHeader; // DOS頭
SrcPeH: PImageNtHeaders; // PE頭
SrcPeSecH: PImageSectionHeaders; // 節(jié)表
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 <> 0) or
(SrcPeH.FileHeader.Characteristics and IMAGE_FILE_EXECUTABLE_IMAGE = 0)
or (SrcPeH.FileHeader.SizeOfOptionalHeader <> SizeOf(TImageOptionalHeader)) then exit;
SrcPeSecH := Pointer(Integer(SrcPeH)+SizeOf(TImageNtHeaders));
ImageSize := CalcTotalImageSize(SrcMz, Len, SrcPeH, SrcPeSecH);
if ImageSize = 0 then
exit;
Mem := VirtualAlloc(nil, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配內(nèi)存
if Mem <> nil then
begin
// 計算需要復(fù)制的PE頭字節(jié)數(shù)
l := SrcPeH.OptionalHeader.SizeOfHeaders;
for i := 0 to SrcPeH.FileHeader.NumberOfSections - 1 do
if (SrcPeSecH[i].PointerToRawData <> 0) 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 := 0 to PeH.FileHeader.NumberOfSections - 1 do
begin
// 定位該節(jié)在內(nèi)存中的位置
if PeSecH[i].VirtualAddress <> 0 then
Pt := Pointer(Cardinal(Mem) + PeSecH[i].VirtualAddress);
if PeSecH[i].SizeOfRawData <> 0 then
begin
// 復(fù)制數(shù)據(jù)到內(nèi)存
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 定位到下一節(jié)開始位置
end
else
pt := pointer(Cardinal(pt) + GetAlignedSize(PeSecH[i].Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment));
end;
result := True;
end;
end;
type
TVirtualAllocEx = function (hProcess: THandle; lpAddress: Pointer;
dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
var
MyVirtualAllocEx: TVirtualAllocEx = nil;
function IsNT: Boolean;
begin
result := Assigned(MyVirtualAllocEx);
end;
{ 生成外殼程序命令行 }
function PrepareShellExe(CmdParam: string; BaseAddr, ImageSize: Cardinal): string;
var
r, h, sz: Cardinal;
p: Pointer;
fid, l: Integer;
buf: Pointer;
peH: PImageNtHeaders;
peSecH: PImageSectionHeaders;
begin
if IsNT then
{ NT 系統(tǒng)下直接使用自身程序作為外殼進(jìn)程 }
result := ParamStr(0)+CmdParam
else begin
// 由于98系統(tǒng)下無法重新分配外殼進(jìn)程占用內(nèi)存,所以必須保證運行的外殼程序能容納目標(biāo)進(jìn)程并且加載地址一致
// 此處使用的方法是從資源中釋放出一個事先建立好的外殼程序,然后通過修改其PE頭使其運行時能加載到指定地址并至少能容納目標(biāo)進(jìn)程
r := FindResource(HInstance, 'SHELL_EXE', RT_RCDATA);
h := LoadResource(HInstance, r);
p := LockResource(h);
l := SizeOfResource(HInstance, r);
GetMem(Buf, l);
Move(p^, Buf^, l); // 讀到內(nèi)存
FreeResource(h);
peH := Pointer(Integer(Buf) + PImageDosHeader(Buf)._lfanew);
peSecH := Pointer(Integer(peH) + sizeof(TImageNtHeaders));
peH.OptionalHeader.ImageBase := BaseAddr; // 修改PE頭重的加載基址
if peH.OptionalHeader.SizeOfImage < ImageSize then // 目標(biāo)比外殼大,修改外殼程序運行時占用的內(nèi)存
begin
sz := Imagesize - peH.OptionalHeader.SizeOfImage;
Inc(peH.OptionalHeader.SizeOfImage, sz); // 調(diào)整總占用內(nèi)存數(shù)
Inc(peSecH[peH.FileHeader.NumberOfSections-1].Misc.VirtualSize, sz); // 調(diào)整最后一節(jié)占用內(nèi)存數(shù)
end;
// 生成外殼程序文件名, 為本程序改后綴名得到的
// 由于不想 uses SysUtils (一旦 use 了程序?qū)⒃龃?0K左右), 而且偷懶,所以只支持最多運行11個進(jìn)程,后綴名為.dat, .da0~.da9
result := ParamStr(0);
result := copy(result, 1, length(result) - 4) + '.dat';
r := 0;
while r < 10 do
begin
fid := CreateFile(pchar(result), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if fid < 0 then
begin
result := copy(result, 1, length(result)-3)+'da'+Char(r+Byte('0'));
inc(r);
end
else begin
//SetFilePointer(fid, Imagesize, nil, 0);
//SetEndOfFile(fid);
//SetFilePointer(fid, 0, nil, 0);
WriteFile(fid, Buf^, l, h, nil); // 寫入文件
CloseHandle(fid);
break;
end;
end;
result := result + CmdParam; // 生成命令行
FreeMem(Buf);
end;
end;
{ 是否包含可重定向列表 }
function HasRelocationTable(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 = packed record
VirtualAddress: cardinal;
SizeOfBlock: cardinal;
end;
{ 重定向PE用到的地址 }
procedure DoRelocation(peH: PImageNtHeaders; OldBase, NewBase: Pointer);
var
Delta: Cardinal;
p: PImageBaseRelocation;
pw: PWord;
i: Integer;
begin
Delta := Cardinal(NewBase) - peH.OptionalHeader.ImageBase;
p := pointer(cardinal(OldBase) + peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
while (p.VirtualAddress + p.SizeOfBlock <> 0) do
begin
pw := pointer(Integer(p) + Sizeof(p^));
for i := 1 to (p.SizeOfBlock - Sizeof(p^)) div 2 do
begin
if pw^ and $F000 = $3000 then
Inc(PCardinal(Cardinal(OldBase) + p.VirtualAddress + (pw^ and $0FFF))^, Delta);
inc(pw);
end;
p := Pointer(pw);
end;
end;
type
TZwUnmapViewOfSection = function (Handle, BaseAdr: Cardinal): Cardinal; stdcall;
{ 卸載原外殼占用內(nèi)存 }
function UnloadShell(ProcHnd, BaseAddr: Cardinal): Boolean;
var
M: HModule;
ZwUnmapViewOfSection: TZwUnmapViewOfSection;
begin
result := False;
m := LoadLibrary('ntdll.dll');
if m <> 0 then
begin
ZwUnmapViewOfSection := GetProcAddress(m, 'ZwUnmapViewOfSection');
if assigned(ZwUnmapViewOfSection) then
result := (ZwUnmapViewOfSection(ProcHnd, BaseAddr) = 0);
FreeLibrary(m);
end;
end;
{ 創(chuàng)建外殼進(jìn)程并獲取其基址、大小和當(dāng)前運行狀態(tài) }
function CreateChild(Cmd: string; var Ctx: TContext; var ProcHnd, ThrdHnd, ProcId, BaseAddr, ImageSize: Cardinal): Boolean;
var
si: TStartUpInfo;
pi: TProcessInformation;
Old: Cardinal;
MemInfo: TMemoryBasicInformation;
p: Pointer;
begin
FillChar(si, Sizeof(si), 0);
FillChar(pi, SizeOf(pi), 0);
si.cb := sizeof(si);
result := CreateProcess(nil, PChar(Cmd), nil, nil, False, CREATE_SUSPENDED, nil, nil, si, pi); // 以掛起方式運行進(jìn)程
if result then
begin
ProcHnd := pi.hProcess;
ThrdHnd := pi.hThread;
ProcId := pi.dwProcessId;
{ 獲取外殼進(jìn)程運行狀態(tài),[ctx.Ebx+8]內(nèi)存處存的是外殼進(jìn)程的加載基址,ctx.Eax存放有外殼進(jìn)程的入口地址 }
ctx.ContextFlags := CONTEXT_FULL;
GetThreadContext(ThrdHnd, ctx);
ReadProcessMemory(ProcHnd, Pointer(ctx.Ebx+8), @BaseAddr, SizeOf(Cardinal), Old); // 讀取加載基址
p := Pointer(BaseAddr);
{ 計算外殼進(jìn)程占有的內(nèi)存 }
while VirtualQueryEx(ProcHnd, p, MemInfo, Sizeof(MemInfo)) <> 0 do
begin
if MemInfo.State = MEM_FREE then
break;
p := Pointer(Cardinal(p) + MemInfo.RegionSize);
end;
ImageSize := Cardinal(p) - Cardinal(BaseAddr);
end;
end;
{ 創(chuàng)建外殼進(jìn)程并用目標(biāo)進(jìn)程替換它然后執(zhí)行 }
function AttachPE(CmdParam: string; peH: PImageNtHeaders; peSecH: PImageSectionHeaders;
Ptr: Pointer; ImageSize: Cardinal; var ProcId: Cardinal): Cardinal;
var
s: string;
Addr, Size: Cardinal;
ctx: TContext;
Old: Cardinal;
p: Pointer;
Thrd: Cardinal;
begin
result := INVALID_HANDLE_VALUE;
s := PrepareShellExe(CmdParam, peH.OptionalHeader.ImageBase, ImageSize);
if CreateChild(s, ctx, result, Thrd, ProcId, Addr, Size) then
begin
p := nil;
if (peH.OptionalHeader.ImageBase = Addr) and (Size >= ImageSize) then // 外殼進(jìn)程可以容納目標(biāo)進(jìn)程并且加載地址一致
begin
p := Pointer(Addr);
VirtualProtectEx(result, p, Size, PAGE_EXECUTE_READWRITE, Old);
end
else if IsNT then // 98 下失敗
begin
if UnloadShell(result, Addr) then // 卸載外殼進(jìn)程占有內(nèi)存
// 重新按目標(biāo)進(jìn)程加載基址和大小分配內(nèi)存
p := MyVirtualAllocEx(Result, Pointer(peH.OptionalHeader.ImageBase), ImageSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (p = nil) and hasRelocationTable(peH) then // 分配內(nèi)存失敗并且目標(biāo)進(jìn)程支持重定向
begin
// 按任意基址分配內(nèi)存
p := MyVirtualAllocEx(result, nil, ImageSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if p <> nil then
DoRelocation(peH, Ptr, p); // 重定向
end;
end;
if p <> nil then
begin
WriteProcessMemory(Result, Pointer(ctx.Ebx+8), @p, Sizeof(DWORD), Old); // 重置目標(biāo)進(jìn)程運行環(huán)境中的基址
peH.OptionalHeader.ImageBase := Cardinal(p);
if WriteProcessMemory(Result, p, Ptr, ImageSize, Old) then // 復(fù)制PE數(shù)據(jù)到目標(biāo)進(jìn)程
begin
ctx.ContextFlags := CONTEXT_FULL;
if Cardinal(p) = Addr then
ctx.Eax := peH.OptionalHeader.ImageBase + peH.OptionalHeader.AddressOfEntryPoint // 重置運行環(huán)境中的入口地址
else
ctx.Eax := Cardinal(p) + peH.OptionalHeader.AddressOfEntryPoint;
SetThreadContext(Thrd, ctx); // 更新運行環(huán)境
ResumeThread(Thrd); // 執(zhí)行
CloseHandle(Thrd);
end
else begin // 加載失敗,殺掉外殼進(jìn)程
TerminateProcess(Result, 0);
CloseHandle(Thrd);
CloseHandle(Result);
Result := INVALID_HANDLE_VALUE;
end;
end
else begin // 加載失敗,殺掉外殼進(jìn)程
TerminateProcess(Result, 0);
CloseHandle(Thrd);
CloseHandle(Result);
Result := INVALID_HANDLE_VALUE;
end;
end;
end;
function MemExecute(const ABuffer; Len: Integer; CmdParam: string; var ProcessId: Cardinal): Cardinal;
var
peH: PImageNtHeaders;
peSecH: PImageSectionHeaders;
Ptr: Pointer;
peSz: Cardinal;
begin
result := INVALID_HANDLE_VALUE;
if alignPEToMem(ABuffer, Len, peH, peSecH, Ptr, peSz) then
begin
result := AttachPE(CmdParam, peH, peSecH, Ptr, peSz, ProcessId);
VirtualFree(Ptr, peSz, MEM_DECOMMIT);
//VirtualFree(Ptr, 0, MEM_RELEASE);
end;
end;
initialization
MyVirtualAllocEx := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'VirtualAllocEx');
end.
寫了一個簡單程序測試通過:)
program Test;
//{$APPTYPE CONSOLE}
uses
SysUtils,
Classes,
PEUnit in 'PEUnit.pas';
var
ABuffer: array of byte;
Stream: TFileStream;
ProcessId: Cardinal;
begin
Stream:=TFileStream.Create('Target.exe', fmOpenRead);
try
SetLength(ABuffer, Stream.Size);
Stream.ReadBuffer(ABuffer[0], Stream.Size);
MemExecute(ABuffer[0], Stream.Size, '', ProcessId);
finally
Stream.Free;
end;
end.
總結(jié)
以上是生活随笔為你收集整理的从内存中加载并启动一个exe的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果员工出逃现象严重:人才挽留成大难题
- 下一篇: 身上的WZSZF01重担开始减轻