Process List AND Modules List 64 bit (HELP!)

Just starting out? Need help? Post your questions and find answers here.
Mike Yurgalavage
Enthusiast
Enthusiast
Posts: 118
Joined: Thu May 17, 2007 8:35 pm
Location: USA

Process List AND Modules List 64 bit (HELP!)

Post by Mike Yurgalavage »

I am porting my 32 bit apps up to 64 bit. One of the things I am having problems with is getting ahold of the Processes and then the Modules of that process. Note the code below:

Code: Select all

#NbProcessesMax=10000
Global Dim ProcessesArray.l(#NbProcessesMax)

Procedure GetProcessList() 
  If OpenLibrary(0, "psapi.dll") 
    EnumProcesses = GetFunction(0, "EnumProcesses") 
    EnumProcessModules = GetFunction(0, "EnumProcessModules") 
    GetModuleBaseName = GetFunction(0, "GetModuleBaseNameA") 
    CallFunctionFast(EnumProcesses, ProcessesArray(), #NbProcessesMax, @nProcesses) 
    For k = 0 To nProcesses >> 2 
      hProcess = OpenProcess_(#PROCESS_QUERY_INFORMATION | #PROCESS_VM_READ, #False, ProcessesArray(k)) 
      If hProcess 
        CallFunctionFast(EnumProcessModules, hProcess, @BaseModule, 4, @cbNeeded) 
        Prozess$ = Space(cbNeeded) 
        CallFunctionFast(GetModuleBaseName, hProcess, BaseModule, @Prozess$, cbNeeded) 
        If Len(Prozess$) <> 0               ;z.B. System
          ;AddGadgetItem(Gadget, -1, Prozess$ + Chr(10) + Str(ProcessesArray(k))) 
          Debug Prozess$+" "+Str(ProcessesArray(k))
        EndIf 
        CloseHandle_(hProcess) 
      EndIf 
    Next 
    CloseLibrary(0) 
  EndIf 
EndProcedure 


GetProcessList()
this is similar to my own code and works. However, I need to be able to list the Modules for each Process and their Base Address in memory. This needs to work with 64 bit purebasic in 64 bit OS.

the following info would be helpful.

[program name] [PID][Handle]
then modules of that program below it like this:
[program][base address of program]
[module 1][base address of module 1]
[etc.][etc.]

traversing through each module within that program (modules such as ntdll.dll, etc.)

my old module code looked like this but it doesn't work... hWnd is handle to the process in question.

Code: Select all

; ----------------------------------------
;Get Base Address of .exe or .dll
; ----------------------------------------

GetBaseAddress:
;{

baseaddress = 0 ; reset baseaddress

app2 = OpenProcess_(#PROCESS_ALL_ACCESS,Null,hWnd)

hProcess = app2

  ws_psapi_lib = 1 
  ws_psapi_h.l = OpenLibrary(ws_psapi_lib,"PSAPI.DLL")
  *ws_EnumProcessModules = GetFunction(ws_psapi_lib,"EnumProcessModules") 
  *ws_GetModuleBaseNameA = GetFunction(ws_psapi_lib,"GetModuleBaseNameA")
  
#PROCESS_QUERY_INFORMATION = $400 
#PROCESS_VM_READ = $10 

Dim aProcesses.l(1024) 
Dim hMods.l(1024) 


If OpenLibrary(1, "Psapi.dll") = #Null 

  MessageRequester("Problem!", "Could not load the 'Paspi.dll' library.",#PB_MessageRequester_Ok) 
  End
  
EndIf 

  buffer$=Space(4024) 
  hProcess = OpenProcess_(#PROCESS_QUERY_INFORMATION | #PROCESS_VM_READ, #False, hWnd) 

If CallFunction(1, "EnumProcessModules", hProcess, hMods(), 4096, @cbNeeded) 

Debug cbNeeded/4-1

  For i = 0 To  cbNeeded/4-1
  

  
  If CallFunction(1, "GetModuleFileNameExA" ,hProcess, hMods(i), @buffer$, 4024) 
    
    
    sModName$=Space(#MAX_PATH)
    CallFunction (1,"GetModuleBaseNameA" ,hProcess, hMods(i), @sModName$, Len(sModName$)) 
    

    ;Debug baseaddress
      Debug sModName$
    
    Delay(1000)
    
    If UCase(sModName$) = UCase(File$)
    
        
      baseaddress=hmods(i) ; store base address of File$ to variable baseaddress
      Debug baseaddress
      Debug sModName$
       
    EndIf
    
    If UCase(sModName$) = UCase(ProgramName$)
    
    CopyPath$=buffer$
    
    Dir$= GetTemporaryDirectory()
    
    Result = CopyFile(CopyPath$, Dir$+"Copy.dll")
    
    EndIf

  EndIf 
  
    Next 
    
EndIf 
  
CloseLibrary(ws_psapi_lib) 
At any rate, a tight, small code or procedure that outlines the running processes (and their PID and Handle) and then another tight procedure outlining the modules and thier base address within memory of a particular process would be appreciated. it needs to run on 64 bit OS windows and work ON 64 bit running processes.

thx-

best,
Mike
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Process List AND Modules List 64 bit (HELP!)

Post by srod »

Personally I would use CreateToolhelp32Snapshot_() which should work fine on x64 (untested).

The following is some code I happen to have lying around. It lists, for the calling process, all modules, their names, filename and base address (within the context of the parent process).

Code: Select all

;This little routine lists all modules associated with the current process.
;srod.

me32.MODULEENTRY32
me32\dwSize = SizeOf(MODULEENTRY32)

hSnapShot = CreateToolhelp32Snapshot_(#TH32CS_SNAPMODULE, 0) ;Change the zero for any processID.
If hSnapShot
  If Module32First_(hSnapShot, me32) 
    Debug "Module name = "+PeekS(@me32\szModule) + "     Filename = " + PeekS(@me32\szExePath) + "    Base address = $" + Hex(me32\modBaseAddr, #PB_Integer)
    Repeat 
      result = Module32Next_(hSnapShot, me32)
      If result
        Debug "Module name = "+PeekS(@me32\szModule) + "     Filename = " + PeekS(@me32\szExePath) + "    Base address = $" + Hex(me32\modBaseAddr, #PB_Integer)
      EndIf 
    Until result = #False 
  EndIf
  CloseHandle_(hSnapShot)
Else
  MessageRequester("Error", "CreateToolhelp32Snapshot_() failed!")
EndIf
To extend this to all running processes then you would use an outer loop and call CreateToolhelp32Snapshot_(#TH32CS_SNAPPROCESS, 0) and then enumerate the processes in order to pull out each process' processID etc. In the inner loop (basically the code above) you then call CreateToolhelp32Snapshot_(#TH32CS_SNAPMODULE, pid), where pid is the aforementioned processID.

As I say, I reckon this might be simpler than monkeying around with the psapi library. :)
I may look like a mule, but I'm not a complete ass.
Perkin
Enthusiast
Enthusiast
Posts: 504
Joined: Thu Jul 03, 2008 10:13 pm
Location: Kent, UK

Re: Process List AND Modules List 64 bit (HELP!)

Post by Perkin »

One thing, shouldn't a lot of the *.l vars (handles/pointers etc) now be *.i because of x64
%101010 = $2A = 42
Mike Yurgalavage
Enthusiast
Enthusiast
Posts: 118
Joined: Thu May 17, 2007 8:35 pm
Location: USA

Re: Process List AND Modules List 64 bit (HELP!)

Post by Mike Yurgalavage »

srod wrote:Personally I would use CreateToolhelp32Snapshot_() which should work fine on x64 (untested).

The following is some code I happen to have lying around. It lists, for the calling process, all modules, their names, filename and base address (within the context of the parent process).

Code: Select all

;This little routine lists all modules associated with the current process.
;srod.

me32.MODULEENTRY32
me32\dwSize = SizeOf(MODULEENTRY32)

hSnapShot = CreateToolhelp32Snapshot_(#TH32CS_SNAPMODULE, 0) ;Change the zero for any processID.
If hSnapShot
  If Module32First_(hSnapShot, me32) 
    Debug "Module name = "+PeekS(@me32\szModule) + "     Filename = " + PeekS(@me32\szExePath) + "    Base address = $" + Hex(me32\modBaseAddr, #PB_Integer)
    Repeat 
      result = Module32Next_(hSnapShot, me32)
      If result
        Debug "Module name = "+PeekS(@me32\szModule) + "     Filename = " + PeekS(@me32\szExePath) + "    Base address = $" + Hex(me32\modBaseAddr, #PB_Integer)
      EndIf 
    Until result = #False 
  EndIf
  CloseHandle_(hSnapShot)
Else
  MessageRequester("Error", "CreateToolhelp32Snapshot_() failed!")
EndIf
To extend this to all running processes then you would use an outer loop and call CreateToolhelp32Snapshot_(#TH32CS_SNAPPROCESS, 0) and then enumerate the processes in order to pull out each process' processID etc. In the inner loop (basically the code above) you then call CreateToolhelp32Snapshot_(#TH32CS_SNAPMODULE, pid), where pid is the aforementioned processID.

As I say, I reckon this might be simpler than monkeying around with the psapi library. :)

this is great sRod and thanks for all the help you give here. I must admit that I am lost as to how to translate my "OUTER LOOP" code to hold this within it without resorting to my old calling the psapi.dll. do you think you can write a very tight code outer loop to create the PID's that would then call this inner loop?

for now i came up with this:

Code: Select all

;This little routine lists all modules associated with the current process.
;srod.

Procedure ListModules(Pid)
me32.MODULEENTRY32
me32\dwSize = SizeOf(MODULEENTRY32)

hSnapShot = CreateToolhelp32Snapshot_(#TH32CS_SNAPMODULE, Pid) ;Change the zero for any processID.
If hSnapShot
  If Module32First_(hSnapShot, me32) 
    Debug "Module name = "+PeekS(@me32\szModule) + "     Filename = " + PeekS(@me32\szExePath) + "    Base address = $" + Hex(me32\modBaseAddr, #PB_Integer)
    Repeat 
      result = Module32Next_(hSnapShot, me32)
      If result
        Debug "Module name = "+PeekS(@me32\szModule) + "     Filename = " + PeekS(@me32\szExePath) + "    Base address = $" + Hex(me32\modBaseAddr, #PB_Integer)
      EndIf 
    Until result = #False 
  EndIf
  CloseHandle_(hSnapShot)
Else
  MessageRequester("Error", "CreateToolhelp32Snapshot_() failed!")
EndIf
EndProcedure


Procedure GetPid(FiletoFind$)
  kernel32 = OpenLibrary(0, "Kernel32.dll")
  OpenLibrary(1, "psapi.dll")
  If kernel32
    CreateToolhelpSnapshot = GetFunction(0, "CreateToolhelp32Snapshot")
    ProcessFirst           = GetFunction(0, "Process32First")
    ProcessNext            = GetFunction(0, "Process32Next")
    If CreateToolhelpSnapshot And ProcessFirst And ProcessNext
      Process.PROCESSENTRY32\dwSize = SizeOf(PROCESSENTRY32)
      Snapshot = CallFunctionFast(CreateToolhelpSnapshot, #TH32CS_SNAPPROCESS, 0)
      If Snapshot
        ProcessFound = CallFunctionFast(ProcessFirst, Snapshot, Process)
        While ProcessFound
          PID = Process\th32ProcessID
          hProcess = OpenProcess_(#PROCESS_ALL_ACCESS, 0, PID)
          If hProcess
            Name$ = Space(1024)
            CallFunction(1, "GetModuleFileNameExA", hProcess, 0, @Name$, Len(Name$))
            CloseHandle_(hProcess)
          EndIf
          Temp.s = PeekS(@Process\szExeFile)
          If Temp <> "[System Process]"
              ;AddGadgetItem(GD, - 1, Str(PID) + Chr(10) + Temp + Chr(10) + Name$)
              If UCase(Temp)=UCase(FiletoFind$)
              Debug Temp
              PIDFound=PID              
              EndIf

          EndIf
          ProcessFound = CallFunctionFast(ProcessNext, Snapshot, Process)
        Wend
      EndIf
      CloseHandle_(Snapshot)
    EndIf
  EndIf
  ProcedureReturn PIDFound
EndProcedure

#SE_DEBUG_NAME = "SeDebugPrivilege"

Procedure EnableDebugPrivNT()
  ;-http://www.purebasic.fr/english/viewtopic.php?t=31161&highlight=enabledebugprivnt

  DebugValue.LUID;
  tkp.TOKEN_PRIVILEGES

  ; Retrieve a handle of the access token
  If Not OpenProcessToken_(GetCurrentProcess_(), #TOKEN_ADJUST_PRIVILEGES | #TOKEN_QUERY, @hToken)
    ProcedureReturn #False;
  EndIf

  ; Enable the SE_DEBUG_NAME privilege
  If Not LookupPrivilegeValue_("", #SE_DEBUG_NAME, @DebugValue)
    ProcedureReturn #False;
  EndIf

  NewState.TOKEN_PRIVILEGES
  NewState\PrivilegeCount = 1
  NewState\Privileges[0]\Luid\HighPart = DebugValue\HighPart
  NewState\Privileges[0]\Luid\LowPart = DebugValue\LowPart
  NewState\Privileges[0]\Attributes = #SE_PRIVILEGE_ENABLED
  AdjustTokenPrivileges_(hToken, #False, @NewState, SizeOf(TOKEN_PRIVILEGES), @PreviousState.TOKEN_PRIVILEGES, @ReturnLength)

  If GetLastError_() <> #ERROR_SUCCESS
    ProcedureReturn #False
  EndIf

  ProcedureReturn #True
EndProcedure

EnableDebugPrivNT()
Pid = GetPid("testme.exe")
result = ListModules(Pid)
best,
Mike
Post Reply