View Single Post
  #6  
Old 12-12-2006, 06:54 AM
Kovachev Kovachev is offline
Member
 
Join Date: Mar 2001
Posts: 33
Default RE: Get the param string of an exe

The only way I found some times ago when I was dealing with such a problem was code injection + remote code execution.
It is very simple to do that. The following code is tested on win NT, 2000 and XP with 32bit executables and it could retrieve the command line even on windows system processes. The code is tested for memory leaks and it seems to be safe.


Code:
program CmdLineDump;
{$APPTYPE CONSOLE}
{$IMAGEBASE $13140000}

uses
  Windows, SysUtils, TLHelp32;

const
  SE_DEBUG_NAME = 'SeDebugPrivilege' ;
  NL = #10 + #13; // new line + carrier return

type
  // GCLW abbreviation stands for GetCommandLineW
  // windows API located in kernel32.dll
  tGCLW = function(): PWideChar; stdcall;

  // The data structure to be injected in remote process
  // it contains the pointer to GCLW API and the pointer
  // for the result buffer.
  PInjectData = ^TInjectData;
  TInjectData = record
    pGCLW: Pointer;
    Buff: Pointer;
  end;

// The code to be injected and executed in
// the remote process
function InjectedCode(Param: PInjectData): LongWord; stdcall;
begin
  Param^.Buff := tGCLW(Param^.pGCLW);

  for Result := 0 to 512 do
    if PWideChar(Param^.Buff)[Result] = #0 then Break;
end;

// Code injection routine
procedure InjectToTarget(hProcess: THandle; EntryPoint: Pointer; CodeSize: Integer);
var
  InitData: TInjectData;
  InitDataAddr, WriteAddr: Pointer;
  ThreadHandle: THandle;
  BytesWritten, TheadID: DWORD;

  CmdLineLength: LongWord;
  CmdLineBuff: PWideChar;
  CmdLineSize: LongWord;
begin
  // Get the address there GCLW API is loaded in memory
  InitData.pGCLW := GetProcAddress(LoadLibrary('kernel32.dll'), 'GetCommandLineW');

  // Allocate memory in the memory space of the remote process
// and copy InitData there
  InitDataAddr := VirtualAllocEx(hProcess, nil, SizeOf(InitData), MEM_COMMIT, PAGE_READWRITE);
  if (InitDataAddr <> nil) then begin
    WriteProcessMemory(hProcess, InitDataAddr, (@InitData), SizeOf(InitData), BytesWritten);
  end;

  // Allocate memory for the injected function (injected code)
  // and copy it there
  WriteAddr := VirtualAllocEx(hProcess, nil, CodeSize, MEM_COMMIT, PAGE_READWRITE);
  if (WriteAddr <> nil) then begin
    WriteProcessMemory(hProcess, WriteAddr, EntryPoint, CodeSize, BytesWritten);

    // The following lines checks if we have written something
    // in the remote process and if yes it executes
    // our code and waits for result
    // then copies back the InitData from remote memory
    // to be able to retrive the command line
    // and finally frees the allocated memory
    if BytesWritten = CodeSize then begin
      ThreadHandle := CreateRemoteThread(hProcess, nil, 0, WriteAddr, InitDataAddr, 0, TheadID);
      WaitForSingleObject(ThreadHandle, INFINITE);
      GetExitCodeThread(ThreadHandle, CmdLineLength);

      CmdLineSize := SizeOf(WideChar)*(CmdLineLength+1);
      GetMem(CmdLineBuff, CmdLineSize);

      ReadProcessMemory(hProcess, InitDataAddr, (@InitData), SizeOf(InitData), BytesWritten);
      ReadProcessMemory(hProcess, InitData.Buff, CmdLineBuff, CmdLineSize, BytesWritten);
      Writeln('Commend line (',CmdLineLength,') = ',WideString(CmdLineBuff));

      VirtualFreeEx(hProcess, WriteAddr, 0, MEM_RELEASE);
    end;
  end;

  VirtualFreeEx(hProcess, InitDataAddr,  0, MEM_RELEASE);
end;

// This routine asks the system for DebugPrivileges for
// our program so it could "debug" other programs
// i.e. reasd/write to their memory and so on ...
procedure GetDebugPrivileges;
var
  hToken: THandle;
  tkp: TTokenPrivileges;
  retval: dword;
begin
  if not OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then begin
    Writeln('ERROR! OPT() failed, GLE()='+IntToStr(GetLastError())+' SeDebugPrivilege is not available.');
    Readln; Exit;
  end;

  if not LookupPrivilegeValue(nil, SE_DEBUG_NAME, tkp.Privileges[0].Luid) then begin
    Writeln('ERROR! LPV() failed, GLE()='+IntToStr(GetLastError())+' SeDebugPrivilege is not available.');
    Readln; Exit;
  end;

  tkp.PrivilegeCount := 1;
  tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;

  if not AdjustTokenPrivileges(hToken, false, tkp, 0, nil, retval) then begin
    Writeln('ERROR! ATP() failed, GLE()='+IntToStr(GetLastError())+' SeDebugPrivilege is not available.');
    Readln; Exit;
  end;

  CloseHandle(hToken);
end;

var
  ProcessHandle: THandle;
  PID: Cardinal;

// This routine retrieves all running process and returns
// their process id (PID)
procedure EnumerateProcesses;
var
  H: THandle;
  R: Boolean;
  PE: TProcessEntry32;
begin
  Writeln;
  Writeln(Format('%6s | %s', ['PID', 'Process']));

  H := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if H <> INVALID_HANDLE_VALUE then begin
    PE.dwSize := SizeOf(PE);
    R := Process32First(H, PE);
    while R do begin
      Writeln(Format('%6d | %s', [PE.th32ProcessID, PE.szExeFile]));
      R := Process32Next(H, PE);
    end;
    CloseHandle(H);
  end else begin
    Writeln('ERROR! CT32S() failed, GLE()='+IntToStr(GetLastError())+'.');
    Readln; Exit;
  end;
  Writeln;
end;

begin
  Writeln;
  Writeln('+----------------------------------------+');
  Writeln('| Process enumeration and code injection |');
  Writeln('|           2004, St. Kovachev           |');
  Writeln('+----------------------------------------+');
  Writeln;

  if ParamCount = 0 then begin
    Writeln('Usage :');
    Writeln('To get process command line: CmdLineDump.exe [pid]');
    Writeln('To view proceses list: CmdLineDump.exe -list');
    Writeln; Exit;
  end;

  if LowerCase(ParamStr(1)) = '-list' then begin
    EnumerateProcesses;
    Writeln; Exit;
  end;

  Write('Get debuger privileges . . . ');
  GetDebugPrivileges;
  Writeln('done!'+NL);

  PID := StrToInt(ParamStr(1));

  Writeln(Format('Injecting code @ process %d . . .', [PID]));
  ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
  if ProcessHandle = INVALID_HANDLE_VALUE then begin
    Writeln(Format('ERROR! OP(%d) failed, GLE()=%d', [PID, GetLastError()]));
    Readln; Exit;
  end;
  InjectToTarget(ProcessHandle, @InjectedCode, 1000);
  CloseHandle(ProcessHandle);

  Writeln;
end.
St. Kovachev
Reply With Quote