Solved

Get Command Line of Any Process

Posted on 2006-11-16
50
956 Views
Last Modified: 2008-01-09
How can I get the command line of a process like Process Explorer does? Thanks
0
Comment
Question by:orangutang
  • 22
  • 16
  • 11
  • +1
50 Comments
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17962308
Add a listbox named "list1" to a form:

Put This Code In Declarations:


Private Const TH32CS_SNAPHEAPLIST As Long = &H1
Private Const TH32CS_SNAPPROCESS As Long = &H2
Private Const TH32CS_SNAPTHREAD As Long = &H4
Private Const TH32CS_SNAPMODULE As Long = &H8
Private Const TH32CS_INHERIT As Long = &H80000000
Private Const TH32CS_SNAPALL As Long = (TH32CS_SNAPHEAPLIST Or TH32CS_SNAPPROCESS Or TH32CS_SNAPTHREAD Or TH32CS_SNAPMODULE)

Private Const MAX_MODULE_NAME32 As Long = 255 + 1
Private Const MAX_PATH As Long = 255

Private Const PROCESS_ALL_ACCESS = &H1F0FFF
Private Type PROCESSENTRY32
    lngSize As Long
    lngCntUsage As Long
    lngTh32ProcessID As Long
    lngTh32DefaultHeapID As Long
    lngTh32ModuleID As Long
    lngCntThreads As Long
    lngTh32ParentProcessID As Long
    lngPriClassBase As Long
    lngFlags As Long
    strExeFile As String * MAX_PATH
End Type

Private Type MODULEENTRY32
    lngSize As Long
    lngModuleID As Long
    lngProcessID As Long
    lngGlobalCntUsage As Long
    lngProCntUsage As Long
    lngModBaseAddr As Long
    lngModeBaseSize As Long
    lngHModule As Long
    strModule As String * MAX_MODULE_NAME32
    strExePath As String * MAX_PATH
End Type

Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal lngFlags As Long, ByVal lngProcessID As Long) As Long
Private Declare Function Process32First Lib "kernel32" (ByVal hSnapShot As Long, hProcessEntry32 As PROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapShot As Long, hProcessEntry32 As PROCESSENTRY32) As Long
Private Declare Function Module32First Lib "kernel32" (ByVal hSnapShot As Long, hModuleEntry32 As MODULEENTRY32) As Long
Private Declare Function Module32Next Lib "kernel32" (ByVal hSnapShot As Long, hModuleEntry32 As MODULEENTRY32) As Long




Here is the function:

Public Sub loadProcesses()
On Error Resume Next
Dim lngProcessSnap As Long, lngPriorityClass As Long
Dim boolGotModule As Boolean, hProcess As Long
Dim peProcessEntry As PROCESSENTRY32
Dim meModuleEntry As MODULEENTRY32

lngProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0)

If (lngProcessSnap = INVALID_HANDLE_VALUE) Then
 Exit Sub
End If

peProcessEntry.lngSize = LenB(peProcessEntry)

 If (Process32First(lngProcessSnap, peProcessEntry)) Then
    Do
     List1.AddItem LCase(Left(peProcessEntry.strExeFile, InStr(peProcessEntry.strExeFile, Chr(0)) - 1))
    Loop While Process32Next(lngProcessSnap, peProcessEntry)
 End If
CloseHandle lngProcessSnap
End Sub




To call function:

Private Sub Form_Load()

Call loadProcesses

End Sub





If this isnt what you're looking for, please let me know.

Brian
0
 
LVL 22

Author Comment

by:orangutang
ID: 17962318
But does it get the command line of the process?
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17962326
What do you mean command line? That gives the executable file name of the running processes.
0
 
LVL 22

Author Comment

by:orangutang
ID: 17962337
It just gets the names of the processes. If you don't know what I want, download http://download.sysinternals.com/Files/ProcessExplorer.zip. Double click a process, click the Image tab, and you'll see a command line box.
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17962372
Thats what my code will do.
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17962375
Look at the other members of the PROCESSENTRY32 structure.

Brian
0
 
LVL 22

Author Comment

by:orangutang
ID: 17962644
Maybe, open Process Explorer and double click one of the svchost.exe processes. It should have something like " -k DcomLaunch" at the end of the .exe string.
0
 
LVL 29

Assisted Solution

by:nffvrxqgrcfqvvc
nffvrxqgrcfqvvc earned 50 total points
ID: 17963984
Interesting question, I don't know of any other way but to directly access the memory location of the process using ReadProcessMemory. I will write an example and see how it works out.
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17967803
@egl1044, The location of command line parameters are stored in EAX when the program loads, so depending on the programming language the program was written in, the final pointer to the command line parameters should very...this is my intution on the matter.
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17967813
>>The location of command line parameters are stored in EAX when the program loads

I'm acctually not sure about that, I forget acctually what register contains the pointer to the command line params.

brian
0
 
LVL 19

Assisted Solution

by:BrianGEFF719
BrianGEFF719 earned 50 total points
ID: 17967821
I'm gonna look through the source code of ProcessExplorer and see what they do.
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17967828
nm, its no longer open source :(
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17968418
I'll have a working example for you in 30 minutes.
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17969244
I'm trying to conver the follow c++ code for you:
http://www.codeproject.com/threads/CmdLine.asp

However, Its giving me a hard time, ill let you know
0
 
LVL 27

Accepted Solution

by:
Ark earned 400 total points
ID: 17970256
Hi
Way 1 - simple (using WMI)

Private Sub Form_Load()
   On Error Resume Next
   With ListView1
      .View = lvwReport
      .ColumnHeaders.Clear
      .ColumnHeaders.Add , , "Process executable", 3000
      .ColumnHeaders.Add , , "Command line", 9000
      .ListItems.Clear
      .FullRowSelect = True
   End With
   Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & "." & "\root\cimv2")
   Set colProcesses = objWMIService.ExecQuery("Select * from Win32_Process")
   For Each objProcess In colProcesses
       ListView1.ListItems.Add(, , objProcess.ExecutablePath).SubItems(1) = objProcess.commandline
   Next
End Sub

Private Sub Form_Resize()
   ListView1.Move 0, 0, ScaleWidth, ScaleHeight
   On Error Resume Next
   ListView1.ColumnHeaders(2).Width = ScaleWidth - ListView1.ColumnHeaders(1).Width - 360
End Sub

0
 
LVL 22

Author Comment

