I spent an afternoon flipping through MSDN and wrote this example. For safety, I built a program called PRjzzhost.exe that did nothing with Delphi and used it as the host process being injected.
I wrote a TestDll.Dll, which only has a Log function, which is used to output information in the file Test.Txt. The most important program, project1.exe, is used to inject.
Test environment: windowsserver2003+delphi7.0
The program is very simple, so experts don’t need to read it. Don’t say nonsense, just read the code!
TestDll.Dll source code for test (it will be injected into prjzzhost.exe):
Program codelibraryTestDll;
uses
SysUtils,
System,
windows,
Classes;
procedureLog(s:PChar);stdcall;
var
F:TextFile;
Begin
assignfile(f,'Test.txt');
iffileexists('Test.txt')thenappend(f)
elserewrite(f);
writeeln(f,s);
closefile(f);
end;
procedureDllEntryPoint(dwReason:DWord);
Begin
casedwReasonof
DLL_PROCESS_ATTACH:
Log('dllprocessAttach');
DLL_PROCESS_DETACH:
Log('dllprocessDetach');
DLL_THREAD_ATTACH:
Log('dllthreadAttach');
DLL_THREAD_DETACH:
Log('dllthreadDetach');
end;
end;
exports
Log;
Begin
DllProc:=@DllEntryPoint;
DllEntryPoint(DLL_PROCESS_ATTACH);
end.
The injected host process prjzzhost.exe (it does nothing, it's so innocent:), I won't give the code here because it's too simple, haha.
Finally, the most important thing is:
Source code of project1.exe:
Program codeunitUnit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, tlhelp32;
type
TLog=procedure(s:PChar);stdcall;
TServiceMain=procedure(argc:Integer;VARargv:pchar);stdcall;
EDLLLoadError=class(Exception);
TForm1=class(TForm)
Button3:TButton;
procedureButton3Click(Sender:TObject);
Private
{Privatedeclarations}
public
{Publicdeclarations}
end;
var
Form1:TForm1;
Implementation
{$R*.dfm}
{Enrollment of processes}
procedureGetMyProcessID(constAFilename:string;constPathMatch:Boolean;varProcessID:DWORD);
var
lppe:TProcessEntry32;
SsHandle:Thandle;
FoundAProc, FoundOK:boolean;
Begin
ProcessID:=0;
{Create a system snapshot}
SsHandle:=CreateToolHelp32SnapShot(TH32CS_SnapProcess,0);
{Get the first process in the snapshot}
{Be sure to set the size of the structure, otherwise False will be returned}
lppe.dwSize:=sizeof(TProcessEntry32);
FoundAProc:=Process32First(Sshandle,lppe);
whileFoundAProcdo
Begin
{make a match}
ifPathMatchthen
FoundOK:=AnsiStricomp(lppe.szExefile,PChar(AFilename))=0
else
FoundOK:=AnsiStricomp(PChar(ExtractFilename(lppe.szExefile)),PChar(ExtractFilename(AFilename)))=0;
ifFoundOKthen
Begin
ProcessID:=lppe.th32ProcessID;
break;
end;
{Not found, continue to the next process}
FoundAProc:=Process32Next(SsHandle,lppe);
end;
CloseHandle(SsHandle);
end;
{Set permissions}
functionEnabledDebugPrivilege(constEnabled:Boolean):Boolean;
var
hTk:THandle;{Open token handle}
rtnTemp:Dword;{value returned when adjusting permissions}
TokenPri:TOKEN_PRIVILEGES;
const
SE_DEBUG='SeDebugPrivilege';{Query value}
Begin
Result:=False;
{get process token handle, set permissions}
if(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,hTk))then
Begin
TokenPri.PrivilegeCount:=1;
{get Luid value}
LookupPrivilegeValue(nil,SE_DEBUG,TokenPri.Privileges[0].Luid);
ifEnabledthen
TokenPri.Privileges[0].Attributes:=SE_PRIVILEGE_ENABLED
else
TokenPri.Privileges[0].Attributes:=0;
rtnTemp:=0;
{Set new permissions}
AdjustTokenPrivileges(hTk, False,TokenPri,sizeof(TokenPri),nil,rtnTemp);
Result:=GetLastError=ERROR_SUCCESS;
CloseHandle(hTk);
end;
end;
{Debug function}
procedureOutPutText(varCH:PChar);
var
FileHandle:TextFile;
Begin
AssignFile(FileHandle,'zztest.txt');
Append(FileHandle);
Writeln(FileHandle,CH);
Flush(FileHandle);
CloseFile(FileHandle);
END;
{Inject remote process}
functionInjectTo(constHost,Guest:string;constPID:DWORD=0):DWORD;
var
{Injected process handle, process ID}
hRemoteProcess:THandle;
dwRemoteProcessId:DWORD;
{Size of content written to remote process}
memSize:DWORD;
{Address after writing to remote process}
pszLibFileRemote:Pointer;
iReturnCode:Boolean;
TempVar:DWORD;
{Point to the address of the function LoadLibraryW}
pfnStartAddr:TFNThreadStartRoutine;
{Dll full path, need to be written to the remote process's memory}
pszLibAFilename:PwideChar;
Begin
Results:=0;
{Set permissions}
EnabledDebugPrivilege(True);
{Allocate memory size for the injected dll file path, because it is WideChar, it must be multiplied by 2}
Getmem(pszLibAFilename,Length(Guest)*2+1);
StringToWideChar(Guest, pszLibAFilename,Length(Guest)*2+1);
{get process ID}
ifPID>0then
dwRemoteProcessID:=PID
else
GetMyProcessID(Host, False, dwRemoteProcessID);
{Get remote process handle, have write permission}
hRemoteProcess:=OpenProcess(PROCESS_CREATE_THREAD+{Allow remote creation of threads}
PROCESS_VM_OperaTION+{Allow remote VM operations}
PROCESS_VM_WRITE,{Allow remote VM writing}
FALSE,dwRemoteProcessId);
{Use the function VirtualAllocex to allocate space in the remote process, and write the dll path in WriteProcessMemory}
memSize:=(1+lstrlenW(pszLibAFilename))*sizeof(WCHAR);
pszLibFileRemote:=PWIDESTRING(VirtualAllocEx(hRemoteProcess,nil,memSize,MEM_COMMIT,PAGE_READWRITE));
TempVar:=0;
iReturnCode:=WriteProcessMemory(hRemoteProcess,pszLibFileRemote,pszLibAFilename,memSize,TempVar);
ifiReturnCodethen
Begin
pfnStartAddr:=GetProcAddress(GetModuleHandle('Kernel32'),'LoadLibraryW');
TempVar:=0;
{Start dll in remote process}
Result:=CreateRemoteThread(hRemoteProcess,nil,0,pfnStartAddr,pszLibFileRemote,0,TempVar);
end;
{free memory space}
Freemem(pszLibAFilename);
end;
{test}
procedureTForm1.Button3Click(Sender:TObject);
Begin
InjectTo('prjzzhost.exe',extractfilepath(paramstr(0))+'TestDll.dll');
end;
end.
The code does not consider the aftermath of the dll being loaded. Please do not use the system process for testing to avoid accidents.