DLL Injection w Windows za pomocą nieudokumentowanej funkcji w WinAPI

0

Witam

Czytałem sobie jakiś czas temu o DLL Injection i postanowiłem spróbować swoich sił.
Chciałbym uruchomić swój kod w obcym procesie.

Bawiłem się już z funkcją: CreateRemoteThread - sukces
A aktualnie walczę z nieudokumentowaną funkcją: NtCreateThreadEx

Oto co uzyskałem <kod>:
Dynamic Load Library:

 
#include <windows.h>

///Compilation with option -m32/-m64

extern "C" BOOL __stdcall DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
{
   MessageBox( NULL, "MESSAGE FROM 32 BIT DLL", "Lorem ipsum", MB_ICONINFORMATION | MB_OKCANCEL );
   return 0;
}

DLL Injector:

#include <iostream>
#include <string>
#include <windows.h>
///  64 bit OS - Windows 7
///=====================
///* In this same user context ("User")
///TYPE OF(32/64 bits)
///INJECTOR===DLL===PROCESS===RESULT
///   32      32     32      -SUCESS
///   32      64     32      -FALIED (Status NtCreateThreadEx  1300)
///   32      64     64      -FALIED (Status NtCreateThreadEx  1300)
///   64      32     32      -FALIED (Status NtCreateThreadEx  1300)
///   64      64     32      -FALIED (Status NtCreateThreadEx  1300)
///   64      64     64      -FALIED (Status NtCreateThreadEx  1300)
                    //Handle to process,Address of'LoadLibraryA',see DllAdr
///TO DO
///* Inject DLL to process from normal user context ("User") to higher user context (Zarzadca)
///* Inject DLL to process from normal user context ("User") to other normal user context (User1)


