Routing stdout of process with PPID Spoofing - c++

I tried to write a code that uses CreateProcess() to execute CMD commands and will redirect the stdout to a named pipe. I wanted to add a functionality to spoof the Parent PID so that the cmd will spawn under explorer.exe. Each of the functionalities works on it's own but when I tried to merge them it will not work.
The stdout routing:
int main()
{
HANDLE hStdout_Rd = NULL;
HANDLE hStdout_Wr = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&hStdout_Rd, &hStdout_Wr, &saAttr, NULL);
SetHandleInformation(hStdout_Rd, HANDLE_FLAG_INHERIT, 0);
//Set startup info
STARTUPINFO si;
ZeroMemory(&si, (sizeof(STARTUPINFO)));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdError = hStdout_Wr;
si.hStdOutput = hStdout_Wr;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
CString cmd;
if (CreateProcess(NULL, cmd.GetBuffer(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
//Great success read pipe contents
}
CloseHandle(hStdout_Rd);
CloseHandle(hStdout_Wr);
}
The PPID Spoof:
int main() {
CString cmd;
STARTUPINFOEXA sInfoEX;
PROCESS_INFORMATION pInfo;
SIZE_T sizeT;
HANDLE expHandle = OpenProcess(PROCESS_ALL_ACCESS, false, getParentProcessID());
ZeroMemory(&sInfoEX, sizeof(STARTUPINFOEXA));
InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
sInfoEX.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT);
InitializeProcThreadAttributeList(sInfoEX.lpAttributeList, 1, 0, &sizeT);
UpdateProcThreadAttribute(sInfoEX.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expHandle, sizeof(HANDLE), NULL, NULL);
sInfoEX.StartupInfo.cb = sizeof(STARTUPINFOEXA);
CreateProcessA(NULL, cmd.GetBuffer(), NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, reinterpret_cast<LPSTARTUPINFOA>(&sInfoEX), &pInfo);
return 0;
}
All Together:
int main() {
HANDLE hStdout_Rd = NULL;
HANDLE hStdout_Wr = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CString cmd;
STARTUPINFOEXA sInfoEX;
PROCESS_INFORMATION pInfo;
ZeroMemory(&pInfo, sizeof(PROCESS_INFORMATION));
SIZE_T sizeT;
HANDLE expHandle = OpenProcess(PROCESS_ALL_ACCESS, false, getParentProcessID());
ZeroMemory(&sInfoEX, sizeof(STARTUPINFOEXA));
sInfoEX.StartupInfo = sizeof(STARTUPINFO);
sInfoEX.StartupInfo = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
sInfoEX.StartupInfo = hStdout_Wr;
sInfoEX.StartupInfo = hStdout_Wr;
sInfoEX.StartupInfo = SW_HIDE;
InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
sInfoEX.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT);
InitializeProcThreadAttributeList(sInfoEX.lpAttributeList, 1, 0, &sizeT);
UpdateProcThreadAttribute(sInfoEX.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expHandle, sizeof(HANDLE), NULL, NULL);
sInfoEX.StartupInfo.cb = sizeof(STARTUPINFOEXA);
if (CreateProcessA(NULL, cmd.GetBuffer(), NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, reinterpret_cast<LPSTARTUPINFOA>(&sInfoEX), &pInfo))
{
//Read pipe contents
}
return 0;
}
Is there anything I'm missing?

Related

Why does the manifest file have no effect

