Using the NtQuerySystemInformationEx() API, I made a DLL that hides the Notepad process in 64bit.
But in 64bit, when I inject this DLL, nothing happens. But the DLL injector does not have a problem. Because other 64bit DLLs are injected ok.
What's the problem? I am trying to inject this DLL into the Process Explorer 64bit program. I want to hide the Notepad process from that Process Explorer.
The reason I used this API is that there is no Nt(Zw)QueryInformation() in Process Explorer x64, so I used the most similar Nt(Zw)QueryinformationEx().
#include <Windows.h>
#include <tchar.h>
#define STR_MODULE_NAME (L"hide.dll")
#define STR_HIDE_PROCESS_NAME (L"notepad.exe")
#define STATUS_SUCCESS (0x00000000L)
typedef LONG NTSTATUS;
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
BYTE Reserved1[52];
PVOID Reserved2[3];
HANDLE UniqueProcessId;
} SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;
typedef NTSTATUS(WINAPI* PFNTQUERYSYSTEMINFORMATIONEX)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
BYTE g_pOrgZwQSI[16] = { 0, };
BOOL hook64_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect, dwlowAddress, dwhighAddress, dwmov;
BYTE pBuf[14] = { 0x68, 0, };
PBYTE pByte;
pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if (pByte[0] == 0x68)
return FALSE;
VirtualProtect((LPVOID)pFunc, 16, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(pOrgBytes, pFunc, 16);
memset(pFunc, 0x90, 16);
dwlowAddress = (DWORD)((DWORD64)pfnNew & 0xffffffff);
memcpy(&pBuf[1], &dwlowAddress, 4);
dwmov = 0x042444C7;
memcpy(&pBuf[5],&dwmov , 4);
dwhighAddress = (DWORD64)pfnNew >> 32;
memcpy(&pBuf[9],&dwhighAddress , 4);
pBuf[13] = 0xC3;
memcpy(pFunc, &pBuf, 14);
VirtualProtect((LPVOID)pFunc, 16, dwOldProtect, &dwOldProtect);
return TRUE;
}
BOOL unhook64_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect;
PBYTE pByte;
pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if (pByte[0] != 0x68)
return FALSE;
VirtualProtect((LPVOID)pFunc, 16, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(pFunc, pOrgBytes, 16);
VirtualProtect((LPVOID)pFunc, 16, dwOldProtect, &dwOldProtect);
return TRUE;
}
NTSTATUS WINAPI NewNtQuerySystemInformationEx(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength)
{
NTSTATUS status;
FARPROC pFunc;
PSYSTEM_PROCESS_INFORMATION pCur, pPrev = 0;
char szProcName[MAX_PATH] = { 0, };
unhook64_by_code("ntdll.dll", "NtQuerySystemInformationEx", g_pOrgZwQSI);
pFunc = GetProcAddress(GetModuleHandleA("ntdll.dll"),
"NtQuerySystemInformationEx");
status = ((PFNTQUERYSYSTEMINFORMATIONEX)pFunc)
(SystemInformationClass, InputBuffer,
InputBufferLength, SystemInformation,
SystemInformationLength, ReturnLength);
if (status != STATUS_SUCCESS)
goto __NTQUERYSYSTEMINFORMATION_END;
if (SystemInformationClass == SystemProcessInformation)
{
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE)
{
if (pCur->Reserved2[1] != NULL)
{
if (!_tcsicmp((PWSTR)pCur->Reserved2[2], STR_HIDE_PROCESS_NAME))
{
if (pCur->NextEntryOffset == 0)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur; // 원하는 프로세스를 못 찾은 경우만 pPrev 세팅
}
if (pCur->NextEntryOffset == 0)
break;
pCur = (PSYSTEM_PROCESS_INFORMATION)((uintptr_t)pCur + pCur->NextEntryOffset);
}
}
__NTQUERYSYSTEMINFORMATION_END:
hook64_by_code("ntdll.dll", "NtQuerySystemInformationEx",
(PROC)NewNtQuerySystemInformationEx, g_pOrgZwQSI);
return status;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
hook64_by_code("ntdll.dll", "NtQuerySystemInformationEx",
(PROC)NewNtQuerySystemInformationEx, g_pOrgZwQSI);
break;
case DLL_PROCESS_DETACH:
unhook64_by_code("ntdll.dll", "NtQuerySystemInformationEx",
g_pOrgZwQSI);
break;
}
return TRUE;
}
Related
I'm making an anti cheat module that checks all open handles directed to the current process and the module should return all open handles whether they are from the system, from the user or whatever.
The problem is that it is returning only 1 handle and it is from the process itself.
Simultaneously with ProcessExplorer I am analyzing the current process and through ProcessExplorer I receive all the handles directed to it, example: ProcessExplorer, csrss, lsrss, svhost and etc.
Could someone help me to make the code return all the handles directed to the process like in ProcessExplorer?
I look forward to any contact.
Grateful.
#include <iostream>
#include <string>
#include <Windows.h>
#include <TlHelp32.h>
#include <Psapi.h>
#include <Shlwapi.h>
using namespace std;
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "psapi.lib")
#pragma region NT Structures
#define NT_SUCCESS(x) ((x) >= 0)
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
#define SystemHandleInformation 16
#define ObjectBasicInformation 0
#define ObjectNameInformation 1
#define ObjectTypeInformation 2
#define PROCESS_QUERY (0x1400)
#define PROCESS_FULL_CONTROL (0x1fffff)
typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
typedef NTSTATUS(NTAPI* _NtDuplicateObject)(
HANDLE SourceProcessHandle,
HANDLE SourceHandle,
HANDLE TargetProcessHandle,
PHANDLE TargetHandle,
ACCESS_MASK DesiredAccess,
ULONG Attributes,
ULONG Options
);
typedef NTSTATUS(NTAPI* _NtQueryObject)(
HANDLE ObjectHandle,
ULONG ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef struct _SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, * PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG HandleCount;
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
typedef enum _POOL_TYPE
{
NonPagedPool,
PagedPool,
NonPagedPoolMustSucceed,
DontUseThisType,
NonPagedPoolCacheAligned,
PagedPoolCacheAligned,
NonPagedPoolCacheAlignedMustS
} POOL_TYPE, * PPOOL_TYPE;
typedef struct _OBJECT_TYPE_INFORMATION
{
UNICODE_STRING Name;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccess;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
USHORT MaintainTypeList;
POOL_TYPE PoolType;
ULONG PagedPoolUsage;
ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;
#pragma endregion
// for getting an address of a procedure in memory.
PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName)
{
return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
}
void EnableDebugPrivilege()
{
HANDLE token;
LUID luid;
TOKEN_PRIVILEGES tp;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token);
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token, FALSE, &tp, sizeof(tp), NULL, NULL);
CloseHandle(token);
}
int Handle_Scan()
{
EnableDebugPrivilege();
while (1)
{
DWORD pid = GetCurrentProcessId();
_NtQuerySystemInformation NtQuerySystemInformation =
(_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
_NtDuplicateObject NtDuplicateObject =
(_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject");
_NtQueryObject NtQueryObject =
(_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject");
NTSTATUS status;
PSYSTEM_HANDLE_INFORMATION handleInfo;
ULONG handleInfoSize = 0x10000;
HANDLE processHandle;
ULONG i;
processHandle = OpenProcess(PROCESS_QUERY | PROCESS_FULL_CONTROL | PROCESS_DUP_HANDLE | PROCESS_ALL_ACCESS | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);
while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH)
handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);
for (i = 0; i < handleInfo->HandleCount; i++)
{
SYSTEM_HANDLE handle = handleInfo->Handles[i];
HANDLE dupHandle = NULL;
POBJECT_TYPE_INFORMATION objectTypeInfo;
PVOID objectNameInfo;
UNICODE_STRING objectName;
ULONG returnLength;
if (handle.ProcessId != pid)
continue;
NT_SUCCESS(NtDuplicateObject(processHandle, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, 0, 0, 0));
objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
NT_SUCCESS(NtQueryObject(dupHandle, ObjectTypeInformation, objectTypeInfo, 0x1000, NULL));
if (handle.GrantedAccess == 0x0012019f)
{
std::free(objectTypeInfo);
CloseHandle(dupHandle);
continue;
}
objectNameInfo = malloc(0x1000);
if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectNameInformation, objectNameInfo, 0x1000, &returnLength)))
{
objectNameInfo = realloc(objectNameInfo, returnLength);
if (!NT_SUCCESS(NtQueryObject(
dupHandle,
ObjectNameInformation,
objectNameInfo,
returnLength,
NULL
)))
{
std::free(objectTypeInfo);
std::free(objectNameInfo);
CloseHandle(dupHandle);
continue;
}
}
objectName = *(PUNICODE_STRING)objectNameInfo;
wstring ObjectBuffer = objectTypeInfo->Name.Buffer;
// We are only interested about handles to files & processes
if (ObjectBuffer.find(L"File") != wstring::npos || ObjectBuffer.find(L"Process") != wstring::npos)
{
HANDLE CurrentProcess = GetCurrentProcess();
HANDLE procHandle = OpenProcess(PROCESS_QUERY | PROCESS_FULL_CONTROL | PROCESS_DUP_HANDLE | PROCESS_ALL_ACCESS | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, handle.ProcessId);
HANDLE DuplicatedHandle = 0;
// Duplicating the handle, now we can do basically anything with it.
if (DuplicateHandle(procHandle, (HANDLE)handle.Handle, CurrentProcess, &DuplicatedHandle, 0, false, DUPLICATE_SAME_ACCESS))
{
WCHAR NameBlock[256];
wstring block = NameBlock;
K32GetProcessImageFileNameW(DuplicatedHandle, NameBlock, 256);
PathStripPathW(NameBlock);
wcout << L"Handle to " << ObjectBuffer << ": " << NameBlock << " Id: " << handle.ProcessId << endl;
}
}
std::free(objectTypeInfo);
std::free(objectNameInfo);
CloseHandle(dupHandle);
}
std::free(handleInfo);
CloseHandle(processHandle);
cin.get();
}
return 0;
}
void DetectHandle() {
CreateThread(NULL, NULL, LPTHREAD_START_ROUTINE(Handle_Scan), NULL, 0, 0);
}
I tried to add SetDebugPrivilege but it didn't help, I also tried to add more parameters in OpenProcess with the hope of a lack of permissions but I was unsuccessful again.
My doubt is about how to use the value returned by the API to reconstruct the image.
Also, does the way I'm creating the Bitmap preserve the picture transparency?
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
LPCWSTR base64 = L"";
DWORD dwSkip;
DWORD dwFlags;
DWORD dwDataLen;
CryptStringToBinary(
base64,
_tcslen(base64),
CRYPT_STRING_BASE64,
NULL,
&dwDataLen,
&dwSkip,
&dwFlags);
DWORD imageSize = dwDataLen;
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);
LPVOID pImage = ::GlobalLock(hMem);
memcpy(pImage, ???? , imageSize);
IStream* pStream = NULL;
::CreateStreamOnHGlobal(hMem, FALSE, &pStream);
Gdiplus::Image image(pStream);
image.GetWidth();
int wd = image.GetWidth();
int hgt = image.GetHeight();
auto format = image.GetPixelFormat();
Bitmap* bmp = new Bitmap(wd, hgt, format);
auto gg = std::unique_ptr<Graphics>(Graphics::FromImage(bmp));
gg->Clear(Color::Transparent);
gg->DrawImage(&image, 0, 0, wd, hgt);
HICON hIcon;
bmp->GetHICON(&hIcon);
pStream->Release();
GlobalUnlock(hMem);
GlobalFree(hMem);
wc.hIcon = hIcon;
My doubt is about how to use the value returned by the API to reconstruct the image.
You are calling CryptStringToBinary() only 1 time, to calculate the size of the decoded bytes. You are even allocating memory to receive the decoded bytes. But, you are not actually decoding the base64 string to produce the bytes. You need to call CryptStringToBinary() a second time for that, eg:
LPCWSTR base64 = L"...";
DWORD dwStrLen = static_cast<DWORD>(wcslen(base64)); // or: 0
DWORD dwDataLen = 0;
if (!CryptStringToBinaryW(
base64,
dwStrLen,
CRYPT_STRING_BASE64,
NULL,
&dwDataLen,
NULL,
NULL))
{
// error handling...
}
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, dwDataLen);
if (!hMem)
{
// error handling...
}
LPVOID pImage = ::GlobalLock(hMem);
if (!CryptStringToBinaryW(
base64,
dwStrLen,
CRYPT_STRING_BASE64,
(BYTE*) pImage,
&dwDataLen,
NULL,
NULL))
{
// error handling...
}
::GlobalUnlock(hMem);
// use hMem as needed...
::GlobalFree(hMem);
we can use CreateIconFromResourceEx instead gdi++ . example of convert base64 to HICON (assumed that wholea icon file is converted to base64)
struct ICONDIRENTRY
{
BYTE bWidth; // Width of the image
BYTE bHeight; // Height of the image (times 2)
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // how many bytes in this resource?
DWORD dwImageOffset; // where in the file is this image
};
struct ICONDIR
{
WORD NotInFile; // not in file
WORD Reserved; // Reserved
WORD Type; // resource type (IMAGE_ICON for icons)
WORD Count; // how many images?
ICONDIRENTRY Entries[]; // the entries for each image
};
HICON LoadIcon(_In_ ICONDIR* pid, _In_ ULONG cb, _In_ ULONG cxDesired, _In_ ULONG cyDesired)
{
if (cb < sizeof(ICONDIR) || pid->Type != IMAGE_ICON)
{
return 0;
}
if (ULONG Count = pid->Count)
{
if (cb < sizeof(ICONDIR) + Count * sizeof(ICONDIRENTRY))
{
return FALSE;
}
ICONDIRENTRY* Entry = pid->Entries;
do
{
if (Entry->bWidth == cxDesired && Entry->bHeight == cyDesired)
{
if (Entry->wPlanes != 1)
{
return 0;
}
switch (Entry->bColorCount)
{
case 0:
case 1:
case 4:
case 8:
case 16:
case 24:
case 32:
break;
default:
return 0;
}
DWORD dwImageOffset = Entry->dwImageOffset + FIELD_OFFSET(ICONDIR, Reserved);
if (cb < dwImageOffset)
{
return 0;
}
DWORD dwBytesInRes = Entry->dwBytesInRes;
if (cb - dwImageOffset < dwBytesInRes)
{
return 0;
}
return CreateIconFromResourceEx((PBYTE)pid + dwImageOffset, dwBytesInRes, TRUE, 0x00030000, cxDesired, cyDesired, 0);
}
} while (Entry++, --Count);
}
return 0;
}
HICON LoadIcon(_In_ PSTR psz, _In_ ULONG cch, _In_ int cxDesired, _In_ int cyDesired)
{
ULONG cb = 0;
PUCHAR pb = 0;
PUCHAR buf = 0;
HICON hi = 0;
while (CryptStringToBinaryA(psz, cch, CRYPT_STRING_BASE64, pb, &cb, 0, 0))
{
if (pb)
{
hi = LoadIcon((ICONDIR*)buf, cb + FIELD_OFFSET(ICONDIR, Reserved), cxDesired, cyDesired);
break;
}
if (cb < sizeof(ICONDIR))
{
break;
}
if (!(buf = new UCHAR[cb + FIELD_OFFSET(ICONDIR, Reserved)]))
{
break;
}
pb = buf + FIELD_OFFSET(ICONDIR, Reserved);
}
if (buf) delete [] buf;
return hi;
}
for convert icon file to base 64 can use next code:
BOOL IsValidIcon(_In_ ICONDIR* pid, _In_ ULONG cb)
{
if (cb < sizeof(ICONDIR) || pid->Type != IMAGE_ICON)
{
return FALSE;
}
if (ULONG Count = pid->Count)
{
if (cb < sizeof(ICONDIR) + Count * sizeof(ICONDIRENTRY))
{
return FALSE;
}
ICONDIRENTRY* Entry = pid->Entries;
do
{
if (Entry->wPlanes != 1)
{
return FALSE;
}
switch (Entry->bColorCount)
{
case 0:
case 1:
case 4:
case 8:
case 16:
case 24:
case 32:
break;
default:
return FALSE;
}
DWORD dwImageOffset = Entry->dwImageOffset + FIELD_OFFSET(ICONDIR, Reserved);
if (cb < dwImageOffset)
{
return FALSE;
}
DWORD dwBytesInRes = Entry->dwBytesInRes;
if (cb - dwImageOffset < dwBytesInRes)
{
return FALSE;
}
} while (Entry++, --Count);
}
return FALSE;
}
BOOL mi(_In_ PCWSTR lpIconName, _Out_ PSTR* ppsz, _Out_ ULONG* pcch)
{
HANDLE hFile = CreateFileW(lpIconName, FILE_GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL f = FALSE;
FILE_STANDARD_INFO fsi;
if (GetFileInformationByHandleEx(hFile, FileStandardInfo, &fsi, sizeof(fsi)))
{
if ((ULONG64)fsi.EndOfFile.QuadPart - 1 < 0x100000)
{
if (PBYTE buf = new UCHAR[fsi.EndOfFile.LowPart + FIELD_OFFSET(ICONDIR, Reserved)])
{
PBYTE pb = buf + FIELD_OFFSET(ICONDIR, Reserved);
if (ReadFile(hFile, pb, fsi.EndOfFile.LowPart, &fsi.EndOfFile.LowPart, 0) &&
IsValidIcon((ICONDIR*)buf, fsi.EndOfFile.LowPart) + FIELD_OFFSET(ICONDIR, Reserved))
{
ULONG cch = 0;
PSTR psz = 0;
while (CryptBinaryToStringA(pb, fsi.EndOfFile.LowPart, CRYPT_STRING_BASE64|CRYPT_STRING_NOCRLF, psz, &cch))
{
if (psz)
{
*pcch = cch, *ppsz = psz, psz = 0, f = TRUE;
break;
}
if (!(psz = new CHAR[cch]))
{
break;
}
}
if (psz)
{
delete [] psz;
}
}
delete [] buf;
}
}
}
CloseHandle(hFile);
return f;
}
for test icon:
ULONG cch;
PSTR psz;
if (mi(L"...ico", &psz, &cch))
{
HICON hi = LoadIcon(psz, cch, 48, 48);
delete [] psz;
if (hi)
{
TASKDIALOGCONFIG TaskConfig = {
sizeof(TaskConfig), 0, 0, TDF_USE_HICON_MAIN, TDCBF_CLOSE_BUTTON, 0, {hi}
};
TaskDialogIndirect(&TaskConfig, 0, 0, 0);
DestroyIcon(hi);
}
}
I'm trying to implement some anti-debugging/anti-reversing techinques in order to prevent my (python) executable to be debugged/reversed, but when I try to launch it from x32dbg or python -m pdb they don't work, letting x32gdb/pdb to access my code and execute the executable/script.
The techniques I'm using are written in c++; I import the c++ code in my python code through a dll generated thorugh Visual Studio 2019, and everything works fine, meaning that I can call the functions in my dll from the executable/python without any error/exception.
I found the techniques online, I didn't wrote them, but also the simplest technique (isDebuggerPresent() function from debugapi.h windows library) does not work, and this is only a call to a function.
Here are the techniques I implemented in my dll
int isThereADebugger()
{
if (IsDebuggerPresent())
{
cout << "Stop debugging program!" << endl;
exit(-1);
}
return 0;
}
// Current PEB for 64bit and 32bit processes accordingly
PVOID GetPEB()
{
#ifdef _WIN64
return (PVOID)__readgsqword(0x0C * sizeof(PVOID));
#else
return (PVOID)__readfsdword(0x0C * sizeof(PVOID));
#endif
}
// Get PEB for WOW64 Process
PVOID GetPEB64()
{
PVOID pPeb = 0;
#ifndef _WIN64
BOOL isWow64 = FALSE;
typedef BOOL(WINAPI* pfnIsWow64Process)(HANDLE hProcess, PBOOL isWow64);
pfnIsWow64Process fnIsWow64Process = (pfnIsWow64Process)
GetProcAddress(GetModuleHandleA("Kernel32.dll"), "IsWow64Process");
if (fnIsWow64Process(GetCurrentProcess(), &isWow64))
{
if (isWow64)
{
pPeb = (PVOID)__readfsdword(0x0C * sizeof(PVOID));
pPeb = (PVOID)((PBYTE)pPeb + 0x1000);
}
}
#endif
return pPeb;
}
#define FLG_HEAP_ENABLE_TAIL_CHECK 0x10
#define FLG_HEAP_ENABLE_FREE_CHECK 0x20
#define FLG_HEAP_VALIDATE_PARAMETERS 0x40
#define NT_GLOBAL_FLAG_DEBUGGED (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS)
void CheckNtGlobalFlag()
{
PVOID pPeb = GetPEB();
PVOID pPeb64 = GetPEB64();
DWORD offsetNtGlobalFlag = 0;
#ifdef _WIN64
offsetNtGlobalFlag = 0xBC;
#else
offsetNtGlobalFlag = 0x68;
#endif
DWORD NtGlobalFlag = *(PDWORD)((PBYTE)pPeb + offsetNtGlobalFlag);
if (NtGlobalFlag & NT_GLOBAL_FLAG_DEBUGGED)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
if (pPeb64)
{
DWORD NtGlobalFlagWow64 = *(PDWORD)((PBYTE)pPeb64 + 0xBC);
if (NtGlobalFlagWow64 & NT_GLOBAL_FLAG_DEBUGGED)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
}
PIMAGE_NT_HEADERS GetImageNtHeaders(PBYTE pImageBase)
{
PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)pImageBase;
return (PIMAGE_NT_HEADERS)(pImageBase + pImageDosHeader->e_lfanew);
}
PIMAGE_SECTION_HEADER FindRDataSection(PBYTE pImageBase)
{
static const std::string rdata = ".rdata";
PIMAGE_NT_HEADERS pImageNtHeaders = GetImageNtHeaders(pImageBase);
PIMAGE_SECTION_HEADER pImageSectionHeader = IMAGE_FIRST_SECTION(pImageNtHeaders);
int n = 0;
for (; n < pImageNtHeaders->FileHeader.NumberOfSections; ++n)
{
if (rdata == (char*)pImageSectionHeader[n].Name)
{
break;
}
}
return &pImageSectionHeader[n];
}
void CheckGlobalFlagsClearInProcess()
{
PBYTE pImageBase = (PBYTE)GetModuleHandle(NULL);
PIMAGE_NT_HEADERS pImageNtHeaders = GetImageNtHeaders(pImageBase);
PIMAGE_LOAD_CONFIG_DIRECTORY pImageLoadConfigDirectory = (PIMAGE_LOAD_CONFIG_DIRECTORY)(pImageBase
+ pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress);
if (pImageLoadConfigDirectory->GlobalFlagsClear != 0)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
void CheckGlobalFlagsClearInFile()
{
HANDLE hExecutable = INVALID_HANDLE_VALUE;
HANDLE hExecutableMapping = NULL;
PBYTE pMappedImageBase = NULL;
__try
{
PBYTE pImageBase = (PBYTE)GetModuleHandle(NULL);
PIMAGE_SECTION_HEADER pImageSectionHeader = FindRDataSection(pImageBase);
TCHAR pszExecutablePath[MAX_PATH];
DWORD dwPathLength = GetModuleFileName(NULL, pszExecutablePath, MAX_PATH);
if (0 == dwPathLength) __leave;
hExecutable = CreateFile(pszExecutablePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE == hExecutable) __leave;
hExecutableMapping = CreateFileMapping(hExecutable, NULL, PAGE_READONLY, 0, 0, NULL);
if (NULL == hExecutableMapping) __leave;
pMappedImageBase = (PBYTE)MapViewOfFile(hExecutableMapping, FILE_MAP_READ, 0, 0,
pImageSectionHeader->PointerToRawData + pImageSectionHeader->SizeOfRawData);
if (NULL == pMappedImageBase) __leave;
PIMAGE_NT_HEADERS pImageNtHeaders = GetImageNtHeaders(pMappedImageBase);
PIMAGE_LOAD_CONFIG_DIRECTORY pImageLoadConfigDirectory = (PIMAGE_LOAD_CONFIG_DIRECTORY)(pMappedImageBase
+ (pImageSectionHeader->PointerToRawData
+ (pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress - pImageSectionHeader->VirtualAddress)));
if (pImageLoadConfigDirectory->GlobalFlagsClear != 0)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
__finally
{
if (NULL != pMappedImageBase)
UnmapViewOfFile(pMappedImageBase);
if (NULL != hExecutableMapping)
CloseHandle(hExecutableMapping);
if (INVALID_HANDLE_VALUE != hExecutable)
CloseHandle(hExecutable);
}
}
int GetHeapFlagsOffset(bool x64)
{
return x64 ?
TRUE ? 0x70 : 0x14 : //x64 offsets
TRUE ? 0x40 : 0x0C; //x86 offsets
}
int GetHeapForceFlagsOffset(bool x64)
{
return x64 ?
TRUE ? 0x74 : 0x18 : //x64 offsets
TRUE ? 0x44 : 0x10; //x86 offsets
}
void CheckHeap()
{
PVOID pPeb = GetPEB();
PVOID pPeb64 = GetPEB64();
PVOID heap = 0;
DWORD offsetProcessHeap = 0;
PDWORD heapFlagsPtr = 0, heapForceFlagsPtr = 0;
BOOL x64 = FALSE;
#ifdef _WIN64
x64 = TRUE;
offsetProcessHeap = 0x30;
#else
offsetProcessHeap = 0x18;
#endif
heap = (PVOID) * (PDWORD_PTR)((PBYTE)pPeb + offsetProcessHeap);
heapFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapFlagsOffset(x64));
heapForceFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapForceFlagsOffset(x64));
if (*heapFlagsPtr & ~HEAP_GROWABLE || *heapForceFlagsPtr != 0)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
if (pPeb64)
{
heap = (PVOID) * (PDWORD_PTR)((PBYTE)pPeb64 + 0x30);
heapFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapFlagsOffset(true));
heapForceFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapForceFlagsOffset(true));
if (*heapFlagsPtr & ~HEAP_GROWABLE || *heapForceFlagsPtr != 0)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
}
void trapCheckFlag() {
BOOL isDebugged = TRUE;
__try
{
__asm
{
pushfd
or dword ptr[esp], 0x100 // set the Trap Flag
popfd // Load the value into EFLAGS register
nop
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// If an exception has been raised – debugger is not present
isDebugged = FALSE;
}
if (isDebugged)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
int isRemoteDebuggerPresent() {
BOOL isDebuggerPresent = FALSE;
if (CheckRemoteDebuggerPresent(GetCurrentProcess(), &isDebuggerPresent))
{
if (isDebuggerPresent)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
return 0;
}
typedef NTSTATUS(NTAPI* pfnNtQueryInformationProcess)(
_In_ HANDLE ProcessHandle,
_In_ UINT ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
//const UINT ProcessDebugPort = 7;
int ntQueryInfoProc()
{
pfnNtQueryInformationProcess NtQueryInformationProcess = NULL;
NTSTATUS status;
DWORD isDebuggerPresent = 0;
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
if (NULL != hNtDll)
{
NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
if (NULL != NtQueryInformationProcess)
{
status = NtQueryInformationProcess(
GetCurrentProcess(),
7,
&isDebuggerPresent,
sizeof(DWORD),
NULL);
if (status == 0x00000000 && isDebuggerPresent != 0)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
}
return 0;
}
typedef NTSTATUS(NTAPI* pfnNtSetInformationThread)(
_In_ HANDLE ThreadHandle,
_In_ ULONG ThreadInformationClass,
_In_ PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength
);
const ULONG ThreadHideFromDebugger = 0x11;
void HideFromDebugger()
{
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
pfnNtSetInformationThread NtSetInformationThread = (pfnNtSetInformationThread)
GetProcAddress(hNtDll, "NtSetInformationThread");
NTSTATUS status = NtSetInformationThread(GetCurrentThread(),
ThreadHideFromDebugger, NULL, 0);
}
EXCEPTION_DISPOSITION ExceptionRoutine(
PEXCEPTION_RECORD ExceptionRecord,
PVOID EstablisherFrame,
PCONTEXT ContextRecord,
PVOID DispatcherContext)
{
if (EXCEPTION_INVALID_HANDLE == ExceptionRecord->ExceptionCode)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
return ExceptionContinueExecution;
}
int handleTracing() {
__asm
{
// set SEH handler
push ExceptionRoutine
push dword ptr fs : [0]
mov dword ptr fs : [0] , esp
}
CloseHandle((HANDLE)0xBAAD);
__asm
{
// return original SEH handler
mov eax, [esp]
mov dword ptr fs : [0] , eax
add esp, 8
}
return 0;
}
#define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A
void dbgMessages() {
WCHAR* outputString = (WCHAR*)L"Any text";
ULONG_PTR args[4] = { 0 };
args[0] = (ULONG_PTR)wcslen(outputString) + 1;
args[1] = (ULONG_PTR)outputString;
__try
{
RaiseException(DBG_PRINTEXCEPTION_WIDE_C, 0, 4, args);
printf("Debugger detected");
exit(-1);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
printf("Debugger NOT detected");
}
}
void DoSmth()
{
Sleep(1000);
}
int timeApproach() {
const int g_doSmthExecutionTime = 1050;
SYSTEMTIME sysTimeStart;
SYSTEMTIME sysTimeEnd;
FILETIME timeStart, timeEnd;
GetSystemTime(&sysTimeStart);
DoSmth();
GetSystemTime(&sysTimeEnd);
SystemTimeToFileTime(&sysTimeStart, &timeStart);
SystemTimeToFileTime(&sysTimeEnd, &timeEnd);
double timeExecution = (timeEnd.dwLowDateTime - timeStart.dwLowDateTime) / 10000.0;
if (timeExecution < g_doSmthExecutionTime)
{
std::cout << "Debugger not present";
}
else
{
std::cout << "Debugger present";
}
return 0;
}
In my python script I call those function sequentially at the entry point, like
if __name__ == "__main__":
antiDebugger = ctypes.CDLL('C:\\absolute\\path\\to\\antiDebugger.dll')
antiDebugger.isThereADebugger()
antiDebugger.CheckNtGlobalFlag()
antiDebugger.CheckGlobalFlagsClearInProcess()
antiDebugger.CheckGlobalFlagsClearInFile()
antiDebugger.CheckHeap()
antiDebugger.trapCheckFlag()
antiDebugger.isRemoteDebuggerPresent()
antiDebugger.ntQueryInfoProc()
antiDebugger.timeApproach()
antiDebugger.handleTracing()
antiDebugger.HideFromDebugger()
antiDebugger.dbgMessages()
customFunction()
Actually, I also tried to put them at the beginning of every function in my python script, but also that didn't work.
I generate the executable with pyarmor combined with pyinstaller using the --onefile option
My windows version is the x64 windows 10 Pro, while my python version is 3.8.2
I know that all those techniques might be too much, but I just want them to work in order to try to break them, specifically the executable side.
Can anyone tell me how to make this techniques work?
As you know there is some difference between a process name and it's description, for example the dwm.exe process's description is Desktop Window Manager
I can check the name of the processes with this code:
#include <windows.h>
#include <TlHelp32.h>
#include <Winternl.h>
typedef NTSTATUS (NTAPI *NTQUERYINFORMATIONPROCESS)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
int main()
{
PEB Peb = {0};
DWORD dwSize = 0;
DWORD dwPID = 0;
HANDLE hProcess = NULL;
HANDLE hProcessSnap = NULL;
WCHAR PsPath[MAX_PATH] = {0};
WCHAR wszProcName[20] = L"dwm.exe"; //Desktop Window Manager
PROCESSENTRY32 PsEntry32 = {0};
PROCESS_BASIC_INFORMATION PsBasicInfo = {0};
RTL_USER_PROCESS_PARAMETERS RtlUserPsParams = {0};
NTQUERYINFORMATIONPROCESS NtFunction = NULL;
if((hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) != INVALID_HANDLE_VALUE)
{
PsEntry32.dwSize = sizeof(PROCESSENTRY32);
if(!Process32First(hProcessSnap, &PsEntry32))
{
CloseHandle(hProcessSnap);
return FALSE;
}
do
{
if(lstrcmpiW(PsEntry32.szExeFile, wszProcName) == 0)
{
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PsEntry32.th32ProcessID);
if(hProcess != INVALID_HANDLE_VALUE)
{
NtFunction = (NTQUERYINFORMATIONPROCESS)GetProcAddress(LoadLibraryW(L"ntdll.dll"), "NtQueryInformationProcess");
if(NtFunction)
{
if(NtFunction(hProcess, ProcessBasicInformation, &PsBasicInfo, sizeof(PROCESS_BASIC_INFORMATION), &dwSize) == ERROR_SUCCESS)
{
ReadProcessMemory(hProcess, PsBasicInfo.PebBaseAddress, &Peb, sizeof(PEB), (SIZE_T*)&dwSize);
ReadProcessMemory(hProcess, Peb.ProcessParameters, &RtlUserPsParams, sizeof(RTL_USER_PROCESS_PARAMETERS), (SIZE_T*)&dwSize);
ReadProcessMemory(hProcess, RtlUserPsParams.ImagePathName.Buffer, PsPath, RtlUserPsParams.ImagePathName.Length, (SIZE_T*)&dwSize);
dwPID = PsEntry32.th32ProcessID;
}
}
CloseHandle(hProcess);
}
}
}while(Process32Next(hProcessSnap, &PsEntry32));
CloseHandle(hProcessSnap);
}
return 0;
}
now I want to check the processes description
Is it possible to get all processes description one by one and check them?
I use ToolHelp32Snapshot() to get the module path instead of your PEB method, following that I:
GetFileVersionInfoSizeA() to get the size of the version structure
GetFileVersionInfoA() to pull the data from that structure into a local char array.
VerQueryValue() with "\VarFileInfo\Translation" to get the language code pages
Then I loop through the different language code pages to create the subblock string required for the next query.
Then I use VerQueryValue() with the correct language code page inserted inside the sub block and store the result into another char array.
Then we print this string to console.
#include <iostream>
#include <string>
#include <Windows.h>
#include <TlHelp32.h>
#include <strsafe.h>
#pragma comment(lib,"Version.lib")
std::string GetModulePath(std::string moduleName, DWORD procId)
{
std::string path;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
if (hSnap != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 modEntry{};
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hSnap, &modEntry))
{
do
{
if (!_stricmp(modEntry.szModule, moduleName.c_str()))
{
path = modEntry.szExePath;
break;
}
} while (Module32Next(hSnap, &modEntry));
}
}
CloseHandle(hSnap);
return path;
}
std::string GetFilePath(std::string procName)
{
std::string path;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 procEntry;
procEntry.dwSize = sizeof(procEntry);
if (Process32First(hSnap, &procEntry))
{
do
{
if (!_stricmp(procEntry.szExeFile, procName.c_str()))
{
path = GetModulePath(procName, procEntry.th32ProcessID);
break;
}
} while (Process32Next(hSnap, &procEntry));
}
}
CloseHandle(hSnap);
return path;
}
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
};
int main()
{
std::string path = GetFilePath("notepad++.exe");
DWORD verSize = GetFileVersionInfoSizeA(path.c_str(), 0);
char* data = new char[verSize]{ 0 };
GetFileVersionInfoA(path.c_str(), 0, verSize, data);
UINT length;
LANGANDCODEPAGE* lpTranslate;
VerQueryValue(data, "\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &length);
char SubBlock[50]{ 0 };
for (unsigned int i = 0; i < (length / sizeof(struct LANGANDCODEPAGE)); i++)
{
HRESULT result = StringCchPrintf(SubBlock, 50,
TEXT("\\StringFileInfo\\%04x%04x\\FileDescription"),
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage);
char* description = new char[0x100]{ 0 };
UINT length2;
VerQueryValue(data, SubBlock, (LPVOID*)&description, &length2);
std::cout << description << "\n";
}
getchar();
return 0;
}
Must run as administrator, and only tested on x86.
i'm trying to make exe packer
5
extract exe to buffer(in my case: vector)
add new section
get offset of new EP and run
but after call newmain i got 0xC0000005
main.cpp:
pastebin
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "main.h"
typedef struct _BASE_RELOCATION_ENTRY
{
WORD Offset : 12;
WORD Type : 4;
} BASE_RELOCATION_ENTRY;
BOOL applyRelocBlock(BASE_RELOCATION_ENTRY* block, size_t entriesNum, DWORD page, PVOID newBase)
{
PVOID ImageBaseAddress = NtCurrentTeb()->Peb->ImageBaseAddress;
BASE_RELOCATION_ENTRY* entry = block;
for (int i = 0; i < entriesNum; i++)
{
DWORD offset = entry->Offset;
DWORD type = entry->Type;
if (entry == NULL || type == 0 || offset == 0)
{
//printf("Applied relocations: %d\n", i);
return TRUE; //finish
}
if (type != 3)
{
printf("Not supported relocations format at %d: %d\n", i, type);
return FALSE;
}
uint32_t* relocateAddr = (uint32_t*)((ULONG_PTR)newBase + page + offset);
(*relocateAddr) = ((*relocateAddr) - (ULONG_PTR)ImageBaseAddress) + (ULONG_PTR)newBase;
entry = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)entry + sizeof(uint16_t));
}
return TRUE;
}
BOOL applyRelocations(PIMAGE_NT_HEADERS NtHeaders, PVOID newBase)
{
PVOID ImageBaseAddress = NtCurrentTeb()->Peb->ImageBaseAddress;
//fetch relocation table from current image:
IMAGE_DATA_DIRECTORY relocDir = NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (relocDir.VirtualAddress == NULL)
{
printf("Cannot relocate - application have no relocation table!");
return FALSE;
}
DWORD maxSize = relocDir.Size;
DWORD parsedSize = 0;
DWORD relocAddr = relocDir.VirtualAddress;
IMAGE_BASE_RELOCATION* reloc = NULL;
while (parsedSize < maxSize)
{
reloc = (IMAGE_BASE_RELOCATION*)(relocAddr + parsedSize + (ULONG_PTR)ImageBaseAddress);
parsedSize += reloc->SizeOfBlock;
if (reloc->VirtualAddress == NULL || reloc->SizeOfBlock == 0)
{
continue;
}
printf("RelocBlock: %p %p\n", reloc->VirtualAddress, reloc->SizeOfBlock);
size_t entriesNum = (reloc->SizeOfBlock - 2 * sizeof(uint32_t)) / sizeof(uint16_t);
DWORD page = reloc->VirtualAddress;
BASE_RELOCATION_ENTRY* block = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)reloc + sizeof(uint32_t) + sizeof(uint32_t));
if (applyRelocBlock(block, entriesNum, page, newBase) == FALSE)
{
return FALSE;
}
}
return TRUE;
}
bool checkLibs()
{
return load_ntdll_functions() && load_kernel32_functions();
}
bool mapAndRun()
{
HANDLE hSection = NULL;
PVOID ImageBaseAddress = NtCurrentTeb()->Peb->ImageBaseAddress;
PIMAGE_NT_HEADERS NtHeaders = RtlImageNtHeader(ImageBaseAddress);
if (NtHeaders == NULL)
{
printf("[ERROR] RtlImageNtHeader failed, error : %d\n", GetLastError());
return false;
}
LARGE_INTEGER MaximumSize;
ULONG ImageSize = NtHeaders->OptionalHeader.SizeOfImage;
MaximumSize.LowPart = ImageSize;
MaximumSize.HighPart = 0;
NTSTATUS Status = NULL;
if ((Status = ZwCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != STATUS_SUCCESS)
{
printf("[ERROR] ZwCreateSection failed, status : %x\n", Status);
system("pause");
return false;
}
printf("Section handle: %x\n", hSection);
HANDLE hProcess = NULL;
PVOID pSectionBaseAddress = NULL;
SIZE_T ViewSize = 0;
DWORD dwInheritDisposition = 1; //VIEW_SHARE
// map the section in context of current process:
if ((Status = NtMapViewOfSection(hSection, GetCurrentProcess(), &pSectionBaseAddress, NULL, NULL, NULL, &ViewSize, dwInheritDisposition, NULL, PAGE_EXECUTE_READWRITE)) != STATUS_SUCCESS)
{
printf("[ERROR] NtMapViewOfSection failed, status : %x\n", Status);
system("pause");
return false;
}
printf("Created new section, BaseAddress: %p ViewSize: %p\n", pSectionBaseAddress, ViewSize);
printf("Mapping into: %p <- current image: %p %p\n", pSectionBaseAddress, ImageBaseAddress, ImageSize);
RtlCopyMemory(pSectionBaseAddress, ImageBaseAddress, ImageSize);
ZwClose(hSection);
hSection = NULL;
if (applyRelocations(NtHeaders, pSectionBaseAddress) == FALSE) {
printf("Applying relocations failed, cannot continue!");
ZwTerminateProcess(GetCurrentProcess(), STATUS_FAILURE);
}
printf("Applied relocations!\n");
//
std::vector<unsigned char> extractedData = unpackExe(); //packe exe
IMAGE_NT_HEADERS INH;
IMAGE_DOS_HEADER IDH;
memcpy(&IDH, &extractedData[0], sizeof(IDH));
memcpy(&INH, (void*)((DWORD)&extractedData[0] + IDH.e_lfanew), sizeof(INH));
LARGE_INTEGER MaximumSizeEX;
ULONG ImageSizeEX = INH.OptionalHeader.SizeOfImage;
MaximumSizeEX.LowPart = ImageSizeEX;
MaximumSizeEX.HighPart = 0;
ULONG_PTR exEP = INH.OptionalHeader.AddressOfEntryPoint;
ULONG_PTR offsetFromBase = exEP - (ULONG_PTR)ImageBaseAddress;
printf("extracted EP offset: %p\n", offsetFromBase);
ULONG_PTR newMain = ((ULONG_PTR)pSectionBaseAddress + offsetFromBase);
printf("extracted EP address in new section: %p\n", newMain);
__asm {
call newMain
};
return true;
}
bool mapAndExecute()
{
if (checkLibs())
{
if (mapAndRun())
{
}
}
return true;
}
int main()
{
mapAndExecute();
std::cin.get();
}
main.h:pastebin: pastebin.com/Spc5WTsQ
#pragma once
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#if !defined NTSTATUS
typedef LONG NTSTATUS;
#endif
#define STATUS_SUCCESS 0
#define STATUS_FAILURE (-1)
#define NtCurrentProcess() ((HANDLE)-1)
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
typedef LONG NTSTATUS, *PNTSTATUS;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _PEB_LDR_DATA
{
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
typedef struct _PEB
{
BYTE Reserved1[2]; /* 00 */
BYTE BeingDebugged; /* 02 */
BYTE Reserved2[5]; /* 03 */
HMODULE ImageBaseAddress; /* 08 */
PPEB_LDR_DATA LdrData; /* 0c */
DWORD ProcessParameters; /* 10 */
PVOID __pad_14; /* 14 */
HANDLE ProcessHeap; /* 18 */
BYTE __pad_1c[36]; /* 1c */
DWORD TlsBitmap; /* 40 */
ULONG TlsBitmapBits[2]; /* 44 */
BYTE __pad_4c[24]; /* 4c */
ULONG NumberOfProcessors; /* 64 */
BYTE __pad_68[128]; /* 68 */
PVOID Reserved3[59]; /* e8 */
ULONG SessionId; /* 1d4 */
} PEB, *PPEB;
typedef struct _TEB
{
NT_TIB Tib; /* 000 */
PVOID EnvironmentPointer; /* 01c */
CLIENT_ID ClientId; /* 020 */
PVOID ActiveRpcHandle; /* 028 */
PVOID ThreadLocalStoragePointer; /* 02c */
PPEB Peb; /* 030 */
ULONG LastErrorValue; /* 034 */
BYTE __pad038[140]; /* 038 */
ULONG CurrentLocale; /* 0c4 */
BYTE __pad0c8[1752]; /* 0c8 */
PVOID Reserved2[278]; /* 7a0 */
UNICODE_STRING StaticUnicodeString; /* bf8 used by advapi32 */
WCHAR StaticUnicodeBuffer[261]; /* c00 used by advapi32 */
PVOID DeallocationStack; /* e0c */
PVOID TlsSlots[64]; /* e10 */
LIST_ENTRY TlsLinks; /* f10 */
PVOID Reserved4[26]; /* f18 */
PVOID ReservedForOle; /* f80 Windows 2000 only */
PVOID Reserved5[4]; /* f84 */
PVOID TlsExpansionSlots; /* f94 */
} TEB, *PTEB;
typedef
void
(*PKNORMAL_ROUTINE) (
void* NormalContext,
void* SystemArgument1,
void* SystemArgument2
);
typedef struct {
int info;
PKNORMAL_ROUTINE fun;
} *PIO_STATUS_BLOCK;
// Make sure we print the __stdcall properly
typedef
void
(__stdcall *PIO_APC_ROUTINE) (
void* ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
long Reserved
);
#if !defined PROCESSINFOCLASS
typedef LONG PROCESSINFOCLASS;
#endif
#if !defined THREADINFOCLASS
typedef LONG THREADINFOCLASS;
#endif
#if !defined PPEB
typedef struct _PEB *PPEB;
#endif
#if !defined PROCESS_BASIC_INFORMATION
typedef struct _PROCESS_BASIC_INFORMATION {
PVOID Reserved1;
PPEB PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
#endif;
/*
typedef LONG NTSTATUS, *PNTSTATUS;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
*/
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
typedef NTSTATUS(WINAPI * PFN_ZWQUERYINFORMATIONPROCESS)(HANDLE, PROCESSINFOCLASS,
PVOID, ULONG, PULONG);
//ntdll api:
NTSTATUS(NTAPI *ZwQueryInformationProcess)(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength OPTIONAL
);
NTSTATUS(NTAPI *ZwCreateSection)(
__out PHANDLE SectionHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in PLARGE_INTEGER MaximumSize,
__in ULONG SectionPageProtection,
__in ULONG AllocationAttributes,
__in HANDLE FileHandle
);
NTSTATUS(NTAPI *NtMapViewOfSection)(
__in HANDLE SectionHandle,
__in HANDLE ProcessHandle,
__inout PVOID *BaseAddress,
__in ULONG_PTR ZeroBits,
__in SIZE_T CommitSize,
__inout PLARGE_INTEGER SectionOffset,
__inout PSIZE_T ViewSize,
__in DWORD InheritDisposition,
__in ULONG AllocationType,
__in ULONG Win32Protect
);
NTSTATUS(NTAPI *ZwCreateThreadEx) (
__out PHANDLE ThreadHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in HANDLE ProcessHandle,
__in PVOID StartRoutine,
__in PVOID Argument,
__in ULONG CreateFlags,
__in ULONG_PTR ZeroBits,
__in SIZE_T StackSize,
__in SIZE_T MaximumStackSize,
__in PVOID AttributeList
);
NTSTATUS(NTAPI *ZwUnmapViewOfSection) (
__in HANDLE ProcessHandle,
__in PVOID BaseAddress
);
NTSTATUS(NTAPI *ZwClose) (
__in HANDLE Handle
);
NTSTATUS(NTAPI *ZwTerminateProcess) (
__in HANDLE ProcessHandle,
__in NTSTATUS ExitStatus
);
NTSTATUS(NTAPI *NtQueueApcThread)(
__in HANDLE ThreadHandle,
__in PVOID ApcRoutine,
__in PVOID ApcRoutineContext OPTIONAL,
__in PVOID ApcStatusBlock OPTIONAL,
__in ULONG ApcReserved OPTIONAL
);
NTSTATUS(NTAPI *ZwSetInformationThread) (
__in HANDLE ThreadHandle,
__in THREADINFOCLASS ThreadInformationClass,
__in PVOID ThreadInformation,
__in ULONG ThreadInformationLength
);
PIMAGE_NT_HEADERS(NTAPI *RtlImageNtHeader) (
__in PVOID ModuleAddress
);
//kernel32 api
BOOL
(WINAPI *CreateProcessInternalW)(HANDLE hToken,
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation,
PHANDLE hNewToken
);
trying to adoptate source from here
https://github.com/hasherezade/snippets/tree/master/inject4
The data you have loaded is in the loading application's data space and execution of data is blocked in modern memory protected operating systems - because that was previously a common and rather easy exploit for viruses.
In order to run, an executable must have been loaded and located by the operating system.
Not sure how to post a large code snippet in response to a request that was a remark. Michael Haephrati asked for an x64 example of executing data as x64 code. Here's the code (x64 only for this code, the calling conventions differ in 32 bit code):
#include <windows.h>
#include <iostream>
typedef int(*PMULFUNC)(int l, int r);
PMULFUNC buildFunction()
{
LPVOID pMem = VirtualAlloc(NULL, 6, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
unsigned char *pNext = (unsigned char *)pMem;
*pNext = 0x8b; ++pNext; // mov eax, ecx
*pNext = 0xc1; ++pNext;
*pNext = 0x0f; ++pNext; // imul eax, edx
*pNext = 0xaf; ++pNext;
*pNext = 0xc2; ++pNext;
*pNext = 0xc3; ++pNext; // ret
return (PMULFUNC)pMem;
}
int main()
{
using namespace std;
PMULFUNC pMul = buildFunction();
int l = 5;
for (int r=1; r<=12; ++r)
{
cout << l << "x" << r << "=" << (*pMul)(l, r) << endl;
}
return 0;
}
typedef int(__stdcall *PMULFUNC)(int l, int r);
PMULFUNC buildFunction()
{
LPVOID pMem = VirtualAlloc(NULL, 6, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
unsigned char *pNext = (unsigned char *)pMem;
*pNext = 0x8b; ++pNext; // mov eax, ecx
*pNext = 0xc1; ++pNext;
*pNext = 0x0f; ++pNext; // imul eax, edx
*pNext = 0xaf; ++pNext;
*pNext = 0xc2; ++pNext;
*pNext = 0xc3; ++pNext; // ret
return (PMULFUNC)pMem;
}
int main()
{
using namespace std;
PMULFUNC pMul = buildFunction();
int l = 5;
for (int r=1; r<=12; ++r)
{
cout << l << "x" << r << "=" << (*pMul)(l, r) << endl;
}
return 0;
}
In response to the specific question, it's possible but advanced. EXEs aren't simply copied into memory: references to other DLL's are resolved, sections that are adjacent in the image may be separated in memory, and many other things. In short you would have to re-implement the loader. But one thing is certain, if a page has the correct permissions it can be executed,
It would be impossible to run an executable from memory without having the executable data reside in a physical file. Windows won't allow it. No windows API provides mechanism to execute file directly from memory. All windows API like CreateProcess() or ShellExcute() require a physical file to be present. If there is a way, it would be considered a vulnerability and will be soon patched.