Wednesday, January 23, 2019

Beginning DLL Injection with Windows 10x64 and Visual Studio 2017 - Verifying it works

In the two previous posts, this one and this one, we first created our sample DLL code file and then the executable file. However, while that is all good, how do we know that it works. In this post we take a look at some different tool that can help us to confirm it works.

First up, once the file has been compiled and the executable launched, the following output is printed
on the screen:


DLLInjection-Basics.exe 7852
----\\//---- DLL Injection Basics by Nik Alleyne | www.securitynik.com ----\\//----
[*] My current process ID is:[15808]
[*] Enumerating all processes on the system ...
    -> [+] Successfully enumerated all processes
[*] Searching through [211] process looking for the process with PID:[7852] ...
...............................................................................................................................................................................................................
    -> [*] Process with PID:[7852] found
[*] Attempting to give myself [Debug] privileges ...
    -> [*] Successfully looked up current privileges
    -> [+] Successfully gained [SeDebugPrivilege] privileges!
[*] Attempting to attach to the remote process with PID:[7852] ...
    -> [+] Successfully attached to the remote process with PID:[7852]
    -> [+] The handle returned to the remote process is [00000000000001D8]
[*] Attempting to allocate space within the remote process with PID:[7852] ...
    -> [+] Successfully allocated space within the remote process
    -> [+] The base addressed returned for the remote process was [000002C426A40000]
[*] Attempting to write [SampleDLL.dll] into the memory of process with PID:[7852] at base address:[000002C426A40000] ...
    -> [+] Successfully wrote [260] bytes to the process with PID:[7852] memory
[*] Attempting to create a new thread in the remote process ...
    -> [+] Successfully created the new thread in the remote process
    -> [*] The handle received for the remote thread is [00000000000001DC]
    -> [*] I'm done my work. Let's now get out of here!
----------\\//--- www.securitynik.com ---//\\----------

The above suggests that everything went well. Let's first verify that a process with PID 7852 is running on our system.

C:\Users\Security Nik>tasklist /FI "PID eq 7852"

Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
notepad.exe                  7852 Console                    1     15,676 K

From above it would be safe to say the PID 18296 belongs to notepad.exe.

Next up, above the application says it has obtained "Debug" privileges. Let's confirm this.





































If you notice above, the PID is different from the one I originally had. Not to worry about that, I just had to rerun the code to get the debug information as I had not captured it before. However, as can be seen above the program has obtained "SeDebugPrivilege" which allows it to "Debug Programs".

Let's poke around a bit more in Process Explorer and look at the memory of this process. Below we see "SampleDLL.dll" has been written into this process's memory.















If we look at the modules tab, we see the SampleDLL.dll module is loaded.








Let's now use WinDBG to look at the memory base address "000002C426A40000" to see what we have here.

0:002> db 000002C426A40000
000002c4`26a40000  45 3a 5c 43 6f 64 65 5c-53 61 6d 70 6c 65 44 4c  E:\Code\SampleDL
000002c4`26a40010  4c 2e 64 6c 6c 00 00 00-00 00 00 00 00 00 00 00  L.dll...........
000002c4`26a40020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
000002c4`26a40030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
000002c4`26a40040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
000002c4`26a40050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
000002c4`26a40060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
000002c4`26a40070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

From above we see the path "E:\Code\SampleDLL.dll" to our DLL is loaded at the base addressed returned when we ran the program.

Let's see if we can learn anything else about this DLL. Let's first look at the "lm" command which list the modules. Let's be specific with our search.


0:002> lm m sample*
Browse full module list
start             end                 module name
00007ffb`82fe0000 00007ffb`82feb000   SampleDLL   (deferred)             


Let's next see what other information we can get about this DLL

0:002> lmDvm SampleDLL
Browse full module list
start             end                 module name
00007ffb`82fe0000 00007ffb`82feb000   SampleDLL   (deferred)             
    Image path: E:\Code\SampleDLL.dll
    Image name: SampleDLL.dll
    Browse all global symbols  functions  data
    Timestamp:        Tue Jan  1 12:18:39 2019 (5C2BA0EF)
    CheckSum:         00000000
    ImageSize:        0000B000
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
    Information from resource tables:


If we look below at the starting address specified by this DLL, we can see this is an executable file based on the "MZ" magic (Signature)  and we see the MS-DOS stub also.