by:orangutang
ID: 17970350
I'm just curious, will that work in Windows 95/98/ME?
0
 
LVL 27

Expert Comment

by:Ark
ID: 17970375
You can install WMI on those machines, see http://www.microsoft.com/downloads/details.aspx?FamilyID=98a4c5ba-337b-4e92-8c18-a63847760ea5&DisplayLang=en

BTW, here is a hard way - injecting API call into remote process (note: work on NT platforms only)

'=======module code====
Option Explicit

Private Const TH32CS_SNAPHEAPLIST As Long = &H1
Private Const TH32CS_SNAPPROCESS As Long = &H2
Private Const TH32CS_SNAPTHREAD As Long = &H4
Private Const TH32CS_SNAPMODULE As Long = &H8
Private Const TH32CS_INHERIT As Long = &H80000000
Private Const TH32CS_SNAPALL As Long = (TH32CS_SNAPHEAPLIST Or TH32CS_SNAPPROCESS Or TH32CS_SNAPTHREAD Or TH32CS_SNAPMODULE)

Private Const MAX_MODULE_NAME32 As Long = 255 + 1
Private Const MAX_PATH As Long = 255

Private Const PROCESS_ALL_ACCESS = &H1F0FFF
Private Type PROCESSENTRY32
    lngSize As Long
    lngCntUsage As Long
    lngTh32ProcessID As Long
    lngTh32DefaultHeapID As Long
    lngTh32ModuleID As Long
    lngCntThreads As Long
    lngTh32ParentProcessID As Long
    lngPriClassBase As Long
    lngFlags As Long
    strExeFile As String * MAX_PATH
End Type

Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal lngFlags As Long, ByVal lngProcessID As Long) As Long
Private Declare Function Process32First Lib "kernel32" (ByVal hSnapShot As Long, hProcessEntry32 As PROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapShot As Long, hProcessEntry32 As PROCESSENTRY32) As Long
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function CreateRemoteThread Lib "kernel32.dll" (ByVal hProcess As Long, ByVal lpThreadAttributes As Long, ByVal dwStackSize As Long, lpStartAddress As Long, ByVal lpParameter As Long, ByVal dwCreationFlags As Long, lpThreadId As Long) As Long
Private Declare Function GetExitCodeThread Lib "kernel32.dll" (ByVal hThread As Long, lpExitCode As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long

Public Sub EnumProcesses(lv As Object)
   On Error Resume Next
   Dim lngProcessSnap As Long, lngPriorityClass As Long
   Dim hKernel As Long, fnCmdLine As Long
   Dim hProcess As Long, pid As Long, lngAddress As Long, lngWritten As Long
   Dim peProcessEntry As PROCESSENTRY32
   Dim li As Object
   Dim s As String, s1 As String
   
   lngProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0)
   lv.View = lvwReport
   lv.ColumnHeaders.Clear
   lv.ColumnHeaders.Add , , "Process name", 3000
   lv.ColumnHeaders.Add , , "Command line", 9000
   lv.ListItems.Clear
   s = String(256, 0)
   If (lngProcessSnap = -1) Then Exit Sub
   peProcessEntry.lngSize = LenB(peProcessEntry)
   hKernel = GetModuleHandle("kernel32")
   If hKernel = 0 Then Exit Sub
   fnCmdLine = GetProcAddress(hKernel, "GetCommandLineA")
   If fnCmdLine = 0 Then Exit Sub
   If (Process32First(lngProcessSnap, peProcessEntry)) Then
       Do
          pid = peProcessEntry.lngTh32ProcessID
          Set li = lv.ListItems.Add(, "pid_" & pid, (Left(peProcessEntry.strExeFile, InStr(peProcessEntry.strExeFile, Chr(0)) - 1)))
          hProcess = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
          lngAddress = CommandLineAddress(hProcess, fnCmdLine)
          If lngAddress <> 0 Then
             ReadProcessMemory hProcess, ByVal lngAddress, ByVal s, Len(s), lngWritten
             s1 = Left(s, lngWritten)
          Else
             s1 = "No command line"
          End If
          li.SubItems(1) = s1
          CloseHandle hProcess
       Loop While Process32Next(lngProcessSnap, peProcessEntry)
   End If
   CloseHandle lngProcessSnap
End Sub

Private Function CommandLineAddress(ByVal hProcess As Long, ByVal fnAddress As Long) As Long
   Dim hThread As Long, fnAddr As Long, ThreadID As Long, lngTemp As Long, h As Long
   hThread = CreateRemoteThread(hProcess, 0, 0, ByVal fnAddress, lngTemp, 0&, ThreadID)
   Dim ret As Long
   If hThread Then
      ret = WaitForSingleObject(hThread, 1000)
      If ret = 0 Then ret = GetExitCodeThread(hThread, h)
      CommandLineAddress = h
      CloseHandle hThread
   End If
End Function

'============Form code=======
'Add ListView on form
Option Explicit
Private Sub Form_Load()
   Caption = "Remote Process command lines"
   EnumProcesses ListView1
End Sub

Private Sub Form_Resize()
   ListView1.Move 0, 0, ScaleWidth, ScaleHeight
   On Error Resume Next
   ListView1.ColumnHeaders(2).Width = ScaleWidth - ListView1.ColumnHeaders(1).Width - 360
End Sub

0
 
LVL 22

Author Comment

by:orangutang
ID: 17970378
And do you know of a way to read the entire memory of a process and search for a string and replace it? I've seen quite a few of them but none of them ever really worked.
0
 
LVL 27

Expert Comment

by:Ark
ID: 17970381
0
 
LVL 22

Author Comment

by:orangutang
ID: 17970386
Wow, you're a genius Ark.
0
 
LVL 27

Expert Comment

by:Ark
ID: 17970389
I was :)
But now still Sage according to new EE level's rules (former genius started fro 500,000 points) :)
0
 
LVL 22

Author Comment

by:orangutang
ID: 17970395
WOW! You're Arkadiy Olovyannikov?! I think I tried contacting you before to ask for permission to use your registry searcher but you didn't respond or you're email address wasn't valid.
0
 
LVL 27

Expert Comment

by:Ark
ID: 17970402
U'r free to use ANY of my code published at public domains
my new e-mail address is ark@msun.ru
0
 
LVL 22

Author Comment

by:orangutang
ID: 17970405
But I'm planning on selling some simple software that uses you're code. I'll email you the details. Thanks for the code and good try egl1044 and BrianGEFF719. :)
0
 
