{ 卸载原外壳占用内存 }
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;
{ 创建外壳进程并获取其基址、大小和当前运行状态 }
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); // 以挂起方式运行进程
if result then
begin
ProcHnd := pi.hProcess;
ThrdHnd := pi.hThread;
ProcId := pi.dwProcessId;
{ 获取外壳进程运行状态,[ctx.Ebx+8]内存处存的是外壳进程的加载基址,ctx.Eax存放有外壳进程的入口地址 }
ctx.ContextFlags := CONTEXT_FULL;
GetThreadContext(ThrdHnd, ctx);
ReadProcessMemory(ProcHnd, Pointer(ctx.Ebx+8), @BaseAddr, SizeOf(Cardinal), Old); // 读取加载基址
p := Pointer(BaseAddr);
{ 计算外壳进程占有的内存 }
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;
{ 创建外壳进程并用目标进程替换它然后执行 }
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 // 外壳进程可以容纳目标进程并且加载地址一致
begin
p := Pointer(Addr);
VirtualProtectEx(result, p, Size, PAGE_EXECUTE_READWRITE, Old);
end
else if IsNT then // 98 下失败
begin
if UnloadShell(result, Addr) then // 卸载外壳进程占有内存
// 重新按目标进程加载基址和大小分配内存
p := MyVirtualAllocEx(Result, Pointer(peH.OptionalHeader.ImageBase), ImageSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (p = nil) and hasRelocationTable(peH) then // 分配内存失败并且目标进程支持重定向
begin
// 按任意基址分配内存
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); // 重置目标进程运行环境中的基址
peH.OptionalHeader.ImageBase := Cardinal(p);
if WriteProcessMemory(Result, p, Ptr, ImageSize, Old) then // 复制PE数据到目标进程
begin
ctx.ContextFlags := CONTEXT_FULL;
if Cardinal(p) = Addr then
ctx.Eax := peH.OptionalHeader.ImageBase + peH.OptionalHeader.AddressOfEntryPoint // 重置运行环境中的入口地址
else
ctx.Eax := Cardinal(p) + peH.OptionalHeader.AddressOfEntryPoint;
SetThreadContext(Thrd, ctx); // 更新运行环境
ResumeThread(Thrd); // 执行
CloseHandle(Thrd);
end
else begin // 加载失败,杀掉外壳进程
TerminateProcess(Result, 0);
CloseHandle(Thrd);
CloseHandle(Result);
Result := INVALID_HANDLE_VALUE;
end;
end
else begin // 加载失败,杀掉外壳进程
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.
上一页 [1] [2] [3]