HANDLE NtCreateThreadEx(HANDLE hProcess,LPVOID lpBaseAddress,    LPVOID lpSpace);
int privileges();
int main()
{
    std::string pathToDLL;
    int PIDOfProcess = 0;
    pathToDLL        = "C:\\dll32.dll";
    DWORD PID        = (DWORD)PIDOfProcess; ///PID
    HANDLE HProcess  = NULL;                ///Handle to proces
    LPVOID LibAddr   = NULL;                ///Address of procedure 'LoadLibraryA'
    LPVOID DllAdr    = NULL;                ///Address of memory in other process
    HANDLE hThread   = NULL;                ///Handle to remote thread
    int WirteStatus  = 0;                   ///Status of writing to memory of other process

    std::cout << "Get PID of process" << std::endl;
    std::cin >> PIDOfProcess;
    ///std::cout << "Get path to DLL" << std::endl;
    ///std::cin >> pathToDLL;

    if( privileges() != 0 )
    {
        std::cout <<  "Cannot get the right privileges" << std::endl;
    }

    HProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    if(HProcess == NULL)
	{
		std::cout << "Could not find process" << std::endl;
        std::cout << GetLastError() << std::endl;
        system("pause");
        return GetLastError();
	}

    DllAdr = (LPVOID)VirtualAllocEx(HProcess, NULL, pathToDLL.size() +1, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if(DllAdr == NULL)
	{
	    std::cout <<"Can not allocate memory." << std::endl;
        std::cout << GetLastError() << std::endl;
        system("pause");
        return GetLastError();
	}

    WirteStatus = WriteProcessMemory(HProcess, (LPVOID)DllAdr, pathToDLL.c_str() ,pathToDLL.size()+1, NULL);
    if(WirteStatus == 0)
	{
		std::cout << "Could not write to process's address space" << std::endl;
        std::cout << GetLastError() << std::endl;
        system("pause");
        return GetLastError();
	}

    LibAddr = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    if(LibAddr == NULL)
	{
		std::cout << "Unable to locate LoadLibraryA" << std::endl;
        std::cout << GetLastError() << std::endl;
        system("pause");
        return GetLastError();
	}

    hThread = NtCreateThreadEx(HProcess,LibAddr,DllAdr);
    if(hThread == NULL)
    {
        std::cout << "Error of 'NtCreateThreadEx': ";
        std::cout << GetLastError() << std::endl;
        system("pause");
        return GetLastError();
    }
    system("pause");
}
HANDLE NtCreateThreadEx(HANDLE hProcess,LPVOID lpBaseAddress,LPVOID lpSpace)
{
    //The prototype of NtCreateThreadEx from undocumented.ntinternals.com
    typedef DWORD (WINAPI * functypeNtCreateThreadEx)(
        PHANDLE                 ThreadHandle,
        ACCESS_MASK             DesiredAccess,
        LPVOID                  ObjectAttributes,
        HANDLE                  ProcessHandle,
        LPTHREAD_START_ROUTINE  lpStartAddress,
        LPVOID                  lpParameter,
        BOOL                    CreateSuspended,
        DWORD                   dwStackSize,
        DWORD                   Unknown1,
        DWORD                   Unknown2,
        LPVOID                  Unknown3
    );

    HANDLE                      hRemoteThread           = NULL;
    HMODULE                     hNtDllModule            = NULL;
    functypeNtCreateThreadEx    funcNtCreateThreadEx    = NULL;

	//Get handle for ntdll which contains NtCreateThreadEx
    hNtDllModule = GetModuleHandle( "ntdll.dll" );
    if ( hNtDllModule == NULL )
    {
        std::cout << "Cannot get module  ntdll.dll  error: " << GetLastError() << std::endl;
    	return NULL;
    }
    funcNtCreateThreadEx = (functypeNtCreateThreadEx)GetProcAddress( hNtDllModule, "NtCreateThreadEx" );
    if ( !funcNtCreateThreadEx )
    {
        std::cout << "Cannot get procedure address  error: " << GetLastError() << std::endl;
    	return NULL;
    }
    funcNtCreateThreadEx( &hRemoteThread,  /*GENERIC_ALL*/0x1FFFFF, NULL, hProcess, (LPTHREAD_START_ROUTINE)lpBaseAddress, lpSpace,
                                                                                                                                                                         FALSE, NULL, NULL, NULL, NULL );
    std::cout << "Status NtCreateThreadEx  " << GetLastError()       << std::endl;
    std::cout << "hRemoteThread:           " << hRemoteThread        << std::endl;
    std::cout << "hNtDllModule:            " << hNtDllModule         << std::endl;
    std::cout << "funcNtCreateThreadEx:    " << funcNtCreateThreadEx << std::endl;
    return hRemoteThread;
}
int privileges()
{
  HANDLE Token;
  TOKEN_PRIVILEGES tp;
  if(OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&Token)) ///It opens the access token associated with a process.
  {
    LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);///Function retrieves the locally unique identifier (LUID)

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

        if (AdjustTokenPrivileges(Token, false, &tp, sizeof(tp), NULL, NULL) != 0)///Function enables or disables privileges in the specified access token.
        {
            return 0; //OK
        }
   }
   return 1;
}
 

Test App:

#include <iostream>

int main()
{
   int a;
   std::cout << "Podaj liczbe - get a number - Lorem  IPSUM" << std::endl;
   std::cin >> a;
   return 0;
}

 

PROBLEM:
Udało mi się jedynie wstrzyknąć 32 bitowa DLL 32 bitowym injectorem do 32 bitowego procesu - patrz "tablica prawdy" w kodzie injectora
Czy ktoś wie dlaczego tak się dzieje ? Co robie nie tak ?
#EDIT 1

///TYPE OF(32/64 bits)
///INJECTOR===DLL===PROCESS===RESULT
/// 32 32 32 -SUCESS
/// 32 64 32 -FALIED (Status NtCreateThreadEx 1300)
/// 32 64 64 -FALIED (Status NtCreateThreadEx 1300)
/// 64 32 32 -FALIED (Status NtCreateThreadEx 1300)
/// 64 64 32 -FALIED (Status NtCreateThreadEx 1300)
/// 64 64 64 -FALIED (Status NtCreateThreadEx 1300)