LVL 27

Expert Comment

by:Ark
ID: 17970410
>>But I'm planning on selling some simple software that uses you're code<<
Wish you success :)
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 22

Author Comment

by:orangutang
ID: 17970422
Oops. I mean "your". Anyway, I sent you the details.
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17974372
Btw, I'm still working on getting that C++ code converted since there is NOT A SINGLE visual basic example that does that. Once i'm done i'll post it on PSC with a link in this thread.
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17974376
>>there is NOT A SINGLE visual basic example that does that

I ment to say, API example.
0
 
LVL 27

Expert Comment

by:Ark
ID: 17976322
>>Btw, I'm still working on getting that C++ code <<

Here is implementation:

'=====mPrivileges.bas=========
Option Explicit

Private Const SE_DEBUG_NAME = "SeDebugPrivilege"
Private Const SE_PRIVILEGE_ENABLED = &H2
Private Const ANYSIZE_ARRAY = 1
Private Const TOKEN_ADJUST_PRIVILEGES = &H20
Private Const TOKEN_QUERY = &H8

Private Type LARGE_INTEGER
  LowPart As Long
  HighPart As Long
End Type

Private Type LUID_AND_ATTRIBUTES
  pLuid As LARGE_INTEGER
  Attributes As Long
End Type

Private Type TOKEN_PRIVILEGES
  PrivilegeCount As Long
  Privileges(ANYSIZE_ARRAY) As LUID_AND_ATTRIBUTES
End Type

Private Declare Function OpenProcessToken Lib "advapi32.dll" ( _
  ByVal ProcessHandle As Long, _
  ByVal DesiredAccess As Long, _
  TokenHandle As Long) As Long

Private Declare Function LookupPrivilegeValue Lib "advapi32.dll" _
  Alias "LookupPrivilegeValueA" ( _
  ByVal lpSystemName As String, _
  ByVal lpName As String, _
  lpLuid As LARGE_INTEGER) As Long

Private Declare Function AdjustTokenPrivileges Lib "advapi32" ( _
  ByVal TokenHandle As Long, _
  ByVal DisableAllPrivileges As Long, _
  ByRef NewState As TOKEN_PRIVILEGES, _
  ByVal BufferLength As Long, _
  ByRef PreviousState As Any, _
  ByRef ReturnLength As Any) As Long

Private Declare Function GetCurrentProcess Lib "kernel32" () As Long

Public Function EnableDebugPrivNT() As Boolean
  Dim hToken As Long
  Dim li As LARGE_INTEGER
  Dim tkp As TOKEN_PRIVILEGES
 
  If OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES _
                      Or TOKEN_QUERY, hToken) = 0 Then Exit Function
 
  If LookupPrivilegeValue("", SE_DEBUG_NAME, li) = 0 Then Exit Function
 
  tkp.PrivilegeCount = 1
  tkp.Privileges(0).pLuid = li
  tkp.Privileges(0).Attributes = SE_PRIVILEGE_ENABLED
 
  EnableDebugPrivNT = AdjustTokenPrivileges(hToken, False, tkp, 0, ByVal 0&, 0)
End Function

'=====mEnumProcesses.bas====
Option Explicit

Private Const TH32CS_SNAPHEAPLIST As Long = &H1
Private Const TH32CS_SNAPPROCESS As Long = &H2
Private Const TH32CS_SNAPTHREAD As Long = &H4
Private Const TH32CS_SNAPMODULE As Long = &H8
Private Const TH32CS_INHERIT As Long = &H80000000
Private Const TH32CS_SNAPALL As Long = (TH32CS_SNAPHEAPLIST Or TH32CS_SNAPPROCESS Or TH32CS_SNAPTHREAD Or TH32CS_SNAPMODULE)

Private Const MAX_MODULE_NAME32 As Long = 255 + 1
Private Const MAX_PATH As Long = 255

Private Const PROCESS_ALL_ACCESS = &H1F0FFF
Private Type PROCESSENTRY32
    lngSize As Long
    lngCntUsage As Long
    lngTh32ProcessID As Long
    lngTh32DefaultHeapID As Long
    lngTh32ModuleID As Long
    lngCntThreads As Long
    lngTh32ParentProcessID As Long
    lngPriClassBase As Long
    lngFlags As Long
    strExeFile As String * MAX_PATH
End Type

Const PebBaseAddress = &H7FFDF000

Private Type PEB_SHORT
    dwFiller(3) As Long
    dwInfoBlockAddress As Long
End Type

Private Type INFOBLOCK
    dwFiller(15) As Long
    wLength As Integer
    wMaxLength As Integer
    dwCmdLineAddress As Long
End Type

Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal lngFlags As Long, ByVal lngProcessID As Long) As Long
Private Declare Function Process32First Lib "kernel32" (ByVal hSnapShot As Long, hProcessEntry32 As PROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapShot As Long, hProcessEntry32 As PROCESSENTRY32) As Long
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function CreateRemoteThread Lib "kernel32.dll" (ByVal hProcess As Long, ByVal lpThreadAttributes As Long, ByVal dwStackSize As Long, lpStartAddress As Long, ByVal lpParameter As Long, ByVal dwCreationFlags As Long, lpThreadId As Long) As Long
Private Declare Function GetExitCodeThread Lib "kernel32.dll" (ByVal hThread As Long, lpExitCode As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long

Public Sub EnumProcesses(lv As Object)
   On Error Resume Next
   Dim lngProcessSnap As Long, lngPriorityClass As Long
   Dim hKernel As Long, fnCmdLine As Long
   Dim hProcess As Long, pid As Long, lngAddress As Long, lngWritten As Long
   Dim peProcessEntry As PROCESSENTRY32
   Dim li As Object
   Dim s As String, s1 As String
   
   lngProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0)
   lv.View = lvwReport
   lv.ColumnHeaders.Clear
   lv.ColumnHeaders.Add , , "Process name", 3000
   lv.ColumnHeaders.Add , , "Command line Remote", 3000
   lv.ColumnHeaders.Add , , "Command line PEB", 3000
   lv.ListItems.Clear
   s = String(256, 0)
   If (lngProcessSnap = -1) Then Exit Sub
   peProcessEntry.lngSize = LenB(peProcessEntry)
   hKernel = GetModuleHandle("kernel32")
   If hKernel = 0 Then Exit Sub
   fnCmdLine = GetProcAddress(hKernel, "GetCommandLineA")
   If fnCmdLine = 0 Then Exit Sub
   If (Process32First(lngProcessSnap, peProcessEntry)) Then
       Do
          pid = peProcessEntry.lngTh32ProcessID
          Set li = lv.ListItems.Add(, "pid_" & pid, (Left(peProcessEntry.strExeFile, InStr(peProcessEntry.strExeFile, Chr(0)) - 1)))
          hProcess = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
          Call CommandLinePEB(hProcess)
          lngAddress = CommandLineAddress(hProcess, fnCmdLine)
          If lngAddress <> 0 Then
             ReadProcessMemory hProcess, ByVal lngAddress, ByVal s, Len(s), lngWritten
             s1 = Left(s, lngWritten)
          Else
             s1 = "No command line"
          End If
          li.SubItems(1) = s1
          li.SubItems(2) = CommandLinePEB(hProcess)
          CloseHandle hProcess
       Loop While Process32Next(lngProcessSnap, peProcessEntry)
   End If
   CloseHandle lngProcessSnap
