Question about ntqueryprocessinformation?

Apr 12, 2020 at 9:07pm
So I really don't have much of a point to doing this, really just playing around, testing etc. Really just a learning exercise.

So I'm attempting to use ntqueryprocessinformation() and it returns good results i have error handling in there to make sure its executing correctly. The issue is when I start attempting to follow the paths created by the various structures that are associated with this dll. I'm using the process basic information parameter so it returns a pbi structure that i have already initialized. I don't think there's any issues with the pbi. However when I attempt to read from the peb structure i get access violations. I've tried doing these reads in a variety of ways. First I just created a pointer to a peb that i initialized as nullptr and wrote the peb pointer to it and attempted to directly dereference the pointer to the upp structure. Violation. I can do a readprocessmemory on the memory address and retrieve it that way but it just seems like overkill. Also the last thing I attempted was to initialize my own peb structure, then do readprocessmemory on the pi.baseaddress and read the entire contents of the structure into the structure that i created and even tho the function succeeded the value of peb.processparameters was just a bunch of zeros... I'd just like to add that I'm opening the process with process all access and it succeeds so I should have all rights to everything. I feel like I'm doing this correctly, I been playing around with it for 2 days now. Its just really weird. I'd also like to mention that I've looked at numerous examples, most of which are outdated etc but everything im doing is based off the examples. My code isn't even that complicated. I'm almost wondering if I need to load the dll into my processes memory space or something. Its just weird bc the nt function succeeds.
Last edited on Apr 12, 2020 at 9:08pm
Apr 12, 2020 at 9:35pm
Probably would help to show the code where the access violation is happening.
Apr 12, 2020 at 11:50pm
The access violations happen at any attempt to dereference any pointers or relevant information. I think i got something that works for the most part but in the UNICODE_STRING struct the buffer was null even tho everything else seemed to be in tact.

I mean its as simple as if ntqueryinformation returns a pbi structure. inside the pbi structure is a member that points to a PEB structure. In my peanut sized mind i should be able to create a PEB object and make it equal to the dereferenced pointer that points to returned PEB structure hence

PEB peb=pbi->PebBaseAddress; (i think thats correct idk head spinning)

idk heres my code. I'd also like to mention that i manually entered in the pid of the process i was targeting. Not very dynamic but fine for just playing around.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <Windows.h> 
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")
#include <iostream>



int main()
{
    HANDLE hwd = OpenProcess(PROCESS_ALL_ACCESS, TRUE, 1932);

    if (hwd == NULL) {
        std::cout << "openproc failed  " << hwd << std::endl;
        exit;
    }

PROCESS_BASIC_INFORMATION pi;
    NTSTATUS status = NtQueryInformationProcess(hwd, ProcessBasicInformation, &pi, sizeof(pi), nullptr);

   
    if (NT_SUCCESS(status)) {
        PEB peb;
        RTL_USER_PROCESS_PARAMETERS upp;
        UNICODE_STRING uni;
        WCHAR cmd[50];
        SIZE_T read; 

        if (hwd) {
            
            if (!ReadProcessMemory(hwd, pi.PebBaseAddress, &peb, sizeof(PEB), NULL)) {
                std::cout << "readprocmemory failed pi.base" << std::endl;
            }
            if (!ReadProcessMemory(hwd, peb.ProcessParameters, &upp, sizeof(PRTL_USER_PROCESS_PARAMETERS), NULL)) {
                std::cout << "readprocmemory failed peb.proc" << std::endl;
            }
            if (!ReadProcessMemory(hwd, &upp.CommandLine, &uni, sizeof(UNICODE_STRING), &read)) {
                std::cout << "readprocmemory failed upp.cmd" << std::endl;
            }

            if (!ReadProcessMemory(hwd, &uni.Buffer, &cmd, uni.Length, &read)) {
                std::cout << "readprocmemory failed uni.buff" << std::endl;
            }
            
        std::cout << read << "  read  &&  cmd..."<< cmd << "  && uni.length  " << uni.MaximumLength << std::endl;
   
        }

    }
            
    if (!NT_SUCCESS(status)) {
        std::cout << "nt failed  " << status << std::endl;
        if (NT_INFORMATION(status)) {
            std::cout << "ntinfo" << std::endl;
        }
        if (NT_ERROR(status)) {
            std::cout << "nterror" << std::endl;
        }
        if (NT_WARNING(status)) {
            std::cout << "ntwarning" << std::endl;
        }
    }

    if (hwd) {
        CloseHandle(hwd);
    }
}



edit. I'm now attempting to adjusting token privileges to see if that changes anything.
Last edited on Apr 13, 2020 at 1:23am
Apr 13, 2020 at 1:12am
Are any of your "x failed" if branches hit? (lines 31, 34, 37, 41)?
If so, you should be able to use GetLastError() to see what the specific error code is.

One possible issue I see that according to MSDN, UNICODE_STRING.Length does not account for the null-terminator, and that
the strings returned by the various LSA functions might not be null-terminated.

So it might be dangerous to assume that your cmd will be null-terminated.

When I run your program (and change the process id to be a valid process on my machine), the error code I get after line 36 is 299, which means
ERROR_PARTIAL_COPY
299 (0x12B)
Only part of a ReadProcessMemory or WriteProcessMemory request was completed.