Jest tu taka mała tabelka myśle że w miare przejrzysta.
W pierwszej kolumnie jest typ (architektura na jaką kompilowałem) programu wstrzykującego.
W drugiej kolumnie jest typ (architektura na jaką kompilowałem) biblioteki dynamicznie ładowanej.
W trzeciej kolumnie jest typ (architektura) programu atakowanego.

Pracuje na 64 bitowym systemie operacyjnym Microsoft Virus Windows 7.

Udało mi się jedynie wstrzyknąć 32 bitowa DLL 32 bitowym injectorem do 32 bitowego procesu

Jak wynika z tabelki tylko to udało mi się osiągnąć zaś w pozostałych próbach otrzymuje kod błędu 1300
Kod ten oznacza:

Nie wszystkie wywoływane uprawnienia lub grupy są przypisane komputerowi wywołującemu.

Kompilator nie zgłasza żadnych błędów podczas kompilacji tylko klika ostrzeżeń.
Do kompilacji na architekture 32-bit użyłem GNU GCC

g++ (tdm-2) 4.8.1

a na 64 bit

g++ (tdm64-1) 5.1.0

Chce jeszcze dodać że działam na koncie standardowym ale myśle że dla procesu uruchomionego w tym samym kontekście użytkownika nie powinno mieć to znaczenia.

Nie mam pomysłu co jest przyczyną niepowodzeń, niestety nie znalazłem niczego przydatnego w sieci.

======
Na podstawie
http://www.p-programowanie.pl/cpp/dll-injection/ - Dll Injection
http://www.codeproject.com/Questions/369890/Ask-about-NtCreateThreadEx-in-Window-x - Ask about NtCreateThreadEx in Window 7 x64!
http://www.rohitab.com/discuss/topic/39535-code-injections-beginner-and-advanced/ Code Injections [beginner and advanced]
http://securityxploded.com/ntcreatethreadex.php Remote Thread Execution in System Process using NtCreateThreadEx for Vista & Windows7
http://cpp0x.pl/dokumentacja/WinAPI/Systemowe-kody-bledow-1300-1699/1470 Systemowe kody błędów (1300-1699)

0

Jeśli proces do którego injectujesz jest 32 bitowy, wtedy launcher i dllka muszą być 32 bitowe. Jeśli proces jest 64 bitowy, wtedy właściwie launcher i dllka muszą być 64 bitowe.
Inne konfiguracje nie mogą być brane pod uwagę. Nie można załadować do 32 bitowego procesu 64 bitowej dllki i vice versa.
Jeśli chodzi o injectora, no to sprawa jest nieco bardziej skomplikowana. Problem jest tutaj:

LibAddr = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

Cały problem w tym, że w injectorach wykorzystujemy pewne stałe zachowanie Windowsów, które polega na tym, że kernel32.dll jest zawsze na tym samym stałym adresie w każdym procesie w konkretnej architekturze. Przez co możemy stworzyć wątek, który wykona owe LoadLibraryA, z pewnym parametrem.

No i właśnie, każda architektura ma swój własny kernel32.dll, więc pobranie adresu do 32 bitowego LoadLibraryA i spróbowanie wykonania go jakkolwiek na 64 bitowej platformie zakończy się niepowodzeniem, ponieważ funkcje (np. LoadLibraryA) w różnych architekturach znajdują się w innych miejscach w pamięci. A poza tym, nawet gdyby znajdowałyby się na tych samych adresach, to już samo podanie adresu do nazwy dllki, którą chcemy zainjectować jest bardzo trudne a może i nawet niemożliwe.

