I am making a DLL injector. A really simple one: console app, input DLL, input process. But I'm getting an error on line 26:
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
Can someone help me?
#include<Windows.h> //DWORD
#include <iostream>
#include <string>
#include <psapi.h> //EnumProcessModules
#include <VersionHelpers.h>
#include <atlstr.h> // CString
#define CREATE_THREAD_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ)
BOOL InjectDLL(DWORD ProcessID)
{
LPCSTR DLL_PATH = "dll file here";
LPVOID LoadLibAddy, RemoteString;
if (!ProcessID)
return false;
HANDLE Proc = OpenProcess(CREATE_THREAD_ACCESS, FALSE, ProcessID);
if (!Proc)
{
std::cout << "OpenProcess() failed: " << GetLastError() << std::endl;
return false;
}
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, strlen(DLL_PATH) + 1, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(Proc, RemoteString, (LPVOID)DLL_PATH, strlen(DLL_PATH)+1, NULL);
CreateRemoteThread(Proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, RemoteString, NULL, NULL);
CloseHandle(Proc);
return true;
...
You did not say what kind of error you are getting. But I suspect you are getting a compile-time error on GetModuleHandle("kernel32.dll"). If your project is set to use the Unicode character set, you would need to use GetModuleHandle(L"kernel32.dll") instead. Or better, since GetModuleHandle() is a TCHAR-based macro, use TEXT("kernel32.dll") to match:
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA");
Otherwise, use GetModuleHandleA() or GetModuleHandleW() instead:
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryA");
That being said, you are also not doing any error handling when allocating memory or creating the remote thread. And your logic is incomplete as you are leaking allocated memory and resources. You need to wait for the remote thread to finish, and then free the virtual memory you allocate, and close the handle to the thread.
Try this instead:
#include <Windows.h> //DWORD
#include <iostream>
#include <string>
#include <psapi.h> //EnumProcessModules
#include <VersionHelpers.h>
#include <atlstr.h> // CString
#define CREATE_THREAD_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ)
bool InjectDLL(DWORD ProcessID)
{
LPCSTR DLL_PATH = "dll file here";
int DLL_PATH_SIZE = strlen(DLL_PATH) + 1;
if (ProcessID == 0)
return false;
FARPROC LoadLibAddy = GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA");
if (!LoadLibAddy)
{
DWORD err = GetLastError();
std::cout << "Can't find LoadLibraryA: " << err << std::endl;
return false;
}
HANDLE Proc = OpenProcess(CREATE_THREAD_ACCESS, FALSE, ProcessID);
if (!Proc)
{
DWORD err = GetLastError();
std::cout << "OpenProcess() failed: " << err << std::endl;
return false;
}
LPVOID RemoteString = VirtualAllocEx(Proc, NULL, DLL_PATH_SIZE, MEM_COMMIT, PAGE_READWRITE);
if (!RemoteString)
{
DWORD err = GetLastError();
std::cout << "VirtualAllocEx() failed: " << err << std::endl;
CloseHandle(Proc);
return false;
}
if (!WriteProcessMemory(Proc, RemoteString, DLL_PATH, DLL_PATH_SIZE, NULL))
{
DWORD err = GetLastError();
std::cout << "WriteProcessMemory() failed: " << err << std::endl;
VirtualFreeEx(Proc, RemoteString, 0, MEM_RELEASE);
CloseHandle(Proc);
return false;
}
HANDLE Thread = CreateRemoteThread(Proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, RemoteString, NULL, NULL);
if (!Thread)
{
DWORD err = GetLastError();
std::cout << "CreateRemoteThread() failed: " << err << std::endl;
VirtualFreeEx(Proc, RemoteString, 0, MEM_RELEASE);
CloseHandle(Proc);
return false;
}
WaitForSingleObject(Thread, INFINITE);
// If the target process is 32bit, you can use GetExitCodeThread()
// to find out if LoadLibraryA() was successful or not.
//
// If the target process is 64bit, it is much harder to determine
// that. You would have to allocate an entire function containing
// shellcode that calls LoadLibraryA() and saves the result in
// memory that you can then read via ReadProcessMemory(). Or,
// you would have to enumerate the target process's modules list
// looking for the DLL that you just injected.
CloseHandle(Thread);
VirtualFreeEx(Proc, RemoteString, 0, MEM_RELEASE);
CloseHandle(Proc);
return true;
}
Related
Well, basically I need to write a DLL in C++ that I will inject into notepad.exe.
This DLL suppose to open a pop-out message or a window with my name in it.
The problem is that I'm kinda new with DLL and I started a week ago. I'm having trouble opening a window or a message with a DLL using "windows.h".
I did try using MessageBox but it's not working.
that's my injector
that i took from: https://www.fxp.co.il/showthread.php?t=15051062
#include <iostream>
#include <direct.h>
#include <windows.h>
#include <Tlhelp32.h>
LPCTSTR SzToLPCTSTR(char* szString);
char* GetCurrentDir();
void WaitForProcessToAppear(LPCTSTR lpcszProc, DWORD dwDeley);
DWORD GetProcessIdByName(LPCTSTR lpcszProc);
BOOL InjectDll(DWORD dwPid, char* szDllPath);
int main()
{
char szProc[MAX_PATH], szDll[MAX_PATH];
char* szDllPath = (char*)malloc(MAX_PATH);
LPTSTR lpszProc = NULL;
while (true)
{
std::cout << "Process: ";
std::cin >> szProc;
std::cout << "DLL Injection: ";
std::cin >> szDll;
szDllPath = GetCurrentDir();
strcat_s(szDllPath, MAX_PATH, "\\");
strcat_s(szDllPath, MAX_PATH, szDll);
std::cout << "Waiting for process..." << std::endl;
WaitForProcessToAppear(SzToLPCTSTR(szProc), 100);
if (InjectDll(GetProcessIdByName(SzToLPCTSTR(szProc)), szDllPath)) std::cout << "Injection succeeded!" << std::endl;
else std::cout << "Injection failed!" << std::endl;
std::cout << "\n";
}
return 0;
}
char* GetCurrentDir()
{
char* szRet = (char*)malloc(MAX_PATH);
_getcwd(szRet, MAX_PATH);
return szRet;
}
LPCTSTR SzToLPCTSTR(char* szString)
{
LPTSTR lpszRet;
size_t size = strlen(szString) + 1;
lpszRet = (LPTSTR)malloc(MAX_PATH);
mbstowcs_s(NULL, lpszRet, size, szString, _TRUNCATE);
return lpszRet;
}
void WaitForProcessToAppear(LPCTSTR lpcszProc, DWORD dwDeley)
{
HANDLE hSnap;
PROCESSENTRY32 peProc;
BOOL bAppeared = FALSE;
while (!bAppeared)
{
if ((hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) != INVALID_HANDLE_VALUE)
{
peProc.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnap, &peProc))
while (Process32Next(hSnap, &peProc) && !bAppeared)
if (!lstrcmp(lpcszProc, peProc.szExeFile))
bAppeared = TRUE;
}
CloseHandle(hSnap);
Sleep(dwDeley);
}
}
DWORD GetProcessIdByName(LPCTSTR lpcszProc)
{
HANDLE hSnap;
PROCESSENTRY32 peProc;
DWORD dwRet = -1;
if ((hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) != INVALID_HANDLE_VALUE)
{
peProc.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnap, &peProc))
while (Process32Next(hSnap, &peProc))
if (!lstrcmp(lpcszProc, peProc.szExeFile))
dwRet = peProc.th32ProcessID;
}
CloseHandle(hSnap);
return dwRet;
}
BOOL InjectDll(DWORD dwPid, char* szDllPath)
{
DWORD dwMemSize;
HANDLE hProc;
LPVOID lpRemoteMem, lpLoadLibrary;
BOOL bRet = FALSE;
if ((hProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD, FALSE, dwPid)) != NULL)
{
dwMemSize = strlen(szDllPath) + 1;
if ((lpRemoteMem = VirtualAllocEx(hProc, NULL, dwMemSize, MEM_COMMIT, PAGE_READWRITE)) != NULL)
if (WriteProcessMemory(hProc, lpRemoteMem, (LPCVOID)szDllPath, dwMemSize, NULL))
{
lpLoadLibrary = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
if (CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpLoadLibrary, lpRemoteMem, 0, NULL) != NULL)
bRet = TRUE;
}
}
CloseHandle(hProc);
return bRet;
}
And that's my DLL:
#include <windows.h>
BOOL WINAPI DllMain( HMODULE hModule,
DWORD fdwReason,
LPVOID lpvReserved
)
{
if (fdwReason == DLL_PROCESS_ATTACH)
MessageBox(NULL, L"Injected by Matan Oshri", L"Hello World", MB_OK);
return TRUE;
}
Please let me know if u see any kind of mistake.
I appreciate all the help I can get.
Thank you.
First I can inject the DLL with the sample code, make sure the Dll, injector.exe and notepad.exe are in the same bits(Usually, notepad.exe starts with 64-bit).
I am getting the error code = 5 if I use 32-bits of DLL or injector.exe with 64-bits notepad.exe
Second, According to the CreateRemoteThread document:
hProcess
A handle to the process in which the thread is to be created. The
handle must have the PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION,
PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access
rights, and may fail without these rights on certain platforms. For
more information, see Process Security and Access Rights.
(Although the above sample works for me with out PROCESS_QUERY_INFORMATION and PROCESS_VM_READ)
Third, There is something worth improving in your code:
char* szDllPath = (char*)malloc(MAX_PATH);
szDllPath = GetCurrentDir();
/*{
char* szRet = (char*)malloc(MAX_PATH);
_getcwd(szRet, MAX_PATH);
return szRet;
}*/
This will cause the memory of the first application to be leaked.
LPCTSTR SzToLPCTSTR(char* szString);
We could use Unicode string directly.
sample:
#include <iostream>
#include <direct.h>
#include <windows.h>
#include <Tlhelp32.h>
//LPCWSTR SzToLPCTSTR(char* szString);
//char* GetCurrentDir();
void WaitForProcessToAppear(LPCWSTR lpcszProc, DWORD dwDeley);
DWORD GetProcessIdByName(LPCWSTR lpcszProc);
BOOL InjectDll(DWORD dwPid, LPCWSTR szDllPath);
int main()
{
while (true)
{
std::wstring szProc, szDll;
std::wstring szDllPath;
std::wcout << L"Process: ";
std::wcin >> szProc;
std::wcout << L"DLL Injection: ";
std::wcin >> szDll;
WCHAR dir[MAX_PATH] = { 0 };
GetCurrentDirectoryW(MAX_PATH, dir);
szDllPath = dir;
szDllPath += L"\\";
szDllPath += szDll;
std::wcout << L"Waiting for process..." << std::endl;
WaitForProcessToAppear(szProc.c_str(), 100);
if (InjectDll(GetProcessIdByName(szProc.c_str()), szDllPath.c_str())) std::wcout << L"Injection succeeded!" << std::endl;
else std::wcout << L"Injection failed!" << std::endl;
std::wcout << L"\n";
}
return 0;
}
//char* GetCurrentDir()
//{
// char* szRet = (char*)malloc(MAX_PATH);
// _getcwd(szRet, MAX_PATH);
// return szRet;
//}
//LPCWSTR SzToLPCTSTR(char* szString)
//{
// LPTSTR lpszRet;
// size_t size = strlen(szString) + 1;
// lpszRet = (LPTSTR)malloc(MAX_PATH);
// mbstowcs_s(NULL, lpszRet, size, szString, _TRUNCATE);
// return lpszRet;
//}
void WaitForProcessToAppear(LPCWSTR lpcszProc, DWORD dwDeley)
{
HANDLE hSnap;
PROCESSENTRY32 peProc;
BOOL bAppeared = FALSE;
while (!bAppeared)
{
if ((hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) != INVALID_HANDLE_VALUE)
{
peProc.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnap, &peProc))
while (Process32Next(hSnap, &peProc) && !bAppeared)
if (!lstrcmp(lpcszProc, peProc.szExeFile))
{
bAppeared = TRUE;
break;
}
}
CloseHandle(hSnap);
Sleep(dwDeley);
}
}
DWORD GetProcessIdByName(LPCWSTR lpcszProc)
{
HANDLE hSnap;
PROCESSENTRY32 peProc;
DWORD dwRet = -1;
if ((hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) != INVALID_HANDLE_VALUE)
{
peProc.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnap, &peProc))
while (Process32Next(hSnap, &peProc))
if (!lstrcmp(lpcszProc, peProc.szExeFile))
{
dwRet = peProc.th32ProcessID;
break;
}
}
CloseHandle(hSnap);
return dwRet;
}
BOOL InjectDll(DWORD dwPid, LPCWSTR szDllPath)
{
DWORD dwMemSize;
HANDLE hProc;
LPVOID lpRemoteMem, lpLoadLibrary;
BOOL bRet = FALSE;
if ((hProc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwPid)) != NULL)
{
dwMemSize = (wcslen(szDllPath) + 1) * sizeof(WCHAR);
if ((lpRemoteMem = VirtualAllocEx(hProc, NULL, dwMemSize, MEM_COMMIT, PAGE_READWRITE)) != NULL)
if (WriteProcessMemory(hProc, lpRemoteMem, (LPCVOID)szDllPath, dwMemSize, NULL))
{
lpLoadLibrary = GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW");
if (CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpLoadLibrary, lpRemoteMem, 0, NULL) != NULL)
bRet = TRUE;
}
}
CloseHandle(hProc);
return bRet;
}
Finally, according to the limitations in DllMain:
Call functions in User32.dll or Gdi32.dll. Some functions load
another DLL, which may not be initialized.
You cannot use MessageBox in DllMain, but you could use OutputDebugStringW instead to show a message, and use DebugView to check the debug string.
DLL:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
OutputDebugStringW(L"Injected DLL_PROCESS_ATTACH by Matan Oshri");
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Or use Console API to pop up a console:
DWORD dwSize;
WCHAR string[] = L"Hello World";
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
AllocConsole();
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), string, 12, &dwSize, NULL);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
This program enumerate all handles and get their names.
For pID 4 OpenProcess gets error 5 with SeDebugPrivilege.
UAC off. Running from Admin.
Enable SeDebugPrivilege
BOOL EnableDebugPrivilege(BOOL bEnable)
{
HANDLE hToken = nullptr;
LUID luid;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) return FALSE;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) return FALSE;
TOKEN_PRIVILEGES tokenPriv;
tokenPriv.PrivilegeCount = 1;
tokenPriv.Privileges[0].Luid = luid;
tokenPriv.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) return FALSE;
_tprintf(_T("Privileges error: %d\n", GetLastError()));
return TRUE;
}
Enumerate handles
DWORD EnumerateFileHandles(ULONG pid)
{
HINSTANCE hNtDll = LoadLibrary(_T("ntdll.dll"));
assert(hNtDll != NULL);
PFN_NTQUERYSYSTEMINFORMATION NtQuerySystemInformation =
(PFN_NTQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll,
"NtQuerySystemInformation");
assert(NtQuerySystemInformation != NULL);
PFN_NTQUERYINFORMATIONFILE NtQueryInformationFile =
(PFN_NTQUERYINFORMATIONFILE)GetProcAddress(hNtDll,
"NtQueryInformationFile");
DWORD nSize = 4096, nReturn;
PSYSTEM_HANDLE_INFORMATION pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)
HeapAlloc(GetProcessHeap(), 0, nSize);
while (NtQuerySystemInformation(SystemExtendedHandleInformation, pSysHandleInfo,
nSize, &nReturn) == STATUS_INFO_LENGTH_MISMATCH)
{
HeapFree(GetProcessHeap(), 0, pSysHandleInfo);
nSize += 4096;
pSysHandleInfo = (SYSTEM_HANDLE_INFORMATION*)HeapAlloc(
GetProcessHeap(), 0, nSize);
}
DWORD dwFiles = 0;
_tprintf(_T("Handles Number: %d\n"), pSysHandleInfo->NumberOfHandles);
for (ULONG i = 0; i < pSysHandleInfo->NumberOfHandles; i++)
{
PSYSTEM_HANDLE pHandle = &(pSysHandleInfo->Handles[i]);
if (pHandle->ProcessId == 4)
{
HANDLE hProcess = OpenProcess(
PROCESS_DUP_HANDLE, FALSE, pHandle->ProcessId);
if (hProcess == NULL)
{
_tprintf(_T("OpenProcess failed w/err 0x%08lx\n"), GetLastError());
continue;
}
HANDLE hCopy;
if (!DuplicateHandle(hProcess, (HANDLE)pHandle->Handle,
GetCurrentProcess(), &hCopy, MAXIMUM_ALLOWED, FALSE, 0))
continue;
TCHAR buf[MAX_PATH];
if (GetFinalPathNameByHandle(hCopy, buf, sizeof(buf), VOLUME_NAME_DOS))
wprintf(L"p%d:h%d:t%d:\t%s\n", pHandle->ProcessId, pHandle->Handle, pHandle->ObjectTypeNumber, buf);
CloseHandle(hProcess);
CloseHandle(hCopy);
}
}
HeapFree(GetProcessHeap(), 0, pSysHandleInfo);
return dwFiles;
}
On windows 7 x64 it's work fine.
But on Windows 10 x64 OpenProcess returns error 5 with SeDebugPrivilege.
How open system process(pID 4) on windows 10.
You can't open a handle for it as the documentation for OpenProcess specifically says it'll fail:
If the specified process is the Idle process or one of the CSRSS
processes, this function fails and the last error code is
ERROR_ACCESS_DENIED because their access restrictions prevent
user-level code from opening them.
If you want to get system process names, you could try to use CreateToolhelp32Snapshot() to get the snapshot of the process, then use Process32First() and Process32Next() to enumerate the all process.
Here is an example:
#include <iostream>
#include <stdio.h>
#include <windows.h>
#include <string>
#include <TlHelp32.h>
using namespace std;
int main()
{
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//get the snapshot
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
cout << "CreateToolhelp32Snapshot Error!" << endl;
return false;
}
BOOL bResult = Process32First(hProcessSnap, &pe32);
int num(0);
while(bResult)
{
cout << "[" << ++num << "] : " << "Process Name:"<< pe32.szExeFile << " " << "ProcessID:" << pe32.th32ProcessID << endl;
bResult = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
}
Hope it could help you!
there's something wrong with this api. i already check return function. no error. but output nothing.
HANDLE hSnapProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
PROCESSENTRY32 process;
process.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnapProcess, &process);
do
{
if (process.th32ProcessID != 0)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process.th32ProcessID);
if (hProcess != NULL)
{
wchar_t filePath[MAX_PATH];
if (!GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
{
std::wcout << filePath << std::endl;
}
}
CloseHandle(hProcess);
}
} while (Process32Next(hSnapProcess, &process));
CloseHandle(hSnapProcess);
Two clear mistakes can be seen in your code, both can be understood by reading the documentation.
Firstly, in GetModuleFileNameEx:
The handle must have the PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access rights.
Your handle only has PROCESS_QUERY_INFORMATION.
Secondly, again in GetModuleFileNameEx:
If the function succeeds, the return value specifies the length of the string copied to the buffer.
If the function fails, the return value is zero.
Your logic is back-to-front. Replace:
if (!GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
with
if (GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
Nothing is wrong with this API, the only thing that's wrong here is your code.
The documentation clearly states that the return value of GetModuleFileNameExW is the length of the string copied to the buffer.
If the return value is 0, the function has failed.
So you simply need to write this:
...
if (GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH) != 0)
{
// handle "success" case
}
...
BTW CloseHandle(hProcess); should be inside the if (hProcess != NULL) block.
Full working example with error checks
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
int main()
{
HANDLE hSnapProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (hSnapProcess != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 process;
process.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnapProcess, &process);
do
{
if (process.th32ProcessID != 0)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process.th32ProcessID);
if (hProcess != NULL)
{
wchar_t filePath[MAX_PATH];
if (GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
{
std::wcout << filePath << std::endl;
}
else
{
std::wcout << L"GetModuleFileNameExW failed with error" << GetLastError() << std::endl;
}
CloseHandle(hProcess);
}
}
} while (Process32Next(hSnapProcess, &process));
CloseHandle(hSnapProcess);
}
else
{
std::wcout << L"CreateToolhelp32Snapshot failed with error" << GetLastError() << std::endl;
}
}
I have written a function that attempts to read a child process's command line output via a pipe. This should be a simple subset of the MSDN Creating a Child Process with Redirected Input and Output article, but I am clearly making an error of some sort.
The ReadFile(...) call below blocks forever no matter if I place it before or after the WaitForSingleObject(...) call that should signal the end of the child process.
I have read all the answers that suggest "Use asynchronous ReadFile" and I am open to that suggestion if someone could give me some idea how that is accomplished on a pipe. Although I don't understand why asynchronous I/O should be needed for this case.
#include "stdafx.h"
#include <string>
#include <windows.h>
unsigned int launch( const std::string & cmdline );
int _tmain(int argc, _TCHAR* argv[])
{
launch( std::string("C:/windows/system32/help.exe") );
return 0;
}
void print_error( unsigned int err )
{
char* msg = NULL;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&msg,
0, NULL );
std::cout << "------ Begin Error Msg ------" << std::endl;
std::cout << msg << std::endl;
std::cout << "------ End Error Msg ------" << std::endl;
LocalFree( msg );
}
unsigned int launch( const std::string & cmdline )
{
TCHAR cl[_MAX_PATH*sizeof(TCHAR)];
memset( cl, 0, sizeof(cl) );
cmdline.copy( cl, (_MAX_PATH*sizeof(TCHAR)) - 1);
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
SECURITY_ATTRIBUTES saAttr;
memset( &saAttr, 0, sizeof(saAttr) );
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000) )
throw std::runtime_error( "StdoutRd CreatePipe" );
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0) )
throw std::runtime_error( "Stdout SetHandleInformation" );
STARTUPINFO startupInfo;
memset( &startupInfo, 0, sizeof(startupInfo) );
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
char* rawEnvVars = GetEnvironmentStringsA();
//__asm _emit 0xcc;
PROCESS_INFORMATION processInfo;
memset( &processInfo, 0, sizeof(processInfo) );
std::cout << "Start [" << cmdline << "]" << std::endl;
if ( CreateProcessA( 0, &cl[0], 0, 0, false,
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
rawEnvVars, 0, &startupInfo, &processInfo ) )
{
//CloseHandle( stdoutWriteHandle );
DWORD wordsRead;
char tBuf[257] = {'\0'};
bool success = true;
std::string outBuf("");
unsigned int t;
while(success) {
//__asm _emit 0xcc;
std::cout << "Just before ReadFile(...)" << std::endl;
success = ReadFile( stdoutReadHandle, tBuf, 256, &wordsRead, NULL);
(t=GetLastError())?print_error(t):t=t;
std::cout << "Just after ReadFile(...) | read " << wordsRead<< std::endl;
std::cout << ".";
if( success == false ) break;
outBuf += tBuf;
tBuf[0] = '\0';
}
std::cout << "output = [" << outBuf << "]" << std::endl;
if ( WaitForSingleObject( processInfo.hProcess, INFINITE ) == WAIT_OBJECT_0 )
{
unsigned int exitcode = 0;
GetExitCodeProcess( processInfo.hProcess, (LPDWORD)&exitcode );
std::cout << "exitcode = [" << exitcode << "]" << std::endl;
//__asm _emit 0xcc;
CloseHandle( processInfo.hProcess );
CloseHandle( processInfo.hThread );
return exitcode;
}
}
else
{
DWORD procErr = GetLastError();
std::cout << "FAILED TO CREATE PROCESS!" << std::endl;
print_error( procErr );
}
return -1;
} // end launch()
There are a few bugs in your code, but the most important is that you've specified FALSE for the bInheritHandles argument to CreateProcess. The new process can't use the pipe if it doesn't inherit the handle to it. In order for a handle to be inherited, the bInheritHandles argument must be TRUE and the handle must have inheritance enabled.
Other issues:
You're specifying CREATE_UNICODE_ENVIRONMENT but passing an ANSI environment block. Note that it is easier to pass NULL for lpEnvironment and let the system copy the environment block for you. You should still specify CREATE_UNICODE_ENVIRONMENT in this case, as described in the documentation, because your environment block might contain Unicode characters.
Similarly, if you're calling CreateProcessA you should be using STARTUPINFOA.
You don't zero-terminate tBuf each time around the loop, so you'll get spurious extra characters in your output buffer.
You need to close stdoutWriteHandle before you enter your read loop, or you won't know when the subprocess exits. (Or you could use asynchronous IO and check for process exit explicitly.)
GetLastError() is undefined if an API function succeeds, so you should only be calling it if ReadFile returns FALSE. (Of course, in this case this is purely cosmetic since you aren't acting on the error code.)
For reference, here is my corrected version of your code. I've turned it into plain C (sorry!) because that's what I'm familiar with. I compiled and tested in Unicode mode, but I think it should work without modification in ANSI mode too.
#define _WIN32_WINNT _WIN32_WINNT_WIN7
#include <windows.h>
#include <stdio.h>
void launch(const char * cmdline_in)
{
PROCESS_INFORMATION processInfo;
STARTUPINFOA startupInfo;
SECURITY_ATTRIBUTES saAttr;
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
char cmdline[256];
char outbuf[32768];
DWORD bytes_read;
char tBuf[257];
DWORD exitcode;
strcpy_s(cmdline, sizeof(cmdline), cmdline_in);
memset(&saAttr, 0, sizeof(saAttr));
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000))
{
printf("CreatePipe: %u\n", GetLastError());
return;
}
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0))
{
printf("SetHandleInformation: %u\n", GetLastError());
return;
}
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
// memset(&processInfo, 0, sizeof(processInfo)); // Not actually necessary
printf("Starting.\n");
if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, NULL, 0, &startupInfo, &processInfo))
{
printf("CreateProcessA: %u\n", GetLastError());
return;
}
CloseHandle(stdoutWriteHandle);
strcpy_s(outbuf, sizeof(outbuf), "");
for (;;) {
printf("Just before ReadFile(...)\n");
if (!ReadFile(stdoutReadHandle, tBuf, 256, &bytes_read, NULL))
{
printf("ReadFile: %u\n", GetLastError());
break;
}
printf("Just after ReadFile, read %u byte(s)\n", bytes_read);
if (bytes_read > 0)
{
tBuf[bytes_read] = '\0';
strcat_s(outbuf, sizeof(outbuf), tBuf);
}
}
printf("Output: %s\n", outbuf);
if (WaitForSingleObject(processInfo.hProcess, INFINITE) != WAIT_OBJECT_0)
{
printf("WaitForSingleObject: %u\n", GetLastError());
return;
}
if (!GetExitCodeProcess(processInfo.hProcess, &exitcode))
{
printf("GetExitCodeProcess: %u\n", GetLastError());
return;
}
printf("Exit code: %u\n", exitcode);
CloseHandle( processInfo.hProcess );
CloseHandle( processInfo.hThread );
return;
}
int main(int argc, char** argv)
{
launch("C:\\windows\\system32\\help.exe");
return 0;
}
There is an "LPOVERLAPPED lpOverlapped" parameter to ReadFile() which you have set to NULL. Looks like the only way to go is to allow overlapped I/O on your pipe and then use the WaitForSingleObject() for the "overlapped.hEvent".
Another way is to use the ConnectNamedPipe function and create the OVERLAPPED struct for the pipe.
I am trying to list all modules on a specific process, but I am getting "Access denied", even when I set token privileges.
Here is the code:
#include <cstdlib>
#include <iostream>
#include <windows.h>
#include <psapi.h>
#include <Tlhelp32.h>
using namespace std;
#pragma comment(lib, "cmcfg32.lib")
BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
{
char buf[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 255, NULL);
cout << "LookupPrivilegeValue error: " << buf;
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege) { tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; }
else { tp.Privileges[0].Attributes = 0; }
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD)NULL))
{
char buf[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 255, NULL);
cout << "AdjustTokenPrivileges error: " << buf;
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
int GetPID(char pname[])
{
PROCESSENTRY32 pEntry;
HANDLE hSnapshot = NULL;
pEntry.dwSize = sizeof(PROCESSENTRY32);
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
Process32First(hSnapshot,&pEntry);
do { if(strcmp(pEntry.szExeFile, pname) == 0) { return pEntry.th32ProcessID; } } while(Process32Next(hSnapshot,&pEntry));
return 0;
}
int main()
{
HANDLE currentToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, ¤tToken);
if (!SetPrivilege(currentToken, SE_DEBUG_NAME, TRUE))
{
MessageBox(0, "Unable to adjust privileges", "Error", MB_ICONERROR);
}
DWORD ID = GetPID("test.exe");
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ID);
if(!hProcess)
{
MessageBox(0, "Process not found", "Error", MB_ICONERROR);
}
else
{
HMODULE hMods[2048];
DWORD cbNeeded;
if(EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
{
for (unsigned int i = 0; i < (cbNeeded/sizeof(HMODULE)); i++)
{
TCHAR szModName[MAX_PATH];
if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName)/sizeof(TCHAR)))
{
cout << "DLL: " << szModName << " Handle: " << hMods[i] << endl;
}
}
}
else
{
char buf[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 255, NULL);
cout << "Error: " << buf;
}
system("pause");
}
CloseHandle(hProcess);
return 0;
}
Note that I can list process modules of any other process, but I can't with a specific one.
Both process are running with the same user credentials.
Can you tell me if I am doing something wrong?
Use Process Explorer to see the Security of kernel objects you are interested in. May be the target process has set its owner/DACL information such that it disallows READ for other processes. AntiVirus programs, services, file-system/kernel-driver are such kind of processes denying such actions.
And more importantly: it depends on the elevation/admin/ring-level of your own process.
ADDED:
Privileges doesn't directly apply to objects, but to the system as a whole. Try opening with TOKEN_ALL_ACCESS and see if it succeeds.