0:002> db 00007ffb`82fe0000
00007ffb`82fe0000  4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00  MZ..............
00007ffb`82fe0010  b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00  ........@.......
00007ffb`82fe0020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00007ffb`82fe0030  00 00 00 00 00 00 00 00-00 00 00 00 f8 00 00 00  ................
00007ffb`82fe0040  0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68  ........!..L.!Th
00007ffb`82fe0050  69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f  is program canno
00007ffb`82fe0060  74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20  t be run in DOS 
00007ffb`82fe0070  6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00  mode....$.......


If we look into into the process memory a bit, we can see some strings


0:002> db 00007ffb`82fe4470 L170
00007ffb`82fe4470  67 20 53 61 6d 70 6c 65-44 4c 4c 2e 64 6c 6c 20  g SampleDLL.dll 
00007ffb`82fe4480  0a 00 00 00 00 00 00 00-50 72 6f 63 65 73 73 20  ........Process 
00007ffb`82fe4490  77 69 74 68 20 50 49 44-3d 5b 00 00 00 00 00 00  with PID=[......
00007ffb`82fe44a0  5b 2a 5d 20 50 72 6f 63-65 73 73 20 73 74 61 72  [*] Process star
00007ffb`82fe44b0  74 65 64 20 6f 6e 3a 20-00 00 00 00 00 00 00 00  ted on: ........
00007ffb`82fe44c0  2d 2d 2d 2d 2d 2d 2d 2d-2d 2d 2d 2d 2d 3d 3d 3d  -------------===
00007ffb`82fe44d0  3d 3d 3d 2d 2d 2d 2d 2d-2d 2d 2d 2d 2d 2d 2d 2d  ===-------------
00007ffb`82fe44e0  7c 7c 2d 2d 2d 2d 2d 2d-2d 2d 2d 2d 2d 2d 2d 2d  ||--------------
00007ffb`82fe44f0  3d 3d 3d 3d 3d 3d 2d 2d-2d 2d 2d 2d 2d 2d 2d 2d  ======----------
00007ffb`82fe4500  2d 2d 2d 0a 00 00 00 00-20 20 20 20 20 20 20 20  ---.....        
00007ffb`82fe4510  20 20 20 20 20 20 57 65-6c 63 6f 6d 65 20 74 6f        Welcome to
00007ffb`82fe4520  20 53 65 63 75 72 69 74-79 4e 69 6b 27 73 20 57   SecurityNik's W
00007ffb`82fe4530  6f 72 6c 64 20 0a 00 00-00 00 00 00 00 00 00 00  orld ...........
00007ffb`82fe4540  20 20 20 20 20 20 20 54-68 69 73 20 44 4c 4c 20         This DLL 
00007ffb`82fe4550  77 61 73 20 69 6e 6a 65-63 74 65 64 20 62 79 20  was injected by 
00007ffb`82fe4560  74 68 65 20 74 6f 6f 6c-20 27 44 4c 4c 49 6e 6a  the tool 'DLLInj
00007ffb`82fe4570  65 63 74 69 6f 6e 2d 42-61 73 69 63 73 2e 65 78  ection-Basics.ex
00007ffb`82fe4580  65 27 0a 20 00 00 00 00-00 00 00 00 00 00 00 00  e'. ............
00007ffb`82fe4590  20 20 20 20 20 20 20 20-20 20 20 20 20 20 20 20                  
00007ffb`82fe45a0  20 20 20 20 4e 69 6b 20-41 6c 6c 65 79 6e 65 20      Nik Alleyne 
00007ffb`82fe45b0  7c 7c 20 77 77 77 2e 73-65 63 75 72 69 74 79 6e  || www.securityn
00007ffb`82fe45c0  69 6b 2e 63 6f 6d 20 20-20 20 20 20 20 20 20 20  ik.com          
00007ffb`82fe45d0  20 20 0a 20 00 00 00 00-00 00 00 00 00 00 00 00    . ............

Now let's take a look at some of Process Monitor's output.



From above we see Process Monitor capturing the "Thread Create" activity and then immediately, we see there is activity for the file "SampleDLL.dll". If look closely we will see the "Load Image" for SampleDLL.dll and then we see activity for our file "C:\Users\Security Nik\SecurityNik.txt" being written and close.

Last but not least, our text file was created and have the following contents:

c:\Users\Security Nik\ type c:\home\SecurityNik\SecurityNik.txt
Process with PID=[10512loading Sample DLL
[*] Process started execution on current local time:Tue Jan  1 11:38:36 2019
-------------======-------------||--------------======-------------
              Welcome to SecurityNik's World
       This DLL was injected by the tool 'DLLInjection-Basics.exe'
                     Nik Alleyne || www.securitynik.com
 -------------======-------------||----------------======-----------


Once again, don't get too hung-up on the fact that the PIDs may not match throughout, the concepts remain the same it is just that I did not have all the information ready at one time.

Hope you enjoyed the read.

References:
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/lm--list-loaded-modules-

Beginning DLL Injection with Windows 10x64 and Visual Studio 2017 - The DLL Code

In this post, we have the sample code used to make the "SampleDLL.dll" file. This file works in conjunction with the code in this next post.

The basic idea here is that this DLL creates a a text file on the fileystem and writes out some basic information which confirms that it is was called and successfully executed.


// dllmain.cpp : Defines the entry point for the DLL application.

/*
Author: Nik Alleyne
Author Blog: www.securitynik.com
Date: 2019-01-10
File: sampleDLL.c

Note: This code was developed stricly for education purposes and is not to be used for anything malicious.
If you use this program in any malicious way or damage your computing systems, in no way am I responsible.

*/

#include "stdafx.h"

#include <time.h>
#include <iostream>
#include <fstream>
using namespace std;

// Disable warning about deprecated function
#pragma warning(disable:4996);

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  reasonForCall,
                       LPVOID lpReserved
                     )
{
 ofstream myTextFile;
 time_t currentTime = time(NULL);

    switch (reasonForCall)
    {
    case DLL_PROCESS_ATTACH:
  myTextFile.open("SecurityNik.txt");
  myTextFile << "Process with PID=[" << GetCurrentProcessId() << "] loading SampleDLL.dll \n" ;
  myTextFile << "[*] Process started on: " << ctime(&currentTime);
  myTextFile << "-------------======-------------||--------------======-------------\n";
  myTextFile << "              Welcome to SecurityNik's World \n"                 ;
  myTextFile << "       This DLL was injected by the tool 'DLLInjection-Basics.exe'\n ";
  myTextFile << "                    Nik Alleyne || www.securitynik.com            \n ";
  myTextFile << "-------------======-------------||----------------======-----------\n";
  myTextFile.close();
  break;
 
    case DLL_THREAD_ATTACH:
   // printf("Thread has been created!\n");
   break;

    case DLL_THREAD_DETACH:
   // printf("Thread is exiting!\n");
   break;

    case DLL_PROCESS_DETACH:
  // printf("Process is exiting!\n");
        break;
    }
    return TRUE;
}
/*
References:
http://www.cplusplus.com/doc/tutorial/files/
https://www.cprogramming.com/tutorial/lesson10.html
https://www.tutorialspoint.com/cplusplus/cpp_date_time.htm
https://support.microsoft.com/en-us/help/815065/what-is-a-dll
https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4996?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(C4996)%26rd%3Dtrue&view=vs-2017
*/

Well there is not much to this post as the next post, has most of the work which needed to be done. See you there.



Beginning DLL Injection with Windows 10x64 and Visual Studio 2017 - The Exe Code

This post is a continuation from the previous one, in which we developed the DLL source code. This post focuses on the exe code which is needed to call and execute the DLL. I've put as much comments as possible in the code to ensure not only that you are able to read and understand but also that I can remember clearly when I need to revisit this. At the same time, I've ensured I referenced the materials I used to come up with this code. Note, most of this code is from Microsoft's MSDN and other sites. I simply re-purpose the code to suit y need.


// DLLInjection-Basics.cpp : Defines the entry point for the console application.
//

/*
Author: Nik Alleyne
Author Blog: www.securitynik.com
Date: 2019-01-10
File: dllInjection-Basics.c

Note: This code was developed stricly for education purposes and is not to be used for anything malicious.
If you use this program in any malicious way or damage your computing systems, in no way am I responsible.

*/


#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <psapi.h>
#include <string.h>


// This function basically prints out the usage information to the screen if someone enters less than or more than 1 argument
void usageInfo()
{
 printf("DLLInjection-Basics.exe  \nNik Alleyne | www.securitynik.com \n");
 printf("Usage: To use this program, you must enter the DLLInjection-Basics.exe followed by the PID\n");
 printf("eg. DLLInjection-Basics.exe 1020 \n");
 printf("-----------------------------------------------------------\n");
 printf("This tool should be used only for educational purposes. \nIf you plan to use it for something nefarious, \nyou should STOP using it immediately.\n");
 printf("------------------------------------------------------------\n");
 exit(0);
}


// This function is used for granting this program 'Debug' privileges
BOOL setDeugPrivilege(HANDLE myToken, LPCTSTR myDesiredAccess, BOOL trueFalse)
{
 TOKEN_PRIVILEGES myTokenPrivileges;
 LUID myLuid;

 printf("[*] Attempting to give myself [Debug] privileges ... \n");
 Sleep(2000);
 if (!LookupPrivilegeValue(NULL, myDesiredAccess, &myLuid))
 {
  printf("    -> [!] Encountered an error while looking up privileges. The error code was %d \n", GetLastError());
  return FALSE;
 }
 else
  printf("    -> [*] Successfully looked up current privileges \n");
 
 Sleep(2000);
 
 // Identify as 1 the number of items in the privilege array
 myTokenPrivileges.PrivilegeCount = 1;
 myTokenPrivileges.Privileges[0].Luid = myLuid;

 if (trueFalse)
  myTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
 else
  myTokenPrivileges.Privileges[0].Attributes = 0;

 // After all of that, let's now officially enable the privilege
 if ((AdjustTokenPrivileges(myToken, FALSE, &myTokenPrivileges, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) && GetLastError() == 0 )
  printf("    -> [+] Successfully gained [%ws] privileges! \n", myDesiredAccess);
 else
 {
  printf("    -> [!] Damm! Looks like we hit a snag while setting the 'Debug' Privilege. \n");
  printf("    -> [!] Try running me again as administrator. \n");
  printf("    -> [!] Current error code is %d \n", GetLastError());
  return FALSE;
 }

 return TRUE;
}



int main(int argc, char *argv[])
{

 // clear the screnn before we get going
 system("cls");

 // Check the number of arguments being passed. If not equal to 1 print info from usage function above
 // Note below we are checking "!=2" this is because the program itself is argv[0]. This means the PID will be argv[1]
 // Hence the total of 2 arguments being checked
 if (argc != 2)
  usageInfo();

 printf("----\\\\//---- DLL Injection Basics by Nik Alleyne | www.securitynik.com ----\\\\//---- \n");
 Sleep(2000);
 printf("[*] My current proces ID is:[%d] \n", GetCurrentProcessId());
 Sleep(2000);
 
 //Let's enumerate the process ID entered on the command line as argv[1]
 // Variables to be used by EnumProcesses Function arguments
 DWORD myProcessList[1024], myProcessListArraySize, myProcessCount;
 unsigned int count = 0;

 // Enumerate all processes on the system
 printf("[*] Enumerating all processes on the system ... \n");
 Sleep(2000);
 if (!EnumProcesses(myProcessList, sizeof(myProcessList), &myProcessListArraySize))
 {
  printf("   -> [+] Uh oh! Looks like we it a snag. Try me again later. \n");
  return FALSE;
 }
 else
 {
  printf("    -> [+] Successfully enumerated all processes \n");
  Sleep(2000);
 }
 
 // Now that we have enumerated all the processes, let's get the total number returned
 myProcessCount = myProcessListArraySize / sizeof(DWORD);

 // Let's now search the processes to look for the one with the process ID entered on the command line
 printf("[*] Searching through [%u] process looking for the process with PID:[%u] ... \n", myProcessCount, atoi(argv[1]));
 Sleep(2000);
 // This variable keep track of whether or not the PID is found from the returned list
 BOOL pidFound = FALSE;
 
 // Step through the list of process IDs returned 
 for (count; count < myProcessCount; count++)
 {
  // If you want to see all currently running PIDs uncomment the printf statement below
  // printf("[*] PID:[%u] \n", myProcessList[count]);

  // compare each entry as they are returned against the value entered in the command line
  if (myProcessList[count] == atoi(argv[1]))
  {
   printf("\n    -> [*] Process with PID:[%u] found \n", atoi(argv[1]));
   Sleep(2000);
   pidFound = TRUE;
   break;
  }
  else
  {
   printf(".");
   Sleep(1);
  }

 }
 // if the PID is not found, print a message to the screen
 if (!pidFound)
 {
  printf("\n    -> [!] Bummer! Could not find a process with PID:[%u] \n", atoi(argv[1]));
  return FALSE;
 }
 
 // Gain a handle to the current process
 HANDLE myCurrentProcess = GetCurrentProcess();
 HANDLE myToken;

 // Call the function defined above to set the debug privileges
 if (OpenProcessToken(myCurrentProcess, TOKEN_ALL_ACCESS, &myToken))
  if (!setDeugPrivilege(myToken, SE_DEBUG_NAME, TRUE))
   return FALSE;
 
 // Attach to the process specified by the PID provided
 Sleep(2000);
 printf("[*] Attempting to attach to the remote process with PID:[%u] ... \n", atoi(argv[1]));
 Sleep(2000);
 HANDLE myRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, atoi(argv[1]));

 if ( myRemoteProcess )
 {
  printf("    -> [+] Successfully attached to the remote process with PID:[%u] \n", atoi(argv[1]));
  Sleep(2000);
  printf("    -> [+] The handle returned to the remote process is [%p] \n", myRemoteProcess);
  Sleep(2000);
 }
 else
 {
  printf("    -> [!] Error encountered while attaching to the remote process with PID:[%u] \n", atoi(argv[1]));
  printf("    -> [!] The error code was %u \n", GetLastError());
  return FALSE;
 }


 // Now that we have attached to the process, let's allocate space within it
 // VOID *myRemoteProcessBaseAddress = NULL;
 printf("[*] Attempting to allocate space within the remote process with PID:[%u] ... \n", atoi(argv[1]));
 Sleep(2000);
 VOID *myRemoteProcessBaseAddress = VirtualAllocEx(myRemoteProcess, (LPVOID)NULL, 256, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

 if ( myRemoteProcessBaseAddress != NULL )
 {
  printf("    -> [+] Successfully allocated space within the remote process\n");
  Sleep(2000);
  printf("    -> [+] The base addressed returned for the remote process was [%p] \n", myRemoteProcessBaseAddress);
  Sleep(2000);
 }
 else
 {
  printf("    -> [!] Looks like we hit a road block with allocating space in the process \n");
  printf("    -> [!] The error code returned was %d \n", GetLastError());
  return FALSE;
 }

 //  Now that we have allocated the space in the process memory, let's write our dll to this space
 printf("[*] Attempting to write [SampleDLL.dll] into the memory of process with PID:[%u] at base address:[%p] ...\n", atoi(argv[1]), myRemoteProcessBaseAddress);
 Sleep(2000);
 // define the path for my DLL
 CHAR myDLLPath[MAX_PATH] = {0};
 SIZE_T myNumOfBytesWritten = 0;
 strcpy_s(myDLLPath, sizeof(myDLLPath) + 1, "E:\\Code\\SampleDLL.dll");

 if (WriteProcessMemory(myRemoteProcess, myRemoteProcessBaseAddress, (LPVOID *)myDLLPath, sizeof(myDLLPath), &myNumOfBytesWritten))
  printf("    -> [+] Successfully wrote [%u] bytes to the process with PID:[%d] memory \n", myNumOfBytesWritten, atoi(argv[1]));
 else
 {
  printf("    -> [!] Error occurred while writing to the process memory");
  printf("    -> [!] The error code was %d \n", GetLastError());
  return FALSE;
 }

 // Let's prepare to wrap this up by creating a thread in the remote process
 Sleep(2000);
 printf("[*] Attempting to create a new thread in the remote process ... \n");
 Sleep(2000);
 HANDLE myRemoteProcessNewThread = CreateRemoteThread(myRemoteProcess, (LPSECURITY_ATTRIBUTES)NULL, 0, (LPTHREAD_START_ROUTINE)(GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA")), myRemoteProcessBaseAddress, 0, (LPDWORD)NULL);
 if (myRemoteProcessNewThread != NULL)
 {
  printf("    -> [+] Successfully created the new thread in the remote process \n");
  Sleep(2000);
  printf("    -> [*] The handle received for the remote thread is [%p]\n", myRemoteProcessNewThread);
  Sleep(2000);
  printf("    -> [*] I'm done my work. Let's now get out of here! \n");
  Sleep(2000);
  printf("----------\\\\//--- www.securitynik.com ---//\\\\----------\n");
 }
 else
 {
  printf("    -> [!] Bummer! Looks like we have an error \n");
  return FALSE;
 }
 // Sleep(100000);
    return 0;
}

/*
References:
http://blog.opensecurityresearch.com/2013/01/windows-dll-injection-basics.html
http://deniable.org/misc/inject-all-the-things
https://docs.microsoft.com/en-us/previous-versions/td1esda9(v=vs.140)
https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-getprocaddress
https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-loadlibrarya
https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createremotethread
https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess
https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-openprocess
https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-openprocesstoken
https://docs.microsoft.com/en-us/windows/desktop/api/psapi/nf-psapi-enumprocesses
https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-adjusttokenprivileges
https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_luid_and_attributes
https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_privilege_set
https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_token_privileges
https://docs.microsoft.com/en-us/windows/desktop/Debug/system-error-codes
https://docs.microsoft.com/en-us/windows/desktop/Memory/memory-protection-constants
https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-security-and-access-rights
https://docs.microsoft.com/en-us/windows/desktop/psapi/enumerating-all-processes
https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/access-rights-for-access-token-objects
https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/enabling-and-disabling-privileges-in-c--
https://docs.microsoft.com/en-us/windows/desktop/SecGloss/a-gly
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debug-privilege
https://msdn.microsoft.com/en-us/library/windows/desktop/aa366890%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
https://msdn.microsoft.com/en-us/library/windows/desktop/ms681674%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
https://stackoverflow.com/questions/13080927/about-the-msdn-code-example-enabling-and-disabling-privileges
https://vulnerablelife.wordpress.com/2017/01/12/windows-dll-injection-basics/
https://www.codeproject.com/Articles/20084/A-More-Complete-DLL-Injection-Solution-Using-Creat
*/


See you in the next post where we confirm whether or not this is actually working as expected.

Walking Windows 10 process list with WinDbg

This post is based on me refreshing my knowledge about processes in Windows and is based primarily on Ami Awbadhho article on his Jump$ blog. While that post focused on 32 bit Windows, I'm looking more at my Windows 10 64 bit.

To begin walking the Windows process list, we have to start by first enabling kernel mode debugging on our host. To enable kernel mode debugging on Windows 10, I did the following within an "Administrator" command prompt (cmd.exe).

C:\WINDOWS\system32>bcdedit /debug ON
The operation completed successfully.

Once the computer rebooted and I started WinDbg, the next thing was to take a look at what the doubly linked list structure looks like.

lkd> dt _LIST_ENTRY
nt!_LIST_ENTRY
   +0x000 Flink            : Ptr64 _LIST_ENTRY
   +0x008 Blink            : Ptr64 _LIST_ENTRY

Note: to get help on the commands I've used, in the input bar of the WinDbg "command" window, execute ".hh" and this will bring up the help screen. Once in there search for your command to learn what they do.

Above, we see that the list has two entries. One is the "Flink" and the second is the "Blink". To understand how these are used, we can look at the following blog post on "Kernel-Mode Basics: Windows Linked Lists" from the folks at OSR Online.
 
To begin walking the list, we need to find the head of this list. We can do this by looking at "nt!PsActiveProcessHead".

lkd> x nt!PsActiveProcessHead
fffff800`625ad3b0 nt!PsActiveProcessHead = <no type information>