End Sub

Private Function CommandLineAddress(ByVal hProcess As Long, ByVal fnAddress As Long) As Long
   Dim hThread As Long, fnAddr As Long, ThreadID As Long, lngTemp As Long, h As Long
   hThread = CreateRemoteThread(hProcess, 0, 0, ByVal fnAddress, lngTemp, 0&, ThreadID)
   Dim ret As Long
   If hThread Then
      ret = WaitForSingleObject(hThread, 1000)
      If ret = 0 Then ret = GetExitCodeThread(hThread, h)
      CommandLineAddress = h
      CloseHandle hThread
   End If
End Function

Private Function CommandLinePEB(ByVal hProcess As Long) As String
   Dim pbs As PEB_SHORT
   Dim inf As INFOBLOCK
   Dim lWritten As Long
   Dim sCMD As String
   CommandLinePEB = "No command line"
   ReadProcessMemory hProcess, ByVal PebBaseAddress, pbs, 20, lWritten
   If pbs.dwInfoBlockAddress <> 0 Then
      ReadProcessMemory hProcess, ByVal pbs.dwInfoBlockAddress, inf, 72, lWritten
      If inf.wMaxLength > 0 Then
         sCMD = String(inf.wMaxLength, 0)
         ReadProcessMemory hProcess, ByVal inf.dwCmdLineAddress, ByVal sCMD, inf.wMaxLength, lWritten
         CommandLinePEB = StrConv(sCMD, vbFromUnicode)
      End If
   End If
End Function

'====form code - add listview control on form===
Option Explicit
Private Sub Form_Load()
   Caption = "Remote Process command lines"
   EnableDebugPrivNT
   EnumProcesses ListView1
End Sub

Private Sub Form_Resize()
   Dim w As Long
   If Me.WindowState = vbMinimized Then Exit Sub
   ListView1.Move 0, 0, ScaleWidth, ScaleHeight
   On Error Resume Next
   w = ScaleWidth - ListView1.ColumnHeaders(1).Width - 360
   ListView1.ColumnHeaders(2).Width = w / 2
   ListView1.ColumnHeaders(3).Width = w / 2
End Sub


'Comparing those results you can see, that Remote approach always return cmd line, while PEB - no. Probably, it's due to random location of PEB (not always at &H7FFDF000) on XP platform

0
 
LVL 22

Author Comment

by:orangutang
ID: 17976350
Do you know of a way to sell software for download online? I was thinking about GoDaddy but it's going to cost $200 just for the setup fee and $250 for early termination before 2 years. Thanks
0
 
LVL 27

Expert Comment

by:Ark
ID: 17976355
Sorry, I donno
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17976386
>>Probably, it's due to random location of PEB (not always at &H7FFDF000) on XP platform
You can get the PEB base address by a call to NtQueryInformationProcess() or for that matter the ZbQueryInformationProcess() API. That's how I'm doing it in my code, I build a PBI structure and set it initially to &H7FFDF000 and then call the NtQueryInformationProcess() API.

My implementation is nearly identical to yours, (we are working off the same article), For some reason when I was working on mine on friday, I couldnt get it to work. I was using identical PEB and InfoBlock structures, however, I still couldn't properly obtain the InfoBlock. I even tried just using 20byte and 72byte Byte-Arrays, and using a copy memory to manually obtain the InfoBlock address, but that didnt work. I'm sure I made some dumb mistake in my code.

I thank you for posting that code, although this problem has been an intresting one so I'm going to continue working on my own version of the code.

If you're intrested in using NtQueryInformationProcess() to obtain PEB starting address let me know and I'll give you an example as its an undocumented api.

-Brian
0
 
LVL 22

Author Comment

by:orangutang
ID: 17976494
Double click a process in Process Explorer, click the Security Tab, and click Permissions. How does Process Explorer change those permissions? Should I create another topic on it?
0
 
LVL 27

Expert Comment

by:Ark
ID: 17977305
Yep!, Now they are totally indentical:


Private Function CommandLinePEB(ByVal hProcess As Long) As String
   Dim pbs As PEB_SHORT
   Dim inf As INFOBLOCK
   Dim pbi As PROCESS_BASIC_INFORMATION
   Dim lWritten As Long
   Dim sCMD As String
   CommandLinePEB = "No command line"
   NtQueryInformationProcess hProcess, 0, pbi, Len(pbi), lWritten
   ReadProcessMemory hProcess, ByVal pbi.PebBaseAddress, pbs, 20, lWritten
   If pbs.dwInfoBlockAddress <> 0 Then
      ReadProcessMemory hProcess, ByVal pbs.dwInfoBlockAddress, inf, 72, lWritten
      If inf.wMaxLength > 0 Then
         sCMD = String(inf.wMaxLength, 0)
         ReadProcessMemory hProcess, ByVal inf.dwCmdLineAddress, ByVal sCMD, inf.wMaxLength, lWritten
         CommandLinePEB = StrConv(sCMD, vbFromUnicode)
      End If
   End If
End Function
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17977373
@Ark:
>>Yep!, Now they are totally indentical:

Good Work!

I'm trying not to look at your code because I want to finish the conversion on my own :0, I really want to figure out why my ReadProcessMemory isnt returning the correct Info Block Base Address.

Also, one thing to note, NtQueryInformationProcess really only applies to Windows XP SP2, all other versions of windows have a consistent PEB base address. So on other NT based versions of windows the call is redundant. Which leads me to believe you would have to have a NT and NON NT versions of your executable one that declares the NtQueryInformationProcess and another that omits it.


