<?xml version="1.0" encoding="windows-1252"?>
<node id="1008938" title="NtQuerySystemInformation/Task Manger processes tab with Win32::API" created="2012-12-15 01:04:09" updated="2012-12-15 01:04:09">
<type id="1042">
CUFP</type>
<author id="857302">
bulk88</author>
<data>
<field name="doctext">
Someone asked for Task Manager style information, without parsing pslist (a MS command line tool similar to Task Manager and Process Explorer), or using WMI (the "official way" according to [mod://Win32::Process::Info]). So here is the answer in Perl. The author of [mod://Win32::Process::Info] decided to not use Native API [mod://Win32::Process::Info::NT] leaving only WMI as the alternative according to what I've been told. Anyway, here is calling NtQuerySystemInformation for Task Manager like info using Win32::API. Tested on Windows XP 64 bit and 32 bit corresponding bit Perls on each OS. It is rough (uninformative errors, assumptions about buffer sizes, etc), and the last 1/3rd of the struct is never processed. I dont plan to finish it, but more than half the work was done. Comparing the Dumper outputs with Task Manager processes tab shows this is the same info.
&lt;code&gt;
#!/usr/bin/perl -w
use Win32::API;
use Math::Int64 qw ( native_to_int64  int64_to_string);
use Data::Dumper;
use warnings;
use strict; 
BEGIN {
    use Config;
    eval ' sub PTR_LET () { "'
    .($Config{ptrsize} == 8 ? 'Q' : 'L').
    '" }';
}

#Win32::API doesnt do enums
Win32::API::Type-&gt;typedef( 'SYSTEM_INFORMATION_CLASS', 'ULONG' );
my $api = Win32::API::More-&gt;new('ntdll.dll',
'                      
NTSTATUS WINAPI NtQuerySystemInformation(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
);');
#uninfomative error, GetLastError should be fetched
die "Win32::API failure" if !$api;

#this should really be a loop, a fixed 65KB is wrong,
#NtQuerySystemInformation will fail with STATUS_INFO_LENGTH_MISMATCH if the buffer
#is not long enough

#changing \x00 to \xff and comparing the before and after will show what
#reserved fields are never filled and truly have no meaning
my $buf = "\x00" x 65000;
my $retlen = 0;
# 5 == SystemProcessInformation
my $ret = $api-&gt;Call(5, $buf, length($buf), $retlen );

#todo print NTSTATUS code
die "failed " if $ret;
#ImageName.Buffer is a pointer to inside $buf, if $buf is realloced
#there is a problem, so save the PV, then use that to convert Buffer to
#offset not a ptr later
my $bufptr = unpack(PTR_LET, pack('p', $buf));
#how far are we into the buffer, part of extracting ImageName.Buffer
my $bufoffset = 0;
print "retlen $retlen ret $ret\n";
#cut off uninit data at the end
$buf = substr($buf, 0, $retlen);
my %h;
do {
    ($h{NextEntryOffset},
     $h{NumberOfThreads},
    $h{Reserved1},
    $h{Reserved2},
    $h{Reserved3},
    $h{CreateTime},
    $h{UserTime},
    $h{KernelTime},
    $h{ImageName},
    $h{BasePriority},
    $h{ProcessId},
    $h{InheritedFromProcessId},
    $h{HandleCount},
    $h{Reserved4},
    $h{Reserved5},
    $h{PrivatePageCount}
    ) = unpack('LL(a[8])[6]a[SS'.(PTR_LET eq 'Q' ? 'x!['.PTR_LET.']' : '').PTR_LET.']lx!['.PTR_LET.']'
                        #pid         
                        .PTR_LET.''
                        #parent pid 
                        .PTR_LET.
                        #handle count, 2 reserveds, private page count
                        'LLLL'
                        #VM_COUNTERS VirtualMemoryCounters;
                        #IO_COUNTERS IoCounters;
                        #SYSTEM_THREAD Threads[0];
                        #not done/todo    
                        , $buf);
    #portable 64 bit int conversion, these are strings now
    $h{Reserved1} = int64_to_string(native_to_int64($h{Reserved1}));
    $h{Reserved2} = int64_to_string(native_to_int64($h{Reserved2}));
    $h{Reserved3} = int64_to_string(native_to_int64($h{Reserved3}));
    $h{CreateTime} = int64_to_string(native_to_int64($h{CreateTime}));
    $h{UserTime} = int64_to_string(native_to_int64($h{UserTime}));
    $h{KernelTime} = int64_to_string(native_to_int64($h{KernelTime}));

    {
        #this is still packed, make a copy
        my $in = $h{ImageName};
        $h{ImageName} = {};
        ($h{ImageName}-&gt;{Length},
         $h{ImageName}-&gt;{MaximumLength},
         $h{ImageName}-&gt;{Buffer}
        ) = unpack('SS'.(PTR_LET eq 'Q' ? 'x!['.PTR_LET.']' : '').PTR_LET, $in);
        #Buffer is still packed, but convert the pointer to the offset into the
        #$buf
        $h{ImageName}-&gt;{Buffer} = substr($buf, $h{ImageName}-&gt;{Buffer}-$bufptr-$bufoffset,
                                         $h{ImageName}-&gt;{Length});
        #$h{ImageName}-&gt;{Buffer} is in UTF16 wont print nicely to console
    }
    #slice off the processed SYSTEM_PROCESS_INFORMATION struct and trailing
    #VLA/unprocesed data
    $buf = substr($buf, $h{NextEntryOffset});
    $bufoffset += $h{NextEntryOffset};
    print Dumper(\%h);
} while ($h{NextEntryOffset});

&lt;/code&gt;
This is the struct that the above script decodes. Debate exists about what is the correct struct for SystemProcessInformation . This is correct for Windows XP I guess. Vista/7 IDK.
&lt;code&gt;
typedef struct _SYSTEM_PROCESS_INFORMATION 
{
 ULONG NextEntryOffset;
 ULONG NumberOfThreads;
 LARGE_INTEGER Reserved[3];
 LARGE_INTEGER CreateTime;
 LARGE_INTEGER UserTime;
 LARGE_INTEGER KernelTime;
 UNICODE_STRING ImageName;
 KPRIORITY BasePriority;
 HANDLE ProcessId;
 HANDLE InheritedFromProcessId;
 ULONG HandleCount;
 ULONG Reserved2[2];
 ULONG PrivatePageCount;
 VM_COUNTERS VirtualMemoryCounters;
 IO_COUNTERS IoCounters;
 SYSTEM_THREAD Threads[0];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
&lt;/code&gt;
The h in %h means hash.</field>
</data>
</node>