I tried to open photoshop.exe using C++, but photoshop.exe.manifest did not take effect. If you manually double-click to open photoshop.exe file that shows normal working.
The registry has set and reboot system:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\SideBySide]
"PreferExternalManifest"=dword:00000001
Maybe it's the path?
TCHAR szCommandLineName[200]= _T("Photoshop.exe");
TCHAR szCommandLinePath[200] = _T("F:\\Program Files\\Adobe Photoshop 2020\\");
TCHAR szCommandLine[200] = _T("F:\\Program Files\\Adobe Photoshop 2020\\Photoshop.exe");
TCHAR buf[1000];
GetCurrentDirectory(1000, buf);
TRACE(_T("Current Directory:%s\n"), buf);
SetCurrentDirectory(szCommandLinePath);
//::WinExec("F:\\Program Files\\Adobe Photoshop 2020\\Photoshop.exe", SW_SHOW);
//ShellExecuteW(NULL, _T("open"), _T("photoshop.exe.bat"), NULL, szCommandLinePath, SW_SHOWNORMAL);
//return;
// system("photoshop.exe");
// return;
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESHOWWINDOW;
//si.wShowWindow = SW_HIDE;
si.wShowWindow = TRUE;
BOOL bRet = ::CreateProcess(
szCommandLine,
NULL,
NULL,
NULL,
FALSE, //bInheritHandles
NULL, //dwCreationFlags
NULL, //lpEnvironment
NULL,//lpCurrentDirectory
&si,
&pi);
int nError = GetLastError();
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
return ;

How to execute commands in the same process

