Thread List
The CRYPT32 thread is only present during initial loading. All others appear to be constant.Is there a way to grab StdOut from a specific thread? Or could it be a completely different outstream type?
Code: Select all
#NoEnv
#Persistent
if (!A_IsUnicode)
MsgBox The KF server sends strings in UTF-16. Continuing with ANSI AHK anyway (which may or may not work)...
; Start the server:
_PROCESS_INFORMATION(pi)
VarSetCapacity(si, (siCb := A_PtrSize == 8 ? 104 : 68), 0), NumPut(siCb, si,, "UInt")
if (!DllCall("CreateProcess", "Ptr", 0
,"Str", A_Desktop . "\KF2-Server-Installer-Manager\Installer\steamcmd\kf2server\Binaries\Win64\KFServer.exe" ; command line
,"Ptr", 0
,"Ptr", 0
,"Int", False
,"UInt", CREATE_SUSPENDED := 0x00000004 ; needs to be created suspended so that we can get KFServer's PID and to make sure the named pipe is created before KFServer tries looking for it and fails in doing so
,"Ptr", 0
,"Str", A_Desktop . "\KF2-Server-Installer-Manager\Installer\steamcmd\kf2server\Binaries\Win64" ; starting/working directory (,"Ptr", 0 instead to inherit this process's)
,"Ptr", &si
,"Ptr", &pi))
{
DieWithLastError("CreateProcess")
}
hProcess := NumGet(pi,, "Ptr")
hThread := NumGet(pi, A_PtrSize, "Ptr")
dwProcessId := NumGet(pi, A_PtrSize * 2, "UInt")
OnExit("AtExit")
hPipe := DllCall("CreateNamedPipe"
,"Str", Format("\\.\pipe\{:u}cout", dwProcessId)
,"UInt", PIPE_ACCESS_INBOUND := 0x00000001 | FILE_FLAG_OVERLAPPED := 0x40000000
,"UInt", PIPE_TYPE_BYTE := 0x00000000
,"UInt", 1
,"UInt", 0
,"UInt", 0
,"UInt", 0
,"Ptr", 0, "Ptr")
if (hPipe == -1) {
DieWithLastError("CreateNamedPipe")
}
; resume the process
DllCall("ResumeThread", "Ptr", hThread)
,WaitOnEvents()
WaitOnEvents() {
global hPipe, hProcess
static GetOverlappedResult := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandle", "Str", "kernel32.dll", "Ptr"), "AStr", "GetOverlappedResult", "Ptr")
,MsgWaitForMultipleObjectsEx := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandle", "Str", "user32.dll", "Ptr"), "AStr", "MsgWaitForMultipleObjectsEx", "Ptr")
,hEvent := DllCall("CreateEvent", "Ptr", 0, "Int", False, "Int", False, "Ptr", 0, "Ptr"), overlapped, handles, buffer
,ConnectNamedPipe := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandleW", "WStr", "kernel32.dll", "Ptr"), "AStr", "ConnectNamedPipe", "Ptr")
if (!VarSetCapacity(overlapped)) {
VarSetCapacity(overlapped, 32, 0)
,NumPut(hEvent, overlapped, 2*A_PtrSize+8, "Ptr")
VarSetCapacity(handles, A_PtrSize * 2) ; handles to wait on, specified in a C array for MsgWaitForMultipleObjectsEx
,NumPut(hProcess, handles,, "Ptr")
,NumPut(hEvent, handles, A_PtrSize, "Ptr")
VarSetCapacity(buffer, 4096*2)
}
FileDelete, %A_Desktop%\dsffds.txt
notConnected := !DllCall(ConnectNamedPipe, "Ptr", hPipe, "Ptr", &overlapped) && A_LastError == 997
if (!notConnected)
DllCall("ReadFile", "Ptr", hPipe, "Ptr", &buffer, "UInt", 4096*2, "Ptr", 0, "Ptr", &overlapped) ; if already connected, try and initially get some output
Loop { ; stolen from Lexikos: wait on the process to terminate, while allowing messages to be pumped etc.
r := DllCall(MsgWaitForMultipleObjectsEx, "UInt", 2, "Ptr", &handles, "UInt", 0xFFFFFFFF, "UInt", 0x4FF, "UInt", 0x6, "UInt")
if (r == 0 || r == 0xFFFFFFFF) ; first object (process) signalled (terminated) / failure
ExitApp
else if (r == 1) { ; the same event is used for two purposes: the first being it's signalled when KFServer connects to it and the other is when ReadFile has something from the pipe that we should read
if (!notConnected) {
if (DllCall(GetOverlappedResult, "Ptr", hPipe, "Ptr", &overlapped, "UInt*", len, "Int", True)) {
NumPut(0, buffer, len, "UShort")
FileAppend, % StrGet(&buffer,, "UTF-16"), %A_Desktop%\dsffds.txt, UTF-16
}
DllCall("ReadFile", "Ptr", hPipe, "Ptr", &buffer, "UInt", 4096*2, "Ptr", 0, "Ptr", &overlapped) ; another ReadFile call is needed to get more output as it occurs
} else {
notConnected := False
;DllCall("ResetEvent", "Ptr", hEvent)
DllCall("ReadFile", "Ptr", hPipe, "Ptr", &buffer, "UInt", 4096*2, "Ptr", 0, "Ptr", &overlapped) ; start the initial reading when available
}
}
Sleep -1
}
}
AtExit()
{
global hProcess, hThread
OnExit(A_ThisFunc, 0)
if (DllCall("WaitForSingleObject", "Ptr", hProcess, "UInt", 0) == 258) ; process still running
DllCall("TerminateProcess", "Ptr", hProcess, "UInt", 1)
DllCall("CloseHandle", "Ptr", hThread)
DllCall("CloseHandle", "Ptr", hProcess)
return 0
}
DieWithLastError(failedFuncName)
{
dw := A_LastError
ccherrFmt := DllCall("FormatMessage", "UInt", 0x00000100 | 0x00001000 | 0x00000200, "Ptr", 0, "UInt", dw, "UInt", 1024, "Ptr*", errFmt, "UInt", 0, "Ptr", 0, "UInt")
MsgBox % Format("{:s} failed ({:u}){:s}", failedFuncName, dw, ccherrFmt ? ": " . StrGet(errFmt, ccherrFmt) : "")
if (ccherrFmt)
DllCall("LocalFree", "Ptr", errFmt, "Ptr")
ExitApp 1
}
_PROCESS_INFORMATION(ByRef pi) {
static piCb := A_PtrSize == 8 ? 24 : 16
if (IsByRef(pi))
VarSetCapacity(pi, piCb, 0)
}
That's the one. The CodeProject page by the author describes it well. A pure AHK hooking library can be found here, but I've never had the pleasure of using it as MinHook has always worked well for me, even before I understood the basics of dllcalling in AutoHotkey.Masonjar13 wrote:Do you mean this MinHook? I've never heard of it before, but that's absolutely worth my time to look into. Is there an AHK library available? I didn't see any come up with my search.
No problem. API Monitor showed SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE) being called by the server which seemed odd to me. I looked at the server in hex rays and saw something along the lines ofThis is a huge help, thank you! And, might I ask how you figured this out, since it was (afayk) undocumented?
Code: Select all
WCHAR buf[1024]; wsprintf(buf, L"\\\\.\\pipe\\%dcout", GetCurrentProcessId()); HANDLE hPipe = CreateFileW(buf,...); SetStdHandle(STD_OUTPUT_HANDLE, hPipe);
Users browsing this forum: Bing [Bot], bobstoner289, Ralf_Reddings200244 and 283 guests