From above, we see that our Active Process Head can be found at address "fffff800`625ad3b0".

Now that we have the pointer to the list, let's see what its Flink and Blink points to:

lkd> dt _LIST_ENTRY fffff800`625ad3b0
nt!_LIST_ENTRY
 [ 0xffff998b`c62cb328 - 0xffff998b`e9ae9368 ]
   +0x000 Flink            : 0xffff998b`c62cb328 _LIST_ENTRY [ 0xffff998b`c636b328 - 0xfffff800`625ad3b0 ]
   +0x008 Blink            : 0xffff998b`e9ae9368 _LIST_ENTRY [ 0xfffff800`625ad3b0 - 0xffff998b`e636d368 ]

Ok! From above we see some values returned for the Flink and Blink. Let's now learn where within the EPROCESS structure is the _LIST_ENTRY for "ActiveProcessLinks" found. Rather than list the entire EPROCESS structure, I've taken a bit of a short cut by specifying the "-y" option to get the specific field.

lkd> dt nt!_EPROCESS -y ActiveProcessLinks
   +0x2e8 ActiveProcessLinks : _LIST_ENTRY

To help get further clarity, let's pick on a process which is currently running. To see the list of processes currently known by the Kernel, let's use the "!process" extension.

lkd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS ffff998bc62cb040
    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 001aa002  ObjectTable: ffffd605aca14040  HandleCount: 6914.
    Image: System
....

PROCESS ffff998bd5c3b080
    SessionId: 1  Cid: 1858    Peb: baa6893000  ParentCid: 13d8
    DirBase: 31d200002  ObjectTable: 00000000  HandleCount:   0.
    Image: notepad.exe

PROCESS ffff998bd6303080
    SessionId: 1  Cid: 234c    Peb: e97bb99000  ParentCid: 198c
    DirBase: 3dc400002  ObjectTable: 00000000  HandleCount:   0.
    Image: firefox.exe

....

Above, we see a snapshot of some of the process returned from my use of "!process 0 0". Let's pick on "notepad.exe" at address "ffff998bd5c3b080"

lkd> !process 0xffff998bd5c3b080 0
PROCESS ffff998bd5c3b080
    SessionId: 1  Cid: 1858    Peb: baa6893000  ParentCid: 13d8
    DirBase: 31d200002  ObjectTable: 00000000  HandleCount:   0.
    Image: notepad.exe

Now that we have the process at the address and confirm it is "notepad.exe", let's now look at the EPROCESS structure of it.

lkd> dt nt!_EPROCESS ffff998bd5c3b080 -y ActiveProcessLinks
   +0x2e8 ActiveProcessLinks : _LIST_ENTRY [ 0xffff998b`d6303368 - 0xffff998b`d3d2c868 ]

From above, just as we saw before the "ActiveProcessLinks" can be found at +0x2e8. Therefore, to confirm if we subtract 0x2e8 from ffff998bd5c3b080, we will get to the head of the EPROCESS structure, let's test that theory.

lkd> dt nt!_EPROCESS ffff998bd5c3b080-0x2e8
   +0x000 Pcb              : _KPROCESS
   +0x2d8 ProcessLock      : _EX_PUSH_LOCK
   +0x2e0 UniqueProcessId  : 0xffffd605`b6216ea6 Void
   +0x2e8 ActiveProcessLinks : _LIST_ENTRY [ 0x00000001`00b60003 - 0xffff998b`d5c3b088 ]
   +0x2f8 RundownProtect   : _EX_RUNDOWN_REF
   +0x300 Flags2           : 0xd5c3b098
   +0x300 JobNotReallyActive : 0y0
   +0x300 AccountingFolded : 0y0
   +0x300 NewProcessReported : 0y0
