I want to read asynchronously from a child process's stdin, so I create an IOCP loop that listens for when ReadFile() is done. But ReadFile() is never done, it always returns ERROR_IO_PENDING, and WriteFile() always returns ERROR_IO_PENDING.
Node.js can asynchronously read from a child process:
const child_process = require("child_process")
var p = child_process.spawn("gcc")
p.stdout.on('data', data=>console.log(String(data)))
p.stderr.on('data', data=>console.log(String(data)))
How to do this in the Win32 API?
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <wchar.h>
#pragma comment (lib, "User32.lib")
static HANDLE iocp = INVALID_HANDLE_VALUE;
typedef struct PROCESS {
OVERLAPPED ol;
HANDLE hProcess,
stdin_read, stdin_write,
stdout_read, stdout_write,
stderr_read, stderr_write;
char buf[100];
} PROCESS, *PPROCESS, *LPROCESS;
DWORD WINAPI Worker(LPVOID param);
BOOL create_pipe(HANDLE* pserver_pipe, HANDLE* pclient_pipe, PROCESS* p) {
static __int64 counter=0;
HANDLE server_pipe, client_pipe;
int err;
WCHAR name[64];
for (;;) {
swprintf(name, sizeof(name), L"\\\\?\\pipe\\child\\%Id.%p", counter, p);
server_pipe = CreateNamedPipeW(
name,
PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE | WRITE_DAC,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1,
65536,
65536,
0,
NULL);
if (server_pipe != INVALID_HANDLE_VALUE)
break;
err = GetLastError();
if (err != ERROR_PIPE_BUSY && err != ERROR_ACCESS_DENIED) {
return FALSE;
}
counter++;
}
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof sa;
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = 1;
client_pipe = CreateFileW(name,
GENERIC_READ|WRITE_DAC,
0,
&sa,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (client_pipe==INVALID_HANDLE_VALUE){
return FALSE;
}
if (CreateIoCompletionPort(client_pipe, iocp, (ULONG_PTR)p, 0)==NULL){
return FALSE;
}
if (CreateIoCompletionPort(server_pipe, iocp, (ULONG_PTR)p, 0)==NULL){
return FALSE;
}
*pclient_pipe = client_pipe;
*pserver_pipe = server_pipe;
return TRUE;
}
int wmain()
{
iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
assert(iocp);
PROCESS child_process{};
assert(create_pipe(&child_process.stdout_write, &child_process.stdout_read, &child_process));
assert(create_pipe(&child_process.stderr_write, &child_process.stderr_read, &child_process));
assert(create_pipe(&child_process.stdin_write, &child_process.stdin_read, &child_process));
HANDLE hThread = CreateThread(NULL, 0, Worker, iocp, 0, NULL);
assert(hThread);
WCHAR szCmdline[]=L"cmd";
PROCESS_INFORMATION piProcInfo{};
STARTUPINFOW siStartInfo{.cb = sizeof(STARTUPINFO),
.dwFlags=STARTF_USESTDHANDLES,
.hStdInput=child_process.stdin_read,
.hStdOutput=child_process.stdout_write,
.hStdError=child_process.stderr_write};
assert(CreateProcessW(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo));
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
// CloseHandle(child_process.stdout_write);
// CloseHandle(child_process.stdin_read);
ReadFile(child_process.stdout_read, child_process.buf, sizeof(child_process.buf), NULL, &child_process.ol);
int err = GetLastError();
if (err!=ERROR_IO_PENDING){
printf("Error in ReadFile %d\n", err);
}else{
puts("ReadFile is pending...\n");
}
char buf[100];
DWORD dwIn;
for(;;){
ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE), buf, sizeof buf, &dwIn, NULL);
if (dwIn<=0)
break;
WriteFile(child_process.stdin_write, buf, dwIn, NULL, &child_process.ol);
err = GetLastError();
if (err!=ERROR_IO_PENDING){
printf("Error in WriteFile %d\n", err);
}else{
puts("WriteFile is pending...\n");
}
}
PostQueuedCompletionStatus(iocp, 0, 0, 0);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(iocp);
}
DWORD WINAPI Worker(LPVOID param) {
DWORD dwIoSize = 0;
LPOVERLAPPED ol;
PROCESS* ctx;
for(;;){
BOOL bSuccess = GetQueuedCompletionStatus((HANDLE)param, &dwIoSize,
(PDWORD_PTR)&ctx,
&ol,
INFINITE);
if (ctx == NULL) {
printf("ctx is NULL, maybe you call PostQueuedCompletionStatus? err=%d\n", GetLastError());
break;
}
if (bSuccess==FALSE || dwIoSize == 0) {
printf("GetQueuedCompletionStatus does not success(maybe EOF reached?) err=%d\n", GetLastError());
break;
}
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), ctx->buf, dwIoSize, NULL, NULL);
ReadFile(ctx->stdout_read, ctx->buf, sizeof(ctx->buf), NULL, &ctx->ol);
}
return 0;
}
After search on Github, it's quite easy.
Create a Named Pipe for current process(read), and create a File for child process(write).
Github links:
https://github.com/parasol-framework/parasol/blob/c5ad64188c7496e3ff54eb75fd03c9e3124fe08b/src/core/microsoft/processes.c
https://github.com/dinamsky/malware-botnets/blob/4cd142d10f971cb93c334b6f48c12c85bcc8f63a/Phatbot-stoney/Phatbot-stoney/cmdshell.cpp
https://github.com/grimjoey/jlstudio/blob/e5d2d81f0a94d2020f2e912e43a487d9cb6f7c33/src/jls/process.cpp
https://github.com/Ai-Thinker-Open/Ai-Thinker-Open_ESP32-S2_SDK/blob/7d75213674b4572f90c68162ad6fe9b16dae65ad/tools/windows/tool_setup/cmdlinerunner/cmdlinerunner.c
https://github.com/LeonColt/skripsi/blob/141af593ec65cd7adaedf7a90fc4cd7cde5cc602/Maschinen/RedirectIOChildProcess.cpp
redirect stdout and stderr
#include <windows.h>
#include <assert.h>
#include <stdio.h>
struct Stdio;
typedef void (*Callback)(struct Stdio* self, DWORD len);
struct Stdio {
OVERLAPPED ol;
HANDLE pipe;
BYTE buf[100];
Callback callback;
};
typedef struct CTX {
struct Stdio Stdout, Stderr, Stdin;
} CTX, *LCTX, *LPCTX;
HANDLE Hstdout=INVALID_HANDLE_VALUE;
HANDLE Hstderr=INVALID_HANDLE_VALUE;
DWORD WINAPI Worker(LPVOID iocp){
struct Stdio *stdio;
OVERLAPPED *ol;
DWORD dwIoSize;
for(;;){
if (!GetQueuedCompletionStatus(iocp, &dwIoSize, (PDWORD_PTR)&stdio, &ol, INFINITE) || dwIoSize==0 || ol==NULL || stdio==NULL){
switch (GetLastError()){
case ERROR_BROKEN_PIPE:
puts("the process has been exited, exit thread...");
break;
default:
printf("error = %d, exit thread\n", GetLastError());
}
break;
}
stdio->callback(stdio, dwIoSize);
}
return 0;
}
void OnStdoutRead(struct Stdio *self, DWORD len){
WriteConsoleA(Hstdout, self->buf, len, NULL, NULL);
ReadFile(self->pipe, self->buf, sizeof(self->buf), NULL, &self->ol);
}
void OnStderrRead(struct Stdio *self, DWORD len){
WriteConsoleA(Hstderr, self->buf, len, NULL, NULL);
ReadFile(self->pipe, self->buf, sizeof(self->buf), NULL, &self->ol);
}
int wmain(){
assert(Hstdout = GetStdHandle(STD_OUTPUT_HANDLE));
assert(Hstderr = GetStdHandle(STD_ERROR_HANDLE));
HANDLE Pstdout, Pstderr; // child process's
CTX ctx{.Stdout=Stdio{.callback=OnStdoutRead}, .Stderr=Stdio{.callback=OnStderrRead}};
HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
HANDLE hThread = CreateThread(NULL, 0, Worker, iocp, 0, NULL); // Worker thread
SECURITY_ATTRIBUTES sa{.nLength=sizeof(SECURITY_ATTRIBUTES), .bInheritHandle=TRUE};
const WCHAR* pipe_name1 = L"\\\\.\\Pipe\\child-1";
assert((ctx.Stdout.pipe = CreateNamedPipeW(
pipe_name1,
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE,
1,
4096,
4096,
5000,
NULL))!=INVALID_HANDLE_VALUE);
assert(INVALID_HANDLE_VALUE != (Pstdout = CreateFileW(
pipe_name1,
GENERIC_WRITE,
0,
&sa,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL)));
const WCHAR *pipe_name2 = L"\\\\.\\Pipe\\child-2";
assert((ctx.Stderr.pipe = CreateNamedPipeW(
pipe_name2,
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE,
1,
4096,
4096,
5000,
NULL))!=INVALID_HANDLE_VALUE);
assert((Pstderr = CreateFileW(
pipe_name2,
GENERIC_WRITE,
0,
&sa,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL))!=INVALID_HANDLE_VALUE);
STARTUPINFOW si{.cb = sizeof(si),
.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW,
.wShowWindow = SW_HIDE,
.hStdInput = GetStdHandle(STD_INPUT_HANDLE), // use current stdin
.hStdOutput = Pstdout,
.hStdError = Pstderr};
WCHAR cmd[] = L"powershell"; // or cmd, py, bash...
PROCESS_INFORMATION pInfo{};
assert(CreateProcessW(
NULL, cmd, NULL, NULL,
TRUE, 0, NULL, NULL,
&si, &pInfo));
assert(CloseHandle(Pstdout)); // we don't need this
assert(CloseHandle(Pstderr)); // we don't need this
assert(CreateIoCompletionPort(ctx.Stdout.pipe, iocp, (ULONG_PTR)&ctx.Stdout, 0));
assert(CreateIoCompletionPort(ctx.Stderr.pipe, iocp, (ULONG_PTR)&ctx.Stderr, 0));
ReadFile(ctx.Stdout.pipe, ctx.Stdout.buf, sizeof(ctx.Stdout.buf), NULL, &ctx.Stdout.ol);
ReadFile(ctx.Stderr.pipe, ctx.Stdout.buf, sizeof(ctx.Stderr.buf), NULL, &ctx.Stderr.ol);
WaitForSingleObject(pInfo.hProcess, INFINITE); // wait for process exit
PostQueuedCompletionStatus(iocp, 0, 0, NULL); // tell IOCP Worker exit
WaitForSingleObject(hThread, INFINITE); // wait for thread exit
assert(CloseHandle(hThread));
assert(CloseHandle(ctx.Stderr.pipe));
assert(CloseHandle(ctx.Stdout.pipe));
assert(CloseHandle(pInfo.hProcess));
assert(CloseHandle(iocp));
assert(CloseHandle(Hstderr));
puts("exit main function..."); // !!important: before close stdout
assert(CloseHandle(Hstdout));
}
Redirect stdin, stdout, stderr
#include <windows.h>
#include <assert.h>
#include <stdio.h>
static HANDLE Hstdout, Hstderr, Hstdin, HstopEvent;
struct Stdio;
typedef void (*Callback)(struct Stdio* self, DWORD len);
struct Stdio {
OVERLAPPED ol;
HANDLE pipe;
BYTE buf[100];
Callback callback;
};
typedef struct CTX {
struct Stdio Stdout, Stderr, Stdin;
} CTX, *LCTX, *LPCTX;
DWORD WINAPI Worker(LPVOID iocp){
struct Stdio *stdio;
OVERLAPPED *ol;
DWORD dwIoSize;
for(;;){
if (!GetQueuedCompletionStatus(iocp, &dwIoSize, (PDWORD_PTR)&stdio, &ol, INFINITE) || dwIoSize==0 || ol==NULL || stdio==NULL){
switch (GetLastError()){
case ERROR_BROKEN_PIPE:
SetEvent(HstopEvent);
puts("the process has been exited, exit thread...");
break;
default:
printf("error = %d, exit thread\n", GetLastError());
}
break;
}
stdio->callback(stdio, dwIoSize);
}
return 0;
}
void OnStdoutRead(struct Stdio *self, DWORD len){
WriteConsoleA(Hstdout, self->buf, len, NULL, NULL);
ReadFile(self->pipe, self->buf, sizeof(self->buf), NULL, &self->ol);
}
void OnStderrRead(struct Stdio *self, DWORD len){
WriteConsoleA(Hstderr, self->buf, len, NULL, NULL);
ReadFile(self->pipe, self->buf, sizeof(self->buf), NULL, &self->ol);
}
void OnStdinWriteComplete(struct Stdio *self, DWORD len){
printf("[%u bytes write to stdin]\n", len);
}
int wmain(){
assert((Hstdout = GetStdHandle(STD_OUTPUT_HANDLE))!=INVALID_HANDLE_VALUE);
assert((Hstderr = GetStdHandle(STD_ERROR_HANDLE))!=INVALID_HANDLE_VALUE);
assert((Hstdin = GetStdHandle(STD_INPUT_HANDLE))!=INVALID_HANDLE_VALUE);
assert((HstopEvent=CreateEventW(NULL, FALSE, FALSE, NULL))!=INVALID_HANDLE_VALUE);
CTX ctx{.Stdout=Stdio{.callback=OnStdoutRead}, .Stderr=Stdio{.callback=OnStderrRead}, .Stdin=Stdio{.callback=OnStdinWriteComplete}};
STARTUPINFOW si{.cb = sizeof(si), .dwFlags = STARTF_USESTDHANDLES};
HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
HANDLE hThread = CreateThread(NULL, 0, Worker, iocp, 0, NULL); // Worker thread
SECURITY_ATTRIBUTES sa{.nLength=sizeof(SECURITY_ATTRIBUTES), .bInheritHandle=TRUE};
const WCHAR* pipe_name1 = L"\\\\.\\Pipe\\child-1";
assert((ctx.Stdout.pipe = CreateNamedPipeW(
pipe_name1,
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE,
1,
4096,
4096,
5000,
NULL))!=INVALID_HANDLE_VALUE);
assert((si.hStdOutput = CreateFileW(
pipe_name1,
GENERIC_WRITE,
0,
&sa,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL))!=INVALID_HANDLE_VALUE);
const WCHAR *pipe_name2 = L"\\\\.\\Pipe\\child-2";
assert((ctx.Stderr.pipe = CreateNamedPipeW(
pipe_name2,
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE,
1,
4096,
4096,
5000,
NULL))!=INVALID_HANDLE_VALUE);
assert((si.hStdError = CreateFileW(
pipe_name2,
GENERIC_WRITE,
0,
&sa,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL))!=INVALID_HANDLE_VALUE);
const WCHAR *pipe_name3 = L"\\\\.\\Pipe\\child-3";
assert((ctx.Stdin.pipe = CreateNamedPipeW(
pipe_name3,
PIPE_ACCESS_OUTBOUND,
PIPE_TYPE_BYTE,
1,
4096,
4096,
5000,
NULL))!=INVALID_HANDLE_VALUE);
assert((si.hStdInput = CreateFileW(
pipe_name3,
GENERIC_READ,
0,
&sa,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL))!=INVALID_HANDLE_VALUE);
WCHAR cmd[] = L"powershell"; // or cmd, py, bash...
PROCESS_INFORMATION pInfo{};
assert(CreateProcessW(
NULL, cmd, NULL, NULL,
TRUE, 0, NULL, NULL,
&si, &pInfo));
assert(CloseHandle(si.hStdError)); // we don't need this
assert(CloseHandle(si.hStdInput)); // we don't need this
assert(CloseHandle(si.hStdOutput)); // we don't need this
assert(CreateIoCompletionPort(ctx.Stdout.pipe, iocp, (ULONG_PTR)&ctx.Stdout, 0));
assert(CreateIoCompletionPort(ctx.Stderr.pipe, iocp, (ULONG_PTR)&ctx.Stderr, 0));
ReadFile(ctx.Stdout.pipe, ctx.Stdout.buf, sizeof(ctx.Stdout.buf), NULL, &ctx.Stdout.ol);
ReadFile(ctx.Stderr.pipe, ctx.Stdout.buf, sizeof(ctx.Stderr.buf), NULL, &ctx.Stderr.ol);
DWORD dwIoSize;
for(;;){
CHAR buf[100];
ReadConsoleA(Hstdin, buf, sizeof(buf), &dwIoSize, NULL);
if (WaitForSingleObject(HstopEvent, 0)==WAIT_OBJECT_0)
break;
WriteFile(ctx.Stdin.pipe, buf, dwIoSize, NULL, &ctx.Stdin.ol);
if (WaitForSingleObject(HstopEvent, 0)==WAIT_OBJECT_0)
break;
}
PostQueuedCompletionStatus(iocp, 0, 0, NULL); // tell IOCP Worker exit
WaitForSingleObject(hThread, INFINITE); // wait for thread exit
assert(CloseHandle(hThread));
assert(CloseHandle(ctx.Stderr.pipe));
assert(CloseHandle(ctx.Stdout.pipe));
assert(CloseHandle(pInfo.hProcess));
assert(CloseHandle(iocp));
assert(CloseHandle(Hstderr));
assert(CloseHandle(HstopEvent));
puts("exit main function..."); // !!important: before close stdout
assert(CloseHandle(Hstdout));
}
I am just a beginner in coding. I know the basics of c++ and c#. I was making a dll injector. I also followed a tutorial for a part. One part of the thing I have from the tutorial has an error and it is "Too many initializer values". I don't know what that means and why it is there. The error is at the nullptr in the next line
void* allocated_memory(h_process, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
It is at the first nullptr. If I remove that nullptr then I get the error at the MAX_PATH
biggest part of code:
#include <iostream>
#include <Windows.h>
#include <string>
#include <thread>
#include <libloaderapi.h>
void get_id(const char* windows_title, DWORD &process_id)
{
GetWindowThreadProcessId(FindWindow(NULL, windows_title), &process_id);
}
void error(const char* error_title, const char* error_message)
{
MessageBox(NULL, error_message, error_title, NULL);
exit(-1);
}
bool file_exists(std::string file_name)
{
struct stat buffer;
return (stat(file_name.c_str(), &buffer) == 0);
}
int main()
{
DWORD proc_id = NULL;
char dll_path(MAX_PATH);
const char* dll_name = "PW";
const char* window_title = "Pixel Worlds";
if (!file_exists(dll_name))
{
error("file_exists", "Something went wrong");
}
HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, NULL, proc_id);
void* allocated_memory(h_process, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
HANDLE h_thread = CreateRemoteThread(h_process, nullptr, NULL, LPTHREAD_START_ROUTINE(LoadLibraryA), allocated_memory, NULL, nullptr);
CloseHandle(h_process);
VirtualFreeEx(h_process, allocated_memory, NULL, MEM_RELEASE);
MessageBox(NULL, "Successfully injected", "Success", NULL);
}
You appear to be trying to use the VirtualAllocEx WinAPI routine but you have not named that function in your attempted call, which is just incorrect syntax.
In place of the line:
void* allocated_memory(h_process, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
you need to declare your void* pointer and allocate the memory it points to with a call like the following:
void* allocated_memory = VirtualAllocEx(h_process, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
The target is notepad.exe
The code to insert is a PE file called Hello.
PE FILE:
Error:
error occurred --> if (IDH->e_magic== IMAGE_DOS_SIGNATURE
error content is "Exception through: read access violation. IDH was 0xC4."
The following is the whole code.
#include <Windows.h>
#include <TlHelp32.h>
#include <stdio.h>
#include <tchar.h>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <tchar.h>
#include <locale.h>
#define FILE_PATH ("C:/Windows/System32/notepad.exe")
#define PE_FILE_PATH ("C:/Users/code1/Desktop/Hello.exe")
typedef LONG(WINAPI* NtUnmapViewOfSection)(HANDLE ProcessHandle, PVOID BaseAddress);
void ProcHollowing(LPSTR szFilePath, PVOID ); //pFile);
PVOID pFile();
int main(){
ProcHollowing((LPSTR)(FILE_PATH), pFile());
return 0;
}
PVOID pFile() { //PE파일의 DATA 추출 부분 (.text 추출)
HANDLE hFile = CreateFile((LPCWSTR)PE_FILE_PATH, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, 0);
if (hFile == INVALID_HANDLE_VALUE) {
printf("Fail: Hello.exe 파일이 없습니다.");
}
return hFile;
}
void ProcHollowing(LPSTR szFilePath /*TargetProc*/, PVOID pFile/*PE Data*/) {
PIMAGE_DOS_HEADER IDH;/*PE 구조의 첫 시작부분 DOS 헤더*/
PIMAGE_NT_HEADERS INH;/*PE헤더*/
PIMAGE_SECTION_HEADER ISH; //섹션 헤더
PROCESS_INFORMATION PI; //프로세스 정보
STARTUPINFOA SI; //생성시 프로세스에 대한 윈도우 스테이션, 데스크탑, 표준 핸들 및 주 창의 모양을 지정하는 구조체.
PCONTEXT CTX;
PDWORD dwImageBase; //생성된 프로세스의 Image Base 주소
NtUnmapViewOfSection NewNtUnmapViewOfSection;
LPVOID pImageBase;
int Count;
IDH = PIMAGE_DOS_HEADER(pFile);
if (IDH->e_magic== IMAGE_DOS_SIGNATURE/*PE 파일이 맞는지 시그니처를 통해 확인한다.*/) { //IMAGE_DOS_SIGNATURE는 4D5A 즉 MZ
_tprintf(TEXT("SUCCESS: This is PE File\n"));
INH = PIMAGE_NT_HEADERS(DWORD(pFile) + IDH->e_lfanew); // Dos header의 끝부분 + 시작 부분으로 PE Header를 구하는 것.
if (INH->Signature == IMAGE_NT_SIGNATURE/*시그니처 확인하여 PE포멧 파일인지 구분*/) {
_tprintf(TEXT("SUCCESS: PE FILE Check\n"));
RtlZeroMemory(&SI, sizeof(SI)); // SI의 크기만큼 0으로 채워준다는 의미. 즉, 초기화
RtlZeroMemory(&PI, sizeof(PI));
bool bResult = CreateProcessA(szFilePath/*실행할 모듈의 이름 notepad.exe*/, NULL, NULL, NULL,
FALSE, CREATE_SUSPENDED, NULL, NULL, &SI, &PI); // 정상 프로세스를 생성. SUSPENDED 상태.
if (bResult) {//프로세스가 잘 생성되면
_tprintf(TEXT("SUCCESS: CreateProcessA\n"));
CTX = PCONTEXT(VirtualAlloc(NULL, sizeof(CTX), MEM_COMMIT, PAGE_READWRITE)); //CTX 가상 메모리 할당.
CTX->ContextFlags = CONTEXT_FULL; //Context 구조에서 초기화해야하는 부분을 나타내는 값.
if (GetThreadContext(PI.hThread, LPCONTEXT(CTX))/*프로세스 정보 획득*/) {
ReadProcessMemory(PI.hProcess, LPCVOID(CTX->Ebx + 8), LPVOID(&dwImageBase), 4, NULL); /*Image Base 주소 구하기*/
//ebx에는 PEB 주소가 들어가 있고 PEB+8에는 Image Base 주소가 들어가있다.
//PEB(Process Environment Block)에는 해당 프로세스에 대한 정보가 들어가있음.
_tprintf(TEXT("SUCCESS: Get Image Base Address\n"));
if (DWORD(dwImageBase) == INH->OptionalHeader.ImageBase) { //Image Base와 PE Header의 OPTIONAL HEADER의 Image Base가 같으면
NewNtUnmapViewOfSection = NtUnmapViewOfSection(GetProcAddress(GetModuleHandleA("ntdll.dll"),
"NtUnmapViewOfSection")); //메모리 할당해지 핸들이랑 주소
_tprintf(TEXT("SUCCESS: Unmap\n"));
NewNtUnmapViewOfSection(PI.hProcess, PVOID(dwImageBase)); //정상 프로세스의 메모리 할당 해지
}
else {
_tprintf(TEXT("FAIL: Unmap\n"));
}
pImageBase = VirtualAllocEx(PI.hProcess, LPVOID(INH->OptionalHeader.ImageBase), //재할당
INH->OptionalHeader.SizeOfImage, 0x3000, PAGE_EXECUTE_READWRITE);
/*악성코드 데이터 삽입 부분 같은데 여기부분 이해 잘 안됨.*/
if (pImageBase) { //재할당 완료 되면
_tprintf(TEXT("SUCCESS: Reassignment\n"));
WriteProcessMemory(PI.hProcess, pImageBase, pFile,/*pFile, 즉 데이터를 넣어주는 부분같음.*/
INH->OptionalHeader.SizeOfHeaders, NULL);//악성코드 삽입(?) 헤더를 바꿔주는 거같음.
for (Count = 0; Count < INH->FileHeader.NumberOfSections; Count++) {
ISH = PIMAGE_SECTION_HEADER(DWORD(pFile) + IDH->e_lfanew + 248 + (Count * 40));
WriteProcessMemory(PI.hProcess,
LPVOID(DWORD(pImageBase) + ISH->VirtualAddress),
LPVOID(DWORD(pFile) + ISH->PointerToRawData),
ISH->SizeOfRawData, NULL);
}
WriteProcessMemory(PI.hProcess, LPVOID(CTX->Ebx + 8),// 메모리 쓰기. Image Base
LPVOID(&INH->OptionalHeader.ImageBase),
4, NULL);
CTX->Eax = DWORD(pImageBase) + INH->OptionalHeader.AddressOfEntryPoint; //AddressOfEntryPoint는 PE파일이 메모리에
//로드된 후 맨 처음 실행되어야 하는 코드의 주소가 포함되어 있음.
//Eax는 Original Entry Point(OEP)가 들어가 있음. 즉, 실행 프로그램의 실제 시작 위치
SetThreadContext(PI.hThread, LPCONTEXT(CTX)); //프로세스 정보 변경
ResumeThread(PI.hThread); //프로세스 재실행
}
else {
_tprintf(TEXT("FAIL: Reassignment\n"));
}
}
else {
_tprintf(TEXT("FAIL: Get Image Base Address\n"));
}
}
else {
_tprintf(TEXT("FAIL: CreateProcessA\n"));
}
}
else {
_tprintf(TEXT("FAIL: PE FILE Check\n"));
}
VirtualFree(pFile, 0, MEM_RELEASE);
}
else { //PE파일이 아니면
_tprintf(TEXT("FAIL: This is not PE File\n"));
}
}
There are too many things to fix for excution
I will not upload the modified code because there are some parts that were not implemented..
First,
PVOID pFile(){ // hFile is a HANDLE but ret type is PVOID... hmmm
...
//HANDLE hFile = CreateFile((LPCWSTR)PE_FILE_PATH, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, 0); // <-- GENREIC_WRITE? and CREATE_ALWAYS?
HANDLE hFile = CreateFile($file_name, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
...
You should read MSDN Docs CreateFile
You create a file handle, but there is no reading method (like API ReadFile)
^ This is the cause of the error.
...
if (DWORD(dwImageBase) == INH->OptionalHeader.ImageBase) {
NewNtUnmapViewOfSection = NtUnmapViewOfSection(GetProcAddress(GetModuleHandleA("ntdll.dll"),
"NtUnmapViewOfSection"));
NewNtUnmapViewOfSection(PI.hProcess, PVOID(dwImageBase));
...
...
WriteProcessMemory(PI.hProcess, LPVOID(CTX->Ebx + 8),
LPVOID(&INH->OptionalHeader.ImageBase),
4, NULL);
...
STUDY ASRL!
...
CTX->Eax = DWORD(pImageBase) + INH->OptionalHeader.AddressOfEntryPoint;
SetThreadContext(PI.hThread, LPCONTEXT(CTX));
ResumeThread(PI.hThread);
...
You ResumeThread before write the Dos Header, Dos Stub, PE Header in child process Memory
you have to fix entire codeㅠㅠ :(
So I had to two friends test the executable on their Vista & Win7 operating systems. Neither had the injected code executed (even when Run as Administrator) but the console open/closed. Does code injection via WriteProcessMemory and CreateRemoteThread still work on Vista or Win7?
The Code
Compiled using /RTCu on Visual Studio 2008 to prevent process crashing while on Windows XP after the remote thread terminates.
CodeInjector.h
#ifndef CODEINJECTOR_H
#define CODEINJECTOR_H
typedef HANDLE(WINAPI *GETPROC)();
typedef HMODULE(WINAPI *PLOADLIBRARYA)(const char *dll);
typedef LPVOID(WINAPI *PGETPROCADDRESS)(HMODULE mod, const char *func);
typedef int (WINAPI *FNMESSAGEBOX)(HWND, LPCSTR, LPCSTR, UINT);
typedef struct _IAT {
PLOADLIBRARYA pLoadLibraryA;
PGETPROCADDRESS pGetProcAddress;
FNMESSAGEBOX fnMessageBox;
} IAT;
typedef struct _DATA {
void *szData[256];
} DATA;
typedef struct _FNARGS {
LPVOID pIat;
LPVOID pData;
} FNARGS;
#endif /* CODEINJECTOR_H */
CodeInjector.cpp
#include "stdafx.h"
#include <iostream>
#include <string>
#include <windows.h>
#include "CodeInjector.h"
using namespace std;
HANDLE getHandleByName(const char* nameWnd)
{
HWND hWnd = FindWindowA(0, nameWnd);
if (hWnd == 0) {
std::cerr << "Cannot find window" << std::endl;
} else {
DWORD pId;
GetWindowThreadProcessId(hWnd, &pId);
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, 0, &tkp, sizeof (tkp), NULL, NULL);
}
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pId);
if (!hProc) {
std::cerr << "Cannot open process: " << GetLastError() << std::endl;
} else {
return hProc;
}cout << hProc;
getchar();
}
return false;
}
static DWORD WINAPI ThreadFunc(FNARGS *info)
{
if (info == NULL || info->pIat == NULL || info->pData == NULL) {
return 0;
}
IAT *iat = (IAT *)info->pIat;
DATA *data = (DATA *)info->pData;
iat->fnMessageBox(NULL, (char*)data->szData[1], (char*)data->szData[0], MB_OK);
return 0;
}
static void ThreadFuncEnd() {}
int main(int argc, char** argv)
{
HANDLE hProc = getHandleByName("Calculator");
DWORD CodeSize = (DWORD) & ThreadFuncEnd - (DWORD) & ThreadFunc;
IAT hIAT;
DWORD hLibModule;
HMODULE hKernel = LoadLibraryA("kernel32.dll");
HMODULE hUser32 = LoadLibraryA("user32.dll");
hIAT.pLoadLibraryA = (PLOADLIBRARYA)GetProcAddress(hKernel, "LoadLibraryA");
hIAT.pGetProcAddress = (PGETPROCADDRESS)GetProcAddress(hKernel, "GetProcAddress");
hIAT.fnMessageBox = (FNMESSAGEBOX)GetProcAddress(hUser32, "MessageBoxA");
LPVOID hIATMemAddr = VirtualAllocEx(hProc, NULL, sizeof (IAT), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProc, hIATMemAddr, (LPVOID) & hIAT, sizeof (IAT), NULL);
DATA hData;
LPVOID hDataMemAddr = VirtualAllocEx(hProc, NULL, sizeof (DATA), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
hData.szData[0] = VirtualAllocEx(hProc, NULL, 64, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
hData.szData[1] = VirtualAllocEx(hProc, NULL, 64, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
hData.szData[2] = VirtualAllocEx(hProc, NULL, 64, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
hData.szData[3] = VirtualAllocEx(hProc, NULL, 64, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
char tmp[64];
strcpy(tmp, "Caption");
WriteProcessMemory(hProc, hData.szData[0], (LPVOID) & tmp, sizeof (tmp), NULL);
strcpy(tmp, "Message");
WriteProcessMemory(hProc, hData.szData[1], (LPVOID) & tmp, sizeof (tmp), NULL);
WriteProcessMemory(hProc, hDataMemAddr, (LPVOID) &hData, sizeof (DATA), NULL);
FNARGS tInfo;
tInfo.pIat = hIATMemAddr;
tInfo.pData = hDataMemAddr;
LPVOID hInfoMemAddr = VirtualAllocEx(hProc, NULL, sizeof (FNARGS), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProc, hInfoMemAddr, (LPVOID) & tInfo, sizeof (FNARGS), NULL);
LPVOID CodeMemAddr = VirtualAllocEx(hProc, NULL, CodeSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProc, CodeMemAddr, (LPVOID) & ThreadFunc, CodeSize, NULL);
HANDLE hRemoteThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)CodeMemAddr, hInfoMemAddr, 0, NULL);
WaitForSingleObject(hRemoteThread, INFINITE);
GetExitCodeThread(hRemoteThread, &hLibModule);
CloseHandle(hProc);
return 0;
}
Not really a surprise. Vista and Windows 7 have increased security. Lots of malware used Code Injection as one of the steps to bypass security mechanisms on Windows XP, and I'm glad to see Microsoft fixed this.