Sposobem na 32 bitowy injector i 64 bitową dllkę i proces jest właściwie zapisanie bytecodu x64 tak jak zapisujesz ścieżkę do pliku, i uruchomienie wątku w procesie z adresem tegoż bytecodu. Problem może być z 32 bitowymi wskaźnikami, i sposób zmuszenia winapi do podawania wyników 64 bitowych i przyjmowania parametrów 64 bitowych. Nie mam pojęcia jak można by było to zrobić.

Jednak jeśli chodzi o wszystko 64 bitowe, no to pewnie coś źle skompilowałeś, że nie działa [coś jest 32 bitowe], lub ewentualnie gdzieś opakowujesz adres do 32 bitowego wskaźnika, i się obcina parę bajtów i wszystko jest już nieprawidłowe. To jest mój kod do injectowania (x64):

 
void foo()
{
        // Create game process
	STARTUPINFOW siStartupInfo = { 0 };
	PROCESS_INFORMATION piProcessInformation = { 0 };
	siStartupInfo.cb = sizeof(siStartupInfo);

	wchar_t szParams[1024] = { 0 }; // enough?
	swprintf(szParams, L"\"%s\" %s", m_szGameExeDirectory, szCmdLine);
	if (!CreateProcessW(m_szGameExeDirectory, szParams, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, m_szGameDirectory, &siStartupInfo, &piProcessInformation))
	{
		MessageBox(NULL, "Cannot create game process.", GAME_NAME_SHORT " - Error", MB_ICONERROR);
		return false;
	}

	// Inject dll
	wchar_t szPath[MAX_PATH] = { 0 };
	wcscpy(szPath, GetClientPath());

	wcscat(szPath, DLL_NAME);

	const wchar_t *injectResult = InjectDll(piProcessInformation.hProcess, szPath);
	if (injectResult != 0)
	{
		wchar_t szMessageError[1024] = { 0 };
		swprintf(szMessageError, L"Could not inject dll into the game process (%s). Please try launching the game again. If this problem still occurs, please try reinstalling the mod.", injectResult);
		MessageBoxW(NULL, szMessageError, 0, MB_ICONERROR);
		TerminateProcess(piProcessInformation.hProcess, 0);
		return false;
	}

	// Restore process thread
	ResumeThread(piProcessInformation.hThread);
}

const wchar_t *InjectDll(HANDLE hProcess, const wchar_t *szDllPath)
{
	const wchar_t *result = L"unknown result";

	SIZE_T sLibPathLen = (wcslen(szDllPath) + 1) * sizeof(wchar_t);
	void *pRmtLibraryPath = VirtualAllocEx(hProcess, NULL, sLibPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (!pRmtLibraryPath)
	{
		return L"can not allocate memory";
	}

	SIZE_T bytesWritten = 0;
	BOOL bWriteProcessMem = WriteProcessMemory(hProcess, pRmtLibraryPath, (void *)szDllPath, sLibPathLen, &bytesWritten);
	if (bytesWritten != sLibPathLen)
	{
		return L"bytes written and path length does not match";
	}

	if (bWriteProcessMem)
	{
		HMODULE hKernel32 = GetModuleHandleW(L"Kernel32");
		if (hKernel32)
		{
			FARPROC pfnLoadLibraryW = GetProcAddress(hKernel32, "LoadLibraryW");
			if (pfnLoadLibraryW)
			{
				DWORD threadId = 0;
				HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnLoadLibraryW, pRmtLibraryPath, 0, &threadId);
				if (hThread)
				{
					WaitForSingleObject(hThread, INFINITE);

					DWORD dwExitCode = 0;
					GetExitCodeThread(hThread, &dwExitCode);
					if (dwExitCode != 0)
					{
						result = nullptr;
					}
					else
					{
						result = L"initialization of client failed";
					}

					CloseHandle(hThread);
				}
				else
				{
					result = L"can not create remote thread";
				}
			}
			else
			{
				result = L"can not get LoadLibraryA address";
			}
			FreeModule(hKernel32);
		}
		else
		{
			result = L"can not get module handle kernel32.dll";
		}
		VirtualFreeEx(hProcess, pRmtLibraryPath, sizeof(pRmtLibraryPath), MEM_RELEASE);
	}
	else
	{
		result = L"can not write process memory";
	}
	return result;
}

