180 lines
5.5 KiB
C++
180 lines
5.5 KiB
C++
|
#define NOTRACE 1
|
||
|
|
||
|
#include "windows.h"
|
||
|
#include "util.h"
|
||
|
#include "faultrep.h"
|
||
|
#include "pchrexec.h"
|
||
|
#include "stdio.h"
|
||
|
#include "stdlib.h"
|
||
|
|
||
|
// ***************************************************************************
|
||
|
LPWSTR MarshallString(LPCWSTR wszSrc, PBYTE pBase, ULONG cbMaxBuf,
|
||
|
PBYTE *ppToWrite, DWORD *pcbWritten)
|
||
|
{
|
||
|
DWORD cb;
|
||
|
PBYTE pwszNormalized;
|
||
|
|
||
|
cb = (wcslen(wszSrc) + 1) * sizeof(WCHAR);
|
||
|
|
||
|
if ((*pcbWritten + cb) > cbMaxBuf)
|
||
|
return NULL;
|
||
|
|
||
|
RtlMoveMemory(*ppToWrite, wszSrc, cb);
|
||
|
|
||
|
// the normalized ptr is the current count
|
||
|
pwszNormalized = (PBYTE)(*ppToWrite - pBase);
|
||
|
|
||
|
// cb is always a mutliple of sizeof(WHCAR) so the pointer addition below
|
||
|
// always produces a result that is 2byte aligned (assuming the input was
|
||
|
// 2byte aligned of course)
|
||
|
*ppToWrite += cb;
|
||
|
*pcbWritten += cb;
|
||
|
|
||
|
return (LPWSTR)pwszNormalized;
|
||
|
}
|
||
|
|
||
|
// **************************************************************************
|
||
|
EFaultRepRetVal PrepareUserManifest(LPWSTR wszExe, DWORD dwSession,
|
||
|
DWORD dwProc, DWORD dwThread)
|
||
|
{
|
||
|
SPCHExecServDWRequest *pesdwreq = NULL;
|
||
|
SPCHExecServDWReply *pesrep = NULL;
|
||
|
EFaultRepRetVal frrvRet = frrvErrNoDW;
|
||
|
HRESULT hr = NOERROR;
|
||
|
DWORD cbReq, cbRead;
|
||
|
WCHAR wszName[MAX_PATH];
|
||
|
BYTE Buf[HANGREP_EXECSVC_BUF_SIZE], *pBuf;
|
||
|
BYTE BufRep[HANGREP_EXECSVC_BUF_SIZE];
|
||
|
|
||
|
VALIDATEPARM(hr, (wszExe == NULL));
|
||
|
if (FAILED(hr))
|
||
|
goto done;
|
||
|
|
||
|
ZeroMemory(&Buf, sizeof(Buf));
|
||
|
pesdwreq = (SPCHExecServDWRequest *)Buf;
|
||
|
cbReq = ((sizeof(SPCHExecServDWRequest) * sizeof(WCHAR)) + sizeof(WCHAR) - 1) / sizeof(WCHAR);
|
||
|
pBuf = Buf + cbReq;
|
||
|
|
||
|
pesdwreq->cbESR = sizeof(SPCHExecServDWRequest);
|
||
|
pesdwreq->pidReqProcess = dwProc;
|
||
|
pesdwreq->thidFault = dwThread;
|
||
|
pesdwreq->ulSessionId = dwSession;
|
||
|
pesdwreq->pvFaultAddr = (UINT64)UnhandledExceptionFilter;
|
||
|
#ifdef _WIN64
|
||
|
pesdwreq->fIs64bit = TRUE;
|
||
|
#else
|
||
|
pesdwreq->fIs64bit = FALSE;
|
||
|
#endif
|
||
|
|
||
|
// marshal in the strings
|
||
|
pesdwreq->wszExe = (UINT64)MarshallString(wszExe, Buf, sizeof(Buf), &pBuf,
|
||
|
&cbReq);
|
||
|
if (pesdwreq->wszExe == 0)
|
||
|
goto done;
|
||
|
|
||
|
pesdwreq->cbTotal = cbReq;
|
||
|
|
||
|
// check and see if the system is shutting down. If so, CreateProcess is
|
||
|
// gonna pop up some annoying UI that we can't get rid of, so we don't
|
||
|
// want to call it if we know it's gonna happen.
|
||
|
if (GetSystemMetrics(SM_SHUTTINGDOWN))
|
||
|
goto done;
|
||
|
|
||
|
// Send the buffer out to the server- wait at most 2m for this to
|
||
|
// succeed. If it times out, bail.
|
||
|
wcscpy(wszName, HANGREP_EXECSVC_DWPIPE);
|
||
|
TESTBOOL(hr, CallNamedPipeW(wszName, Buf, cbReq, &BufRep, sizeof(BufRep),
|
||
|
&cbRead, 120000));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
// determine the error code that indicates whether we've timed out so
|
||
|
// we can set the return code appropriately.
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
pesrep = (SPCHExecServDWReply *)BufRep;
|
||
|
|
||
|
// did the call succeed?
|
||
|
VALIDATEEXPR(hr, (pesrep->fRet == FALSE), Err2HR(pesrep->dwErr));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
fprintf(stdout, "Named pipe call failed: 0x%08x\n", pesrep->dwErr);
|
||
|
SetLastError(pesrep->dwErr);
|
||
|
goto done;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fprintf(stdout, "Named pipe call success\n");
|
||
|
}
|
||
|
|
||
|
// gotta wait for DW to be done before we nuke the manifest file, but if
|
||
|
// it hasn't parsed it in 5 minutes, something's wrong with it.
|
||
|
if (WaitForSingleObject(pesrep->pi.hProcess, 300000) == WAIT_TIMEOUT)
|
||
|
{
|
||
|
frrvRet = frrvErrTimeout;
|
||
|
}
|
||
|
|
||
|
// we're only going to delete the files if DW has finished with them. Yes
|
||
|
// this means we can leave stray files in the temp dir, but this is better
|
||
|
// than having DW randomly fail while sending...
|
||
|
else
|
||
|
{
|
||
|
if (pesrep->wszDump != 0)
|
||
|
{
|
||
|
if (pesrep->wszDump < cbRead &&
|
||
|
pesrep->wszDump >= sizeof(SPCHExecServDWReply))
|
||
|
pesrep->wszDump += (UINT64)pesrep;
|
||
|
else
|
||
|
pesrep->wszDump = 0;
|
||
|
}
|
||
|
|
||
|
if (pesrep->wszDump != 0)
|
||
|
fprintf(stdout, "Dump file: %ls\n", pesrep->wszDump);
|
||
|
|
||
|
if (pesrep->wszManifest != 0)
|
||
|
{
|
||
|
if (pesrep->wszManifest < cbRead &&
|
||
|
pesrep->wszManifest >= sizeof(SPCHExecServDWReply))
|
||
|
pesrep->wszManifest += (UINT64)pesrep;
|
||
|
else
|
||
|
pesrep->wszManifest = 0;
|
||
|
}
|
||
|
|
||
|
if (pesrep->wszManifest != 0)
|
||
|
fprintf(stdout, "Manifest file: %ls\n", pesrep->wszDump);
|
||
|
}
|
||
|
|
||
|
CloseHandle(pesrep->pi.hProcess);
|
||
|
CloseHandle(pesrep->pi.hThread);
|
||
|
|
||
|
frrvRet = frrvOkManifest;
|
||
|
SetLastError(0);
|
||
|
|
||
|
done:
|
||
|
return frrvRet;
|
||
|
}
|
||
|
|
||
|
// **************************************************************************
|
||
|
void __cdecl wmain(int argc, WCHAR **argv)
|
||
|
{
|
||
|
DWORD dwPID;
|
||
|
DWORD dwThID;
|
||
|
DWORD dwSession;
|
||
|
WCHAR *wszExe;
|
||
|
|
||
|
if (argc < 4 || argc > 5)
|
||
|
{
|
||
|
fprintf(stdout, "Usage:\nmdpipe <exe name> <PID> <Thread ID> [<Session ID>]\n");
|
||
|
return;
|
||
|
}
|
||
|
wszExe = argv[1];
|
||
|
dwPID = _wtol(argv[2]);
|
||
|
dwThID = _wtol(argv[3]);
|
||
|
if (argc == 5)
|
||
|
dwSession = _wtol(argv[4]);
|
||
|
else
|
||
|
ProcessIdToSessionId(GetCurrentProcessId(), &dwSession);
|
||
|
|
||
|
PrepareUserManifest(wszExe, dwSession, dwPID, dwThID);
|
||
|
}
|