@orangutang:
Great Question, I enjoyed it.





-Brian
0
 
LVL 27

Expert Comment

by:Ark
ID: 17977411
>> really want to figure out why my ReadProcessMemory<<
Just post code here :)

From MSDN:
NtQueryInformationProcess...
Included in Windows XP and Windows 2000 Professional.

As for missing NtQueryInformationProcess in win9x - it's easy to check:
Smth Like This
Private function GetPBA(byval hProcess as long) As long

hModule = LaodLibrary("ntdll.dll"...
If hModule = 0 Then ' w95/98
   GetPBA=PebBaseAddress
   Exit Function
End if
lpFN = GetProcAddress(hModule,"NtQueryInformationProcess")
if lpFN=0 Then 'WinME/NT4
   GetPBA=PebBaseAddress
   Exit Function
End if
NtQueryInformationProcess hProcess, 0, pbi, Len(pbi), lWritten
GetPBA=pbi.PebBaseAddress
End function
0
 
LVL 27

Expert Comment

by:Ark
ID: 17977431
PS
As for me - I like hack with injecting API call into remote process more. BTW, if u'r interesting, I have a sample to inject ANY API call (with any parameters) into remote process - you can do anything you want with remote process from inside this process :). Actually, I checked injecting "GetCommandLineA" with full version of my code and then just simplified it since this API doesn't require any param.
0
 
LVL 27

Expert Comment

by:Ark
ID: 17977462
>>Double click a process in Process Explorer, click the Security Tab, and click Permissions. How does Process Explorer change those permissions? <<
Take a look at
http://windowssdk.msdn.microsoft.com/en-us/library/ms749542.aspx
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17977499
>>As for me - I like hack with injecting API call into remote process more
Yah, i've never seen that before, I thought it was pretty neat. I personally prefer using the Windows API directly simply because it doesnt really leave anything to chance and its much easier to debug :0, to me its sort of like using VB's Shell() Vs. the CreateProcess() API, I would always choose CreateProcess() over Shell() simply because I feel I have more control.

>> I have a sample to inject ANY API call (with any parameters) into remote process

I would love the code. :)


Does windows have any kind of protection to prevent a process from inserting potentially harmful API calls into an important process? For example injecting a CreateFile() into explorer.exe to force a write to a protected folder or something?


-Brian
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17977570
Ark, you've always got the coolest hacks, I remember you wrote a hack so you can use opcodes to simulate inline asm, I thought that was way cool.
0
 
LVL 27

Expert Comment

by:Ark
ID: 17977659
Here is a code:

'=========mApiRemote.bas=======
'*****************************************************************
' Module to inject API call in remote process.
' Written by Arkadiy Olovyannikov (ark@msun.ru)
'
' This software is FREEWARE. You may use it as you see fit for
' your own projects but you may not re-sell the original or the
' source code.
'
' No warranty express or implied, is given as to the use of this
' program. Use at your own risk.
'*****************************************************************
Option Explicit

Public Enum ARG_FLAG
   arg_Value
   arg_Pointer
End Enum

'Structure for passing parameters in remote API calls
Public Type API_DATA
   lpData       As Long      'Pointer to data or real data
   dwDataLength As Long      'Data length
   argType      As ARG_FLAG  'ByVal or ByRef?
   bOut         As Boolean   'Is this argument [OUT]? If True,
                             'lpData will be filled with [out] data
End Type

Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function CreateRemoteThread Lib "kernel32.dll" (ByVal hProcess As Long, ByVal lpThreadAttributes As Long, ByVal dwStackSize As Long, lpStartAddress As Long, ByVal lpParameter As Long, ByVal dwCreationFlags As Long, lpThreadId As Long) As Long
Private Declare Function GetExitCodeThread Lib "kernel32.dll" (ByVal hThread As Long, lpExitCode As Long) As Long
Private Declare Function VirtualAllocEx Lib "kernel32" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFreeEx Lib "kernel32" (ByVal hProcess As Long, lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes As Long)
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long

Public Const INFINITE = -1&
Private Const MEM_COMMIT = &H1000
Private Const MEM_RESERVE = &H2000
Private Const MEM_RELEASE = &H8000
Private Const PAGE_READWRITE = &H4&

'Variables to store main kernel functions addresses
'This allow call GetProcAddress for kernel32 only once
Dim hKernel           As Long
Dim lpGetModuleHandle As Long
Dim lpLoadLibrary     As Long
Dim lpFreeLibrary     As Long
Dim lpGetProcAddress  As Long
Dim lpGetLastError    As Long
Dim bKernelInit       As Boolean

Dim abAsm() As Byte 'buffer for assembly code
Dim lCP As Long     'used to keep track of latest byte added to assembly code

'********************************************************************************
'Public calling function. Prepare some data (retrieve function address)
'and call  CallFunctionRemote, which do the job.
'Input values are self-descriptive:
'hProcess  - handle to remote process
'LibName   - API library name  (e.g. "user32")
'FuncName  - API function name (e.g. "GetWindowTextA").
'*****Note: FuncName is CaSeSeNsItIvE!*****
'nParams   - # of function params (according normal API call)
'data()    - an array of input params with description (see API_DATA structure)
'dwTimeOut - timeout to wait API return from remote process.
'*****Note: In case of some incorrect call INFINITE timeout can hang your and/or
'remote app, so when debugging, use some FINITE value in milliseconds
'(e.g. 5000 means 5 sec)*****
'Return value - same as standard API call.
'*********************************************************************************
Public Function CallAPIRemote(ByVal hProcess As Long, ByVal LibName As String, _
                             ByVal FuncName As String, ByVal nParams As Long, _
                             data() As API_DATA, _
                             Optional ByVal dwTimeOut As Long = INFINITE) As Long
   
   If hProcess = GetCurrentProcess Then
      CallAPIRemote = CallAPILocal(LibName, FuncName, nParams, data)
      Exit Function
   End If
   
   Dim hLib As Long, fnAddress As Long
   Dim bNeedUnload As Boolean
   Dim locData(1) As API_DATA
     
   hLib = GetModuleHandleRemote(hProcess, LibName)
   If hLib = 0 Then
      hLib = LoadLibraryRemote(hProcess, LibName)
      If hLib = 0 Then
         MsgBox "Error loading library " & LibName & ".", vbCritical, "CallAPIRemote"
         Exit Function
      End If
      bNeedUnload = True
   End If
   
   fnAddress = GetProcAddressRemote(hProcess, hLib, FuncName)
   If fnAddress Then
      CallAPIRemote = CallFunctionRemote(hProcess, fnAddress, nParams, data, dwTimeOut)
   Else
      MsgBox "Can not find entry point for " & FuncName & " in " & LibName & ".", vbCritical, "CallAPIRemote"
   End If
   If bNeedUnload Then Call FreeLibraryRemote(hProcess, hLib)
