unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, PsAPI, TlHelp32;

type
  TfMain = class(TForm)
    btnCreateRemoteThread: TButton;
    procedure btnCreateRemoteThreadClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var fMain: TfMain;

const DLLName = 'AttachLib.dll';

implementation

{$R *.dfm}

//  Отримання відлагоджувальних привілеїв для процесу
function SetDebugPriv: Boolean;
var
  Token: THandle;
  tkp: TTokenPrivileges;
  ReturnLength: DWORD;
begin
  Result := false;
  if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, Token) then
  begin
    if LookupPrivilegeValue(nil, PChar('SeDebugPrivilege'), tkp.Privileges[0].Luid) then
    begin
      tkp.PrivilegeCount := 1;
      tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
      Result := AdjustTokenPrivileges(Token, false, tkp, 0, nil, ReturnLength);
    end;
  end;
end;

procedure TfMain.btnCreateRemoteThreadClick(Sender: TObject);
var PIDs : array [1..200] of DWORD;
    PIDsNeed  : DWORD;
    I         : DWORD;
    hProcess  : THandle;
    hThread   : THandle;
    ModName   : PChar;
    ThreadID  : DWORD;
    Found     : boolean;
    DllPath   : string;
    DllPathA  : PWideChar;
    FarDLLName  : pointer;
    BytesWriten     : DWORD;
    LoadLibraryAddr : FarProc;
begin
//  if not SetDebugPriv then begin
//    ShowMessage('Неможливо отримати потрібні привілеї!');
//    Exit;
//  end;

  Found := false;
  ModName := AllocMem(MAX_PATH + 1);
  FillMemory(@PIDs, SizeOf(PIDs), 0);
  EnumProcesses(@PIDs, SizeOf(PIDs), PIDsNeed);

  // Шукаємо та відкриваємо процес "Блокнота"
  for I := 1 to PIDsNeed div SizeOf(DWORD) do begin
    // Відкриваємо процес з максимально можливими привілеями доступу
    hProcess := OpenProcess(MAXIMUM_ALLOWED, false, PIDs[I]);
    if hProcess = null then Continue;
    if GetModuleBaseName(hProcess, 0, ModName, MAX_PATH) > 0 then
      if Lowercase(ModName) = 'notepad.exe' then begin
        Found := true;
        Break;
      end;
    CloseHandle(hProcess);
  end;
  FreeMem(ModName);

  if not Found then Exit;

  // Шлях до DLL, яка буде завантажена в адресний простір
  // "Блокнота" (вона має бути в папці програми)
  DllPath := ExtractFilePath(ParamStr(0)) + DLLName;
  DLLPathA := PChar(DllPath);
  // Виділяємо пам'ять в адресному просторі "Блокнота",
  // необхідну для запису назви DLL, та отримуємо
  // вказівник на неї
  FarDLLName := VirtualAllocEx(hProcess, nil, Length(DllPathA),
    MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
  if FarDLLName = nil then Exit;
  // Записуємо повну назву DLL в адресний простір "Блокнота"
  // Так як викликатимемо LoadLibraryW, яка використовує двобайтове
  // кодування (Unicode), довжину рядка множимо на 2
  // ** На різних версіях Delphi за замовчуванням можуть застосовуватися
  // ** різні кодування - ANSI чи Unicode - тому слід буде задати потрібну
  // ** довжину та потрібну функцію виклику - LoadLibraryW чи LoadLibraryA
  if not WriteProcessMemory(hProcess, FarDLLName, DllPathA,
    Length(DllPath)*2, BytesWriten) then Exit;
  // Отримуємо адресу функції LoadLibraryW із Kernel32.dll,
  // завантаженої в адресний простір даного процесу
  LoadLibraryAddr := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'LoadLibraryW');
  if LoadLibraryAddr = nil then Exit;
  // Запускаємо віддалений потік, який завантажить нашу DLL в
  // адресний простір "Блокнота". При цьому автоматично спрацює
  // ініціалізаційний код DLL, який і виконає потрібні нам дії
  // (створить новий потік для "Блокнота")
  hThread := CreateRemoteThread(hProcess, nil, 0, LoadLibraryAddr, FarDLLName, 0, ThreadID);
  if hThread = 0 then Exit;

  // Вивільняємо попередньо зарезервовані ресурси
  if FarDLLName <> nil then VirtualFreeEx(hProcess, @FarDLLName, 0, MEM_RELEASE);
  CloseHandle(hThread);
  CloseHandle(hProcess);
end;

end.
