Search code examples
windowswinapidllprivilegesdll-injection

Windows privileges, getting handle of .dll when not admin, not sure


Ok, I'm learning about the Windows API and how to create threads/processes and get debug rights etc. Very new so I apologize if this is a stupid problem.

Anyways, I'm creating a .dll injector to play around with, and have successfully been able to inject 32 and 64 bit processes, including explorer.exe by injecting my .dll file. However, I'm trying to test this in a standard user mode and am having problems figuring out how.

Right now I'm compiling as x64 for both the .dll and injector.exe. I'm trying to inject into a x64 process, primarily explorer.exe (which works on admin). Using Visual Studio 2012, with some optimizations, no manifest, no debug(only when needed). OS= Win 7 x64

Also, I have tried to access the .dll from multiple places, E:\ drive is just another partition on my HD.

  1. Why exactly does this happen?
  2. What are some ways to get around it? (References, explanations, examples wanted if possible)

The NtCreateThreadEx(); call returns a thread that is 0. Thus it did not load correctly.

#include <windows.h>
#include <iostream>
#include <stdlib.h>
#include <tlhelp32.h>

#define CREATE_THREAD_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ) 

using namespace std;

DWORD getPid(string procName);
int privileges();

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;
    PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;

typedef DWORD(WINAPI *NTCREATETHREADEX)
(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID Unknown
);

int main()
{
    cout << sizeof(void*) << endl;
    privileges();  //don't mind of the result, because maybe it fails because you already have that privilege

    DWORD pid = getPid("explorer.exe");
    if (pid == 0) return 1; //error

    HANDLE p;
    p = OpenProcess(CREATE_THREAD_ACCESS, false, pid);
    if (p == NULL) return 1; //error

    char * dll = "E:\\logger_mailer.dll";
    BOOL is32 = FALSE;
    BOOL fnWow64Ret = IsWow64Process(p, &is32);

    if (!fnWow64Ret)
    {
        cout<<"Error!!!!!!"<<endl;
        return 1;
    }

    //if (is32)  //If true process is 32 bit
    //  dll = "D:\\logger_mailer.dll";
    //else
    //{
    //  dll = "D:\\logger_mailer.dll";
    //  //cout<<"Error"<<endl;
    //}

    unsigned long LoadLib = (unsigned long)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

    LPVOID DataAddress = VirtualAllocEx(p, NULL, strlen(dll) + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    WriteProcessMemory(p, DataAddress, dll, strlen(dll), NULL);

    HANDLE thread;
    NTCREATETHREADEX NtCreateThreadEx = (NTCREATETHREADEX)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateThreadEx");
    if (NtCreateThreadEx) {
        NtCreateThreadEx(&thread, GENERIC_ALL, NULL, p, (LPTHREAD_START_ROUTINE)LoadLib, DataAddress, FALSE, NULL, NULL, NULL, NULL);
    }
    else {
        thread = CreateRemoteThread(p, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLib, DataAddress, NULL, NULL);
    }

    if (thread != 0){
        //injection completed
        WaitForSingleObject(thread, INFINITE);   //this waits untill thread thread has finished
        VirtualFree(dll, 0, MEM_RELEASE); //free myFunc memory
        VirtualFree(DataAddress, 0, MEM_RELEASE); //free data memory
        CloseHandle(thread);
        CloseHandle(p);  //don't wait for the thread to finish, just close the handle to the process
        cout << "Injection completed!" << endl;
    }
    else{
        cout << "Error!" << endl;
    }


    system("PAUSE");
    return EXIT_SUCCESS;
}

DWORD getPid(string procName){
    HANDLE hsnap;
    PROCESSENTRY32 pt;
    hsnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    pt.dwSize = sizeof(PROCESSENTRY32);
    do{
        if (!strcmp(pt.szExeFile, procName.c_str())){
            DWORD pid = pt.th32ProcessID;
            CloseHandle(hsnap);
            return pid;
        }
    } while (Process32Next(hsnap, &pt));
    CloseHandle(hsnap);
    return 0;
}

int privileges(){
    HANDLE Token;
    TOKEN_PRIVILEGES tp;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token))
    {
        LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
        tp.PrivilegeCount = 1;
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        if (AdjustTokenPrivileges(Token, 0, &tp, sizeof(tp), NULL, NULL) == 0){
            return 1; //FAIL
        }
        else{
            return 0; //SUCCESS
        }
    }
    return 1;
}

EDIT: So, after David and Harry basically coddled me through stuff, I have found that it is my .dll code that is failing. Also, my use of NtCreateThreadEx() was unneeded as CreateRemoteThread() does indeed work on win 7 x64. Many, posts here on SO and other sites specify otherwise. A simple message box was displayed to verify.

What is weird is that NtCreateThreadEx() works in admin mode, but not in standard.


Solution

  • I can see the following problems:

    1. In the call to WriteProcessMemory you pass the wrong length. You need to pass strlen(...)+1 in order to write the null-terminator.
    2. There is no need to use the undocumented NtCreateThreadEx. Using CreateRemoteThread works perfectly well for injection.
    3. You have only checked for errors on some of the API calls. You need to check errors on all your API calls. If you don't do that, nobody can know whether or not they succeed.

    For what it is worth, I've never had to use AdjustTokenPrivileges to inject. I believe that you can skip that step.


    In a comment you state that you are injecting into a different session. And you find that you need NtCreateThreadEx rather than CreateRemoteThread, and that injection only works when elevated. Of course, the requirement to inject into a process in a different session is pretty important. That should be included in the question rather than appearing in comments.

    However, you should not be remotely surprised that you need to elevate to inject into a process in a different session. And for that scenario then quite possibly adding SE_DEBUG_NAME is needed.

    In short, it seems to me that the system is telling you to run elevated in order to inject into a process in a different session. That seems quite reasonable.


    On the other hand, you also state in comments that CreateRemoteThread returns a non-zero value. Which indicates success. That seems to say that you are mis-diagnosing the problem. If CreateRemoteThread is succeeding then the remote thread will run. You next need to work out what fails after that.

    And you also say that you are trying to inject into the explorer process which does not run in session 0. So there is a lot of confusion here. Perhaps I jumped the gun in adding this answer.

    In any case, in order for you to proceed I recommend:

    1. Fix the call to WriteProcessMemory.
    2. Work out which session the target process is in.
    3. Add error checking.