I made my own function to execute a command and save the result. The problem is the command is executed once and I can't execute several commands with the same "context". For example. If I execute cd .., the next command my cd .. will be ignored. This is my code :
std::string executeCommand(const std::string& cmd)
{
system((cmd + " > temp.txt").c_str());
std::ifstream ifs("temp.txt");
std::string ret{ std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>() };
ifs.close(); // must close the inout stream so the file can be cleaned up
return ret;
}
Can I have a sample with this feature ?
Do you use Windows system?
In Windows, you can interact with cmd through a Pipe.
like this:
#define BUFFER_SIZE 131072 //128*1024‬
void work() {
HANDLE hinRead, hinWrite, houtRead, houtWrite;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = true;
if (!CreatePipe(&hinRead, &hinWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
if (!CreatePipe(&houtRead, &houtWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
STARTUPINFOA si;
ZeroMemory(&si, sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = hinRead;//redirect Input
si.hStdOutput = si.hStdError = houtWrite;//redirect Output
PROCESS_INFORMATION ProcessInformation;
char cmdLine[256] = { 0 };
GetSystemDirectoryA(cmdLine, sizeof(cmdLine));
strcat(cmdLine, ("\\cmd.exe"));
if (CreateProcessA(cmdLine, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &ProcessInformation) == 0) {
MessageBoxA(NULL, "Error CreateProcessA", "Error", MB_ICONERROR);
}
char* pbuff = new char[BUFFER_SIZE];
DWORD dw;
Sleep(100);//wait cmd start
ReadFile(houtRead, pbuff, BUFFER_SIZE-1, &dw, NULL);
pbuff[dw] = 0;
printf(pbuff);//version info
while (1) {
char* p = pbuff;
while ((*p = getchar()) != '\n') {
++p;
}
WriteFile(hinWrite, pbuff, p - pbuff + 1, &dw, 0);
while (1) {
ReadFile(houtRead, pbuff, BUFFER_SIZE - 1, &dw, NULL);
pbuff[dw] = 0;
printf("%s", pbuff);
if (pbuff[dw - 1] == '>')break;
}
}
}
Pipe can read/write like a file.
In other systems, there is also something like pipe,but I'm not familiar with them.
edit: another more clear example
int main() {
HANDLE hinRead, hinWrite, houtRead, houtWrite;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = true;
if (!CreatePipe(&hinRead, &hinWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
if (!CreatePipe(&houtRead, &houtWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
STARTUPINFOA si;
ZeroMemory(&si, sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;//no window
si.hStdInput = hinRead;//redirect Input
si.hStdOutput = si.hStdError = houtWrite;//redirect Output
PROCESS_INFORMATION pi;
char cmdLine[256] = { 0 };
GetSystemDirectoryA(cmdLine, sizeof(cmdLine));
strcat(cmdLine, ("\\cmd.exe"));
if (CreateProcessA(cmdLine, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == 0) {
MessageBoxA(NULL, "Error CreateProcessA", "Error", MB_ICONERROR);
}
Sleep(100);//wait cmd start
char* pbuff = new char[1024 * 128];
DWORD dw;
ReadFile(houtRead, pbuff, 1024 * 128, &dw, NULL), pbuff[dw] = 0, printf("%s", pbuff);//cmd version info
WriteFile(hinWrite, "cd ..\n", 6, &dw, NULL);//send command to cmd.exe
Sleep(100);//wait command execute and cmd outputs
ReadFile(houtRead, pbuff, 1024 * 128, &dw, NULL), pbuff[dw] = 0, printf("%s", pbuff);
WriteFile(hinWrite, "dir\n", 4, &dw, NULL);//another command
Sleep(100);
ReadFile(houtRead, pbuff, 1024 * 128, &dw, NULL), pbuff[dw] = 0, printf("%s", pbuff);
delete[] pbuff;
TerminateProcess(pi.hProcess, 0);//Terminate cmd.exe
return 0;
}

How to execute commands from an attached console

I'm coding a WinAPI GUI program that needs calling ftp and possibly other console programs while getting their console output to act accordingly ie. waiting for ftp to complete execution before reading all its output wouldn't do.
My current approach is calling CreateProcess() to create a cmd.exe process potentially hiding the ugly console window, AttachConsole() to make it my own, GetStdHandle() to get input and output handles, SetConsoleCursorPosition() to the end of the console buffer, and WriteConsole() with commands such as ftp\n or dir\n. Yet this commands are written but not executed. However, I can manually use the same console ( using CreateProcess() with CREATE_NEW_CONSOLE flag ) to type ftp press enter and get it executed.
Previous approaches involved:
Calling ftp directly with CreateProcess() and redirected inputs/outputs.
Couldn't get ftp output until the CreateProcess() process had already ended.
Using system().
Was advised against its usage before getting any output.
My current stripped down code:
// Next two structures might be a bit misleading, they were used for the 1. previous
// approach
PROCESS_INFORMATION piProcInfo;
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION));
STARTUPINFO siStartInfo;
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES security;
security.nLength = sizeof(SECURITY_ATTRIBUTES);
security.lpSecurityDescriptor = NULL;
security.bInheritHandle = FALSE;
CreateProcess( NULL, "cmd", &security, &security, FALSE, NORMAL_PRIORITY_CLASS |
CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &piProcInfo);
uint32_t pidConsole = piProcInfo.dwProcessId;
while ( ! AttachConsole(pidConsole) ){};
HANDLE myConsoleIn, myConsoleOut;
myConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
myConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
Sleep(100);
CONSOLE_SCREEN_BUFFER_INFO myConsoleCursorInformation = {};
GetConsoleScreenBufferInfo(myConsoleOut,&myConsoleCursorInformation);
SetConsoleCursorPosition(myConsoleOut,myConsoleCursorInformation.dwSize);
CHAR myConsoleBuffer[200]="dir\n";
DWORD myConsoleProcessed;
WriteConsole( myConsoleOut, myConsoleBuffer, 4, &myConsoleProcessed, NULL);
How can I get a command written in the console to execute? Is there an alternative to my attempt of ending commands with a trailing \n ie. using WriteConsole() with a dir\n or ftp\n argument.
I thought about sending a keypress to the process in question after typing the desired command. Yet the created console needs not only to manually press the enter key but also having dir, ftp or whatever command to be manually typed.
Please feel free to point out any missing information !
How can I get a command written in the console to execute? Is there an
alternative to my attempt of ending commands with a trailing \n ie.
using WriteConsole() with a dir\n or ftp\n argument.
Try the following code to see if it works:
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
const wchar_t *cmdPath = L"C:\\Windows\\System32\\cmd.exe";
wchar_t *cmdArgs = (wchar_t *)L"C:\\Windows\\System32\\cmd.exe /k dir";
BOOL result = CreateProcess(cmdPath, cmdArgs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
DWORD errCode = GetLastError();
if (!result)
{
std::cout << "Create Process failed: " << GetLastError() << std::endl;
}
/K Run Command and then return to the CMD prompt.
This is useful for testing, to examine variables
Use /C if you want "Run Command and then terminate".
Update: Complete code for communicating with a child process(cmd.exe) using pipes.
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
#define BUFSIZE 1024
void ErrorExit(LPCTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
void ReadFromPipe(void)
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
DWORD bytesAvail = 0;
if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &bytesAvail, NULL)) {
std::cout << "Failed to call PeekNamedPipe" << std::endl;
}
if (bytesAvail) {
DWORD n;
BOOL success = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &n, NULL);
if (!success || n == 0) {
}
bSuccess = WriteFile(hParentStdOut, chBuf,n, &dwWritten, NULL);
}
else
{
break;
}
}
}
void WriteToPipe(void)
{
DWORD dwWritten;
BOOL bSuccess = FALSE;
CHAR buf[] = "dir\n";
bSuccess = WriteFile(g_hChildStd_IN_Wr, buf, sizeof(buf)-1, &dwWritten, NULL);
}
int main()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
ErrorExit(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdin SetHandleInformation"));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(STARTUPINFO);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
TCHAR cmdPath[] = TEXT("C:\\Windows\\System32\\cmd.exe");
BOOL result = CreateProcess(cmdPath, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
DWORD errCode = GetLastError();
if (!result)
{
std::cout << "Create Process failed: " << GetLastError() << std::endl;
}
for (;;)
{
ReadFromPipe();
WriteToPipe();
}
}

C++ CreateProcess 'telnet' is not recognized

When I pass the ipconfig command to the process, it stores the correct results in files.
char cmd[] = "C:\\windows\\system32\\cmd.exe /c ipconfig";
SaveResult("ipconfig1.txt", NULL, cmd);
char appName[] = "C:\\windows\\system32\\cmd.exe";
char cmd2[] = "/c ipconfig";
SaveResult("ipconfig2.txt", appName, cmd2);
But when i pass wuauclt or telnet
char cmd1[] = "C:\\windows\\system32\\cmd.exe /c telnet";
SaveResult("telnet1.txt", NULL, cmd1);
char appName3[] = "C:\\windows\\system32\\cmd.exe";
char cmd3[] = "/c telnet";
SaveResult("telnet2.txt", appName3, cmd3);
char cmd4[] = "C:\\windows\\system32\\cmd.exe /c wuauclt";
SaveResult("wuauclt1.txt", NULL, cmd4);
char appName5[] = "C:\\windows\\system32\\cmd.exe";
char cmd5[] = "/c wuauclt";
SaveResult("wuauclt2.txt", appName5, cmd5);
I get
'wuauclt' is not recognized as an internal or external command,
operable program or batch file.
'telnet' is not recognized as an internal or external command,
operable program or batch file.
How to fix this problem and why it happens? Do it possible to launch through cmd.exe telnet or wuauclt?
Also on this PC wuauclt and telnet in common console opened from start working like expected.
#include "stdafx.h"
#include "windows.h"
wchar_t *convertCharArrayToLPCWSTR(const char* charArray)
{
wchar_t* wString = new wchar_t[4096];
MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096);
return wString;
}
void SaveResult(const char *fileName, const char *appName, const char *commandLine)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE h = CreateFile(convertCharArrayToLPCWSTR(fileName),
FILE_APPEND_DATA,
FILE_SHARE_WRITE | FILE_SHARE_READ,
&sa,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL ret = FALSE;
DWORD flags = CREATE_NO_WINDOW;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = NULL;
si.hStdError = h;
si.hStdOutput = h;
ret = CreateProcess(appName==NULL ? NULL : convertCharArrayToLPCWSTR(appName), commandLine == NULL ? NULL : convertCharArrayToLPCWSTR(commandLine), NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
if (ret)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(h);
}
}
int main()
{
char cmd[] = "C:\\windows\\system32\\cmd.exe /c ipconfig";
SaveResult("ipconfig1.txt", NULL, cmd);
char appName[] = "C:\\windows\\system32\\cmd.exe";
char cmd2[] = "/c ipconfig";
SaveResult("ipconfig2.txt", appName, cmd2);
char cmd1[] = "C:\\windows\\system32\\cmd.exe /c telnet";
SaveResult("telnet1.txt", NULL, cmd1);
char appName3[] = "C:\\windows\\system32\\cmd.exe";
char cmd3[] = "/c telnet";
SaveResult("telnet2.txt", appName3, cmd3);
char cmd4[] = "C:\\windows\\system32\\cmd.exe /c wuauclt";
SaveResult("wuauclt1.txt", NULL, cmd4);
char appName5[] = "C:\\windows\\system32\\cmd.exe";
char cmd5[] = "/c wuauclt";
SaveResult("wuauclt2.txt", appName5, cmd5);
return -1;
}
If you type in ipconfig in console window, the process will show IP information and exit.
On the other hand, if you type in telnet in console window, the process will show a prompt and waits for a response. The process does not finished automatically.
When you run this command with CreateProcess, CreateProcess will return immediately, but the process is not finished. Then you try to close the file handle which is still being used by telnet.
You can use WaitForSingleObject to wait until the process is complete. In the case of telnet the process doesn't complete. The example below demonstrates this problem.
For CreateProcess, supply the whole command line as the second parameter. Make sure the character buffer is writable, and freed at the end.
Side note, it is recommended to use wide character string for a Unicode program. It's fine to promote ANSI to UTF16, but not much is gained in this case. You can also use CreateProcessA along with STARTUPINFOA si = { sizeof(si) }; which accept ANSI character.
void SaveResult(const wchar_t *fileName, const wchar_t *commandLine)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE h = CreateFile(fileName, FILE_WRITE_DATA, FILE_SHARE_WRITE | FILE_SHARE_READ,
&sa, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(h == INVALID_HANDLE_VALUE)
return;
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { sizeof(si) };
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = NULL;
si.hStdError = h;
si.hStdOutput = h;
wchar_t *writable_cmdline = _wcsdup(commandLine);
BOOL success = CreateProcess(NULL, writable_cmdline,
NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
bool finished = false;
//wait for 1 second
for(int i = 0; i < 10; i++)
{
if(WaitForSingleObject(pi.hProcess, 100) <= 0)
{
finished = true;
break;
}
}
if(success)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
CloseHandle(h);
free(writable_cmdline);
if(!finished)
printf("Process didn't finish\n");
}
int main()
{
SaveResult(L"telnet.txt", L"C:\\windows\\system32\\cmd.exe /c telnet");
SaveResult(L"ipconfig.txt", L"C:\\windows\\system32\\cmd.exe /c ipconfig");
return 0;
}

Understanding a self-deleting program in C++

There is a self deleting program
#include <windows.h>
#include <stdio.h>
void main(int argc, char* argv[])
{
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
if (argc == 1)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
CopyFile(argv[0], "1.exe", FALSE);
MoveFile(argv[0], "2.exe");
CreateFile("1.exe", 0, FILE_SHARE_READ, &sa,
OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
CreateProcess(NULL, "1.exe x", NULL, NULL,
TRUE, 0, NULL, NULL, &si, &pi);
}
else if (argc == 2)
{
while(!DeleteFile("2.exe"));
CreateProcess(NULL, "net", NULL, NULL, TRUE,
DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi);
}
}
If I remove this :CreateProcess(NULL, "net", NULL, NULL, TRUE, DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi);
it can't work.
Could anyone explain to me how it works?
Here's an explanation (as I understand things)
void main(int argc, char* argv[])
{
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
if (argc == 1)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
// Make a copy of ourselves which we'll use to delete the version we were run from
CopyFile(argv[0], "1.exe", FALSE);
// Rename the running copy of ourself to another name
MoveFile(argv[0], "2.exe");
// Make sure we delete the copy of ourselves that's going to delete us when we die
CreateFile("1.exe", 0, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
// Invoke the process that will delete us
// allowing it to inherit the handle we just created above.
CreateProcess(NULL, "1.exe x", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
}
else if (argc == 2)
{
// Wait for the original program to die (deleting us and closing a handle), then delete it
while(!DeleteFile("2.exe"));
// Launch a child process which will inherit our file handles
// -- This keeps the file handle with FILE_FLAG_DELETE_ON_CLOSE (which we inherited) alive beyond our lifetime
// this allowing us to be deleted after we've died and our own handle is closed.
CreateProcess(NULL, "notepad", NULL, NULL, TRUE, DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi);
}
}