Some other things,
1. exit is a function, it should be called like a function e.g. exit(1);
However, I do not suggest using exit, it's a very kludgy way to kill the process, and does 0 clean-up. Use return 1; instead (1 is some arbitrary non-zero code to mean "failure").

2. I needed to #include <Ntdef.h> to get your code to compile, so I suggest adding that in there to make your code more portable.

3. If you're printing a wide-char array, you should use std::wcout.
Last edited on Apr 13, 2020 at 1:13am
Apr 13, 2020 at 3:13am
I appreciate the response. Yes im getting the same fail at line 37. Its showing up in debug as some of the memory specifically the buffer to the UNICODE_STRING as 0xCCCCCCCCC. its just really strange bc im targeting a cmd prompt and its still locking me out after i adjusted the tokens to enable debug and impersonate the OS. Im might have to figure out the debug port and attempt to do my buisness in there...
Apr 13, 2020 at 4:17am
> if (!ReadProcessMemory(hwd, &uni.Buffer, &cmd, uni.Length, &read))
If uni.Buffer is a pointer, then you don't need that & there.

Apr 13, 2020 at 6:17am
1
2
3
4
std::wstring unicode_string(uni.Length / sizeof(wchar_t));
if (uni.Length){
    if (!ReadProcessMemory(hwd, &unicode_string[0], &cmd, uni.Length, &read))
    //... 
Apr 13, 2020 at 11:48pm
@salem if you try to remove the & it gives an error.

@helios your posted code also produces an error but idk how that would solve anything as has been said the code begins to fall apart at like 36 when trying to copy the UNICODE_STRING struct.
Apr 14, 2020 at 12:20am
Ah, yeah, actually I did screw up the order of parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if (!ReadProcessMemory(hwd, pi.PebBaseAddress, &peb, sizeof(PEB), NULL)){
    std::cout << "readprocmemory failed pi.base" << std::endl;
}

// See note 1:                                           vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
if (!ReadProcessMemory(hwd, peb.ProcessParameters, &upp, sizeof(RTL_USER_PROCESS_PARAMETERS), NULL)){
    std::cout << "readprocmemory failed peb.proc" << std::endl;
}

//This isn't necessary. upp.CommandLine has already been filled by the previous call.
//if (!ReadProcessMemory(hwd, &upp.CommandLine, &uni, sizeof(UNICODE_STRING), &read)){
//    auto error = GetLastError();
//    std::cout << "readprocmemory failed upp.cmd " << error << std::endl;
//}

std::wstring buffer(upp.CommandLine.Length / sizeof(wchar_t), 0);
if (!ReadProcessMemory(hwd, upp.CommandLine.Buffer, &buffer[0], upp.CommandLine.Length, &read)){
    auto error = GetLastError();
    std::cout << "readprocmemory failed uni.buff " << error << std::endl;
}

std::wcout << buffer << std::endl;

Note 1: This parameter should be sizeof(RTL_USER_PROCESS_PARAMETERS), NOT sizeof(PRTL_USER_PROCESS_PARAMETERS). Note the P at the start of the identifier. In Windows API notation, this means that the type in question is a pointer type, so sizeof(PRTL_USER_PROCESS_PARAMETERS) is basically the same as sizeof(void *).
Apr 14, 2020 at 2:10pm
helios that is fantastic ty. The thing with the size of prtl_user... etc. That was just a typo. But good catch i understand the difference for sure.

My last and final question for you is can you explain this line in more details?

 
std::wstring buffer(upp.CommandLine.Length / sizeof(wchar_t), 0);


I realize what is happening is that wstring is basically a wchar_t array but i thought part of the reason why std::string and im assuming its counterpart std::wstring were supposed to be convenient was (among other things) bc the memory was supposed to be dynamically allocated? Maybe i could be completely off base. I'm not that familiar with working with wide strings (obviously) but if i do like std::string="string" theres not issues with that.

if i try doing std::wstring=upp.CommandLine.Buffer it throws an exception in the xstring file. I'm just curious as to what the difference is. I realize that a "string" is known as a char[6] but the upp.commandline.buffer i'm assuming is raw bytes and since there might not be a terminating character std::wstring might not know when to stop its memory allocation? Im just kinda thinking about this and typing at the same time. Just trying to learn as much as i can. But Ty very interesting and definitely learning alot as i go here.

thankyou to everyone that has responded. I appreciate it . Now that this issue is solved i don't know what to do next lol.
Last edited on Apr 14, 2020 at 2:28pm
Apr 14, 2020 at 2:30pm
I realize what is happening is that wstring is basically a wchar_t array but i thought part of the reason why std::string and im assuming its counterpart std::wstring were supposed to be convenient was (among other things) bc the memory was supposed to be dynamically allocated?
I don't understand what you're asking. The line you quoted does cause dynamic allocation, since the value of upp.CommandLine.Length isn't known until run time.

if i try doing std::wstring=upp.CommandLine.Buffer it throws an exception in the xstring file.
Obviously that won't work. upp.CommandLine.Buffer holds a pointer to memory in a different address space: that of the remote process. Even if that address was actually allocated in the current process you'd just be copying meaningless garbage into the string.
Last edited on Apr 14, 2020 at 2:30pm
Apr 14, 2020 at 8:46pm
ok i gotcha. That is nice little trick. ty for all your help.
Topic archived. No new replies allowed.