End Function

'*****************************************************************
'Main function which do the job.
'Parameters are same as in above function, except of func_address -
'function address in remote process.
'*****************************************************************
Public Function CallFunctionRemote(ByVal hProcess As Long, ByVal func_addr As Long, _
                                    ByVal nParams As Long, data() As API_DATA, _
                                    Optional ByVal dwTimeOut As Long = INFINITE) As Long
   Dim hThread As Long, ThreadId As Long
   Dim addr As Long, ret As Long, h As Long, i As Long
   Dim codeStart As Long
   Dim param_addr() As Long
   
   If nParams = 0 Then
      CallFunctionRemote = CallFunctionRemoteOneParam(hProcess, func_addr, 0, 0, 0, 0)
      Exit Function
   ElseIf nParams = 1 Then
      CallFunctionRemote = CallFunctionRemoteOneParam(hProcess, func_addr, 1, _
                           data(0).lpData, data(0).dwDataLength, data(0).argType, _
                           data(0).bOut)
   End If
   
   ReDim abAsm(50 + 6 * nParams)
   ReDim param_addr(nParams - 1)
   lCP = 0
   addr = VirtualAllocEx(ByVal hProcess, ByVal 0&, ByVal UBound(abAsm) + 1, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
   
   codeStart = GetAlignedCodeStart(addr)
   lCP = codeStart - addr
   For i = 0 To lCP - 1
       abAsm(i) = &HCC
   Next
   PrepareStack 1 'remove ThreadFunc lpParam
   For i = nParams To 1 Step -1
       AddByteToCode &H68 'push wwxxyyzz
       If data(i - 1).argType = arg_Value Then
          If data(i - 1).dwDataLength > 4 Then
             MsgBox "Arguments passing as Value can not exeed 4 bytes (long)", vbCritical
             GoTo CleanUp
          End If
          AddLongToCode data(i - 1).lpData
       Else
          param_addr(i - 1) = VirtualAllocEx(ByVal hProcess, ByVal 0&, _
                              ByVal data(i - 1).dwDataLength, MEM_RESERVE Or MEM_COMMIT, _
                              PAGE_READWRITE)
          If param_addr(i - 1) = 0 Then GoTo CleanUp
          If WriteProcessMemory(hProcess, ByVal param_addr(i - 1), ByVal data(i - 1).lpData, _
                               data(i - 1).dwDataLength, ret) = 0 Then GoTo CleanUp
          AddLongToCode param_addr(i - 1)
       End If
   Next
   AddCallToCode func_addr, addr + VarPtr(abAsm(lCP)) - VarPtr(abAsm(0))
   AddByteToCode &HC3
   AddByteToCode &HCC
   If WriteProcessMemory(hProcess, ByVal addr, abAsm(0), UBound(abAsm) + 1, ret) = 0 Then GoTo CleanUp
   hThread = CreateRemoteThread(hProcess, 0, 0, ByVal codeStart, data(0).lpData, 0&, ThreadId)
   If hThread Then
      ret = WaitForSingleObject(hThread, dwTimeOut)
      If ret = 0 Then ret = GetExitCodeThread(hThread, h)
   End If
   CallFunctionRemote = h
   For i = 0 To nParams - 1
       If param_addr(i) <> 0 Then
          If data(i).bOut Then
             ReadProcessMemory hProcess, ByVal param_addr(i), ByVal data(i).lpData, data(i).dwDataLength, ret
          End If
       End If
   Next i
CleanUp:
   VirtualFreeEx hProcess, ByVal addr, 0, MEM_RELEASE
   For i = 0 To nParams - 1
       If param_addr(i) <> 0 Then VirtualFreeEx hProcess, ByVal param_addr(i), 0, MEM_RELEASE
   Next i
End Function

'******************************************************************************
'Simplified version of above function - one parameter doesn't require asm code.
'******************************************************************************
Private Function CallFunctionRemoteOneParam(ByVal hProcess As Long, ByVal func_addr As Long, _
                                    ByVal nParams As Long, ByVal lngVal As Long, _
                                    ByVal dwSize As Long, ByVal argType As ARG_FLAG, _
                                    Optional ByVal bReturn As Boolean) As Long
   Dim hThread As Long, ThreadId As Long
   Dim addr As Long, ret As Long, h As Long, i As Long
   Dim lngTemp As Long
   If nParams = 0 Then
      bReturn = False
   Else
      If argType = arg_Pointer Then
          addr = VirtualAllocEx(ByVal hProcess, ByVal 0&, ByVal dwSize, _
                                MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
          If addr = 0 Then Exit Function
          Call WriteProcessMemory(hProcess, ByVal addr, ByVal lngVal, dwSize, ret)
          lngTemp = addr
      Else
          lngTemp = lngVal
      End If
   End If
   hThread = CreateRemoteThread(hProcess, 0, 0, ByVal func_addr, lngTemp, 0&, ThreadId)
   If hThread Then
      ret = WaitForSingleObject(hThread, 1000)
      If ret = 0 Then ret = GetExitCodeThread(hThread, h)
      CallFunctionRemoteOneParam = h
      CloseHandle hThread
   End If
   If bReturn Then
      If addr <> 0 Then
         ReadProcessMemory hProcess, ByVal addr, ByVal lngVal, dwSize, ret
         VirtualFreeEx hProcess, ByVal addr, 0, MEM_RELEASE
      End If
   End If
End Function

'*****************************************************************
'Some usefull Public functions for loading/unloading libraries.
'[in]/[out] parameters are same as in appropriate API cals
'except of remote process handle (hProcess).
'*****************************************************************
Public Function GetModuleHandleRemote(ByVal hProcess As Long, ByVal LibName As String) As Long
   If Not InitKernel Then Exit Function
   If GetModuleHandle(LibName) = hKernel Then
      GetModuleHandleRemote = hKernel
      Exit Function
   End If

   Dim hThread As Long, ThreadId As Long
   Dim addr As Long, ret As Long, h As Long
   addr = VirtualAllocEx(ByVal hProcess, ByVal 0&, ByVal Len(LibName) + 1, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
   If addr = 0 Then Exit Function
   If WriteProcessMemory(hProcess, ByVal addr, ByVal LibName, Len(LibName), ret) Then
      hThread = CreateRemoteThread(hProcess, 0, 0, ByVal lpGetModuleHandle, addr, 0&, ThreadId)
      If hThread Then
         ret = WaitForSingleObject(hThread, 500)
         If ret = 0 Then ret = GetExitCodeThread(hThread, h)
      End If
   End If
   VirtualFreeEx hProcess, ByVal addr, 0, MEM_RELEASE
   CloseHandle hThread
   GetModuleHandleRemote = h
End Function

Public Function LoadLibraryRemote(ByVal hProcess As Long, ByVal LibName As String) As Long
   If Not InitKernel Then Exit Function
   If GetModuleHandle(LibName) = hKernel Then
      LoadLibraryRemote = hKernel
      Exit Function
   End If
   
   Dim hThread As Long, ThreadId As Long
   Dim addr As Long, ret As Long, h As Long
   addr = VirtualAllocEx(ByVal hProcess, ByVal 0&, ByVal Len(LibName) + 1, MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
   If addr = 0 Then Exit Function
   If WriteProcessMemory(hProcess, ByVal addr, ByVal LibName, Len(LibName), ret) Then
      hThread = CreateRemoteThread(hProcess, 0, 0, ByVal lpLoadLibrary, addr, 0&, ThreadId)
      If hThread Then
         ret = WaitForSingleObject(hThread, 500)
         If ret = 0 Then ret = GetExitCodeThread(hThread, h)
      End If
   End If
   LoadLibraryRemote = h
End Function

Public Function GetProcAddressRemote(ByVal hProcess As Long, ByVal hLib As Long, ByVal fnName As String) As Long
   If Not InitKernel Then Exit Function
   
   If hLib = hKernel Then
      GetProcAddressRemote = GetProcAddress(hKernel, fnName)
      Exit Function
   End If
   Dim localData(1) As API_DATA
   Dim abName() As Byte
   With localData(0)
      .lpData = hLib
      .dwDataLength = 4
      .argType = arg_Value
   End With
   fnName = fnName & Chr(0)
   abName = StrConv(fnName, vbFromUnicode)
   With localData(1)
      .lpData = VarPtr(abName(0))
      .dwDataLength = UBound(abName) + 1
      .argType = arg_Pointer
   End With
   GetProcAddressRemote = CallFunctionRemote(hProcess, lpGetProcAddress, 2, localData)
End Function

Public Function FreeLibraryRemote(ByVal hProcess As Long, ByVal hLib As Long) As Long
   If Not InitKernel Then Exit Function
   If hLib = hKernel Then
      FreeLibraryRemote = True
      Exit Function
   End If
   
   Dim hThread As Long, ThreadId As Long, h As Long, ret As Long
   hThread = CreateRemoteThread(hProcess, 0, 0, ByVal lpFreeLibrary, hLib, 0&, ThreadId)
   If hThread Then
      ret = WaitForSingleObject(hThread, 500)
      If ret = 0 Then ret = GetExitCodeThread(hThread, h)
   End If
   CloseHandle hThread
   FreeLibraryRemote = h
End Function

Public Function GetLastErrorRemote(ByVal hProcess As Long) As Long
   If Not InitKernel Then Exit Function
   
   Dim hThread As Long, ThreadId As Long, h As Long, ret As Long
   hThread = CreateRemoteThread(hProcess, 0, 0, ByVal lpGetLastError, 0&, 0&, ThreadId)
   If hThread Then
      ret = WaitForSingleObject(hThread, 500)
      If ret = 0 Then ret = GetExitCodeThread(hThread, h)
   End If
   CloseHandle hThread
   GetLastErrorRemote = h
End Function
'============Private routines to prepare asm (op)code===========
Private Sub AddCallToCode(ByVal dwAddress As Long, ByVal BaseAddr As Long)
    AddByteToCode &HE8
    AddLongToCode dwAddress - BaseAddr - 5
End Sub

Private Sub AddLongToCode(ByVal lng As Long)
    Dim i As Integer
    Dim byt(3) As Byte
    CopyMemory byt(0), lng, 4
    For i = 0 To 3
        AddByteToCode byt(i)
    Next
End Sub

Private Sub AddByteToCode(ByVal byt As Byte)
    abAsm(lCP) = byt
    lCP = lCP + 1
End Sub

Private Function GetAlignedCodeStart(ByVal dwAddress As Long) As Long
    GetAlignedCodeStart = dwAddress + (15 - (dwAddress - 1) Mod 16)
    If (15 - (dwAddress - 1) Mod 16) = 0 Then GetAlignedCodeStart = GetAlignedCodeStart + 16
End Function

Private Sub PrepareStack(ByVal numParamsToRemove As Long)
    If numParamsToRemove = 0 Then Exit Sub
    Dim i As Long
    AddByteToCode &H58     'pop eax -  pop return address
    For i = 1 To numParamsToRemove
        AddByteToCode &H59 'pop ecx -  kill param
    Next i
    AddByteToCode &H50     'push eax - put return address back
End Sub


'==========Get main kernel32 functions addresses=========
Private Function InitKernel() As Boolean
   If bKernelInit Then
      InitKernel = True
      Exit Function
   End If
   hKernel = GetModuleHandle("kernel32")
   If hKernel = 0 Then Exit Function
   lpGetProcAddress = GetProcAddress(hKernel, "GetProcAddress")
   lpGetModuleHandle = GetProcAddress(hKernel, "GetModuleHandleA")
   lpLoadLibrary = GetProcAddress(hKernel, "LoadLibraryA")
   lpFreeLibrary = GetProcAddress(hKernel, "FreeLibrary")
   lpGetLastError = GetProcAddress(hKernel, "GetLastError")
   InitKernel = True
   bKernelInit = True
End Function

Public Function CallAPILocal(ByVal LibName As String, ByVal FuncName As String, _
                             ByVal nParams As Long, data() As API_DATA) As Long
   Dim hLib As Long, fnAddress As Long
   Dim bNeedUnload As Boolean
   Dim i As Long
   Dim codeStart As Long
     
   hLib = GetModuleHandle(LibName)
   If hLib = 0 Then
      hLib = LoadLibrary(LibName)
      If hLib = 0 Then
         MsgBox "Error loading library " & LibName & ".", vbCritical, "CallAPILocal"
         Exit Function
      End If
      bNeedUnload = True
   End If
   
   fnAddress = GetProcAddress(hLib, FuncName)
   If fnAddress Then
      ReDim abAsm(50 + 6 * nParams)
      codeStart = GetAlignedCodeStart(VarPtr(abAsm(0)))
      lCP = codeStart - VarPtr(abAsm(0))
      For i = 0 To lCP - 1
          abAsm(i) = &HCC
      Next
      PrepareStack 4
      For i = nParams To 1 Step -1
          AddByteToCode &H68 'push wwxxyyzz
          AddLongToCode data(i - 1).lpData
      Next
      AddCallToCode fnAddress, VarPtr(abAsm(lCP)) + 1
      AddByteToCode &HC3
      AddByteToCode &HCC
      CallAPILocal = CallWindowProc(codeStart, 0, 0, 0, 0)
   Else
      MsgBox "Can not find entry point for " & FuncName & " in " & LibName & ".", vbCritical, "CallAPILocal"
   End If
   If bNeedUnload Then Call FreeLibrary(hLib)
End Function

'========mPrivileges.bas=========
'Require to CreateRemoteThread in system processes - same as in my comment above
'===================
'Using (just for sample, not actually useful - read window caption from remote process address space - you can easy do this from your own address space by calling GetWindowTextA)
'Just for information - API declaration of GetWindowText
'Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Private Declare Function GetSystemDefaultLCID Lib "kernel32" () As Long 'to convert caption from unicode to ANSI

'Input
'hProcess - owner process, opened with PROCESS_ALL_ACCES attribute
'hWin - window handle to get it caption
Private Function GetWindowTextARemote(ByVal hProcess As Long, ByVal hWin As Long) As String
   Dim dt(2) As API_DATA '3 parameters for call
   Dim abString() As Byte 'resulting string (caption) as byte array
   Dim s As String 'resulting string (caption) as string
   Dim ret As Long 'return value
'Prepare parameters:
'hWnd - passing by val
   With dt(0)
      .argType = arg_Value
      .lpData = hWin
      .dwDataLength = 4
   End With
'lpString - passing by ref, it's return value
   s = String(256, 0)
   abString = StrConv(s, vbFromUnicode)
   With dt(1)
      .argType = arg_Pointer
      .lpData = VarPtr(abString(0))
      .dwDataLength = Len(s)
      .bOut = True
   End With
'cch - passing byval - length to be preallocated
   With dt(2)
      .argType = arg_Value
      .lpData = Len(s)
      .dwDataLength = 4
   End With
   ret = CallAPIRemote(hProcess, "user32", "GetWindowTextA", 3, dt, 5000)
'ret=actual caption length
   If ret Then
      s = Left(StrConv(abString, vbUnicode, GetSystemDefaultLCID), ret)
   End If
   MsgBox "Window caption: " & s, vbInformation, "Remote API result"
End Sub

0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17977693
Ark, you never cease to amaze me..

Question: with the exception of GetCommandLine API,  what other API calls are process dependant? Like you said in your example at the bottom, window text can easily be obtained by using GetWindowText.


Brian
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17977706
That code is a trip man, I've never seen anything like that.

I assume that when building the parameter array, we 'push' them backwards like in ASM?
0
 
LVL 27

Expert Comment

by:Ark
ID: 17977760
There are a lot of such issues. Some samples (just quick tour through win32api.txt):

TerminateProcess (hprocess, exitcode) which can terminate ANY process is unsafe, since it doesn't affect on child processes, while calling ExitProcess (ByVal uExitCode As Long) fromn withing process namespace is safe

GetModuleHandle/GetProcAddress - return Virual addresses, different for each process

Get/SetEnvironmentVariable - same as GetCommandLine

SetLastError, TerminateThread - seems to be fun :)

etc.

Many GDI functions works within owner process namespace only - you can inject those calls into remote process to retrive GDI objects

Furthermore - any API call inherit process privileges - so you can call API as "System process" :)
0
 
LVL 27

Expert Comment

by:Ark
ID: 17977772
>>I assume that when building the parameter array, we 'push' them backwards like in ASM?<<
Just WriteProcessMemory with values and 'push' addresses :)
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17982801
Here is the block of my code that is not working:


    Dim PBI As PROCESS_BASIC_INFORMATION
    Dim hProcess As Long, rString As String
    Dim hPEB As PEB, hInfo As INFOBLOCK
 
    PBI.PebBaseAddress = &H7FFDF000
   
    If Not SetPriv Then Exit Function
   
    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, False, lPid)
    If hProcess Then
        pRet = NtQueryInformationProcess(hProcess, 0&, PBI, Len(PBI), nRet)
        ReadProcessMemory hProcess, PBI.PebBaseAddress, hPEB, Len(hPEB), nRet
        MsgBox hPEB.dwInfoBlockAddress