.....

From above we see we are at the top of the EPROCESS structure. Let's move on to see how we can map the rest of the processes. To do so, let's do like Ami and go back to something we did before. That is to once again, figure out the head of the list by looking at the address of "PSActiveProcessHead".

lkd> x nt!PsActiveProcessHead
fffff800`625ad3b0 nt!PsActiveProcessHead = <no type information>

Once again, we get the value we had before in "fffff800`625ad3b0". Now that we have this, let's look at the _LIST_ENTRY once again.

lkd> dt nt!_LIST_ENTRY fffff800`625ad3b0
 [ 0xffff998b`c62cb328 - 0xffff998b`ea802568 ]
   +0x000 Flink            : 0xffff998b`c62cb328 _LIST_ENTRY [ 0xffff998b`c636b328 - 0xfffff800`625ad3b0 ]
   +0x008 Blink            : 0xffff998b`ea802568 _LIST_ENTRY [ 0xfffff800`625ad3b0 - 0xffff998b`e7b2b868 ]

Once again, the values look similar to what we saw before. Let's use the Flink value "0xffff998b`c62cb328" to begin walking the list. Do remember, we need to subtract "0x2e8" to get to the beginning of the next EPROCESS structure. Let's do that and grab the "ImageFileName" and the "ActiveProcessLinks" of the next process.