0

Inne konfiguracje nie mogą być brane pod uwagę. Nie można załadować do 32 bitowego procesu 64 bitowej dllki i vice versa.

To wiem, to było dla testu jaki błąd wyskoczy :)
Naturalnie mam zamiar używać w ten sposób jak mówisz

Jeśli proces do którego injectujesz jest 32 bitowy, wtedy launcher i dllka muszą być 32 bitowe. Jeśli proces jest 64 bitowy, wtedy właściwie launcher i dllka muszą być 64 bitowe.

LibAddr = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); 

Czy nie wystarczy wyszukać tej procedury w 64 bitowym kernel32.dll (ale to brzmi .....)
Spróbuje zmienić "kernel32.dll" na C:\Windows\SysWOW64\kernel32.dll
Musze obadać.....

0

Dobra dalej mam kłopot
Wszystko rekompilowałem i kłopot który opisałem w pierwszym poście nie ustąpił

Wstrzykując 64 bit DLL do 64 bit procesu 64 bit injectorem mam błąd numer 1300

Nie wszystkie wywoływane uprawnienia lub grupy są przypisane komputerowi wywołującemu.

Nie wiem jak mam to rozumieć.
Spotkał się może ktoś z czymś takim ?

Dołączam obrazek.
screen01.png

0

Przykładowa apliacja ladująca DLL'ke

#include <iostream>
#include <windows.h>
int main()
{

   std::cout << " Lorem  IPSUM" << std::endl;

   HMODULE HDLL = LoadLibraryA("dll64.dll");

   std::cout << "Error: " << GetLastError() << std::endl;
   while(1)
   {
       std::cout << "petla" << std::endl;
       Sleep(1000);
   }
   return 0;
}

Bibliotekę udało się załadować lecz otrzymuje kod błędu 1114

Procedura inicjowania biblioteki dołączanej dynamicznie (DLL) nie powiodła się.

Aplikacja budowana jest z przełącznikiem -m64
Uruchomienie aplikacji jako administrator nic nie daje.
Trochę dziwne bo skoro DLLMain jest główna funkcją DLL'ki (wykonuje się ona gdyż jak widać messageBox się pokazuje)
to nie mam pojęcia jaka procedura się nie powiodła.

Wypróbuje czy to samo dzieje się podczas atakowania 32-bitowej aplikacji .....

PS.
Kod DLL'ki znajduje się w pierwszym poście.

====================
screen01.png

#EDIT1
Gdy testowa aplikacja ląduje 32 bitowa DLL'ke otrzymuje ten sam kod błędu - 1114
Gdy wstrzykuje 32 bit ijnectorem 32 bit DLL'ke do 32 bit aplikacji to działa - pokazuje się messageBox lecz
funkcja NtCreateThreadEx zwraca błąd 1300 czyli jak wspomniano wcześniej

Nie wszystkie wywoływane uprawnienia lub grupy są przypisane komputerowi wywołującemu.

Okazuje się jednak że gdy injector ma prawa administratorskie kod błędu tej funkcji to 0 (czyli powodzenie)

Mimo wszystko dalej nie wiem czemu 64 bit wersja nie działa.
screen02.PNG

0

Całkowicie pomińmy 32 bitową wersje. Jeśli działa, to nie wspominaj jej.

Kod błędu 1114 dostajesz prawdopodobnie dlatego, że zwracasz 0 (czyli FALSE) z DllMain.

Cały problem w tym, że LoadLibrary "wpisze" do GetLastError błąd, ale ty nie możesz go pobrać, bo jesteś w innym procesie. Kiedyś tutaj na forum pokazywałem pewien kod, który sobie z tym radził ale chyba nikt nie zrozumiał o co w nim tak na prawdę chodziło.

