花了一個下午翻了MSDN,寫了這個例子,為了安全,我用Delphi建了個什麼也沒有作的程式prjzzhost.exe,將它用作被注入的宿主進程.
寫了一個TestDll.Dll,裡面只有一個Log函數,用來在檔案Test.Txt中輸出資訊.最重要的一個程式project1.exe是用來注入的.
測試環境: windows server 2003 + delphi 7.0
程式很簡單,高手就不用看了.廢話不說了,看代碼吧!
測試用的TestDll.Dll原始碼(它將被注入到prjzzhost.exe中去): 程式碼library TestDll;
uses
SysUtils,
System,
windows,
Classes;
procedure Log( s : PChar);stdcall;
var
F : TextFile;
begin
assignfile(f,'Test.txt');
if fileexists('Test.txt') then append(f)
else rewrite(f);
writeln(f,s);
closefile(f);
end;
procedure DllEntryPoint(dwReason:DWord);
begin
case dwReason of
DLL_PROCESS_ATTACH:
Log('dll process Attach');
DLL_PROCESS_DETACH:
Log('dll process Detach');
DLL_THREAD_ATTACH:
Log('dll thread Attach');
DLL_THREAD_DETACH:
Log('dll thread Detach');
end;
end;
exports
Log;
begin
DllProc := @DllEntryPoint;
DllEntryPoint(DLL_PROCESS_ATTACH);
end.
被注入的宿主進程prjzzhost.exe(它什麼也沒有作,好無辜哦:),這裡就不給出代碼了,因為太簡單了,哈哈.
最後,最重要的來了:
project1.exe的原始碼: 程式碼unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,tlhelp32;
type
TLog = procedure(s : PChar);stdcall;
TServiceMain = procedure(argc : Integer; VAR argv : pchar);stdcall;
EDLLLoadError = class(Exception);
TForm1 = class(TForm)
Button3: TButton;
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ 列舉進程 }
procedure GetMyProcessID(const AFilename: string; const PathMatch: Boolean; var ProcessID: DWORD);
var
lppe: TProcessEntry32;
SsHandle: Thandle;
FoundAProc, FoundOK: boolean;
begin
ProcessID :=0;
{ 建立系統快照 }
SsHandle := CreateToolHelp32SnapShot(TH32CS_SnapProcess, 0);
{ 取得快照中的第一個進程 }
{ 一定要設定結構的大小,否則將返回False }
lppe.dwSize := sizeof(TProcessEntry32);
FoundAProc := Process32First(Sshandle, lppe);
while FoundAProc do
begin
{ 進行匹配 }
if PathMatch then
FoundOK := AnsiStricomp(lppe.szExefile, PChar(AFilename)) = 0
else
FoundOK := AnsiStricomp(PChar(ExtractFilename(lppe.szExefile)), PChar(ExtractFilename(AFilename))) = 0;
if FoundOK then
begin
ProcessID := lppe.th32ProcessID;
break;
end;
{ 未找到,繼續下一個進程 }
FoundAProc := Process32Next(SsHandle, lppe);
end;
CloseHandle(SsHandle);
end;
{ 設定許可權 }
function EnabledDebugPrivilege(const Enabled : Boolean) : Boolean;
var
hTk : THandle; { 開啟令牌控制代碼 }
rtnTemp : Dword; { 調整許可權時返回的值 }
TokenPri : TOKEN_PRIVILEGES;
const
SE_DEBUG = 'SeDebugPrivilege'; { 查詢值 }
begin
Result := False;
{ 擷取進程令牌控制代碼,設定許可權 }
if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,hTk)) then
begin
TokenPri.PrivilegeCount := 1;
{ 擷取Luid值 }
LookupPrivilegeValue(nil,SE_DEBUG,TokenPri.Privileges[0].Luid);
if Enabled then
TokenPri.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
else
TokenPri.Privileges[0].Attributes := 0;
rtnTemp := 0;
{ 設定新的許可權 }
AdjustTokenPrivileges(hTk,False,TokenPri,sizeof(TokenPri),nil,rtnTemp);
Result := GetLastError = ERROR_SUCCESS;
CloseHandle(hTk);
end;
end;
{ 調試函數 }
procedure OutPutText(var CH:PChar);
var
FileHandle: TextFile;
Begin
AssignFile(FileHandle,'zztest.txt');
Append(FileHandle);
Writeln(FileHandle,CH);
Flush(FileHandle);
CloseFile(FileHandle);
END;
{ 注入遠程進程 }
function InjectTo(const Host, Guest: string; const PID: DWORD = 0): DWORD;
var
{ 被注入的進程控制代碼,進程ID}
hRemoteProcess: THandle;
dwRemoteProcessId: DWORD;
{ 寫入遠程進程的內容大小 }
memSize: DWORD;
{ 寫入到遠程進程後的地址 }
pszLibFileRemote: Pointer;
iReturnCode: Boolean;
TempVar: DWORD;
{ 指向函數LoadLibraryW的地址 }
pfnStartAddr: TFNThreadStartRoutine;
{ dll全路徑,需要寫到遠程進程的記憶體中去 }
pszLibAFilename: PwideChar;
begin
Result := 0;
{ 設定許可權 }
EnabledDebugPrivilege(True);
{ 為注入的dll檔案路徑分配記憶體大小,由於為WideChar,故要乘2 }
Getmem(pszLibAFilename, Length(Guest) * 2 + 1);
StringToWideChar(Guest, pszLibAFilename, Length(Guest) * 2 + 1);
{ 擷取進程ID }
if PID > 0 then
dwRemoteProcessID := PID
else
GetMyProcessID(Host, False, dwRemoteProcessID);
{ 取得遠程進程控制代碼,具有寫入許可權}
hRemoteProcess := OpenProcess(PROCESS_CREATE_THREAD + {允許遠程建立線程}
PROCESS_VM_OPERATION + {允許遠程VM操作}
PROCESS_VM_WRITE, {允許遠程VM寫}
FALSE, dwRemoteProcessId);
{ 用函數VirtualAllocex在遠程進程分配空間,並用WriteProcessMemory中寫入dll路徑 }
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);
if iReturnCode then
begin
pfnStartAddr := GetProcAddress(GetModuleHandle('Kernel32'), 'LoadLibraryW');
TempVar := 0;
{ 在遠程進程中啟動dll }
Result := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, TempVar);
end;
{ 釋放記憶體空間 }
Freemem(pszLibAFilename);
end;
{ 測試 }
procedure TForm1.Button3Click(Sender: TObject);
begin
InjectTo('prjzzhost.exe', extractfilepath(paramstr(0))+'TestDll.dll');
end;
end.
代碼中並沒有考慮dll被載入後的善後處理,請不要使用系統進程進行測試,以免發生意外.