lkd> dt nt!_EPROCESS 0xffff998b`c62cb328-0x2e8 -y ActiveProcessLinks.Flink -y ActiveProcessLinks.Blink -y ImageFileName
   +0x2e8 ActiveProcessLinks       :  [ 0xffff998b`c636b328 - 0xfffff800`625ad3b0 ]
      +0x000 Flink                    : 0xffff998b`c636b328 _LIST_ENTRY [ 0xffff998b`d0e97868 - 0xffff998b`c62cb328 ]
      +0x008 Blink                    : 0xfffff800`625ad3b0 _LIST_ENTRY [ 0xffff998b`c62cb328 - 0xffff998b`ea78e468 ]
   +0x450 ImageFileName            : [15]  "System"

From above we see the first process is "System". We also see for it's "ActiveProcessLinks.Flink" that is the next process, it is at  "0xffff998b`c636b328" while the "ActiveProcessLinks.Blink" is at "0xfffff800`625ad3b0". Let's use the same logic we did previously to learn what is the next process seen by the Kernel after the "System" process. Do remember to once again subtract "0x2e8" from the "ActiveProcessLinks.Flink" value you received above.

lkd> dt nt!_EPROCESS 0xffff998b`c636b328-0x2e8 -y ActiveProcessLinks.Flink -y ActiveProcessLinks.Blink -y ImageFileName
   +0x2e8 ActiveProcessLinks       :  [ 0xffff998b`d0e97868 - 0xffff998b`c62cb328 ]
      +0x000 Flink                    : 0xffff998b`d0e97868 _LIST_ENTRY [ 0xffff998b`da09e868 - 0xffff998b`c636b328 ]
      +0x008 Blink                    : 0xffff998b`c62cb328 _LIST_ENTRY [ 0xffff998b`c636b328 - 0xfffff800`625ad3b0 ]
   +0x450 ImageFileName            : [15]  "Registry"