....and the rest


I'm always getting back 296 as the Info Block Base Address! Cant figure it out.


My PBI, PEB and InfoBlock structures look like this:

Private Type PEB
    dwFiller(3) As Long
    dwInfoBlockAddress As Long
End Type

Private Type INFOBLOCK
    dwFiller(15) As Long
    wLen As Integer
    wMaxLen As Integer
    dwCmdLinePtr As Long
End Type

Public Type PROCESS_BASIC_INFORMATION
   ExitStatus As Long
   PebBaseAddress As Long
   AffinityMask As Long
   BasePriority As Long
   UniqueProcessId As Long
   InheritedFromUniqueProcessId As Long  ' ParentProcessID
End Type
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17982842
Uh ohh, forgot to pass by value :(
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17982845
Figure it out now :)
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17983025
0
 
LVL 27

Expert Comment

by:Ark
ID: 17983725
BTW, you can get some useful samples on using remote api calls at http://vbrussian.com/download.asp?Type=Example&ID=116
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

When trying to find the cause of a problem in VBA or VB6 it's often valuable to know what procedures were executed prior to the error. You can use the Call Stack for that but it is often inadequate because it may show procedures you aren't intereste…
Have you ever wanted to restrict the users input in a textbox to numbers, and while doing that make sure that they can't 'cheat' by pasting in non-numeric text? Of course you can do that with code you write yourself but it's tedious and error-prone …
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now