pathToDLL        = "C:\\dll32.dll";

To jest dobrze? Upewnij się, że na pewno próbujesz ładować odpowiednią bibliotekę.

0

Witam ponownie - udało mi się rozwiązać wcześniejszy problem z injectorem. (Dla przypomnienia - problem polegał na tym że próba uruchomienia wątku [za pomocą nieudokumentowanej funkcji API systemu Windows] który wykonywał by kod z DLL z uprawnieniami atakowanego procesu nie udawała się)

Źródłem kłopotu było jak się okazało używanie w ? deklaracji prototypu ? zmiennych DWORD (rozmiar 32 bit), zastąpiłem je 64 bitowymi zmiennymi [SIZE_T].
Fragment o którym mowa:

     typedef SIZE_T (WINAPI * functypeNtCreateThreadEx)(
         PHANDLE                 ThreadHandle,
         ACCESS_MASK             DesiredAccess,
         LPVOID                  ObjectAttributes,
         HANDLE                  ProcessHandle,
         LPTHREAD_START_ROUTINE  lpStartAddress,
         LPVOID                  lpParameter,
         BOOL                    CreateSuspended,
/*DWORD*/SIZE_T                  dwStackSize,
         SIZE_T                  Unknown1,
         SIZE_T                  Unknown2,
         LPVOID                  Unknown3
    );

Udało mi się wstrzyknąć kod do procesu działającego w kontekście bieżącego użytkownika - otrzymuje kod błędu 0 wszystko działa jest OK.

Mój injector nie jest wstanie wstrzyknąć kodu do procesu innego użytkownika. Powodem jest prawdopodobnie coś co się [chyba] nazywa separacją sesji.

In Windows Vista, Windows Server 2008, and later versions of Windows, the operating system isolates services in Session 0 and runs applications in other sessions, so services are protected from attacks that originate in application code.

Aplikacja injector.exe działa jak administrator - z tego powodu nie może do procesu użytkownika user1 [zwykłe konto] wstrzyknąć kodu( ERROR_ACCESS_DENIED ).

Aplikacja może atakować jedynie procesy znajdujące się w tym samym kontekście użytkownika.

#CIEKAWE
Będąc administratorem mogę wstrzyknąć kod do procesu użytkownika SYSTEM - otrzyma kod błędu : ERROR_SUCCESS lecz kod nie zostanie wykonany
(Próbowałem wyświetlić wiadomość, utworzyć plik, uruchomić nowy proces oraz wywołać wewnątrz procesu abort() )
Moim zdaniem ze względu na to że SYSTEM to osobny użytkownik powinienem uzyskać kod ERROR_ACCESS_DENIED ale cóż....

I moje pytanie czy można to jakoś objeść? Czy mogę na przykład będąc adminem zaatakować proces gościa ? :(.
Lub też wstrzyknąć kod do aplikacji pracującej w sesji 0 ?

PS Tak sobie myślę że nie należy się sugerować kodem zwracanym prze GetLastErorr bo używając nie udokumentowanej funkcji nie wiemy czy ta funkcja zapisuje prawdziwy kod błędu do jakiejś zmiennej (?flagi?) z której korzysta GetLastError. Jak myślicie ? Dobrze myślę? :).

https://msdn.microsoft.com/en-us/library/windows/hardware/dn653293%28v=vs.85%29.aspx - Impact of Session 0 Isolation on Services and Drivers in Windows.

0

Możesz zawsze napisać na stackoverflow - jak ja chce wymusić odpowiedź na jakieś pytanie, to zakładam konta jedno za drugim i pisze pytania od nowa (no bo zbierają tyle minusów, że mam blokade na pytania). Najczęściej ktoś rzeczowo odpisuje po 3-4 założonym koncie, nawet na jakieś natrętne i off-topowe pytania.

1 użytkowników online, w tym zalogowanych: 0, gości: 1