Above, we see the next process is the "Registry". Let's do two more examples to see if this pattern continues.

lkd> dt nt!_EPROCESS 0xffff998b`d0e97868-0x2e8 -y ActiveProcessLinks.Flink -y ActiveProcessLinks.Blink -y ImageFileName
   +0x2e8 ActiveProcessLinks       :  [ 0xffff998b`da09e868 - 0xffff998b`c636b328 ]
      +0x000 Flink                    : 0xffff998b`da09e868 _LIST_ENTRY [ 0xffff998b`d085b868 - 0xffff998b`d0e97868 ]
      +0x008 Blink                    : 0xffff998b`c636b328 _LIST_ENTRY [ 0xffff998b`d0e97868 - 0xffff998b`c62cb328 ]
   +0x450 ImageFileName            : [15]  "smss.exe"

... and finally for the Flink ...

lkd> dt nt!_EPROCESS 0xffff998b`da09e868-0x2e8 -y ActiveProcessLinks.Flink -y ActiveProcessLinks.Blink -y ImageFileName
   +0x2e8 ActiveProcessLinks       :  [ 0xffff998b`d085b868 - 0xffff998b`d0e97868 ]
      +0x000 Flink                    : 0xffff998b`d085b868 _LIST_ENTRY [ 0xffff998b`d0859868 - 0xffff998b`da09e868 ]
      +0x008 Blink                    : 0xffff998b`d0e97868 _LIST_ENTRY [ 0xffff998b`da09e868 - 0xffff998b`c636b328 ]
   +0x450 ImageFileName            : [15]  "csrss.exe"

Looks good so far. Let's verify this by looking by at the "!process" extension.

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS ffff998bc62cb040
    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 001aa002  ObjectTable: ffffd605aca14040  HandleCount: 7052.
    Image: System

PROCESS ffff998bc636b040
    SessionId: none  Cid: 0060    Peb: 00000000  ParentCid: 0004
    DirBase: 1e0c00002  ObjectTable: ffffd605aca28c00  HandleCount:   0.
    Image: Registry

PROCESS ffff998bd0e97580
    SessionId: none  Cid: 01d4    Peb: 65129c2000  ParentCid: 0004
    DirBase: 296d10002  ObjectTable: ffffd605af644540  HandleCount:  52.
    Image: smss.exe

PROCESS ffff998bda09e580
    SessionId: 0  Cid: 02a8    Peb: 705bc80000  ParentCid: 028c
    DirBase: 199900002  ObjectTable: ffffd605ae720c00  HandleCount: 756.
    Image: csrss.exe

PROCESS ffff998bd085b580
    SessionId: 0  Cid: 0304    Peb: 3aa038000  ParentCid: 028c
    DirBase: 45bc00002  ObjectTable: ffffd605af212380  HandleCount: 155.
    Image: wininit.exe

PROCESS ffff998bd0859580
    SessionId: 1  Cid: 030c    Peb: 5e45a62000  ParentCid: 02f8
    DirBase: 334200002  ObjectTable: ffffd605aea12680  HandleCount: 1213.
    Image: csrss.exe

PROCESS ffff998bda3ea480
    SessionId: 0  Cid: 0354    Peb: a3794a6000  ParentCid: 0304
    DirBase: 19a00002  ObjectTable: ffffd605afc25e80  HandleCount: 847.
    Image: services.exe

PROCESS ffff998bd85e8580
    SessionId: 0  Cid: 0368    Peb: 6c5042e000  ParentCid: 0304
    DirBase: 107a00002  ObjectTable: ffffd605afd3b8c0  HandleCount: 1749.
    Image: lsass.exe

PROCESS ffff998bd8e25080
    SessionId: 1  Cid: 03a4    Peb: 35e2b86000  ParentCid: 02f8
    DirBase: 46a800002  ObjectTable: ffffd605b091f600  HandleCount: 256.
    Image: winlogon.exe

....

PROCESS ffff998bea2e4580
    SessionId: 1  Cid: 26cc    Peb: 1002d1000  ParentCid: 0f34
    DirBase: 4a700002  ObjectTable: ffffd605b9b0a040  HandleCount: 142.
    Image: SystemIdleCheck.exe

PROCESS ffff998bf5ea0380
    SessionId: 0  Cid: 27ac    Peb: ce9260a000  ParentCid: 0354
    DirBase: 61900002  ObjectTable: ffffd605c637b680  HandleCount: 192.
    Image: WmiApSrv.exe
The snapshot above looks to tie in nicely with what we have learned so far. I believe if we continue this pattern, we will be able to walk the tree and identify all the processes via the Flink. However, we did not verify the Blink. Let's test a few of those before we move on.

Believe it or not, it is just the same as we did above. Only difference is instead of using the address in the Flink, we instead use the addresses in the Blink.

Let's look at the "Registry" process to see what it points to via its Blink.

lkd> !process ffff998bc636b040 0
PROCESS ffff998bc636b040
    SessionId: none  Cid: 0060    Peb: 00000000  ParentCid: 0004
    DirBase: 1e0c00002  ObjectTable: ffffd605aca28c00  HandleCount:   0.
    Image: Registry

Let's look at its "ActiveProcessLinks.Flink" and "ActiveProcessLinks.Blink".

lkd> dt nt!_EPROCESS ffff998bc636b040 -y ActiveProcessLinks.Flink -y ActiveProcessLinks.Blink -y ImageFileName
   +0x2e8 ActiveProcessLinks       :  [ 0xffff998b`d0e97868 - 0xffff998b`c62cb328 ]
      +0x000 Flink                    : 0xffff998b`d0e97868 _LIST_ENTRY [ 0xffff998b`da09e868 - 0xffff998b`c636b328 ]
      +0x008 Blink                    : 0xffff998b`c62cb328 _LIST_ENTRY [ 0xffff998b`c636b328 - 0xfffff800`625ad3b0 ]
   +0x450 ImageFileName            : [15]  "Registry"

If we now look at the Blink value to see where it points, we see we get:

lkd> dt nt!_EPROCESS 0xffff998b`c62cb328-0x2e8 -y ActiveProcessLinks.Flink -y ActiveProcessLinks.Blink -y ImageFileName
   +0x2e8 ActiveProcessLinks       :  [ 0xffff998b`c636b328 - 0xfffff800`625ad3b0 ]
      +0x000 Flink                    : 0xffff998b`c636b328 _LIST_ENTRY [ 0xffff998b`d0e97868 - 0xffff998b`c62cb328 ]
      +0x008 Blink                    : 0xfffff800`625ad3b0 _LIST_ENTRY [ 0xffff998b`c62cb328 - 0xffff998b`ea5a35a8 ]
   +0x450 ImageFileName            : [15]  "System"

Nice!!! As expected, "Registry" Blink points to the previous process in the list which is "System". Let's look at one more. Once again, considering what we looked at previously, let's look at the "smss.exe" process to see where that points. If all goes according to plan, its Blink should be pointing to "Registry". Let's see if this holds true.

lkd> !process ffff998bd0e97580 0
PROCESS ffff998bd0e97580
    SessionId: none  Cid: 01d4    Peb: 65129c2000  ParentCid: 0004
    DirBase: 296d10002  ObjectTable: ffffd605af644540  HandleCount:  52.
    Image: smss.exe

Let's look at its "ActiveProcessLinks.Flink" and "ActiveProcessLinks.Blink".

lkd> dt nt!_EPROCESS ffff998bd0e97580 -y ActiveProcessLinks.Flink -y ActiveProcessLinks.Blink -y ImageFileName
   +0x2e8 ActiveProcessLinks       :  [ 0xffff998b`da09e868 - 0xffff998b`c636b328 ]
      +0x000 Flink                    : 0xffff998b`da09e868 _LIST_ENTRY [ 0xffff998b`d085b868 - 0xffff998b`d0e97868 ]
      +0x008 Blink                    : 0xffff998b`c636b328 _LIST_ENTRY [ 0xffff998b`d0e97868 - 0xffff998b`c62cb328 ]
   +0x450 ImageFileName            : [15]  "smss.exe"

Let's now test the Blink value of "0xffff998b`c636b328" to see where it points.


lkd> dt nt!_EPROCESS 0xffff998b`c636b328-0x2e8 -y ActiveProcessLinks.Flink -y ActiveProcessLinks.Blink -y ImageFileName
   +0x2e8 ActiveProcessLinks       :  [ 0xffff998b`d0e97868 - 0xffff998b`c62cb328 ]
      +0x000 Flink                    : 0xffff998b`d0e97868 _LIST_ENTRY [ 0xffff998b`da09e868 - 0xffff998b`c636b328 ]
      +0x008 Blink                    : 0xffff998b`c62cb328 _LIST_ENTRY [ 0xffff998b`c636b328 - 0xfffff800`625ad3b0 ]
   +0x450 ImageFileName            : [15]  "Registry"

Nice!! Very Nice!!! So we confirm that the Flink and Blink points to the next process structure and the previous structure respectively.

Now for the image below. The image below represents a snapshot of what we saw above. However, I did not map all processes but the concept remains the same.









Well hope you enjoyed this post.

As always, do leave a comment if you think I missed something.

Reference:
http://jumpdollar.blogspot.com/2014/09/windbg-walking-windows-linked-lists.html
http://www.osronline.com/article.cfm?article=499
https://www.blackhat.com/presentations/win-usa-04/bh-win-04-butler.pdf