4837 lines
146 KiB
C
4837 lines
146 KiB
C
/*
|
|
*
|
|
* dmserv.c
|
|
*
|
|
* The debug monitor server
|
|
*
|
|
*/
|
|
|
|
#include "dmp.h"
|
|
#include "xboxp.h"
|
|
#include "xconfig.h"
|
|
#include "av.h"
|
|
#include "xlaunch.h"
|
|
#undef DeleteFile
|
|
#include "d3d8.h"
|
|
|
|
void ShowIPAddress(ULONG);
|
|
DWORD DedicatedServThread(LPVOID lpv);
|
|
|
|
RTL_CRITICAL_SECTION csEch;
|
|
RTL_CRITICAL_SECTION csAccept;
|
|
CST rgcst[MAX_CONNECTIONS];
|
|
const char rgchHex[] = "0123456789ABCDEF";
|
|
char rgchDbgName[256];
|
|
ULONG ulIpAddr;
|
|
BOOL g_fServShutdown;
|
|
BOOL g_fDebugging;
|
|
BOOL g_fLockLevel;
|
|
BOOL g_fDebugSecureMode;// = TRUE;
|
|
ULARGE_INTEGER g_luBoxId;
|
|
KEVENT kevtServ;
|
|
PETHREAD pthrServ;
|
|
extern CHH rgchh[];
|
|
extern int cchh;
|
|
|
|
INITIALIZED_OBJECT_STRING_RDATA(ostDash, "\\Device\\Harddisk0\\Partition1\\DASHBOARD.XBX");
|
|
CHAR XdkLauncherPathname[] = "\\Device\\Harddisk0\\Partition2\\xshell.xbe";
|
|
|
|
/* Extension stuff */
|
|
ECH *rgpech[MAX_ECH];
|
|
|
|
NTSTATUS
|
|
WINAPI
|
|
XWriteTitleInfoNoReboot(
|
|
PCOSTR pszLaunchPath,
|
|
PCOSTR pszDDrivePath,
|
|
DWORD dwLaunchDataType,
|
|
DWORD dwTitleId,
|
|
PLAUNCH_DATA pLaunchData
|
|
);
|
|
|
|
BOOL
|
|
WINAPI
|
|
XapiFormatFATVolume(
|
|
POBJECT_STRING VolumePath
|
|
);
|
|
|
|
void InitServ(void)
|
|
{
|
|
InitializeCriticalSection(&csEch);
|
|
InitializeCriticalSection(&csAccept);
|
|
KeInitializeEvent(&kevtServ, NotificationEvent, FALSE);
|
|
}
|
|
|
|
void StopServ(void)
|
|
{
|
|
int icst;
|
|
PETHREAD pthr;
|
|
|
|
/* Loop through the socket list and close each one down */
|
|
EnterCriticalSection(&csAccept);
|
|
g_fServShutdown = TRUE;
|
|
for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
|
|
if(rgcst[icst].s != INVALID_SOCKET) {
|
|
/* If this socket isn't running on the current thread, it may be
|
|
* inside of a select. In that case, we take it off the thread,
|
|
* stop the select, and give the thread a little bit of time to
|
|
* exit the select */
|
|
pthr = rgcst[icst].pthrDedicated;
|
|
rgcst[icst].pthrDedicated = NULL;
|
|
if(pthr != PsGetCurrentThread()) {
|
|
KeAlertThread(&pthr->Tcb, KernelMode);
|
|
Sleep(20);
|
|
}
|
|
shutdown(rgcst[icst].s, SD_SEND);
|
|
}
|
|
}
|
|
LeaveCriticalSection(&csAccept);
|
|
|
|
/* Wait for the sockets to close */
|
|
Sleep(200);
|
|
}
|
|
|
|
int SgnCompareRgch(const char *sz1, const char *sz2, int cch)
|
|
{
|
|
while(cch-- && *sz1) {
|
|
char ch1 = *sz1++;
|
|
char ch2 = *sz2++;
|
|
if(ch1 >= 'a' && ch2 <= 'z')
|
|
ch1 -= 32;
|
|
if(ch2 >= 'a' && ch2 <= 'z')
|
|
ch2 -= 32;
|
|
if(ch1 != ch2)
|
|
return ch1 - ch2;
|
|
}
|
|
if(*sz1)
|
|
return *sz1;
|
|
return cch < 0 ? 0 : -*sz2;
|
|
}
|
|
|
|
BOOL FEqualRgch(const char *sz1, const char *sz2, int cch)
|
|
{
|
|
return SgnCompareRgch(sz1, sz2, cch) == 0;
|
|
}
|
|
|
|
DWORD DwHexFromSz(LPCSTR sz, LPCSTR *szOut)
|
|
{
|
|
DWORD dw = 0;
|
|
|
|
for(;;) {
|
|
if(*sz >= '0' && *sz <= '9')
|
|
dw = dw * 16 + (*sz - '0');
|
|
else if(*sz >= 'A' && *sz <= 'F')
|
|
dw = dw * 16 + (*sz - 'A' + 10);
|
|
else if(*sz >= 'a' && *sz <= 'f')
|
|
dw = dw * 16 + (*sz - 'a' + 10);
|
|
else
|
|
break;
|
|
++sz;
|
|
}
|
|
if(szOut)
|
|
*szOut = sz;
|
|
return dw;
|
|
}
|
|
|
|
DWORD DwFromSz(LPCSTR sz, int *pcchUsed)
|
|
{
|
|
DWORD dw = 0;
|
|
LPCSTR szStart = sz;
|
|
|
|
/* Process decimal, octal, or hex */
|
|
if(*sz == '0') {
|
|
++sz;
|
|
if(*sz == 'x')
|
|
dw = DwHexFromSz(++sz, &sz);
|
|
else
|
|
while(*sz >= '0' && *sz <= '7')
|
|
dw = dw * 8 + (*sz++ - '0');
|
|
} else
|
|
while(*sz >= '0' && *sz <= '9')
|
|
dw = dw * 10 + (*sz++ - '0');
|
|
if(pcchUsed)
|
|
*pcchUsed = sz - szStart;
|
|
return dw;
|
|
}
|
|
|
|
void CloseConn(int icst)
|
|
{
|
|
KIRQL irql;
|
|
|
|
ASSERT(rgcst[icst].s != INVALID_SOCKET);
|
|
closesocket(rgcst[icst].s);
|
|
|
|
/* The server thread will attempt to read data from this socket once
|
|
* pechDedicated is cleared, and may attempt to reuse this
|
|
* connection state as soon as the socket ID is set to INVALID. We
|
|
* need to do all of this as one atomic operation */
|
|
irql = KeRaiseIrqlToDpcLevel();
|
|
rgcst[icst].dwStatus = 0;
|
|
rgcst[icst].pechDedicated = NULL;
|
|
rgcst[icst].pthrDedicated = NULL;
|
|
rgcst[icst].s = INVALID_SOCKET;
|
|
KeLowerIrql(irql);
|
|
}
|
|
|
|
void InitDmcc(int icst)
|
|
{
|
|
rgcst[icst].dmcc.BytesRemaining = -1;
|
|
rgcst[icst].dmcc.Buffer = NULL;
|
|
rgcst[icst].dmcc.CustomData = &rgcst[icst].ccs;
|
|
rgcst[icst].dmcc.HandlingFunction = NULL;
|
|
rgcst[icst].dwStatus &= ~(CONN_BINARY | CONN_BINSEND | CONN_MULTILINE);
|
|
}
|
|
|
|
BOOL PrintSockLine(SOCKET s, LPCSTR sz)
|
|
{
|
|
char szT[1024];
|
|
int ich;
|
|
|
|
for(ich = 0; sz[ich] && ich < sizeof szT - 2; ++ich)
|
|
szT[ich] = sz[ich];
|
|
|
|
if(sz[ich]) {
|
|
/* Didn't get the whole string, so we have to send two packets */
|
|
if(send(s, sz, strlen(sz), 0) <= 0)
|
|
return FALSE;
|
|
if(send(s, "\015\012", 2, 0) <= 0)
|
|
return FALSE;
|
|
} else {
|
|
szT[ich++] = '\015';
|
|
szT[ich++] = '\012';
|
|
if(send(s, szT, ich, 0) <= 0)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ReadSockLine(SOCKET s, char *sz, int cch)
|
|
{
|
|
int cb;
|
|
int ich = 0;
|
|
|
|
--cch; /* save room for \0 */
|
|
while(ich < cch) {
|
|
cb = recv(s, &sz[ich], 1, 0);
|
|
if(cb <= 0) {
|
|
if(ich == 0)
|
|
return FALSE;
|
|
sz[ich] = 0;
|
|
return TRUE;
|
|
}
|
|
if(sz[ich] == '\012') {
|
|
sz[ich] = 0;
|
|
return TRUE;
|
|
}
|
|
if(sz[ich] != '\015')
|
|
++ich;
|
|
}
|
|
sz[ich] = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
__inline BOOL FIsSpace(char ch)
|
|
{
|
|
return ch == ' ' || ch == '\015' || ch == 0;
|
|
}
|
|
|
|
const char *PchGetParam(LPCSTR szCmd, LPCSTR szKey, BOOL fNeedValue)
|
|
{
|
|
const char *pchTok;
|
|
int cchTok;
|
|
BOOL fQuote = FALSE;
|
|
|
|
/* Skip the command */
|
|
for(pchTok = szCmd; !FIsSpace(*pchTok); ++pchTok);
|
|
|
|
while(*pchTok) {
|
|
/* Skip leading spaces */
|
|
while(*pchTok && FIsSpace(*pchTok))
|
|
++pchTok;
|
|
if(!*pchTok)
|
|
return NULL;
|
|
for(cchTok = 0; !FIsSpace(pchTok[cchTok]); ++cchTok) {
|
|
if(pchTok[cchTok] == '=') {
|
|
if(FEqualRgch(szKey, pchTok, cchTok))
|
|
return pchTok + cchTok + 1; /* Skip the '=' */
|
|
break;
|
|
}
|
|
}
|
|
/* If we didn't see the '=' we need to check anyway */
|
|
if(!fNeedValue && pchTok[cchTok] != '=' && FEqualRgch(szKey, pchTok,
|
|
cchTok))
|
|
return pchTok + cchTok;
|
|
/* No match, so we need to skip past the value */
|
|
pchTok += cchTok;
|
|
while(*pchTok && (!FIsSpace(*pchTok) || fQuote))
|
|
if(*pchTok++ == '"')
|
|
fQuote = !fQuote;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int CchOfWord(LPCSTR sz)
|
|
{
|
|
int cch;
|
|
for(cch = 0; !FIsSpace(sz[cch]); ++cch);
|
|
return cch;
|
|
}
|
|
|
|
void GetParam(LPCSTR szLine, LPSTR szBuf, int cchBuf)
|
|
{
|
|
int cch = 0;
|
|
BOOL fQuote = FALSE;
|
|
|
|
while(cch < cchBuf-1 && *szLine && (!FIsSpace(*szLine) || fQuote)) {
|
|
if(*szLine == '"') {
|
|
if(fQuote && szLine[1] == '"') {
|
|
/* Double quote inside a string gets copied as a single
|
|
* quote */
|
|
szBuf[cch++] = '"';
|
|
szLine += 2;
|
|
} else {
|
|
fQuote = !fQuote;
|
|
++szLine;
|
|
}
|
|
} else
|
|
szBuf[cch++] = *szLine++;
|
|
}
|
|
szBuf[cch] = 0;
|
|
}
|
|
|
|
BOOL FGetSzParam(LPCSTR szLine, LPCSTR szKey, LPSTR szBuf, int cchBuf)
|
|
{
|
|
LPCSTR pch = PchGetParam(szLine, szKey, TRUE);
|
|
if(!pch)
|
|
return FALSE;
|
|
GetParam(pch, szBuf, cchBuf);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL FGetDwParam(LPCSTR szLine, LPCSTR szKey, DWORD *pdw)
|
|
{
|
|
int cch;
|
|
char sz[32];
|
|
LPCSTR pch = PchGetParam(szLine, szKey, TRUE);
|
|
if(!pch)
|
|
return FALSE;
|
|
GetParam(pch, sz, sizeof sz);
|
|
*pdw = DwFromSz(sz, &cch);
|
|
return FIsSpace(sz[cch]);
|
|
}
|
|
|
|
BOOL FGetQwordParam(LPCSTR szLine, LPCSTR szKey, ULARGE_INTEGER *plu)
|
|
{
|
|
int cch;
|
|
char sz[32];
|
|
LPCSTR pch;
|
|
|
|
pch = PchGetParam(szLine, szKey, TRUE);
|
|
if(!pch)
|
|
return FALSE;
|
|
GetParam(pch, sz, sizeof sz - 1);
|
|
sz[sizeof sz - 1] = 0;
|
|
|
|
/* Verify the 0q prefix */
|
|
if(sz[0] != '0' || sz[1] != 'q')
|
|
return FALSE;
|
|
/* Make sure we have a bunch of hex characters */
|
|
for(cch = 2; cch < sizeof sz && !FIsSpace(sz[cch]); ++cch) {
|
|
if(!(sz[cch] >= '0' && sz[cch] <= '9' ||
|
|
sz[cch] >= 'A' && sz[cch] <= 'F' ||
|
|
sz[cch] >= 'a' && sz[cch] <= 'f'))
|
|
return FALSE;
|
|
}
|
|
cch -= 2;
|
|
if(cch <= 0)
|
|
return FALSE;
|
|
|
|
/* Move the text out to the end of the string and fill the preceding
|
|
* characters with zeroes */
|
|
memmove(&sz[sizeof sz - 1 - cch], &sz[2], cch);
|
|
memset(sz, '0', sizeof sz - 1 - cch);
|
|
|
|
/* Now parse out the two dwords */
|
|
plu->LowPart = DwHexFromSz(&sz[sizeof sz - 9], NULL);
|
|
sz[sizeof sz - 9] = 0;
|
|
plu->HighPart = DwHexFromSz(&sz[sizeof sz - 17], NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL FGetNamedDwParam(LPCSTR szLine, LPCSTR szKey, DWORD *pdw, LPSTR szResp)
|
|
{
|
|
if(!FGetDwParam(szLine, szKey, pdw)) {
|
|
sprintf(szResp, "missing %s", szKey);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
ULONG UlAddrFromSz(LPCSTR sz)
|
|
{
|
|
ULONG ul;
|
|
int ib;
|
|
int ich;
|
|
|
|
for(ib = 0; ib < 4; ++ib) {
|
|
BYTE b = 0;
|
|
|
|
for(ich = 0; ich < 3; ++ich) {
|
|
if(sz[ich] < '0' || sz[ich] > '9')
|
|
break;
|
|
b = 10 * b + (sz[ich] - '0');
|
|
}
|
|
if(ich == 0 || sz[ich] != (ib == 3 ? 0 : '.'))
|
|
return 0;
|
|
sz += ich + 1;
|
|
((BYTE *)&ul)[ib ^ 3] = b;
|
|
}
|
|
return ul;
|
|
}
|
|
|
|
LPCSTR SzStdResponse(HRESULT hr)
|
|
{
|
|
LPCSTR pszResp;
|
|
|
|
switch(hr) {
|
|
case XBDM_NOSUCHFILE:
|
|
pszResp = "file not found";
|
|
break;
|
|
case XBDM_NOMODULE:
|
|
pszResp = "no such module";
|
|
break;
|
|
case XBDM_MEMUNMAPPED:
|
|
pszResp = "memory not mapped";
|
|
break;
|
|
case XBDM_NOTHREAD:
|
|
pszResp = "no such thread";
|
|
break;
|
|
case XBDM_INVALIDCMD:
|
|
pszResp = "unknown command";
|
|
break;
|
|
case XBDM_NOTSTOPPED:
|
|
pszResp = "not stopped";
|
|
break;
|
|
case XBDM_MUSTCOPY:
|
|
pszResp = "file must be copied";
|
|
break;
|
|
case XBDM_ALREADYEXISTS:
|
|
pszResp = "file already exists";
|
|
break;
|
|
case XBDM_DIRNOTEMPTY:
|
|
pszResp = "directory not empty";
|
|
break;
|
|
case XBDM_BADFILENAME:
|
|
pszResp = "filename is invalid";
|
|
break;
|
|
case XBDM_CANNOTCREATE:
|
|
pszResp = "file cannot be created";
|
|
break;
|
|
case XBDM_DEVICEFULL:
|
|
pszResp = "no room on device";
|
|
break;
|
|
case XBDM_MULTIRESPONSE:
|
|
pszResp = "multiline response follows";
|
|
break;
|
|
case XBDM_BINRESPONSE:
|
|
pszResp = "binary response follows";
|
|
break;
|
|
case XBDM_READYFORBIN:
|
|
pszResp = "send binary data";
|
|
break;
|
|
case XBDM_CANNOTACCESS:
|
|
pszResp = "access denied";
|
|
break;
|
|
case XBDM_NOTDEBUGGABLE:
|
|
pszResp = "not debuggable";
|
|
break;
|
|
case XBDM_BADCOUNTTYPE:
|
|
pszResp = "type invalid";
|
|
break;
|
|
case XBDM_COUNTUNAVAILABLE:
|
|
pszResp = "data not available";
|
|
break;
|
|
case XBDM_NOTLOCKED:
|
|
pszResp = "box is not locked";
|
|
break;
|
|
case XBDM_KEYXCHG:
|
|
pszResp = "key exchange required";
|
|
break;
|
|
case XBDM_MUSTBEDEDICATED:
|
|
pszResp = "dedicated connection required";
|
|
break;
|
|
case E_OUTOFMEMORY:
|
|
pszResp = "out of memory";
|
|
break;
|
|
case E_UNEXPECTED:
|
|
pszResp = "unexpected error";
|
|
break;
|
|
case E_INVALIDARG:
|
|
pszResp = "bad parameter";
|
|
break;
|
|
case XBDM_NOERR:
|
|
pszResp = "OK";
|
|
break;
|
|
default:
|
|
pszResp = "";
|
|
break;
|
|
}
|
|
return pszResp;
|
|
}
|
|
|
|
HRESULT HrFromStatus(NTSTATUS st, HRESULT hrDefault)
|
|
{
|
|
switch(st) {
|
|
case STATUS_DIRECTORY_NOT_EMPTY:
|
|
return XBDM_DIRNOTEMPTY;
|
|
case STATUS_OBJECT_NAME_COLLISION:
|
|
return XBDM_ALREADYEXISTS;
|
|
case STATUS_OBJECT_PATH_NOT_FOUND:
|
|
case STATUS_OBJECT_NAME_NOT_FOUND:
|
|
return XBDM_NOSUCHFILE;
|
|
case STATUS_OBJECT_PATH_INVALID:
|
|
case STATUS_OBJECT_NAME_INVALID:
|
|
return XBDM_BADFILENAME;
|
|
case STATUS_ACCESS_DENIED:
|
|
return XBDM_CANNOTACCESS;
|
|
case STATUS_DISK_FULL:
|
|
return XBDM_DEVICEFULL;
|
|
case STATUS_INSUFFICIENT_RESOURCES:
|
|
return E_OUTOFMEMORY;
|
|
case STATUS_INVALID_HANDLE:
|
|
return E_INVALIDARG;
|
|
}
|
|
return hrDefault;
|
|
}
|
|
|
|
ULONG DmplOfConnection(PDM_CMDCONT pdmcc)
|
|
{
|
|
if(!pdmcc)
|
|
return -1;
|
|
return rgcst[(PCST)pdmcc - rgcst].dmplCur;
|
|
}
|
|
|
|
BOOL FDedicateConnection(int icst, PECH pech)
|
|
{
|
|
PETHREAD pthrExpect;
|
|
HANDLE hthrNew;
|
|
PVOID pfnCreateThread;
|
|
|
|
pthrExpect = rgcst[icst].pthrDedicated;
|
|
|
|
/* We should only be rededicating a connection running on the current
|
|
* thread */
|
|
if(PsGetCurrentThread() != pthrExpect) {
|
|
DbgPrint("dm: rededicating a connection on the wrong thread!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Create the new thread */
|
|
if(pech) {
|
|
pfnCreateThread = pech->pfnCreateThread;
|
|
if(!pfnCreateThread)
|
|
pfnCreateThread = CreateThread;
|
|
hthrNew = (*(HANDLE (*)())pfnCreateThread)(NULL, 0,
|
|
DedicatedServThread, (LPVOID)icst, CREATE_SUSPENDED, NULL);
|
|
if(!hthrNew)
|
|
return FALSE;
|
|
} else
|
|
pfnCreateThread = NULL;
|
|
|
|
/* Send the success message */
|
|
PrintSockLine(rgcst[icst].s, "205- connection dedicated");
|
|
|
|
/* Mark the connection state for the new thread. We mark the thread ID
|
|
* as NULL; dedicated threads will fill it in when they start */
|
|
rgcst[icst].pthrDedicated = NULL;
|
|
rgcst[icst].pechDedicated = pech;
|
|
|
|
/* Wake up the new thread */
|
|
if(pfnCreateThread) {
|
|
NtResumeThread(hthrNew, NULL);
|
|
CloseHandle(hthrNew);
|
|
} else {
|
|
rgcst[icst].pthrDedicated = pthrServ;
|
|
KeAlertThread(&pthrServ->Tcb, KernelMode);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void SendHrSzResp(SOCKET s, HRESULT hr, LPCSTR szResp, LPSTR szBuf)
|
|
{
|
|
/* Make sure we have an error code we like */
|
|
if(((hr >> 16) & 0x7fff) != FACILITY_XBDM) {
|
|
hr = SUCCEEDED(hr) ? XBDM_NOERR : XBDM_UNDEFINED;
|
|
if(!szResp)
|
|
szResp = SzStdResponse(E_UNEXPECTED);
|
|
} else if((hr & 0xffff) > 0xff)
|
|
hr = XBDM_UNDEFINED;
|
|
|
|
if(FAILED(hr))
|
|
szBuf[0] = '4';
|
|
else
|
|
szBuf[0] = '2';
|
|
szBuf[1] = (char) ('0' + (hr & 0xffff) / 10); // overflow?
|
|
szBuf[2] = (char) ('0' + (hr & 0xffff) % 10);
|
|
szBuf[3] = '-';
|
|
szBuf[4] = ' ';
|
|
if(szResp != szBuf) {
|
|
if(szResp)
|
|
strcpy(szBuf + 5, szResp);
|
|
else
|
|
szBuf[5] = 0;
|
|
}
|
|
PrintSockLine(s, szBuf);
|
|
}
|
|
|
|
HRESULT HrHandleBuiltinCommand(DWORD dmplCur, LPCSTR sz, int cchCmd,
|
|
LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
HRESULT hr;
|
|
int ichhMin, ichhMac, ichhMid;
|
|
int sgn;
|
|
BOOL fPermitted;
|
|
|
|
ichhMin = 0;
|
|
ichhMac = cchh;
|
|
|
|
while(ichhMin < ichhMac) {
|
|
ichhMid = (ichhMin + ichhMac) / 2;
|
|
sgn = SgnCompareRgch(rgchh[ichhMid].szCommand, sz, cchCmd);
|
|
if(sgn == 0)
|
|
break;
|
|
else if(sgn < 0)
|
|
ichhMin = ichhMid + 1;
|
|
else
|
|
ichhMac = ichhMid;
|
|
}
|
|
|
|
if(ichhMin < ichhMac) {
|
|
/* Check for appropriate permission */
|
|
if(rgchh[ichhMid].dmplCombined)
|
|
fPermitted = (dmplCur & rgchh[ichhMid].dmplCombined) ==
|
|
rgchh[ichhMid].dmplCombined;
|
|
else if(rgchh[ichhMid].dmplMask)
|
|
fPermitted = dmplCur & rgchh[ichhMid].dmplMask;
|
|
else
|
|
fPermitted = TRUE;
|
|
if(fPermitted) {
|
|
/* Make sure we're running on the right thread */
|
|
if(!pthrServ || PsGetCurrentThread() == pthrServ ||
|
|
(rgchh[ichhMid].dwFlags & CHH_ANYTHREAD))
|
|
{
|
|
hr = (*rgchh[ichhMid].pfnHandler)(sz, szResp, cchResp, pdmcc);
|
|
} else
|
|
hr = XBDM_MUSTBEDEDICATED;
|
|
} else
|
|
hr = XBDM_CANNOTACCESS;
|
|
} else
|
|
hr = XBDM_INVALIDCMD;
|
|
return hr;
|
|
}
|
|
|
|
void DoSendReceive(int icst, BOOL fSend)
|
|
{
|
|
HRESULT hr;
|
|
SOCKET s = rgcst[icst].s;
|
|
char szBuf[256];
|
|
char szResp[256];
|
|
|
|
if(!rgcst[icst].dmcc.Buffer) {
|
|
*szBuf = 0;
|
|
rgcst[icst].dmcc.Buffer = szBuf;
|
|
rgcst[icst].dmcc.BufferSize = sizeof szBuf;
|
|
}
|
|
szResp[0] = 0;
|
|
if(!fSend) {
|
|
int cb;
|
|
/* We need to receive data. First, we attempt to take it from our
|
|
* command receive buffer */
|
|
cb = rgcst[icst].dwStatus & CONN_RECEIVED;
|
|
if(cb) {
|
|
int ib;
|
|
int ibT;
|
|
|
|
ib = cb;
|
|
if((DWORD)cb > rgcst[icst].dmcc.BytesRemaining)
|
|
ib = rgcst[icst].dmcc.BytesRemaining;
|
|
if((DWORD)cb > rgcst[icst].dmcc.BufferSize)
|
|
ib = rgcst[icst].dmcc.BufferSize;
|
|
if(ib) {
|
|
rgcst[icst].dmcc.DataSize = ib;
|
|
memcpy(rgcst[icst].dmcc.Buffer, rgcst[icst].szBuf, ib);
|
|
for(ibT = 0; ib < cb; )
|
|
rgcst[icst].szBuf[ibT++] = rgcst[icst].szBuf[ib++];
|
|
rgcst[icst].dwStatus = (rgcst[icst].dwStatus &
|
|
~CONN_RECEIVED) | ibT;
|
|
} else
|
|
rgcst[icst].dmcc.DataSize = 0;
|
|
} else {
|
|
/* Receive as much data as we can */
|
|
cb = rgcst[icst].dmcc.BufferSize;
|
|
if((DWORD)cb > rgcst[icst].dmcc.BytesRemaining)
|
|
cb = rgcst[icst].dmcc.BytesRemaining;
|
|
if(cb) {
|
|
cb = recv(s, rgcst[icst].dmcc.Buffer, cb, 0);
|
|
if(cb <= 0) {
|
|
/* Socket is gone, so clean up and flag an error */
|
|
cb = 0;
|
|
CloseConn(icst);
|
|
}
|
|
}
|
|
rgcst[icst].dmcc.DataSize = cb;
|
|
}
|
|
}
|
|
hr = rgcst[icst].dmcc.HandlingFunction(&rgcst[icst].dmcc, szResp,
|
|
sizeof szResp);
|
|
if(fSend) {
|
|
if(SUCCEEDED(hr)) {
|
|
if(rgcst[icst].dmcc.DataSize == -1) {
|
|
/* We have a string to send, so send it */
|
|
PrintSockLine(s, rgcst[icst].dmcc.Buffer);
|
|
} else if(rgcst[icst].dmcc.DataSize) {
|
|
/* We have data to send, so send it */
|
|
int cbSend = rgcst[icst].dmcc.DataSize;
|
|
while(cbSend) {
|
|
int cbSent = send(s, (PBYTE)rgcst[icst].dmcc.Buffer +
|
|
rgcst[icst].dmcc.DataSize - cbSend, cbSend, 0);
|
|
if(cbSent <= 0) {
|
|
/* A send error occurred, so close the connection and
|
|
* tell the handling function we can accept no more
|
|
* data */
|
|
rgcst[icst].dmcc.BytesRemaining = 0;
|
|
rgcst[icst].dmcc.HandlingFunction(&rgcst[icst].dmcc,
|
|
szResp, sizeof szResp);
|
|
CloseConn(icst);
|
|
break;
|
|
} else
|
|
cbSend -= cbSent;
|
|
}
|
|
}
|
|
} else if(hr == XBDM_ENDOFLIST) {
|
|
/* We're out of data, so send end-of-data if appropriate and go
|
|
* back to text mode */
|
|
if(rgcst[icst].dwStatus & CONN_MULTILINE)
|
|
PrintSockLine(s, ".");
|
|
InitDmcc(icst);
|
|
} else {
|
|
/* An unexpected error has occurred. Forcibly terminate the
|
|
* connection */
|
|
CloseConn(icst);
|
|
}
|
|
} else if(rgcst[icst].dmcc.BytesRemaining == 0) {
|
|
/* If we've reached the end of our data stream, we can give the
|
|
* appropriate response */
|
|
LPCSTR pszResp;
|
|
|
|
/* We may have an extended response, in which case we need to
|
|
* prepare for it */
|
|
rgcst[icst].dwStatus &= ~CONN_BINARY;
|
|
rgcst[icst].dmcc.BytesRemaining = -1;
|
|
switch(hr) {
|
|
case XBDM_MULTIRESPONSE:
|
|
rgcst[icst].dwStatus |= CONN_MULTILINE;
|
|
rgcst[icst].dmcc.DataSize = -1;
|
|
break;
|
|
case XBDM_BINRESPONSE:
|
|
rgcst[icst].dwStatus |= CONN_BINSEND;
|
|
break;
|
|
default:
|
|
InitDmcc(icst);
|
|
break;
|
|
}
|
|
if(rgcst[icst].dwStatus & (CONN_MULTILINE | CONN_BINSEND | CONN_BINARY) &&
|
|
!rgcst[icst].dmcc.HandlingFunction)
|
|
{
|
|
/* We gotta fail this */
|
|
hr = E_UNEXPECTED;
|
|
rgcst[icst].dwStatus &= ~(CONN_MULTILINE | CONN_BINSEND | CONN_BINARY);
|
|
szResp[0] = 0;
|
|
}
|
|
|
|
pszResp = szResp[0] ? szResp : SzStdResponse(hr);
|
|
SendHrSzResp(s, hr, pszResp, szBuf);
|
|
}
|
|
|
|
if(rgcst[icst].dmcc.Buffer == szBuf)
|
|
rgcst[icst].dmcc.Buffer = NULL;
|
|
}
|
|
|
|
PECH PechFindHandler(LPCSTR sz, int cch)
|
|
{
|
|
int iech;
|
|
|
|
/* Look up the command processor */
|
|
for(iech = 0; iech < MAX_ECH; ++iech) {
|
|
if(rgpech[iech] && FEqualRgch(rgpech[iech]->szExtName, sz, cch))
|
|
return rgpech[iech];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void HandleCommand(int icst, LPCSTR sz)
|
|
{
|
|
int cchCmd = CchOfWord(sz);
|
|
HRESULT hr = 0;
|
|
int ich;
|
|
char szResp[512];
|
|
LPCSTR pszResp;
|
|
PDM_CMDCONT pdmcc;
|
|
DWORD dmplCur;
|
|
PECH pech = NULL;
|
|
|
|
/* Make sure we're prepared to call this thing */
|
|
if(icst >= 0) {
|
|
pdmcc = &rgcst[icst].dmcc;
|
|
InitDmcc(icst);
|
|
dmplCur = rgcst[icst].dmplCur;
|
|
} else {
|
|
pdmcc = NULL;
|
|
dmplCur = -1;
|
|
}
|
|
szResp[5] = 0;
|
|
/* If this command belongs to an external command processor, we send it
|
|
* there */
|
|
for(ich = 0; ich < cchCmd; ++ich) {
|
|
if(sz[ich] == '!') {
|
|
BOOL fMatchingConnection;
|
|
|
|
/* Look up the command processor */
|
|
pech = PechFindHandler(sz, ich);
|
|
if(pech) {
|
|
/* All external commands require control permission */
|
|
if(dmplCur & DMPL_PRIV_CONTROL) {
|
|
/* Make sure this handler was appropriately dedicated, if
|
|
* necessary */
|
|
if(pech == rgcst[icst].pechDedicated)
|
|
fMatchingConnection = TRUE;
|
|
else if(!pech->pfnCreateThread && !rgcst[icst].pechDedicated)
|
|
fMatchingConnection = TRUE;
|
|
else
|
|
fMatchingConnection = FALSE;
|
|
if(fMatchingConnection) {
|
|
hr = pech->pfnHandler(sz, szResp + 5,
|
|
sizeof szResp - 5, pdmcc);
|
|
if(!hr)
|
|
hr = XBDM_NOERR;
|
|
} else
|
|
hr = XBDM_MUSTBEDEDICATED;
|
|
} else
|
|
hr = XBDM_CANNOTACCESS;
|
|
} else {
|
|
/* No command handler, can't do anything */
|
|
hr = XBDM_INVALIDCMD;
|
|
*szResp = 0;
|
|
}
|
|
break;
|
|
} else if(FIsSpace(sz[ich]))
|
|
break;
|
|
}
|
|
|
|
if(!hr) {
|
|
/* Nobody has handled it yet, so we use our own internal handler.
|
|
* Internal commands must be run on the server thread to avoid
|
|
* possible deadlock if xapi is stopped */
|
|
hr = HrHandleBuiltinCommand(dmplCur, sz, cchCmd, szResp + 5,
|
|
sizeof szResp - 5, pdmcc);
|
|
}
|
|
|
|
if(hr && icst >= 0) {
|
|
/* We have some sort of response to process. First convert to a known
|
|
* error code */
|
|
if(szResp[5])
|
|
pszResp = szResp;
|
|
else
|
|
pszResp = SzStdResponse(hr);
|
|
|
|
/* We may have to indicate a special mode */
|
|
switch(hr) {
|
|
case XBDM_MULTIRESPONSE:
|
|
rgcst[icst].dwStatus |= CONN_MULTILINE;
|
|
rgcst[icst].dmcc.DataSize = -1;
|
|
break;
|
|
case XBDM_BINRESPONSE:
|
|
rgcst[icst].dwStatus |= CONN_BINSEND;
|
|
break;
|
|
case XBDM_READYFORBIN:
|
|
rgcst[icst].dwStatus |= CONN_BINARY;
|
|
break;
|
|
}
|
|
if(rgcst[icst].dwStatus & (CONN_MULTILINE | CONN_BINSEND | CONN_BINARY) &&
|
|
!rgcst[icst].dmcc.HandlingFunction)
|
|
{
|
|
/* We gotta fail this */
|
|
hr = E_UNEXPECTED;
|
|
rgcst[icst].dwStatus &= ~(CONN_MULTILINE | CONN_BINSEND | CONN_BINARY);
|
|
}
|
|
|
|
if(hr == XBDM_DEDICATED) {
|
|
/* We need to spin off a thread to handle this connection */
|
|
if(FDedicateConnection(icst, pech))
|
|
/* The new thread will send the dedicated message */
|
|
hr = 0;
|
|
else
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
if(hr && icst >= 0) {
|
|
/* Now send our response */
|
|
SendHrSzResp(rgcst[icst].s, hr, pszResp, szResp);
|
|
|
|
if(hr == XBDM_READYFORBIN && pdmcc->BytesRemaining == 0)
|
|
/* Zero-length receive, so fake up the receive now to end it */
|
|
DoSendReceive(icst, FALSE);
|
|
}
|
|
}
|
|
|
|
void AnswerName(SOCKET s)
|
|
{
|
|
struct {
|
|
BYTE bRequest;
|
|
BYTE cchName;
|
|
char szName[256];
|
|
} nm;
|
|
struct sockaddr_in sin;
|
|
int cbAddr = sizeof sin;
|
|
int cbPkt;
|
|
|
|
cbPkt = recvfrom(s, (PVOID)&nm, sizeof nm, 0, (struct sockaddr *)&sin,
|
|
&cbAddr);
|
|
switch(nm.bRequest) {
|
|
case 1:
|
|
case 3:
|
|
/* Name request. 1 is to match the name, 3 is to match anything */
|
|
if(nm.bRequest == 1) {
|
|
nm.bRequest = 0;
|
|
/* Need to ensure more than two bytes received before we can
|
|
* look at nm.cchName */
|
|
if(cbPkt < 2 || cbPkt != 2 + nm.cchName)
|
|
/* Ill-formed packet, do nothing */
|
|
break;
|
|
nm.szName[nm.cchName] = 0;
|
|
/* Is it us? */
|
|
if(!nm.cchName || !FEqualRgch(nm.szName, rgchDbgName, nm.cchName)
|
|
|| rgchDbgName[nm.cchName] != 0)
|
|
/* Not for us */
|
|
break;
|
|
}
|
|
/* Answer the request */
|
|
nm.bRequest = 2;
|
|
for(nm.cchName = 0; rgchDbgName[nm.cchName]; ++nm.cchName)
|
|
nm.szName[nm.cchName] = rgchDbgName[nm.cchName];
|
|
break;
|
|
default:
|
|
/* We won't answer a request we don't recognize */
|
|
nm.bRequest = 0;
|
|
break;
|
|
}
|
|
if(nm.bRequest)
|
|
sendto(s, (PVOID)&nm, nm.cchName + 2, 0, (struct sockaddr *)&sin,
|
|
cbAddr);
|
|
}
|
|
|
|
void HandleSocketRead(int icst)
|
|
{
|
|
if(rgcst[icst].dwStatus & CONN_BINARY)
|
|
DoSendReceive(icst, FALSE);
|
|
else {
|
|
/* Text data coming in; read it */
|
|
int cbRcv;
|
|
int cbBuf;
|
|
char *pchBuf;
|
|
char rgbBufT[64];
|
|
int ich;
|
|
|
|
if(rgcst[icst].dwStatus & CONN_OVERFLOW) {
|
|
/* Overflowed the buffer, ignore the data */
|
|
pchBuf = rgbBufT;
|
|
cbBuf = MAX_CONN_BUF - sizeof rgbBufT;
|
|
} else {
|
|
cbBuf = rgcst[icst].dwStatus & CONN_RECEIVED;
|
|
pchBuf = &rgcst[icst].szBuf[cbBuf];
|
|
}
|
|
cbRcv = recv(rgcst[icst].s, pchBuf, MAX_CONN_BUF -
|
|
cbBuf, 0);
|
|
if(cbRcv <= 0) {
|
|
/* Connection is bad, give up */
|
|
CloseConn(icst);
|
|
return;
|
|
}
|
|
if(!(rgcst[icst].dwStatus & CONN_OVERFLOW)) {
|
|
rgcst[icst].dwStatus += cbRcv;
|
|
if((rgcst[icst].dwStatus & CONN_RECEIVED) ==
|
|
MAX_CONN_BUF)
|
|
rgcst[icst].dwStatus |= CONN_OVERFLOW;
|
|
}
|
|
if(pchBuf != rgcst[icst].szBuf) {
|
|
/* We're receiving additional data, so we may need to look
|
|
* back one for the CR */
|
|
--pchBuf;
|
|
++cbRcv;
|
|
}
|
|
/* Let's see if we got our end-of-line, which must be of the
|
|
* form CR-LF or CR-0 */
|
|
for(ich = 0; ich < cbRcv; ++ich)
|
|
if(pchBuf[ich] == '\015')
|
|
break;
|
|
if(ich < cbRcv - 1) {
|
|
/* Got EOL, so let's do our requisite processing */
|
|
if(rgcst[icst].dwStatus & CONN_OVERFLOW) {
|
|
PrintSockLine(rgcst[icst].s, "406- line too long");
|
|
rgcst[icst].dwStatus &= ~(CONN_OVERFLOW | CONN_RECEIVED);
|
|
} else {
|
|
pchBuf[ich++] = 0;
|
|
HandleCommand(icst, rgcst[icst].szBuf);
|
|
if(rgcst[icst].s != INVALID_SOCKET) {
|
|
int ichNew = 0;
|
|
while(++ich < cbRcv)
|
|
rgcst[icst].szBuf[ichNew++] = pchBuf[ich];
|
|
rgcst[icst].dwStatus &= ~CONN_RECEIVED;
|
|
rgcst[icst].dwStatus |= ichNew;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DWORD DedicatedServThread(LPVOID lpv)
|
|
{
|
|
int icst = (int)lpv;
|
|
|
|
DmGetCurrentDmtd()->DebugFlags |= DMFLAG_DEBUGTHREAD;
|
|
rgcst[icst].pthrDedicated = PsGetCurrentThread();
|
|
|
|
while(rgcst[icst].s != INVALID_SOCKET && rgcst[icst].pthrDedicated ==
|
|
PsGetCurrentThread())
|
|
{
|
|
fd_set fds;
|
|
BOOL fSend;
|
|
|
|
FD_ZERO(&fds);
|
|
FD_SET(rgcst[icst].s, &fds);
|
|
fSend = rgcst[icst].dwStatus & (CONN_BINSEND | CONN_MULTILINE);
|
|
|
|
while(select(0, fSend ? NULL : &fds, fSend ? &fds : NULL, NULL, 0) == 0);
|
|
if(!FD_ISSET(rgcst[icst].s, &fds))
|
|
/* How did this happen? */
|
|
CloseConn(icst);
|
|
else if(fSend)
|
|
DoSendReceive(icst, TRUE);
|
|
else
|
|
HandleSocketRead(icst);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DWORD ServerThread(LPVOID pv)
|
|
{
|
|
SOCKET sockListen;
|
|
SOCKET sockName;
|
|
int icst;
|
|
struct sockaddr_in sin;
|
|
fd_set fds;
|
|
fd_set fdsSend;
|
|
NTSTATUS st;
|
|
char sz[128];
|
|
PULARGE_INTEGER plu;
|
|
|
|
//Boost the base priority of this thread so that the dm is response, especially
|
|
//important xbcp.
|
|
KeSetBasePriorityThread(KeGetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
|
|
|
DmGetCurrentDmtd()->DebugFlags |= DMFLAG_DEBUGTHREAD;
|
|
pthrServ = PsGetCurrentThread();
|
|
|
|
for(icst = 0; icst < MAX_CONNECTIONS; ++icst)
|
|
rgcst[icst].s = INVALID_SOCKET;
|
|
sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if(sockListen == INVALID_SOCKET)
|
|
return 0;
|
|
st = TRUE;
|
|
if(0 != setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, (PVOID)&st,
|
|
sizeof st))
|
|
{
|
|
_asm int 3
|
|
closesocket(sockListen);
|
|
return 0;
|
|
}
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_addr.s_addr = 0;
|
|
sin.sin_port = htons(DEBUGGER_PORT);
|
|
if(0 != bind(sockListen, (struct sockaddr *)&sin, sizeof sin)) {
|
|
_asm int 3
|
|
closesocket(sockListen);
|
|
return 0;
|
|
}
|
|
if(0 != listen(sockListen, MAX_CONNECTIONS)) {
|
|
_asm int 3
|
|
closesocket(sockListen);
|
|
return 0;
|
|
}
|
|
|
|
/* Set up our name answering socket */
|
|
sockName = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if(sockName != INVALID_SOCKET) {
|
|
BOOL fReuse = TRUE;
|
|
if(0 != setsockopt(sockName, SOL_SOCKET, SO_REUSEADDR, (PVOID)&fReuse,
|
|
sizeof fReuse))
|
|
{
|
|
NameErr:
|
|
closesocket(sockName);
|
|
sockName = INVALID_SOCKET;
|
|
} else {
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_addr.s_addr = 0;
|
|
sin.sin_port = htons(DEBUGGER_PORT);
|
|
if(0 != bind(sockName, (struct sockaddr *)&sin, sizeof sin))
|
|
goto NameErr;
|
|
}
|
|
}
|
|
|
|
// DbgPrint("dm: listening\n");
|
|
KeSetEvent(&kevtServ, EVENT_INCREMENT, FALSE);
|
|
|
|
/* Now we wait for connections and answer what's there */
|
|
for(;;) {
|
|
do {
|
|
FD_ZERO(&fds);
|
|
FD_ZERO(&fdsSend);
|
|
FD_SET(sockListen, &fds);
|
|
if(sockName != INVALID_SOCKET)
|
|
FD_SET(sockName, &fds);
|
|
for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
|
|
if(rgcst[icst].s != INVALID_SOCKET &&
|
|
rgcst[icst].pthrDedicated == PsGetCurrentThread())
|
|
{
|
|
FD_SET(rgcst[icst].s, rgcst[icst].dwStatus &
|
|
(CONN_BINSEND | CONN_MULTILINE) ? &fdsSend : &fds);
|
|
}
|
|
}
|
|
} while(select(0, &fds, &fdsSend, NULL, NULL) <= 0);
|
|
|
|
/* Accept new connections */
|
|
if(FD_ISSET(sockListen, &fds)) {
|
|
SOCKET sockNew;
|
|
int cb = sizeof sin;
|
|
sockNew = accept(sockListen, (struct sockaddr *)&sin, &cb);
|
|
if(sockNew == INVALID_SOCKET)
|
|
continue;
|
|
EnterCriticalSection(&csAccept);
|
|
if(g_fServShutdown)
|
|
icst = MAX_CONNECTIONS;
|
|
else {
|
|
for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
|
|
if(rgcst[icst].s == INVALID_SOCKET)
|
|
break;
|
|
}
|
|
}
|
|
if(icst < MAX_CONNECTIONS) {
|
|
rgcst[icst].s = sockNew;
|
|
rgcst[icst].pthrDedicated = pthrServ;
|
|
}
|
|
LeaveCriticalSection(&csAccept);
|
|
if(icst == MAX_CONNECTIONS) {
|
|
PrintSockLine(sockNew, "401- max number of connections exceeded");
|
|
fAllowKd = TRUE; // we're going to need to debug if this happens
|
|
closesocket(sockNew);
|
|
} else {
|
|
if(g_fLockLevel) {
|
|
rgcst[icst].dmplCur = 0;
|
|
plu = &rgcst[icst].luConnectNonce;
|
|
KeQuerySystemTime((PLARGE_INTEGER)plu);
|
|
_asm {
|
|
mov ecx, plu
|
|
rdtsc
|
|
xor [ecx], eax
|
|
xor [ecx+4], edx
|
|
}
|
|
sprintf(sz, "201- boxid=0q%08x%08x nonce=0q%08x%08x",
|
|
g_luBoxId.HighPart, g_luBoxId.LowPart, plu->HighPart,
|
|
plu->LowPart);
|
|
PrintSockLine(sockNew, sz);
|
|
} else {
|
|
rgcst[icst].dmplCur = -1;
|
|
PrintSockLine(sockNew, "201- connected");
|
|
}
|
|
rgcst[icst].dwStatus = 0;
|
|
InitDmcc(icst);
|
|
}
|
|
} else if(FD_ISSET(sockName, &fds)) {
|
|
/* Answer name requests */
|
|
AnswerName(sockName);
|
|
} else for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
|
|
if(rgcst[icst].s == INVALID_SOCKET)
|
|
continue;
|
|
if(FD_ISSET(rgcst[icst].s, &fdsSend)) {
|
|
DoSendReceive(icst, TRUE);
|
|
continue;
|
|
}
|
|
if(FD_ISSET(rgcst[icst].s, &fds))
|
|
HandleSocketRead(icst);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL FStartDebugService(void)
|
|
{
|
|
NTSTATUS st;
|
|
HANDLE hthr;
|
|
|
|
st = PsCreateSystemThread(&hthr, NULL, ServerThread, NULL, TRUE);
|
|
return NT_SUCCESS(st);
|
|
}
|
|
|
|
HRESULT DmSetXboxName(LPCSTR szName)
|
|
{
|
|
if(szName) {
|
|
strncpy(rgchDbgName, szName, sizeof rgchDbgName);
|
|
rgchDbgName[sizeof rgchDbgName - 1] = 0;
|
|
} else
|
|
rgchDbgName[0] = 0;
|
|
WriteIniFile();
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT DmGetXboxName(LPSTR szName, LPDWORD pcch)
|
|
{
|
|
DWORD ich;
|
|
|
|
if (!szName || !pcch)
|
|
return E_INVALIDARG;
|
|
|
|
for(ich = 0; rgchDbgName[ich] && ich < *pcch - 1; ++ich)
|
|
*szName++ = rgchDbgName[ich];
|
|
*szName = 0;
|
|
return rgchDbgName[ich] ? XBDM_BUFFER_TOO_SMALL : XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT DmRegisterCommandProcessor(LPCSTR sz, PDM_CMDPROC pfn)
|
|
{
|
|
return DmRegisterCommandProcessorEx(sz, pfn, NULL);
|
|
}
|
|
|
|
HRESULT DmRegisterCommandProcessorEx(LPCSTR sz, PDM_CMDPROC pfn,
|
|
PVOID pfnCreateThread)
|
|
{
|
|
int ich;
|
|
int iech;
|
|
ECH *pech;
|
|
HRESULT hr;
|
|
|
|
if (!sz)
|
|
return E_INVALIDARG;
|
|
|
|
RtlEnterCriticalSection(&csEch);
|
|
if(!pfn) {
|
|
/* We're going to unregister, so find the matching handler and pull
|
|
* it */
|
|
for(ich = 0; sz[ich]; ++ich);
|
|
for(iech = 0; iech < MAX_ECH; ++iech) {
|
|
if(rgpech[iech] && FEqualRgch(rgpech[iech]->szExtName, sz, ich)) {
|
|
DmFreePool(rgpech[iech]);
|
|
rgpech[iech] = NULL;
|
|
}
|
|
}
|
|
hr = XBDM_NOERR;
|
|
} else {
|
|
/* Let's find somewhere to put it */
|
|
for(iech = 0; iech < MAX_ECH; ++iech)
|
|
if(!rgpech[iech])
|
|
break;
|
|
if(iech < MAX_ECH) {
|
|
pech = DmAllocatePoolWithTag(sizeof *pech, 'hcmd');
|
|
} else
|
|
pech = NULL;
|
|
if(pech) {
|
|
for(ich = 0; sz[ich] && ich < sizeof pech->szExtName - 1; ++ich)
|
|
pech->szExtName[ich] = sz[ich];
|
|
if(sz[ich]) {
|
|
DmFreePool(pech);
|
|
hr = E_INVALIDARG;
|
|
} else {
|
|
pech->szExtName[ich] = 0;
|
|
pech->pfnHandler = pfn;
|
|
pech->pfnCreateThread = pfnCreateThread;
|
|
rgpech[iech] = pech;
|
|
hr = XBDM_NOERR;
|
|
}
|
|
} else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
RtlLeaveCriticalSection(&csEch);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrDedicateConnection(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
char szHandler[256];
|
|
int icst;
|
|
PECH pech;
|
|
|
|
if(!pdmcc)
|
|
return E_INVALIDARG;
|
|
|
|
if(PchGetParam(sz, "global", FALSE))
|
|
pech = NULL;
|
|
else if(!FGetSzParam(sz, "handler", szHandler, sizeof szHandler)) {
|
|
strcpy(szResp, "missing handler");
|
|
return E_FAIL;
|
|
} else {
|
|
pech = PechFindHandler(szHandler, strlen(szHandler));
|
|
if(!pech)
|
|
return XBDM_NOSUCHFILE;
|
|
}
|
|
|
|
icst = (PCST)pdmcc - rgcst;
|
|
/* Rededicate the connection. If this is successful, the success
|
|
* message will be sent in the new thread */
|
|
return FDedicateConnection(icst, pech) ? 0 : E_FAIL;
|
|
}
|
|
|
|
HRESULT DmThreadUserData(DWORD tid, PDWORD *ppdwRet)
|
|
{
|
|
PETHREAD pthr;
|
|
NTSTATUS st;
|
|
HRESULT hr = XBDM_NOTHREAD;
|
|
|
|
if (!ppdwRet)
|
|
return E_INVALIDARG;
|
|
|
|
if(tid == DM_CURRENT_THREAD) {
|
|
pthr = PsGetCurrentThread();
|
|
ObfReferenceObject(pthr);
|
|
st = STATUS_SUCCESS;
|
|
} else
|
|
st = PsLookupThreadByThreadId((HANDLE)tid, &pthr);
|
|
if(NT_SUCCESS(st)) {
|
|
DMTD *pdmtd = DmGetCurrentDmtd();
|
|
if(pdmtd) {
|
|
*ppdwRet = &pdmtd->UserData;
|
|
hr = XBDM_NOERR;
|
|
}
|
|
ObDereferenceObject(pthr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrEndConversation(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
int icst;
|
|
|
|
if(!pdmcc)
|
|
return E_INVALIDARG;
|
|
icst = (PCST)pdmcc - rgcst;
|
|
PrintSockLine(rgcst[icst].s, "200- bye");
|
|
CloseConn(icst);
|
|
return 0;
|
|
}
|
|
|
|
HRESULT HrSetDbgName(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
char szNewName[sizeof rgchDbgName];
|
|
HRESULT hr;
|
|
KIRQL irqlSav;
|
|
|
|
if(!FGetSzParam(sz, "name", szNewName, sizeof szNewName)) {
|
|
/* We're querying for the current name */
|
|
strcpy(szResp, rgchDbgName);
|
|
hr = XBDM_NOERR;
|
|
} else if(!FConnectionPermission(pdmcc, DMPL_PRIV_CONFIGURE))
|
|
hr = XBDM_CANNOTACCESS;
|
|
else {
|
|
irqlSav = KeRaiseIrqlToDpcLevel();
|
|
memcpy(rgchDbgName, szNewName, sizeof rgchDbgName);
|
|
hr = XBDM_NOERR;
|
|
KeLowerIrql(irqlSav);
|
|
WriteIniFile();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrToggleGPUCounters(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if(PchGetParam(sz, "enable", FALSE))
|
|
hr = DmEnableGPUCounter(TRUE);
|
|
else if(PchGetParam(sz, "disable", FALSE))
|
|
hr = DmEnableGPUCounter(FALSE);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrToggleKDState(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
HRESULT hr = XBDM_NOERR;
|
|
|
|
if(PchGetParam(sz, "enable", FALSE)) {
|
|
fAllowKd = TRUE;
|
|
strcpy(szResp, "kd enabled");
|
|
} else if(PchGetParam(sz, "disable", FALSE)) {
|
|
fAllowKd = FALSE;
|
|
strcpy(szResp, "kd disabled");
|
|
} else if(PchGetParam(sz, "except", FALSE)) {
|
|
fAllowKd = TRUE;
|
|
ExceptionsToKd();
|
|
} else if(PchGetParam(sz, "exceptif", FALSE)) {
|
|
/* Forward exceptions to KD only if KD is connected */
|
|
if(*KdDebuggerEnabled) {
|
|
fAllowKd = TRUE;
|
|
ExceptionsToKd();
|
|
}
|
|
} else {
|
|
strcpy(szResp, "improper command");
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrSetupNotifyAt(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
HRESULT hr;
|
|
int icst;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
icst = (PCST)pdmcc - rgcst;
|
|
hr = FNotifyAtCmd(rgcst[icst].s, sz, FALSE) ? XBDM_NOERR :
|
|
E_FAIL;
|
|
/* Keep the ini file up-to-date */
|
|
if(SUCCEEDED(hr))
|
|
WriteIniFile();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrSetupNotify(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
int icst;
|
|
HRESULT hr;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
icst = (PCST)pdmcc - rgcst;
|
|
|
|
if(FConvertToNotifySock(rgcst[icst].s)) {
|
|
/* This socket is no longer our responsibility */
|
|
rgcst[icst].s = INVALID_SOCKET;
|
|
hr = 0;
|
|
} else {
|
|
strcpy(szResp, "notification limit exceeded");
|
|
hr = E_FAIL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrDoStopon(BOOL fStop, LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
DWORD dw = 0;
|
|
|
|
if(PchGetParam(sz, "all", FALSE)) {
|
|
dw = -1;
|
|
strcpy(szResp, fStop ? "stop on all" : "stop on none");
|
|
} else {
|
|
if(PchGetParam(sz, "fce", FALSE))
|
|
dw |= DMSTOP_FCE;
|
|
if(PchGetParam(sz, "debugstr", FALSE))
|
|
dw |= DMSTOP_DEBUGSTR;
|
|
if(PchGetParam(sz, "createthread", FALSE))
|
|
dw |= DMSTOP_CREATETHREAD;
|
|
}
|
|
return DmStopOn(dw, fStop);
|
|
}
|
|
|
|
HRESULT HrStopon(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
return HrDoStopon(TRUE, sz, szResp, cchResp, pdmcc);
|
|
}
|
|
|
|
HRESULT HrNostopon(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
return HrDoStopon(FALSE, sz, szResp, cchResp, pdmcc);
|
|
}
|
|
|
|
HRESULT HrGo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
if(dwExecState == DMN_EXEC_START) {
|
|
sprintf(szResp, "not stopped");
|
|
return E_FAIL;
|
|
}
|
|
DmGo();
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrStop(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
if(dwExecState != DMN_EXEC_START) {
|
|
sprintf(szResp, "already stopped");
|
|
return E_FAIL;
|
|
}
|
|
DmStop();
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
BOOL FTitleExists(LPCSTR szDir, LPCSTR szTitle, LPCSTR szCmdLine, BOOL fMapIt)
|
|
{
|
|
HANDLE h;
|
|
OBJECT_ATTRIBUTES oa;
|
|
OBJECT_STRING objectName;
|
|
IO_STATUS_BLOCK iosb;
|
|
BOOL fRet;
|
|
NTSTATUS st;
|
|
OCHAR oszPath[MAX_OBJ_PATH+1];
|
|
OCHAR *pochTitle;
|
|
OCHAR *pochMax;
|
|
int ich;
|
|
|
|
if(!szDir || !szDir[0]) {
|
|
/* No dir name, so we expect szTitle to have the complete path name
|
|
* to the title */
|
|
if(!szTitle || !FFileNameToObName(szTitle, oszPath, sizeof oszPath /
|
|
sizeof(OCHAR)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
pochTitle = NULL;
|
|
} else {
|
|
if(!FFileNameToObName(szDir, oszPath, sizeof oszPath / sizeof(OCHAR)))
|
|
return FALSE;
|
|
pochTitle = oszPath;
|
|
}
|
|
if(!szTitle)
|
|
szTitle = "default.xbe";
|
|
|
|
if(pochTitle) {
|
|
/* Construct a complete path for the title and remember where both
|
|
* parts go */
|
|
pochMax = oszPath + sizeof oszPath / sizeof(OCHAR);
|
|
while(*pochTitle)
|
|
++pochTitle;
|
|
/* If we're in a root dir, don't add the extra '\' */
|
|
if(pochTitle > oszPath && pochTitle[-1] != '\\')
|
|
*pochTitle++ = '\\';
|
|
for(ich = 0; &pochTitle[ich] < pochMax && szTitle[ich]; ++ich)
|
|
pochTitle[ich] = szTitle[ich];
|
|
if(&pochTitle[ich] >= pochMax - 1)
|
|
return FALSE;
|
|
pochTitle[ich] = 0;
|
|
}
|
|
|
|
/* Verify the file exists */
|
|
RtlInitObjectString(&objectName, oszPath);
|
|
InitializeObjectAttributes(&oa, &objectName, OBJ_CASE_INSENSITIVE, NULL,
|
|
NULL);
|
|
st = NtOpenFile(&h, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
fRet = NT_SUCCESS(st);
|
|
if(fRet) {
|
|
NtClose(h);
|
|
if(fMapIt) {
|
|
if(pochTitle) {
|
|
pochTitle[-1] = 0;
|
|
pochMax = oszPath;
|
|
} else {
|
|
pochMax = NULL;
|
|
pochTitle = oszPath;
|
|
}
|
|
/* We require that the command line passed in is the size of the
|
|
* launch data, so we don't need to do any copying here */
|
|
fRet = NT_SUCCESS(XWriteTitleInfoNoReboot(pochTitle, pochMax,
|
|
szCmdLine ? LDT_FROM_DEBUGGER_CMDLINE : LDT_NONE, 0,
|
|
(PLAUNCH_DATA) szCmdLine));
|
|
}
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
HRESULT HrReceiveFileData(PDM_CMDCONT pdmcc, LPSTR szResponse,
|
|
DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
FILE_DISPOSITION_INFORMATION fdi;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
if(pdmcc->DataSize) {
|
|
if(pccs->rgbBuf)
|
|
/* Digest the data in the buffer */
|
|
pccs->ib += pdmcc->DataSize;
|
|
else
|
|
pccs->ib = DISK_BUF_SIZE;
|
|
pdmcc->BytesRemaining -= pdmcc->DataSize;
|
|
} else {
|
|
/* If there's no data, then either we had a zero-length file, or
|
|
* we've lost our socket. In the latter case, we need to delete
|
|
* the file */
|
|
if(pdmcc->BytesRemaining) {
|
|
pdmcc->BytesRemaining = 0;
|
|
fdi.DeleteFile = TRUE;
|
|
NtSetInformationFile(pccs->hFile, &iosb, &fdi, sizeof(fdi),
|
|
FileDispositionInformation);
|
|
}
|
|
}
|
|
|
|
/* See if we're ready for a disk write */
|
|
if(!pdmcc->BytesRemaining || pccs->ib == DISK_BUF_SIZE) {
|
|
NTSTATUS st;
|
|
|
|
if(pccs->hFile) {
|
|
st = NtWriteFile(pccs->hFile, NULL, NULL, NULL, &iosb,
|
|
pccs->rgbBuf, pccs->ib, NULL);
|
|
/* Close the file if we're out of data */
|
|
if(!NT_SUCCESS(st)) {
|
|
/* If an error, try to delete the file and signal an error
|
|
* condition */
|
|
fdi.DeleteFile = TRUE;
|
|
NtSetInformationFile(pccs->hFile, &iosb, &fdi, sizeof(fdi),
|
|
FileDispositionInformation);
|
|
NtClose(pccs->hFile);
|
|
pccs->hFile = NULL;
|
|
pccs->fError = TRUE;
|
|
} else if(!pdmcc->BytesRemaining) {
|
|
NtClose(pccs->hFile);
|
|
pccs->hFile = NULL;
|
|
}
|
|
}
|
|
pccs->ib = 0;
|
|
if(!pdmcc->BytesRemaining && pccs->rgbBuf)
|
|
/* Clean up our buffer */
|
|
DmFreePool(pccs->rgbBuf);
|
|
}
|
|
|
|
/* Make sure the buffer pointer is set up */
|
|
if(pccs->rgbBuf) {
|
|
pdmcc->Buffer = pccs->rgbBuf + pccs->ib;
|
|
pdmcc->BufferSize = DISK_BUF_SIZE - pccs->ib;
|
|
}
|
|
|
|
return pccs->fError ? E_UNEXPECTED : XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrReceiveFile(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
LARGE_INTEGER liSize;
|
|
NTSTATUS st;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
CCS *pccs;
|
|
IO_STATUS_BLOCK iosb;
|
|
FILE_END_OF_FILE_INFORMATION feof;
|
|
FILE_DISPOSITION_INFORMATION fdi;
|
|
HRESULT hr;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
if(!FGetNamedDwParam(sz, "length", &pdmcc->BytesRemaining, szResp))
|
|
return E_FAIL;
|
|
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
liSize.HighPart = 0;
|
|
liSize.LowPart = pdmcc->BytesRemaining;
|
|
|
|
st = FCreateFile(&pccs->hFile, GENERIC_WRITE | DELETE | SYNCHRONIZE,
|
|
szName, &liSize, 0, 0, FILE_OVERWRITE_IF,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if(NT_SUCCESS(st)) {
|
|
/* Set the size of the file now */
|
|
feof.EndOfFile = liSize;
|
|
st = NtSetInformationFile(pccs->hFile, &iosb, &feof, sizeof(feof),
|
|
FileEndOfFileInformation);
|
|
if(NT_SUCCESS(st)) {
|
|
/* Make sure we'll have a buffer for received data */
|
|
pccs->ib = 0;
|
|
pccs->rgbBuf = DmAllocatePoolWithTag(DISK_BUF_SIZE, 'bdbd');
|
|
/* Even if the alloc fails, we'll still have a small static buffer */
|
|
pdmcc->Buffer = pccs->rgbBuf;
|
|
pdmcc->BufferSize = DISK_BUF_SIZE;
|
|
pccs->fError = FALSE;
|
|
pdmcc->HandlingFunction = HrReceiveFileData;
|
|
return XBDM_READYFORBIN;
|
|
} else {
|
|
/* Mark the file for deletion and close the file */
|
|
fdi.DeleteFile = TRUE;
|
|
NtSetInformationFile(pccs->hFile, &iosb, &fdi, sizeof(fdi),
|
|
FileDispositionInformation);
|
|
NtClose(pccs->hFile);
|
|
pccs->hFile = NULL;
|
|
}
|
|
}
|
|
return HrFromStatus(st, XBDM_CANNOTCREATE);
|
|
}
|
|
|
|
HRESULT HrSendFileData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
if(pdmcc->BytesRemaining == 0) {
|
|
/* End of file */
|
|
NtClose(pccs->hFile);
|
|
if(pccs->rgbBuf)
|
|
DmFreePool(pccs->rgbBuf);
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
if(!pccs->fDidSize) {
|
|
/* First we need to send the length */
|
|
pdmcc->BytesRemaining = pccs->ib;
|
|
memcpy(pdmcc->Buffer, &pccs->ib, sizeof(DWORD));
|
|
pdmcc->DataSize = sizeof(DWORD);
|
|
pccs->fDidSize = TRUE;
|
|
} else {
|
|
NTSTATUS st;
|
|
IO_STATUS_BLOCK iosb;
|
|
DWORD cb;
|
|
|
|
cb = pdmcc->BufferSize;
|
|
if(cb > pdmcc->BytesRemaining)
|
|
cb = pdmcc->BytesRemaining;
|
|
st = NtReadFile(pccs->h, NULL, NULL, NULL, &iosb, pdmcc->Buffer, cb,
|
|
NULL);
|
|
if(!NT_SUCCESS(st))
|
|
// BUG BUG -- need to signal read failure
|
|
RtlZeroMemory(pdmcc->Buffer, cb);
|
|
pdmcc->BytesRemaining -= cb;
|
|
pdmcc->DataSize = cb;
|
|
}
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrSendFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
NTSTATUS st;
|
|
IO_STATUS_BLOCK iosb;
|
|
FILE_NETWORK_OPEN_INFORMATION ni;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
CCS *pccs;
|
|
HRESULT hr;
|
|
int icst;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
st = FCreateFile(&pccs->hFile, GENERIC_READ | SYNCHRONIZE, szName,
|
|
NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if(NT_SUCCESS(st)) {
|
|
st = NtQueryInformationFile(pccs->hFile, &iosb, &ni, sizeof ni,
|
|
FileNetworkOpenInformation);
|
|
if(NT_SUCCESS(st)) {
|
|
if(ni.EndOfFile.HighPart) {
|
|
/* File is too big to transfer */
|
|
sprintf(szResp, "file is too big");
|
|
hr = E_FAIL;
|
|
} else {
|
|
/* Get a buffer for sending if we can */
|
|
pdmcc->BufferSize = 1024;
|
|
pccs->rgbBuf = pdmcc->Buffer = DmAllocatePoolWithTag(
|
|
pdmcc->BufferSize, 'bdbd');
|
|
pccs->ib = ni.EndOfFile.LowPart;
|
|
pccs->fDidSize = FALSE;
|
|
pdmcc->BytesRemaining = 1;
|
|
pdmcc->HandlingFunction = HrSendFileData;
|
|
return XBDM_BINRESPONSE;
|
|
}
|
|
} else
|
|
hr = E_UNEXPECTED;
|
|
NtClose(pccs->h);
|
|
} else
|
|
hr = HrFromStatus(st, XBDM_CANNOTACCESS);
|
|
return hr;
|
|
}
|
|
|
|
void GetFileAttrSz(LPSTR sz, ULONG dwAttributes, PLARGE_INTEGER pliChange,
|
|
PLARGE_INTEGER pliCreate, PLARGE_INTEGER pliSize)
|
|
{
|
|
sprintf(sz, "sizehi=0x%x sizelo=0x%x createhi=0x%08x createlo=0x%08x "
|
|
"changehi=0x%08x changelo=0x%08x%s%s%s", pliSize->HighPart,
|
|
pliSize->LowPart, pliCreate->HighPart, pliCreate->LowPart,
|
|
pliChange->HighPart, pliChange->LowPart,
|
|
dwAttributes & FILE_ATTRIBUTE_DIRECTORY ? " directory" : "",
|
|
dwAttributes & FILE_ATTRIBUTE_READONLY ? " readonly" : "",
|
|
dwAttributes & FILE_ATTRIBUTE_HIDDEN ? " hidden" : "");
|
|
}
|
|
|
|
HRESULT HrReportFileAttributes(PDM_CMDCONT pdmcc, LPSTR szResponse,
|
|
DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
for(;;) {
|
|
switch(pdmcc->BytesRemaining++) {
|
|
case 1:
|
|
GetFileAttrSz(pdmcc->Buffer, pccs->fba.FileAttributes,
|
|
&pccs->fba.LastWriteTime, &pccs->fba.CreationTime,
|
|
&pccs->fba.EndOfFile);
|
|
return XBDM_NOERR;
|
|
default:
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
}
|
|
}
|
|
|
|
HRESULT HrGetFileAttributes(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
OCHAR oszFilename[MAX_OBJ_PATH+1];
|
|
NTSTATUS st;
|
|
OBJECT_STRING objFilename;
|
|
OBJECT_ATTRIBUTES oa;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
CCS *pccs;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
/* Turn the filename into an object manager name*/
|
|
if(!FFileNameToObName(szName, oszFilename, sizeof oszFilename /
|
|
sizeof(OCHAR)))
|
|
return XBDM_NOSUCHFILE;
|
|
RtlInitObjectString(&objFilename, oszFilename);
|
|
InitializeObjectAttributes(&oa, &objFilename, OBJ_CASE_INSENSITIVE, NULL,
|
|
NULL);
|
|
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
pdmcc->HandlingFunction = HrReportFileAttributes;
|
|
pdmcc->BytesRemaining = 1;
|
|
st = NtQueryFullAttributesFile(&oa, &pccs->fba);
|
|
return NT_SUCCESS(st) ? XBDM_MULTIRESPONSE : HrFromStatus(st,
|
|
XBDM_CANNOTACCESS);
|
|
}
|
|
|
|
HRESULT HrSetFileAttributes(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
NTSTATUS st;
|
|
HANDLE h;
|
|
IO_STATUS_BLOCK iosb;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
HRESULT hr;
|
|
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
st = FCreateFile(&h, SYNCHRONIZE, szName, NULL, 0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if(NT_SUCCESS(st)) {
|
|
FILE_NETWORK_OPEN_INFORMATION fna;
|
|
|
|
st = NtQueryInformationFile(h, &iosb, &fna, sizeof fna,
|
|
FileNetworkOpenInformation);
|
|
if(NT_SUCCESS(st)) {
|
|
FILE_BASIC_INFORMATION fba;
|
|
DWORD dwAttrMask = 0;
|
|
DWORD dwAttrNew = 0;
|
|
DWORD dwT;
|
|
|
|
RtlZeroMemory(&fba, sizeof fba);
|
|
if(FGetDwParam(sz, "readonly", &dwT)) {
|
|
dwAttrMask |= FILE_ATTRIBUTE_READONLY;
|
|
if(dwT)
|
|
dwAttrNew |= FILE_ATTRIBUTE_READONLY;
|
|
}
|
|
if(FGetDwParam(sz, "hidden", &dwT)) {
|
|
dwAttrMask |= FILE_ATTRIBUTE_HIDDEN;
|
|
if(dwT)
|
|
dwAttrNew |= FILE_ATTRIBUTE_HIDDEN;
|
|
}
|
|
FGetDwParam(sz, "createhi", &fba.CreationTime.HighPart);
|
|
FGetDwParam(sz, "createlo", &fba.CreationTime.LowPart);
|
|
FGetDwParam(sz, "changehi", &fba.LastWriteTime.HighPart);
|
|
FGetDwParam(sz, "changelo", &fba.LastWriteTime.LowPart);
|
|
fba.FileAttributes = fna.FileAttributes ^ ((fna.FileAttributes ^
|
|
dwAttrNew) & dwAttrMask);
|
|
if(!fba.FileAttributes)
|
|
fba.FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
st = NtSetInformationFile(h, &iosb, &fba, sizeof fba,
|
|
FileBasicInformation);
|
|
hr = NT_SUCCESS(st) ? XBDM_NOERR : HrFromStatus(st, E_FAIL);
|
|
}
|
|
NtClose(h);
|
|
} else
|
|
hr = HrFromStatus(st, XBDM_CANNOTACCESS);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrRenameFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
NTSTATUS st;
|
|
HANDLE h;
|
|
IO_STATUS_BLOCK iosb;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
OCHAR oszNewName[MAX_OBJ_PATH+1];
|
|
FILE_RENAME_INFORMATION fri;
|
|
HRESULT hr;
|
|
|
|
if(!FGetSzParam(sz, "newname", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing new name");
|
|
return E_FAIL;
|
|
}
|
|
if(!FFileNameToObName(szName, oszNewName, MAX_OBJ_PATH+1)) {
|
|
strcpy(szResp, "new name does not exist");
|
|
return XBDM_NOSUCHFILE;
|
|
}
|
|
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
/* Get a handle to the original file */
|
|
st = FCreateFile(&h, GENERIC_WRITE | SYNCHRONIZE, szName, NULL, 0, 0,
|
|
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if(NT_SUCCESS(st)) {
|
|
RtlZeroMemory(&fri, sizeof fri);
|
|
RtlInitObjectString(&fri.FileName, oszNewName);
|
|
st = NtSetInformationFile(h, &iosb, &fri, sizeof fri,
|
|
FileRenameInformation);
|
|
NtClose(h);
|
|
switch(st) {
|
|
case STATUS_NOT_SAME_DEVICE:
|
|
hr = XBDM_MUSTCOPY;
|
|
break;
|
|
default:
|
|
if(NT_SUCCESS(st))
|
|
hr = XBDM_NOERR;
|
|
else
|
|
hr = HrFromStatus(st, XBDM_CANNOTCREATE);
|
|
break;
|
|
}
|
|
} else
|
|
hr = XBDM_CANNOTACCESS;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrDeleteFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
NTSTATUS st;
|
|
HANDLE h;
|
|
IO_STATUS_BLOCK iosb;
|
|
FILE_DISPOSITION_INFORMATION fdi;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
HRESULT hr;
|
|
BOOL fIsDirectory;
|
|
char *pch;
|
|
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
fIsDirectory = PchGetParam(sz, "dir", FALSE) != NULL;
|
|
|
|
/* We're going to prohibit the deletion of any drives */
|
|
pch = szName;
|
|
if(*pch == 'x' || *pch == 'X')
|
|
++pch;
|
|
if(pch[1] == ':' && pch[2] == '\\' && pch[3] == 0)
|
|
/* Looks like a drive name */
|
|
return XBDM_CANNOTACCESS;
|
|
|
|
st = FCreateFile(&h, DELETE | SYNCHRONIZE, szName, NULL, 0, 0,
|
|
FILE_OPEN, fIsDirectory ?
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE :
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
|
|
if(NT_SUCCESS(st)) {
|
|
RtlZeroMemory(&fdi, sizeof fdi);
|
|
fdi.DeleteFile = TRUE;
|
|
st = NtSetInformationFile(h, &iosb, &fdi, sizeof fdi,
|
|
FileDispositionInformation);
|
|
if(NT_SUCCESS(st))
|
|
hr = XBDM_NOERR;
|
|
else
|
|
hr = HrFromStatus(st, XBDM_CANNOTACCESS);
|
|
NtClose(h);
|
|
} else {
|
|
switch(st) {
|
|
case STATUS_NOT_A_DIRECTORY :
|
|
sprintf(szResp, "not a directory");
|
|
hr = XBDM_CANNOTACCESS;
|
|
break;
|
|
case STATUS_FILE_IS_A_DIRECTORY:
|
|
sprintf(szResp, "is a directory");
|
|
hr = XBDM_CANNOTACCESS;
|
|
break;
|
|
default:
|
|
hr = HrFromStatus(st, XBDM_CANNOTACCESS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrReportDirList(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
NTSTATUS st;
|
|
IO_STATUS_BLOCK iosb;
|
|
struct
|
|
{
|
|
FILE_DIRECTORY_INFORMATION fna;
|
|
OCHAR ozName[MAX_OBJ_PATH+1];
|
|
} fna;
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
HRESULT hr;
|
|
LPSTR szResp;
|
|
|
|
st = NtQueryDirectoryFile(pccs->h, NULL, NULL, NULL, &iosb,
|
|
&fna.fna, sizeof fna, FileDirectoryInformation,
|
|
NULL, (BOOLEAN)(pccs->i == 0));
|
|
if(pdmcc->BytesRemaining && NT_SUCCESS(st))
|
|
{
|
|
ULONG ich;
|
|
|
|
++pccs->i;
|
|
|
|
fna.fna.FileNameLength /= sizeof(OCHAR); // from bytes to cwch
|
|
if(fna.fna.FileNameLength >= pdmcc->BufferSize)
|
|
fna.fna.FileNameLength = pdmcc->BufferSize - 1;
|
|
strcpy(pdmcc->Buffer, "name=\"");
|
|
for(szResp = pdmcc->Buffer; *szResp; ++szResp);
|
|
for(ich = 0; ich < fna.fna.FileNameLength; ++ich)
|
|
szResp[ich] = (char)fna.fna.FileName[ich];
|
|
szResp[ich++] = '"';
|
|
szResp[ich++] = ' ';
|
|
GetFileAttrSz(szResp + ich, fna.fna.FileAttributes,
|
|
&fna.fna.LastWriteTime, &fna.fna.CreationTime, &fna.fna.EndOfFile);
|
|
hr = XBDM_NOERR;
|
|
} else {
|
|
NtClose(pccs->h);
|
|
DmFreePool(pdmcc->Buffer);
|
|
hr = XBDM_ENDOFLIST;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrGetDirList(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
NTSTATUS st;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
CCS *pccs;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
pccs->i = 0;
|
|
pdmcc->BufferSize = 512;
|
|
pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize);
|
|
if(!pdmcc->Buffer)
|
|
/* We can't fall back on the default 256-byte buffer in this case */
|
|
return E_OUTOFMEMORY;
|
|
pdmcc->HandlingFunction = HrReportDirList;
|
|
pdmcc->DataSize = -1;
|
|
|
|
st = FCreateFile(&pccs->h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szName, NULL, 0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
return NT_SUCCESS(st) ? XBDM_MULTIRESPONSE : HrFromStatus(st,
|
|
XBDM_CANNOTACCESS);
|
|
}
|
|
|
|
HRESULT HrMkdir(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
NTSTATUS st;
|
|
HANDLE h;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
st = FCreateFile(&h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szName,
|
|
NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE, FILE_DIRECTORY_FILE |
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if(NT_SUCCESS(st)) {
|
|
NtClose(h);
|
|
return XBDM_NOERR;
|
|
} else if(st == STATUS_OBJECT_NAME_COLLISION)
|
|
return XBDM_ALREADYEXISTS;
|
|
else
|
|
return HrFromStatus(st, XBDM_CANNOTCREATE);
|
|
}
|
|
|
|
HRESULT HrFormatFAT(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
DWORD dwPartition;
|
|
OCHAR szPartition[MAX_OBJ_PATH + 1];
|
|
OBJECT_STRING ost;
|
|
|
|
if(dwExecState != DMN_EXEC_PENDING) {
|
|
strcpy(szResp, "not stopped");
|
|
return E_FAIL;
|
|
}
|
|
if(!FGetNamedDwParam(sz, "partition", &dwPartition, szResp))
|
|
return E_FAIL;
|
|
if(dwPartition < 1 || dwPartition > 5)
|
|
return XBDM_NOSUCHFILE;
|
|
|
|
sprintf(szPartition, "\\Device\\Harddisk0\\Partition%d", dwPartition);
|
|
RtlInitObjectString(&ost, szPartition);
|
|
if(XapiFormatFATVolume(&ost))
|
|
return XBDM_NOERR;
|
|
|
|
strcpy(szResp, "unable to format");
|
|
return XBDM_CANNOTACCESS;
|
|
}
|
|
|
|
HRESULT DmSetTitle(LPCSTR szDir, LPCSTR szTitle, LPCSTR pchCmdLine)
|
|
{
|
|
if (!szTitle)
|
|
return E_INVALIDARG;
|
|
|
|
if(pchCmdLine) {
|
|
KIRQL irql = KeRaiseIrqlToDpcLevel();
|
|
if(pszCmdLine)
|
|
DmFreePool(pszCmdLine);
|
|
pszCmdLine = DmAllocatePool(sizeof(LD_FROM_DEBUGGER_CMDLINE));
|
|
if(pszCmdLine)
|
|
strcpy(pszCmdLine, pchCmdLine);
|
|
KeLowerIrql(irql);
|
|
if(!pszCmdLine)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if(!FTitleExists(szDir, szTitle, NULL, FALSE))
|
|
return XBDM_CANNOTACCESS;
|
|
if(szDir)
|
|
strcpy(rgchTitleDir, szDir);
|
|
else
|
|
rgchTitleDir[0] = 0;
|
|
strcpy(rgchTitleName, szTitle);
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrSetTitle(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
char szNewDir[sizeof rgchTitleDir];
|
|
char szNewName[sizeof rgchTitleName];
|
|
char *pszDir;
|
|
|
|
pszDir = FGetSzParam(sz, "dir", szNewDir, sizeof szNewDir) ?
|
|
szNewDir : NULL;
|
|
if(!FGetSzParam(sz, "name", szNewName, sizeof szNewName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
return DmSetTitle(pszDir, szNewName, PchGetParam(sz, "cmdline", TRUE));
|
|
}
|
|
|
|
HRESULT HrSetDefaultTitle(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
char szLaunchData[MAX_LAUNCH_PATH + 1];
|
|
char szLaunchDir[MAX_PATH + 1];
|
|
NTSTATUS st;
|
|
HANDLE h;
|
|
IO_STATUS_BLOCK iosb;
|
|
HRESULT hr;
|
|
char *pch;
|
|
OBJECT_STRING ost;
|
|
OBJECT_ATTRIBUTES oa;
|
|
FILE_DISPOSITION_INFORMATION fdi;
|
|
|
|
// Make sure dashboard.xbx file isn't read-only
|
|
InitializeObjectAttributes(&oa, &ostDash, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
st = NtOpenFile(&h, FILE_WRITE_ATTRIBUTES|SYNCHRONIZE, &oa, &iosb, 0,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
if (NT_SUCCESS(st)) {
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
RtlZeroMemory(&BasicInfo,sizeof(BasicInfo));
|
|
BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
NtSetInformationFile(h, &iosb, &BasicInfo, sizeof(BasicInfo), FileBasicInformation);
|
|
NtClose(h);
|
|
}
|
|
|
|
if(PchGetParam(sz, "none", FALSE)) {
|
|
// Set the default title to be the dashboard
|
|
InitializeObjectAttributes(&oa, &ostDash, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
st = NtOpenFile(&h, DELETE | SYNCHRONIZE, &oa, &iosb, 0,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if(NT_SUCCESS(st)) {
|
|
RtlZeroMemory(&fdi, sizeof fdi);
|
|
fdi.DeleteFile = TRUE;
|
|
st = NtSetInformationFile(h, &iosb, &fdi, sizeof fdi,
|
|
FileDispositionInformation);
|
|
hr = NT_SUCCESS(st) ? XBDM_NOERR : E_UNEXPECTED;
|
|
NtClose(h);
|
|
} else
|
|
hr = XBDM_NOERR;
|
|
return hr;
|
|
}
|
|
|
|
RtlZeroMemory(szLaunchData, sizeof szLaunchData);
|
|
|
|
if (PchGetParam(sz, "launcher", FALSE)) {
|
|
// Set the default title to be the xdk launcher
|
|
strcpy(szLaunchData, XdkLauncherPathname);
|
|
pch = strrchr(szLaunchData, '\\');
|
|
ASSERT(pch != NULL);
|
|
pch += 1;
|
|
} else {
|
|
// Set the default title to be the specified XBE
|
|
if(!FGetSzParam(sz, "dir", szLaunchDir, sizeof szLaunchDir - 1)) {
|
|
strcpy(szResp, "missing dir");
|
|
return E_FAIL;
|
|
}
|
|
if(!FFileNameToObName(szLaunchDir, szLaunchData, sizeof szLaunchData - 1))
|
|
return XBDM_NOSUCHFILE;
|
|
for(pch = szLaunchData; *pch; ++pch);
|
|
if(pch[-1] != '\\')
|
|
*pch++ = '\\';
|
|
if(!FGetSzParam(sz, "name", pch, szLaunchData + (sizeof szLaunchData - 1) - pch))
|
|
{
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
/* Verify the file exists */
|
|
RtlInitObjectString(&ost, szLaunchData);
|
|
InitializeObjectAttributes(&oa, &ost, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
st = NtOpenFile(&h, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if(!NT_SUCCESS(st))
|
|
return HrFromStatus(st, XBDM_CANNOTACCESS);
|
|
NtClose(h);
|
|
|
|
/* We've found the file, so now write out the name */
|
|
pch[-1] = TITLE_PATH_DELIMITER;
|
|
InitializeObjectAttributes(&oa, &ostDash, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
st = NtCreateFile(&h, GENERIC_WRITE | DELETE | SYNCHRONIZE,
|
|
&oa, &iosb, NULL, 0, 0, FILE_OVERWRITE_IF,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if(NT_SUCCESS(st)) {
|
|
st = NtWriteFile(h, NULL, NULL, NULL, &iosb, szLaunchData, sizeof
|
|
szLaunchData - 1, NULL);
|
|
if(!NT_SUCCESS(st)) {
|
|
/* Can't write, so try to delete */
|
|
hr = HrFromStatus(st, E_UNEXPECTED);
|
|
RtlZeroMemory(&fdi, sizeof fdi);
|
|
fdi.DeleteFile = TRUE;
|
|
st = NtSetInformationFile(h, &iosb, &fdi, sizeof fdi,
|
|
FileDispositionInformation);
|
|
hr = NT_SUCCESS(st) ? XBDM_NOERR : E_UNEXPECTED;
|
|
} else
|
|
hr = XBDM_NOERR;
|
|
NtClose(h);
|
|
} else
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrReportMemory(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
/* We print 32 bytes per line, which takes 64 characters + 2 newline chars
|
|
* (66 chars) to constitute a line */
|
|
DWORD cb;
|
|
DWORD dwPageBase;
|
|
BYTE b;
|
|
DWORD ibLim = 32;
|
|
DWORD ib;
|
|
LPSTR sz = pdmcc->Buffer;
|
|
BOOL fPageValid;
|
|
|
|
if(!pdmcc->BytesRemaining) {
|
|
/* No more data */
|
|
if(pdmcc->Buffer && pdmcc->BufferSize == 1056) {
|
|
DmFreePool(pdmcc->Buffer);
|
|
pdmcc->Buffer = NULL;
|
|
}
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
cb = pdmcc->BufferSize / 66;
|
|
if(cb > pdmcc->BytesRemaining)
|
|
cb = pdmcc->BytesRemaining;
|
|
dwPageBase = (DWORD)pccs->pb + 0x1000;
|
|
pdmcc->BytesRemaining -= cb;
|
|
|
|
while(cb) {
|
|
ibLim = 32;
|
|
|
|
if(cb < ibLim)
|
|
ibLim = cb;
|
|
for(ib = 0; ib < ibLim; ++ib, ++pccs->pb) {
|
|
if(((DWORD)pccs->pb ^ dwPageBase) & 0xfffff000) {
|
|
dwPageBase = (DWORD)pccs->pb & 0xfffff000;
|
|
fPageValid = MmIsAddressValid(pccs->pb);
|
|
}
|
|
if(fPageValid)
|
|
/* If this address has an associated breakpoint, then the
|
|
* memory we want isn't the int 3, but the breakpoint data */
|
|
fPageValid = FGetMemory(pccs->pb, &b);
|
|
if(fPageValid) {
|
|
sz[2*ib] = rgchHex[(b >> 4) & 0xf];
|
|
sz[2*ib+1] = rgchHex[b & 0xf];
|
|
} else
|
|
sz[2*ib] = sz[2*ib+1] = '?';
|
|
}
|
|
sz[2*ib] = '\015';
|
|
sz[2*ib+1] = '\012';
|
|
sz += 2*ib + 2;
|
|
cb -= ibLim;
|
|
}
|
|
|
|
pdmcc->DataSize = sz - (LPSTR)pdmcc->Buffer;
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrGetMemory(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
if(!FGetNamedDwParam(sz, "addr", (DWORD *)&pccs->pb, szResp))
|
|
return E_FAIL;
|
|
if(!FGetNamedDwParam(sz, "length", &pdmcc->BytesRemaining, szResp))
|
|
return E_FAIL;
|
|
|
|
strcpy(szResp, "memory data follows");
|
|
pdmcc->HandlingFunction = HrReportMemory;
|
|
/* We'd like a ~1k buffer for sending data, but if we can't find the
|
|
* memory, we'll just get the default buffer */
|
|
pdmcc->BufferSize = 1056;
|
|
pdmcc->Buffer = DmAllocatePoolWithTag(pdmcc->BufferSize, 'mdbX');
|
|
return XBDM_MULTIRESPONSE;
|
|
}
|
|
|
|
HRESULT HrSetMemory(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
int i;
|
|
char rgch[5];
|
|
BYTE b;
|
|
DWORD dwPageBase;
|
|
BOOL fPageValid;
|
|
int cb = 0;
|
|
LPCSTR szData;
|
|
BYTE *pb;
|
|
|
|
if(!FGetNamedDwParam(sz, "addr", (DWORD *)&pb, szResp))
|
|
return E_FAIL;
|
|
szData = PchGetParam(sz, "data", FALSE);
|
|
dwPageBase = (DWORD)pb + 0x1000;
|
|
|
|
rgch[0] = '0';
|
|
rgch[1] = 'x';
|
|
rgch[4] = 0;
|
|
|
|
if(szData) {
|
|
for(;;++cb, ++pb) {
|
|
if(FIsSpace(*szData))
|
|
break;
|
|
for(i = 0; i < 2; ++i) {
|
|
if(!(*szData >= '0' && *szData <= '9' ||
|
|
*szData >= 'a' && *szData <= 'f' ||
|
|
*szData >= 'A' && *szData <= 'F')) {
|
|
strcpy(szResp, "400- syntax error in data");
|
|
return E_FAIL;
|
|
}
|
|
rgch[2+i] = *szData++;
|
|
}
|
|
b = (BYTE)DwFromSz(rgch, NULL);
|
|
if(((DWORD)pb ^ dwPageBase) & 0xfffff000) {
|
|
dwPageBase = (DWORD)pb & 0xfffff000;
|
|
fPageValid = MmIsAddressValid(pb);
|
|
}
|
|
if(fPageValid)
|
|
/* If there's a breakpoint at this address, we need to set
|
|
* the breakpoint data, not change the int 3 */
|
|
fPageValid = FSetMemory(pb, b);
|
|
if(!fPageValid) {
|
|
sprintf(szResp, "set %d bytes", cb);
|
|
return XBDM_MEMUNMAPPED;
|
|
}
|
|
}
|
|
}
|
|
sprintf(szResp, "set %d bytes", cb);
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrReportThreads(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
int ich;
|
|
|
|
if(!pdmcc->BytesRemaining || !pccs->i) {
|
|
DmFreePool(pccs->h);
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
/* Pack as many thread ids into the buffer as we can */
|
|
for(ich = 0; (DWORD)ich < pdmcc->BufferSize - 10 && pccs->i; ) {
|
|
sprintf((LPSTR)pdmcc->Buffer + ich, "%d\015\012",
|
|
((LPDWORD)pccs->h)[--pccs->i]);
|
|
while(((LPSTR)pdmcc->Buffer)[ich])
|
|
++ich;
|
|
}
|
|
|
|
pdmcc->DataSize = ich;
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrListThreads(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs;
|
|
HRESULT hr;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
/* Make a list that we think will be big enough */
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
pccs->h = DmAllocatePoolWithTag(256 * sizeof(DWORD), 'mdbX');
|
|
if(!pccs->h)
|
|
return E_OUTOFMEMORY;
|
|
pccs->i = 256;
|
|
hr = DmGetThreadList((LPDWORD)pccs->h, (LPDWORD)&pccs->i);
|
|
if(FAILED(hr)) {
|
|
if(hr == XBDM_BUFFER_TOO_SMALL)
|
|
hr = E_UNEXPECTED;
|
|
DmFreePool(pccs->h);
|
|
} else {
|
|
hr = XBDM_MULTIRESPONSE;
|
|
strcpy(szResp, "thread list follows");
|
|
}
|
|
pdmcc->HandlingFunction = HrReportThreads;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrHaltThread(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
DWORD tid;
|
|
|
|
if(!FGetDwParam(sz, "thread", &tid))
|
|
tid = 0;
|
|
return DmHaltThread(tid);
|
|
}
|
|
|
|
HRESULT HrContinueThread(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
DWORD tid;
|
|
|
|
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
|
|
return E_FAIL;
|
|
return DmContinueThread(tid, (BOOL)PchGetParam(sz, "exception", FALSE));
|
|
}
|
|
|
|
HRESULT HrSuspendThread(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
DWORD tid;
|
|
|
|
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
|
|
return E_FAIL;
|
|
return DmSuspendThread(tid);
|
|
}
|
|
|
|
HRESULT HrResumeThread(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
DWORD tid;
|
|
|
|
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
|
|
return E_FAIL;
|
|
return DmResumeThread(tid);
|
|
}
|
|
|
|
HRESULT HrFunctionCall(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
DWORD tid;
|
|
|
|
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
|
|
return E_FAIL;
|
|
return DmSetupFunctionCall(tid);
|
|
}
|
|
|
|
typedef struct _CREG {
|
|
char *szName;
|
|
DWORD dwOffset;
|
|
DWORD cb;
|
|
} CREG;
|
|
|
|
#define CtxOffset(field) ((DWORD)(&((PCONTEXT)0)->field))
|
|
#define Creg(reg) { #reg, CtxOffset(reg), sizeof ((PCONTEXT)0)->reg }
|
|
#define FpCreg(reg) { #reg, CtxOffset(FloatSave.reg), \
|
|
sizeof ((PCONTEXT)0)->FloatSave.reg }
|
|
|
|
static CREG rgcregCtrl[] = {
|
|
Creg(Ebp), Creg(Esp), Creg(Eip), Creg(EFlags)
|
|
};
|
|
|
|
static CREG rgcregInt[] = {
|
|
Creg(Eax), Creg(Ebx), Creg(Ecx), Creg(Edx), Creg(Edi), Creg(Esi)
|
|
};
|
|
|
|
static CREG rgcregFPCtrl[] = {
|
|
FpCreg(Cr0NpxState)
|
|
};
|
|
|
|
typedef struct _RCC {
|
|
CONTEXT cr;
|
|
union {
|
|
struct {
|
|
int irgcreg;
|
|
CREG *pcreg;
|
|
int icreg;
|
|
CREG *pcregNext;
|
|
};
|
|
struct {
|
|
DWORD tid;
|
|
BOOL fSend;
|
|
int iSend;
|
|
int ib;
|
|
};
|
|
};
|
|
} RCC;
|
|
|
|
CREG *PcregNext(RCC *prcc)
|
|
{
|
|
while(!prcc->icreg) {
|
|
/* Time to switch to the next register set */
|
|
switch(prcc->irgcreg) {
|
|
case 0:
|
|
if((prcc->cr.ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
|
|
prcc->pcreg = rgcregCtrl;
|
|
prcc->icreg = sizeof rgcregCtrl / sizeof(CREG);
|
|
} else
|
|
prcc->icreg = 0;
|
|
++prcc->irgcreg;
|
|
break;
|
|
case 1:
|
|
if((prcc->cr.ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
|
|
prcc->pcreg = rgcregInt;
|
|
prcc->icreg = sizeof rgcregInt / sizeof(CREG);
|
|
} else
|
|
prcc->icreg = 0;
|
|
++prcc->irgcreg;
|
|
break;
|
|
case 2:
|
|
if((prcc->cr.ContextFlags & CONTEXT_FLOATING_POINT) ==
|
|
CONTEXT_FLOATING_POINT)
|
|
{
|
|
prcc->pcreg = rgcregFPCtrl;
|
|
prcc->icreg = sizeof rgcregFPCtrl / sizeof(CREG);
|
|
} else
|
|
prcc->icreg = 0;
|
|
++prcc->irgcreg;
|
|
break;
|
|
default:
|
|
/* Nothing left */
|
|
return prcc->pcregNext = NULL;
|
|
}
|
|
}
|
|
--prcc->icreg;
|
|
return prcc->pcregNext = prcc->pcreg++;
|
|
}
|
|
|
|
#define SIZE_OF_EXT_REGISTERS 0x200
|
|
|
|
HRESULT HrDoExtContext(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
RCC *prcc = (RCC *)pdmcc->CustomData;
|
|
HRESULT hr = XBDM_NOERR;
|
|
BOOL fDone = FALSE;
|
|
|
|
if(prcc->fSend) {
|
|
switch(prcc->iSend++) {
|
|
case 0:
|
|
pdmcc->DataSize = sizeof(DWORD);
|
|
memcpy(pdmcc->Buffer, &pdmcc->BytesRemaining, pdmcc->DataSize);
|
|
break;
|
|
case 1:
|
|
pdmcc->Buffer = &prcc->cr.FloatSave;
|
|
pdmcc->DataSize = pdmcc->BytesRemaining;
|
|
break;
|
|
default:
|
|
pdmcc->BytesRemaining = 0;
|
|
break;
|
|
}
|
|
if(!pdmcc->BytesRemaining) {
|
|
fDone = TRUE;
|
|
hr = XBDM_ENDOFLIST;
|
|
}
|
|
} else if(pdmcc->DataSize != 0) {
|
|
DWORD cbCopy = SIZE_OF_EXT_REGISTERS - prcc->ib;
|
|
if(cbCopy > pdmcc->DataSize)
|
|
cbCopy = pdmcc->DataSize;
|
|
if(cbCopy)
|
|
memcpy((BYTE *)&prcc->cr.FloatSave + prcc->ib, pdmcc->Buffer,
|
|
cbCopy);
|
|
pdmcc->BytesRemaining -= pdmcc->DataSize;
|
|
prcc->ib += cbCopy;
|
|
if(!pdmcc->BytesRemaining) {
|
|
fDone = TRUE;
|
|
prcc->cr.ContextFlags = CONTEXT_EXTENDED_REGISTERS;
|
|
hr = DmSetThreadContext(prcc->tid, &prcc->cr);
|
|
} else
|
|
hr = XBDM_NOERR;
|
|
} else
|
|
fDone = TRUE;
|
|
|
|
if(fDone)
|
|
DmFreePool(prcc);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrGetExtContext(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
RCC *prcc;
|
|
CONTEXT cr;
|
|
HRESULT hr;
|
|
DWORD tid;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
|
|
return E_FAIL;
|
|
cr.ContextFlags = CONTEXT_EXTENDED_REGISTERS;
|
|
hr = DmGetThreadContext(tid, &cr);
|
|
if(SUCCEEDED(hr)) {
|
|
if((cr.ContextFlags & CONTEXT_EXTENDED_REGISTERS) ==
|
|
CONTEXT_EXTENDED_REGISTERS)
|
|
{
|
|
prcc = DmAllocatePoolWithTag(sizeof *prcc, 'rcmd');
|
|
if(!prcc)
|
|
return E_OUTOFMEMORY;
|
|
memcpy(&prcc->cr, &cr, sizeof cr);
|
|
prcc->iSend = 0;
|
|
prcc->fSend = TRUE;
|
|
pdmcc->CustomData = prcc;
|
|
pdmcc->HandlingFunction = HrDoExtContext;
|
|
ASSERT(SIZE_OF_EXT_REGISTERS <= sizeof prcc->cr.FloatSave);
|
|
pdmcc->BytesRemaining = SIZE_OF_EXT_REGISTERS;
|
|
hr = XBDM_BINRESPONSE;
|
|
} else
|
|
hr = XBDM_NOTSTOPPED;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrReportContext(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
RCC *prcc = (RCC *)pdmcc->CustomData;
|
|
DWORD ich = 0;
|
|
DWORD ichLim = pdmcc->BufferSize - 22;
|
|
CREG *pcreg;
|
|
|
|
if(!pdmcc->BytesRemaining || !(pcreg = prcc->pcregNext)) {
|
|
DmFreePool(prcc);
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
/* Fill as much of the buffer as we can */
|
|
while(pcreg && ich < ichLim) {
|
|
if(pcreg->cb == 4)
|
|
sprintf((char *)pdmcc->Buffer + ich, "%s=0x%08x\015\012",
|
|
pcreg->szName, *(DWORD *)((BYTE *)&prcc->cr + pcreg->dwOffset));
|
|
else
|
|
sprintf((char *)pdmcc->Buffer + ich, "%s=0x%04x\015\012",
|
|
pcreg->szName, *(USHORT *)((BYTE *)&prcc->cr + pcreg->dwOffset));
|
|
while(((char *)pdmcc->Buffer)[ich])
|
|
++ich;
|
|
pcreg = PcregNext(prcc);
|
|
}
|
|
|
|
pdmcc->DataSize = ich;
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrGetContext(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
RCC *prcc;
|
|
CONTEXT cr;
|
|
HRESULT hr;
|
|
DWORD tid;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
|
|
return E_FAIL;
|
|
cr.ContextFlags = 0;
|
|
if(PchGetParam(sz, "control", FALSE))
|
|
cr.ContextFlags |= CONTEXT_CONTROL;
|
|
if(PchGetParam(sz, "int", FALSE))
|
|
cr.ContextFlags |= CONTEXT_INTEGER;
|
|
if(PchGetParam(sz, "full", FALSE))
|
|
cr.ContextFlags |= CONTEXT_FULL;
|
|
if(PchGetParam(sz, "fp", FALSE))
|
|
cr.ContextFlags |= CONTEXT_FLOATING_POINT;
|
|
hr = DmGetThreadContext(tid, &cr);
|
|
if(SUCCEEDED(hr)) {
|
|
prcc = DmAllocatePoolWithTag(sizeof *prcc, 'rcmd');
|
|
if(!prcc)
|
|
return E_OUTOFMEMORY;
|
|
memcpy(&prcc->cr, &cr, sizeof cr);
|
|
prcc->icreg = 0;
|
|
prcc->irgcreg = 0;
|
|
PcregNext(prcc);
|
|
pdmcc->CustomData = prcc;
|
|
pdmcc->HandlingFunction = HrReportContext;
|
|
hr = XBDM_MULTIRESPONSE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrSetContext(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
CONTEXT cr;
|
|
HRESULT hr;
|
|
DWORD dw;
|
|
DWORD tid;
|
|
RCC *prcc;
|
|
DWORD cbExtRegs;
|
|
|
|
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
|
|
return E_FAIL;
|
|
|
|
/* If we're going to set the extended context, make sure we'll be able to
|
|
* do so */
|
|
if(FGetDwParam(sz, "ext", &cbExtRegs)) {
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
pdmcc->BytesRemaining = cbExtRegs;
|
|
prcc = DmAllocatePoolWithTag(sizeof *prcc, 'rcmd');
|
|
if(!prcc)
|
|
return E_OUTOFMEMORY;
|
|
} else
|
|
prcc = NULL;
|
|
|
|
/* We need the current context and then we'll modify the appropriate
|
|
* fields */
|
|
cr.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
|
|
hr = DmGetThreadContext(tid, &cr);
|
|
if(SUCCEEDED(hr)) {
|
|
int icreg;
|
|
int irgcreg;
|
|
CREG *pcreg;
|
|
|
|
for(irgcreg = 0; irgcreg < 2; ++irgcreg) {
|
|
switch(irgcreg) {
|
|
case 0:
|
|
pcreg = rgcregCtrl;
|
|
icreg = sizeof rgcregCtrl / sizeof(CREG);
|
|
break;
|
|
case 1:
|
|
pcreg = rgcregInt;
|
|
icreg = sizeof rgcregInt / sizeof(CREG);
|
|
break;
|
|
case 2:
|
|
pcreg = rgcregFPCtrl;
|
|
icreg = sizeof rgcregFPCtrl / sizeof(CREG);
|
|
break;
|
|
}
|
|
|
|
for(; icreg--; ++pcreg) {
|
|
if(FGetDwParam(sz, pcreg->szName, &dw)) {
|
|
if(pcreg->cb == 4)
|
|
*(DWORD *)((BYTE *)&cr + pcreg->dwOffset) = dw;
|
|
else
|
|
*(USHORT *)((BYTE *)&cr + pcreg->dwOffset) = (USHORT)dw;
|
|
}
|
|
}
|
|
}
|
|
hr = DmSetThreadContext(tid, &cr);
|
|
}
|
|
/* Now see if we want to capture the extended context */
|
|
if(prcc) {
|
|
if(SUCCEEDED(hr)) {
|
|
/* We're not going to be getting the cr0 state when the data
|
|
* comes in, so we'll put it in now */
|
|
prcc->cr.FloatSave.Cr0NpxState = cr.FloatSave.Cr0NpxState;
|
|
pdmcc->CustomData = prcc;
|
|
pdmcc->Buffer = NULL;
|
|
prcc->fSend = FALSE;
|
|
prcc->ib = 0;
|
|
prcc->tid = tid;
|
|
pdmcc->HandlingFunction = HrDoExtContext;
|
|
hr = XBDM_READYFORBIN;
|
|
} else
|
|
DmFreePool(prcc);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void FillSzFromWz(char *sz, int cch, WCHAR *wz, int cwch)
|
|
{
|
|
if(cwch < 0) {
|
|
while(*wz && cch > 1) {
|
|
*sz++ = (char)*wz++;
|
|
--cch;
|
|
}
|
|
} else {
|
|
while(cwch && cch > 1) {
|
|
*sz++ = (char)*wz++;
|
|
--cch;
|
|
--cwch;
|
|
}
|
|
}
|
|
*sz = 0;
|
|
}
|
|
|
|
HRESULT HrReportCounters(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
HRESULT hr;
|
|
DWORD cch;
|
|
DM_COUNTINFO dmci;
|
|
|
|
if(!pdmcc->BytesRemaining) {
|
|
DmCloseCounters(pccs->pdmwc);
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
cch = pdmcc->BufferSize;
|
|
hr = DmWalkPerformanceCounters(&pccs->pdmwc, &dmci);
|
|
if(SUCCEEDED(hr))
|
|
sprintf(pdmcc->Buffer, "name=\"%s\" type=0x%08x", dmci.Name,
|
|
dmci.Type);
|
|
else {
|
|
DmCloseCounters(pccs->pdmwc);
|
|
pdmcc->BytesRemaining = 0;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrListCounters(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs;
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
pccs->pdmwc = NULL;
|
|
pdmcc->HandlingFunction = HrReportCounters;
|
|
return XBDM_MULTIRESPONSE;
|
|
}
|
|
|
|
HRESULT HrReportCounterData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
switch(pdmcc->BytesRemaining++) {
|
|
case 1:
|
|
sprintf(pdmcc->Buffer, "type=0x%08x vallo=0x%08x "
|
|
"valhi=0x%08x ratelo=0x%08x ratehi=0x%08x", pccs->dmcd.CountType,
|
|
pccs->dmcd.CountValue.LowPart, pccs->dmcd.CountValue.HighPart,
|
|
pccs->dmcd.RateValue.LowPart, pccs->dmcd.RateValue.HighPart);
|
|
break;
|
|
default:
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrQueryPerformanceCounter(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs;
|
|
HRESULT hr;
|
|
char szName[256];
|
|
HANDLE h;
|
|
DWORD dwType;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
if(!FGetNamedDwParam(sz, "type", &dwType, szResp))
|
|
return E_FAIL;
|
|
|
|
hr = DmOpenPerformanceCounter(szName, &h);
|
|
if(SUCCEEDED(hr)) {
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
hr = DmQueryPerformanceCounterHandle(h, dwType, &pccs->dmcd);
|
|
NtClose(h);
|
|
}
|
|
if(SUCCEEDED(hr)) {
|
|
pdmcc->HandlingFunction = HrReportCounterData;
|
|
pdmcc->BytesRemaining = 1;
|
|
hr = XBDM_MULTIRESPONSE;
|
|
strcpy(szResp, "counter data follows");
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrReportModules(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
HRESULT hr;
|
|
DMN_MODLOAD dmml;
|
|
|
|
if(!pdmcc->BytesRemaining) {
|
|
DmCloseLoadedModules(pccs->pdmwm);
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
hr = DmWalkLoadedModules(&pccs->pdmwm, &dmml);
|
|
if(SUCCEEDED(hr))
|
|
GetModLoadSz(pdmcc->Buffer, &dmml);
|
|
else {
|
|
DmCloseLoadedModules(pccs->pdmwm);
|
|
pdmcc->BytesRemaining = 0;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrListModules(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs;
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
pccs->pdmwm = NULL;
|
|
pdmcc->HandlingFunction = HrReportModules;
|
|
return XBDM_MULTIRESPONSE;
|
|
}
|
|
|
|
HRESULT HrReportModuleSections(PDM_CMDCONT pdmcc, LPSTR szResponse,
|
|
DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
DMN_SECTIONLOAD dmsl;
|
|
HRESULT hr;
|
|
|
|
if(!pdmcc->BytesRemaining) {
|
|
DmCloseModuleSections(pccs->pdmws);
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
hr = DmWalkModuleSections(&pccs->pdmws, NULL, &dmsl);
|
|
if(SUCCEEDED(hr))
|
|
GetSectLoadSz(pdmcc->Buffer, &dmsl);
|
|
else {
|
|
DmCloseModuleSections(pccs->pdmws);
|
|
pdmcc->BytesRemaining = 0;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrListModuleSections(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
HRESULT hr;
|
|
DMN_SECTIONLOAD dmsl;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
pccs->pdmws = NULL;
|
|
hr = DmWalkModuleSections(&pccs->pdmws, szName, &dmsl);
|
|
if(SUCCEEDED(hr) || hr == XBDM_ENDOFLIST) {
|
|
pdmcc->HandlingFunction = HrReportModuleSections;
|
|
RewindDmws(pccs->pdmws);
|
|
hr = XBDM_MULTIRESPONSE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrDoBreak(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
PVOID pvAddr;
|
|
HRESULT hr;
|
|
BOOL fClear;
|
|
|
|
#if 0
|
|
if(PchGetParam(sz, "list", FALSE)) {
|
|
DoListBreakpoints(isock);
|
|
return;
|
|
}
|
|
#endif
|
|
if(PchGetParam(sz, "now", FALSE)) {
|
|
Sleep(250);
|
|
_asm int 3
|
|
return XBDM_NOERR;
|
|
}
|
|
if(PchGetParam(sz, "start", FALSE)) {
|
|
hr = DmSetInitialBreakpoint();
|
|
if(FAILED(hr))
|
|
strcpy(szResp, "execution not pending");
|
|
return hr;
|
|
}
|
|
if(PchGetParam(sz, "clearall", FALSE)) {
|
|
RemoveAllBreakpoints();
|
|
return XBDM_NOERR;
|
|
}
|
|
fClear = PchGetParam(sz, "clear", FALSE) != 0;
|
|
if(FGetNamedDwParam(sz, "addr", (DWORD *)&pvAddr, szResp)) {
|
|
if(fClear)
|
|
/* We're only removing this breakpoint */
|
|
hr = DmRemoveBreakpoint(pvAddr);
|
|
else
|
|
hr = DmSetBreakpoint(pvAddr);
|
|
} else {
|
|
/* See if we're doing a hw breakpoint */
|
|
DWORD dwType = DMBREAK_NONE;
|
|
DWORD dwSize = 0;
|
|
|
|
if(FGetDwParam(sz, "read", (DWORD *)&pvAddr))
|
|
dwType = DMBREAK_READWRITE;
|
|
else if(FGetDwParam(sz, "write", (DWORD *)&pvAddr))
|
|
dwType = DMBREAK_WRITE;
|
|
else if(FGetDwParam(sz, "execute", (DWORD *)&pvAddr))
|
|
dwType = DMBREAK_EXECUTE;
|
|
if(dwType == DMBREAK_NONE)
|
|
/* Never saw a valid command */
|
|
hr = E_FAIL;
|
|
else if(fClear || FGetNamedDwParam(sz, "size", &dwSize, szResp)) {
|
|
szResp[0] = 0;
|
|
hr = DmSetDataBreakpoint(pvAddr, fClear ? DMBREAK_NONE : dwType,
|
|
dwSize);
|
|
} else
|
|
hr = E_FAIL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrIsBreak(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
PVOID pvAddr;
|
|
DWORD dwType;
|
|
HRESULT hr;
|
|
|
|
if(!FGetNamedDwParam(sz, "addr", (DWORD *)&pvAddr, szResp))
|
|
return E_FAIL;
|
|
|
|
hr = DmIsBreakpoint(pvAddr, &dwType);
|
|
if(SUCCEEDED(hr))
|
|
sprintf(szResp, "type=%d", dwType);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrSetConfig(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
ULONG ulIndex;
|
|
DWORD dwValue;
|
|
|
|
if(!FGetNamedDwParam(sz, "index", &ulIndex, szResp))
|
|
return E_FAIL;
|
|
|
|
if(!FGetNamedDwParam(sz, "value", &dwValue, szResp))
|
|
return E_FAIL;
|
|
|
|
if (!NT_SUCCESS(ExSaveNonVolatileSetting(ulIndex, REG_DWORD, &dwValue, sizeof(DWORD))))
|
|
{
|
|
strcpy(szResp, "setvalue failed");
|
|
return E_FAIL;
|
|
}
|
|
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
#if 0
|
|
void DoListBreakpoints(int isock)
|
|
{
|
|
SOCKET s = rgsockConnect[isock];
|
|
BYTE *pb = (BYTE *)-1;
|
|
char sz[64];
|
|
|
|
PrintSockLine(s, "202- breakpoint list follows");
|
|
while(FGetNextBreakpoint(&pb)) {
|
|
sprintf(sz, "0x%08x", pb);
|
|
PrintSockLine(s, sz);
|
|
}
|
|
PrintSockLine(s, ".");
|
|
}
|
|
#endif
|
|
|
|
HRESULT HrIsStopped(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
HRESULT hr;
|
|
DM_THREADSTOP dmts;
|
|
DWORD tid;
|
|
|
|
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
|
|
return E_FAIL;
|
|
hr = DmIsThreadStopped(tid, &dmts);
|
|
if(SUCCEEDED(hr)) {
|
|
switch(dmts.NotifiedReason) {
|
|
case DM_DEBUGSTR:
|
|
sprintf(szResp, "debugstr thread=%d stop",
|
|
dmts.u.DebugStr.ThreadId);
|
|
break;
|
|
case DM_ASSERT:
|
|
sprintf(szResp, "assert prompt thread=%d stop",
|
|
dmts.u.DebugStr.ThreadId);
|
|
break;
|
|
default:
|
|
if(!FGetNotifySz(dmts.NotifiedReason, (DWORD)&dmts.u, szResp))
|
|
strcpy(szResp, "stopped");
|
|
break;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrReportThreadInfo(PDM_CMDCONT pdmcc, LPSTR szResponse,
|
|
DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
switch(pdmcc->BytesRemaining++) {
|
|
case 1:
|
|
sprintf(pdmcc->Buffer, "suspend=%d priority=%d tlsbase=0x%08x",
|
|
pccs->dmti.SuspendCount, pccs->dmti.Priority, pccs->dmti.TlsBase);
|
|
break;
|
|
default:
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrThreadInfo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
HRESULT hr;
|
|
DWORD tid;
|
|
CCS *pccs;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
|
|
return E_FAIL;
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
hr = DmGetThreadInfo(tid, &pccs->dmti);
|
|
if(SUCCEEDED(hr)) {
|
|
pdmcc->HandlingFunction = HrReportThreadInfo;
|
|
pdmcc->BytesRemaining = 1;
|
|
hr = XBDM_MULTIRESPONSE;
|
|
strcpy(szResp, "thread info follows");
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrReportXapiInfo(PDM_CMDCONT pdmcc, LPSTR szResponse,
|
|
DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
switch(pdmcc->BytesRemaining++) {
|
|
case 1:
|
|
sprintf(pdmcc->Buffer, "lasterr=0x%08x", pccs->dmxd.LastErrorOffset);
|
|
break;
|
|
default:
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrXapiInfo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
HRESULT hr;
|
|
DWORD tid;
|
|
CCS *pccs;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
hr = DmGetXtlData(&pccs->dmxd);
|
|
if(SUCCEEDED(hr)) {
|
|
pdmcc->HandlingFunction = HrReportXapiInfo;
|
|
pdmcc->BytesRemaining = 1;
|
|
hr = XBDM_MULTIRESPONSE;
|
|
strcpy(szResp, "xtl info follows");
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrDoLongName(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
HRESULT hr;
|
|
char szModule[MAX_OBJ_PATH+1];
|
|
|
|
if(!FGetSzParam(sz, "name", szModule, sizeof szModule)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
hr = DmGetModuleLongName(szModule, szResp, &cchResp);
|
|
if(hr == E_FAIL)
|
|
strcpy(szResp, "no long name available");
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrReportXbeInfo(PDM_CMDCONT pdmcc, LPSTR szResponse,
|
|
DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
switch(pccs->iiXbe++) {
|
|
case 0:
|
|
sprintf(pdmcc->Buffer, "timestamp=0x%08x", pccs->pxbe->TimeStamp);
|
|
break;
|
|
case 1:
|
|
sprintf(pdmcc->Buffer, "checksum=0x%08x", pccs->pxbe->CheckSum);
|
|
break;
|
|
case 2:
|
|
/* We're a little sneaky with the name -- we know the buffer we have
|
|
* for the name is big enough, so we slide it over, overwriting the
|
|
* timestamp and checksum values, and insert the "name=" in front */
|
|
memmove(&pccs->pxbe->LaunchPath[5], pccs->pxbe->LaunchPath,
|
|
MAX_PATH + 1);
|
|
memcpy(pccs->pxbe->LaunchPath, "name=", 5);
|
|
pdmcc->Buffer = pccs->pxbe->LaunchPath;
|
|
break;
|
|
default:
|
|
pdmcc->BytesRemaining = 0;
|
|
break;
|
|
}
|
|
|
|
if(!pdmcc->BytesRemaining) {
|
|
DmFreePool(pccs->pxbe);
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrXbeInfo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
char szName[MAX_OBJ_PATH+1];
|
|
char *pszName;
|
|
CCS *pccs;
|
|
HRESULT hr;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
if(PchGetParam(sz, "running", FALSE))
|
|
pszName = NULL;
|
|
else if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
} else
|
|
pszName = szName;
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
pccs->pxbe = DmAllocatePoolWithTag(sizeof(DM_XBE), 'dmbX');
|
|
if(!pccs->pxbe)
|
|
return E_OUTOFMEMORY;
|
|
hr = DmGetXbeInfo(pszName, pccs->pxbe);
|
|
if(FAILED(hr))
|
|
DmFreePool(pccs->pxbe);
|
|
else {
|
|
pdmcc->HandlingFunction = HrReportXbeInfo;
|
|
pdmcc->BytesRemaining = 1;
|
|
pccs->iiXbe = 0;
|
|
hr = XBDM_MULTIRESPONSE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
BOOL DmIsDebuggerPresent(void)
|
|
{
|
|
return g_fDebugging;
|
|
}
|
|
|
|
HRESULT HrConnectDebugger(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
if(g_dmi.Flags & DMIFLAG_RUNSHELL)
|
|
return XBDM_NOTDEBUGGABLE;
|
|
if(PchGetParam(sz, "connect", FALSE)) {
|
|
g_fDebugging = TRUE;
|
|
fAllowKd = FALSE;
|
|
} else if(PchGetParam(sz, "disconnect", FALSE))
|
|
g_fDebugging = FALSE;
|
|
else {
|
|
sprintf(szResp, "no command");
|
|
return E_FAIL;
|
|
}
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrSystemTime(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
LARGE_INTEGER li;
|
|
|
|
KeQuerySystemTime(&li);
|
|
if(li.HighPart > 0x20000000)
|
|
return XBDM_CLOCKNOTSET;
|
|
sprintf(szResp, "high=0x%x low=0x%x", li.HighPart, li.LowPart);
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrGetAltAddr(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
XNADDR xna;
|
|
int err;
|
|
|
|
err = XNetGetTitleXnAddr(&xna);
|
|
if(err == XNET_GET_XNADDR_NONE || err == XNET_GET_XNADDR_PENDING)
|
|
return E_FAIL;
|
|
sprintf(szResp, "addr=0x%08x", ntohl(xna.ina.s_addr));
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrSetSystemTime(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
LARGE_INTEGER li;
|
|
NTSTATUS st;
|
|
DWORD tzflag;
|
|
|
|
if(!FGetDwParam(sz, "clockhi", &li.HighPart) || !FGetDwParam(sz, "clocklo", &li.LowPart))
|
|
return XBDM_CLOCKNOTSET;
|
|
st = NtSetSystemTime(&li, NULL);
|
|
|
|
// Set default timezone and language if necessary
|
|
if (FGetDwParam(sz, "tz", &tzflag)) {
|
|
XBOX_USER_SETTINGS userSettings;
|
|
ULONG type, length;
|
|
st = ExQueryNonVolatileSetting(
|
|
XC_MAX_OS,
|
|
&type,
|
|
&userSettings,
|
|
sizeof(userSettings),
|
|
&length);
|
|
|
|
if (NT_SUCCESS(st)) {
|
|
BOOL isInvalid = FALSE;
|
|
// Default to Pacific time zone if not set
|
|
if (XBOX_INVALID_TIMEZONE_SETTING(userSettings)) {
|
|
userSettings.TimeZoneBias = 480;
|
|
strncpy(userSettings.TimeZoneStdName, "PST", XC_TZNAMELEN);
|
|
strncpy(userSettings.TimeZoneDltName, "PDT", XC_TZNAMELEN);
|
|
userSettings.TimeZoneStdBias = 0;
|
|
userSettings.TimeZoneDltBias = -60;
|
|
userSettings.TimeZoneStdDate.Month = 10;
|
|
userSettings.TimeZoneStdDate.Day = 5;
|
|
userSettings.TimeZoneStdDate.DayOfWeek = 0;
|
|
userSettings.TimeZoneStdDate.Hour = 0;
|
|
userSettings.TimeZoneDltDate.Month = 4;
|
|
userSettings.TimeZoneDltDate.Day = 1;
|
|
userSettings.TimeZoneDltDate.DayOfWeek = 0;
|
|
userSettings.TimeZoneDltDate.Hour = 0;
|
|
isInvalid = TRUE;
|
|
}
|
|
|
|
// Default language to English if not set
|
|
if (userSettings.Language == XC_LANGUAGE_UNKNOWN) {
|
|
userSettings.Language = XC_LANGUAGE_ENGLISH;
|
|
isInvalid = TRUE;
|
|
}
|
|
|
|
if (isInvalid) {
|
|
st = ExSaveNonVolatileSetting(
|
|
XC_MAX_OS,
|
|
REG_BINARY,
|
|
&userSettings,
|
|
sizeof(userSettings));
|
|
}
|
|
}
|
|
}
|
|
|
|
return NT_SUCCESS(st) ? XBDM_NOERR : XBDM_CLOCKNOTSET;
|
|
}
|
|
|
|
HRESULT HrReboot(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
int icst;
|
|
DWORD dw = 0;
|
|
|
|
if(!pdmcc)
|
|
return FALSE;
|
|
icst = (PCST)pdmcc - rgcst;
|
|
PrintSockLine(rgcst[icst].s, "200- OK");
|
|
|
|
if(PchGetParam(sz, "stop", FALSE))
|
|
dw |= DMBOOT_STOP;
|
|
if(PchGetParam(sz, "wait", FALSE))
|
|
dw |= DMBOOT_WAIT;
|
|
if(PchGetParam(sz, "warm", FALSE))
|
|
dw |= DMBOOT_WARM;
|
|
if(PchGetParam(sz, "nodebug", FALSE))
|
|
dw |= DMBOOT_NODEBUG;
|
|
DmReboot(dw);
|
|
return 0;
|
|
}
|
|
|
|
HRESULT HrMagicReboot(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
int icst;
|
|
HANDLE h;
|
|
OBJECT_ATTRIBUTES oa;
|
|
OBJECT_STRING objectName;
|
|
IO_STATUS_BLOCK iosb;
|
|
NTSTATUS st;
|
|
OCHAR oszTitle[MAX_OBJ_PATH+1];
|
|
OCHAR *pochTitle;
|
|
OCHAR *poch;
|
|
char szTitle[MAX_OBJ_PATH+1];
|
|
DWORD dwFlags = DMBOOT_NODEBUG | DMBOOT_WARM;
|
|
|
|
if(!FGetSzParam(sz, "title", szTitle, sizeof szTitle)) {
|
|
strcpy(szResp, "missing title");
|
|
return E_FAIL;
|
|
}
|
|
|
|
if(PchGetParam(sz, "debug", FALSE))
|
|
dwFlags &= ~DMBOOT_NODEBUG;
|
|
|
|
if(!FFileNameToObName(szTitle, oszTitle, sizeof oszTitle / sizeof(OCHAR)))
|
|
return XBDM_NOSUCHFILE;
|
|
|
|
for(poch = oszTitle; *poch; ++poch)
|
|
if(*poch == '\\')
|
|
pochTitle = poch;
|
|
|
|
DmStop();
|
|
/* Verify the file exists */
|
|
RtlInitObjectString(&objectName, oszTitle);
|
|
InitializeObjectAttributes(&oa, &objectName, OBJ_CASE_INSENSITIVE, NULL,
|
|
NULL);
|
|
st = NtOpenFile(&h, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if(NT_SUCCESS(st)) {
|
|
NtClose(h);
|
|
*pochTitle++ = 0;
|
|
st = NT_SUCCESS(XWriteTitleInfoNoReboot(pochTitle, oszTitle, LDT_NONE,
|
|
0, NULL));
|
|
if(NT_SUCCESS(st)) {
|
|
icst = (PCST)pdmcc - rgcst;
|
|
PrintSockLine(rgcst[icst].s, "200- OK");
|
|
DmReboot(dwFlags);
|
|
}
|
|
}
|
|
return HrFromStatus(st, E_FAIL);
|
|
}
|
|
|
|
#include "xprofp.h"
|
|
#define XBOX_CAP_FILENAME "E:\\xboxcap.dat"
|
|
|
|
XProfpFileHeader* CAPGetFileHeader()
|
|
{
|
|
XProfpFileHeader* fileheader = (XProfpFileHeader*)
|
|
MmAllocateSystemMemory(XPROF_FILE_HEADER_SIZE, PAGE_READWRITE);
|
|
|
|
if (fileheader) {
|
|
PLIST_ENTRY list, head;
|
|
ULONG count;
|
|
|
|
memset(fileheader, 0, XPROF_FILE_HEADER_SIZE);
|
|
fileheader->version = XPROF_FILE_VERSION;
|
|
|
|
// save information about currently loaded modules
|
|
head = g_dmi.LoadedModuleList;
|
|
list = head->Flink;
|
|
for (count=0; list != head; list=list->Flink) {
|
|
PLDR_DATA_TABLE_ENTRY entry;
|
|
WCHAR* wstr;
|
|
CHAR* str;
|
|
ULONG len;
|
|
|
|
entry = CONTAINING_RECORD(list, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
fileheader->modules[count].loadaddr = (ULONG) entry->DllBase;
|
|
fileheader->modules[count].size = entry->SizeOfImage;
|
|
|
|
len = entry->BaseDllName.Length / sizeof(WCHAR);
|
|
if (len >= XPROF_MAX_MODULE_NAMELEN-1) {
|
|
// The base DLL name is too long
|
|
// Just ignore this module.
|
|
continue;
|
|
}
|
|
|
|
// Truncate unicode name to ASCII
|
|
wstr = entry->BaseDllName.Buffer;
|
|
str = fileheader->modules[count].name;
|
|
while (len--)
|
|
*str++ = (CHAR) *wstr++;
|
|
|
|
// Null-terminator
|
|
*str = 0;
|
|
count++;
|
|
}
|
|
|
|
fileheader->module_count = count;
|
|
}
|
|
|
|
return fileheader;
|
|
}
|
|
|
|
NTSTATUS CAPWriteFile(XProfpGlobals* xprofData)
|
|
{
|
|
BYTE* bufstart = (BYTE*) xprofData->bufstart;
|
|
BYTE* bufend = (BYTE*) xprofData->bufnext_stopped;
|
|
HANDLE file = NULL;
|
|
XProfpFileHeader* fileheader = NULL;
|
|
ULONG count;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK iostatusBlock;
|
|
FILE_END_OF_FILE_INFORMATION endOfFile;
|
|
FILE_ALLOCATION_INFORMATION allocation;
|
|
|
|
// Sanity check
|
|
if (!bufstart || !bufend || bufend <= bufstart) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// Create the file for writing
|
|
// we're doing sequential writes only and
|
|
// we don't need intermediate buffering
|
|
|
|
count = bufend - bufstart;
|
|
ASSERT(((ULONG_PTR) bufstart % PAGE_SIZE) == 0);
|
|
|
|
status = FCreateFile(
|
|
&file,
|
|
GENERIC_WRITE | SYNCHRONIZE,
|
|
XBOX_CAP_FILENAME,
|
|
NULL,
|
|
0,
|
|
0,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_SYNCHRONOUS_IO_NONALERT |
|
|
FILE_SEQUENTIAL_ONLY |
|
|
FILE_NO_INTERMEDIATE_BUFFERING);
|
|
|
|
if (!NT_SUCCESS(status)) goto exit;
|
|
|
|
// Generate the profile data file header
|
|
fileheader = CAPGetFileHeader();
|
|
if (!fileheader) {
|
|
status = STATUS_NO_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
ASSERT(XPROF_FILE_HEADER_SIZE % PAGE_SIZE == 0);
|
|
status = NtWriteFile(
|
|
file,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&iostatusBlock,
|
|
fileheader,
|
|
XPROF_FILE_HEADER_SIZE,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) goto exit;
|
|
|
|
// Write the profile data out to disk
|
|
// size is rounded up to a multiple of 512
|
|
status = NtWriteFile(
|
|
file,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&iostatusBlock,
|
|
bufstart,
|
|
(count + 511) & ~511,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) goto exit;
|
|
|
|
// Set file size
|
|
count += XPROF_FILE_HEADER_SIZE;
|
|
endOfFile.EndOfFile.QuadPart = count;
|
|
status = NtSetInformationFile(
|
|
file,
|
|
&iostatusBlock,
|
|
&endOfFile,
|
|
sizeof(endOfFile),
|
|
FileEndOfFileInformation);
|
|
|
|
if (!NT_SUCCESS(status)) goto exit;
|
|
|
|
allocation.AllocationSize.QuadPart = count;
|
|
status = NtSetInformationFile(
|
|
file,
|
|
&iostatusBlock,
|
|
&allocation,
|
|
sizeof(allocation),
|
|
FileAllocationInformation);
|
|
|
|
exit:
|
|
if (file) { NtClose(file); }
|
|
if (fileheader) { MmFreeSystemMemory(fileheader, XPROF_FILE_HEADER_SIZE); }
|
|
return status;
|
|
}
|
|
|
|
HRESULT HrCAPControl(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
|
|
|
|
if (PchGetParam(sz, "start", FALSE)) {
|
|
// Start profile data collection
|
|
status = DmCapControl(XPROF_START, 0);
|
|
} else {
|
|
// Stop profile data collection
|
|
status = DmCapControl(XPROF_STOP, 0);
|
|
|
|
// Save the profile data to disk
|
|
if (NT_SUCCESS(status)) {
|
|
XProfpGlobals* xprofData = DmProfileData;
|
|
LONG lock;
|
|
|
|
// Prevent other threads from freeing the profile data buffer
|
|
// while we're trying to write it to disk
|
|
lock = InterlockedCompareExchange(&xprofData->lock, 1, 0);
|
|
|
|
if (lock != 0) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
} else {
|
|
CAPWriteFile(xprofData);
|
|
|
|
// Unlock the profile data buffer
|
|
xprofData->lock = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) return XBDM_NOERR;
|
|
|
|
// Note: should we map error status to HRESULT?
|
|
sprintf(szResp, "error status 0x%08x", status);
|
|
return XBDM_UNDEFINED;
|
|
}
|
|
|
|
HRESULT HrIrtSweep(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
|
|
HANDLE file;
|
|
|
|
#ifdef _XBOX_ENABLE_PROFILING
|
|
#define IRTSWEEP_FILENAME "E:\\irtsweep.dat"
|
|
|
|
status = FCreateFile(
|
|
&file,
|
|
GENERIC_WRITE | SYNCHRONIZE,
|
|
IRTSWEEP_FILENAME,
|
|
NULL,
|
|
0,
|
|
0,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_SYNCHRONOUS_IO_NONALERT |
|
|
FILE_SEQUENTIAL_ONLY |
|
|
FILE_NO_INTERMEDIATE_BUFFERING);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = IrtSweep(file);
|
|
NtClose(file);
|
|
}
|
|
|
|
#endif
|
|
|
|
if (NT_SUCCESS(status)) return XBDM_NOERR;
|
|
sprintf(szResp, "error status 0x%08x", status);
|
|
return XBDM_UNDEFINED;
|
|
}
|
|
|
|
HRESULT HrReceiveKernelImage(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
HRESULT hr = XBDM_NOERR;
|
|
DWORD crc;
|
|
CCS *pccs;
|
|
|
|
pccs = pdmcc->CustomData;
|
|
|
|
if (pdmcc->DataSize) {
|
|
if ( pccs->KernelImage.BytesReceived + pdmcc->DataSize <= \
|
|
pccs->KernelImage.ImageSize) {
|
|
CopyMemory(pccs->KernelImage.ImageBuffer + pccs->KernelImage.BytesReceived, \
|
|
pdmcc->Buffer, pdmcc->DataSize);
|
|
}
|
|
pdmcc->BytesRemaining -= pdmcc->DataSize;
|
|
pccs->KernelImage.BytesReceived += pdmcc->DataSize;
|
|
} else if (pdmcc->BytesRemaining) {
|
|
DmFreePool(pccs->KernelImage.ImageBuffer);
|
|
DmFreePool(pdmcc->Buffer);
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
if (pdmcc->BytesRemaining == 0) {
|
|
crc = Crc32(0xFFFFFFFF, pccs->KernelImage.ImageBuffer, pccs->KernelImage.ImageSize);
|
|
if (pccs->KernelImage.Crc != crc) {
|
|
_snprintf(szResponse, cchResponse, "bad data detected in kernel image");
|
|
hr = E_UNEXPECTED;
|
|
} else {
|
|
_snprintf(szResponse, cchResponse, "flashing...");
|
|
Sleep(250);
|
|
hr = FlashKernelImage(pccs->KernelImage.ImageBuffer,
|
|
pccs->KernelImage.ImageSize, szResponse, cchResponse,
|
|
pccs->KernelImage.IgnoreVersionChecking);
|
|
}
|
|
DmFreePool(pccs->KernelImage.ImageBuffer);
|
|
DmFreePool(pdmcc->Buffer);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrFlashKernelImage(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs;
|
|
SIZE_T ImageSize;
|
|
BOOL IgnoreVersionChecking = FALSE;
|
|
|
|
if (!pdmcc) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
if (!FGetNamedDwParam(sz, "length", &ImageSize, szResp) || \
|
|
!FGetNamedDwParam(sz, "crc", &pccs->KernelImage.Crc, szResp)) {
|
|
_snprintf(szResp, cchResp, "Invalid parameter(s)");
|
|
return E_FAIL;
|
|
}
|
|
|
|
FGetNamedDwParam(sz, "ignoreversionchecking", &IgnoreVersionChecking, szResp);
|
|
pccs->KernelImage.IgnoreVersionChecking = IgnoreVersionChecking;
|
|
|
|
pccs->KernelImage.ImageSize = ImageSize;
|
|
pdmcc->BytesRemaining = ImageSize;
|
|
|
|
if (ImageSize < 0x80000 || (ImageSize & (ImageSize - 1)) != 0) {
|
|
_snprintf(szResp, cchResp, "Invalid kernel image size");
|
|
return E_FAIL;
|
|
}
|
|
|
|
pccs->KernelImage.ImageBuffer = DmAllocatePoolWithTag(ImageSize, 'mdbX');
|
|
|
|
if (!pccs->KernelImage.ImageBuffer) {
|
|
_snprintf(szResp, cchResp, "Not enough memory");
|
|
return HrFromStatus(STATUS_NO_MEMORY, XBDM_UNDEFINED);
|
|
}
|
|
|
|
pdmcc->Buffer = DmAllocatePoolWithTag(0x2000, 'mdbX');
|
|
|
|
if (!pdmcc->Buffer) {
|
|
DmFreePool(pccs->KernelImage.ImageBuffer);
|
|
_snprintf(szResp, cchResp, "Not enough memory");
|
|
return HrFromStatus(STATUS_NO_MEMORY, XBDM_UNDEFINED);
|
|
}
|
|
|
|
pdmcc->BufferSize = 0x2000;
|
|
pdmcc->HandlingFunction = HrReceiveKernelImage;
|
|
pccs->KernelImage.BytesReceived = 0;
|
|
|
|
return XBDM_READYFORBIN;
|
|
}
|
|
|
|
typedef struct _DRIVENAME_CONVERSION_ENTRY
|
|
{
|
|
PCOSTR ObjectName;
|
|
ULONG ObjectNameLength; //Without a terminating NULL
|
|
OCHAR DriveName;
|
|
BOOL Visible;
|
|
} DRIVENAME_TABLE_ENTRY, *PDRIVENAME_TABLE_ENTRY;
|
|
extern DRIVENAME_TABLE_ENTRY DriveNameConversionTable[];
|
|
|
|
HRESULT HrGetDriveList(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
char szName[8];
|
|
OBJECT_STRING objectName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK iosb;
|
|
NTSTATUS status;
|
|
HANDLE h;
|
|
DRIVENAME_TABLE_ENTRY *pdte;
|
|
struct {
|
|
OBJECT_DIRECTORY_INFORMATION odi;
|
|
OCHAR sz[256];
|
|
} odi;
|
|
|
|
RtlInitObjectString(&objectName, "");
|
|
InitializeObjectAttributes(&objectAttributes, &objectName,
|
|
OBJ_CASE_INSENSITIVE, ObDosDevicesDirectory(), NULL);
|
|
|
|
status = NtOpenDirectoryObject(&h, &objectAttributes);
|
|
if(NT_SUCCESS(status)) {
|
|
int i = 0;
|
|
|
|
// Query the directory object to get the list of actual drives
|
|
for(;;) {
|
|
status = NtQueryDirectoryObject(h, &odi, sizeof odi, (i == 0), &i,
|
|
NULL);
|
|
if (!NT_SUCCESS(status))
|
|
break;
|
|
|
|
/* Reject this drive if it's not one character plus a colon */
|
|
if(odi.odi.Name.Length != 2 || odi.odi.Name.Buffer[1] != ':')
|
|
continue;
|
|
|
|
/* Compare each drive in the drivename conversion table to the
|
|
* current enumerated drive. If there is a match, then that drive
|
|
* has been remapped - let the table handling code below deal with
|
|
* it */
|
|
for(pdte = DriveNameConversionTable; pdte->ObjectName; ++pdte) {
|
|
BYTE bMask;
|
|
if(pdte->DriveName < 'A' || (pdte->DriveName & 0xDF) > 'Z')
|
|
bMask = 0xFF;
|
|
else
|
|
bMask = 0xDF;
|
|
/* Compare case-insensitive */
|
|
if (!((odi.odi.Name.Buffer[0] ^ pdte->DriveName) & bMask))
|
|
{
|
|
/* Found a matching entry - ignore the entry; it'll get
|
|
* handled in the table handling code below */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pdte->ObjectName)
|
|
{
|
|
HANDLE hT;
|
|
/* The current enumerated drive does not exist in the table.
|
|
* We'll need to handle it here. Is the drive mounted? */
|
|
odi.odi.Name.Buffer[2] = '\\';
|
|
odi.odi.Name.Length = 3;
|
|
InitializeObjectAttributes(&objectAttributes, &odi.odi.Name,
|
|
OBJ_CASE_INSENSITIVE, ObDosDevicesDirectory(), NULL);
|
|
status = NtOpenFile(&hT, SYNCHRONIZE,&objectAttributes, &iosb,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
*szResp++ = odi.odi.Name.Buffer[0];
|
|
NtClose(hT);
|
|
}
|
|
}
|
|
}
|
|
NtClose(h);
|
|
}
|
|
|
|
// Enumerate through the conversion table
|
|
szName[1] = ':';
|
|
szName[2] = '\\';
|
|
szName[3] = 0;
|
|
for(pdte = DriveNameConversionTable; pdte->ObjectName; ++pdte) {
|
|
if (pdte->Visible)
|
|
{
|
|
// Is the drive mounted?
|
|
szName[0] = pdte->DriveName;
|
|
status = FCreateFile(&h, SYNCHRONIZE, szName, NULL, 0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
*szResp++ = pdte->DriveName;
|
|
NtClose(h);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close off the 'list' string value
|
|
*szResp = 0;
|
|
|
|
// Mark that we've been through here once so that next time through we know we're done
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrReportDriveFreeSpace(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_FS_SIZE_INFORMATION NormalSizeInfo;
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
ULARGE_INTEGER FreeBytesAvailableToCaller;
|
|
ULARGE_INTEGER TotalNumberOfBytes;
|
|
ULARGE_INTEGER BytesPerAllocationUnit;
|
|
|
|
if (pdmcc->BytesRemaining-- == 0)
|
|
{
|
|
// Already sent the data, so close the file, free up our buffer, and return "we're done!"
|
|
NtClose(pccs->h);
|
|
DmFreePool(pdmcc->Buffer);
|
|
pdmcc->Buffer = NULL;
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
// Determine the size parameters of the volume.
|
|
Status = NtQueryVolumeInformationFile(
|
|
pccs->h,
|
|
&IoStatusBlock,
|
|
&NormalSizeInfo,
|
|
sizeof(NormalSizeInfo),
|
|
FileFsSizeInformation
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
sprintf(pdmcc->Buffer, "unknown error (%d)", Status);
|
|
return E_FAIL;
|
|
}
|
|
|
|
BytesPerAllocationUnit.QuadPart = NormalSizeInfo.BytesPerSector * NormalSizeInfo.SectorsPerAllocationUnit;
|
|
FreeBytesAvailableToCaller.QuadPart = BytesPerAllocationUnit.QuadPart * NormalSizeInfo.AvailableAllocationUnits.QuadPart;
|
|
TotalNumberOfBytes.QuadPart = BytesPerAllocationUnit.QuadPart * NormalSizeInfo.TotalAllocationUnits.QuadPart;
|
|
|
|
// Add the results to the result string
|
|
sprintf(pdmcc->Buffer, "freetocallerlo=0x%08x freetocallerhi=0x%08x totalbyteslo=0x%08x totalbyteshi=0x%08x totalfreebyteslo=0x%08x totalfreebyteshi=0x%08x",
|
|
FreeBytesAvailableToCaller.LowPart, FreeBytesAvailableToCaller.HighPart,
|
|
TotalNumberOfBytes.LowPart, TotalNumberOfBytes.HighPart,
|
|
FreeBytesAvailableToCaller.LowPart, FreeBytesAvailableToCaller.HighPart);
|
|
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrGetDriveFreeSpace(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
NTSTATUS st;
|
|
char szName[MAX_OBJ_PATH+1];
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
|
|
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
pdmcc->BufferSize = 512;
|
|
pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize);
|
|
pdmcc->HandlingFunction = HrReportDriveFreeSpace;
|
|
pdmcc->BytesRemaining = 1; // Internal counter
|
|
st = FCreateFile(&pccs->h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szName, NULL, 0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
return NT_SUCCESS(st) ? XBDM_MULTIRESPONSE : HrFromStatus(st,
|
|
XBDM_CANNOTACCESS);
|
|
}
|
|
|
|
HRESULT HrSendFrameData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
// end of data?
|
|
if (pdmcc->BytesRemaining <= 0) {
|
|
DmFreePool(pdmcc->Buffer);
|
|
pdmcc->Buffer = NULL;
|
|
|
|
// We've finished sending the data, so start the threads going again
|
|
DmGo();
|
|
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
if(pccs->Screenshot.fFirstTime == FALSE) {
|
|
/* First we need to send the various parameters (pitch, format, buffer size, etc) */
|
|
sprintf(pdmcc->Buffer, "pitch=0x%08x width=0x%08x height=0x%08x format=0x%08x, framebuffersize=0x%08x",
|
|
pccs->Screenshot.Pitch, pccs->Screenshot.Width, pccs->Screenshot.Height, pccs->Screenshot.Format, pdmcc->BytesRemaining);
|
|
pdmcc->DataSize = -1;
|
|
pccs->Screenshot.fFirstTime = TRUE;
|
|
} else {
|
|
// if (buffersize%8k != 0) need to send less data
|
|
DWORD cBytesToSend = min (pdmcc->BytesRemaining, pdmcc->BufferSize);
|
|
// send 8k of data
|
|
memcpy(pdmcc->Buffer, pccs->Screenshot.pbyFrameBuffer, cBytesToSend);
|
|
pdmcc->DataSize = cBytesToSend;
|
|
pdmcc->BytesRemaining -= cBytesToSend;
|
|
|
|
// point at next 8k of data
|
|
pccs->Screenshot.pbyFrameBuffer += cBytesToSend;
|
|
}
|
|
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrScreenshot(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
D3DSurface d3dsurface;
|
|
int width, height, pitch, bitdepth, format;
|
|
DWORD start;
|
|
|
|
if(!g_dmgd.Surface) {
|
|
strcpy(szResp, "can't find screen buffer");
|
|
return E_FAIL;
|
|
}
|
|
|
|
//pdmgd = DmGetCurrentValue((ULONG)((BYTE*)&dm.D3DDriverData - (BYTE*)&dm));
|
|
d3dsurface = *(g_dmgd.Surface);
|
|
|
|
// Stop all threads so that no more Presents are added to the queue.
|
|
DmStop();
|
|
|
|
// Busy-wait until all previously queues Presents are processed by the GPU
|
|
start = NtGetTickCount();
|
|
while( *(g_dmgd.FlipCounter) != *(g_dmgd.FrameCounter))
|
|
{
|
|
if ( (NtGetTickCount() - start) >= 2000 ) {
|
|
DmGo();
|
|
strcpy(szResp, "can't lock GPU");
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
// Extract size information
|
|
width = (d3dsurface.Size & D3DSIZE_WIDTH_MASK) + 1;
|
|
height = ((d3dsurface.Size & D3DSIZE_HEIGHT_MASK) >> D3DSIZE_HEIGHT_SHIFT) + 1;
|
|
pitch = (((d3dsurface.Size & D3DSIZE_PITCH_MASK) >> D3DSIZE_PITCH_SHIFT) + 1) * D3DTEXTURE_PITCH_ALIGNMENT;
|
|
format = (d3dsurface.Format & D3DFORMAT_FORMAT_MASK) >> D3DFORMAT_FORMAT_SHIFT;
|
|
|
|
switch(format) {
|
|
case D3DFMT_LIN_A8R8G8B8:
|
|
bitdepth = 4;
|
|
break;
|
|
|
|
case D3DFMT_LIN_X8R8G8B8:
|
|
bitdepth = 4;
|
|
break;
|
|
|
|
case D3DFMT_LIN_R5G6B5:
|
|
bitdepth = 2;
|
|
break;
|
|
|
|
case D3DFMT_LIN_X1R5G5B5:
|
|
bitdepth = 2;
|
|
break;
|
|
|
|
default:
|
|
DmGo();
|
|
strcpy(szResp, "Unexpected framebuffer format");
|
|
return E_FAIL;
|
|
}
|
|
|
|
pccs->Screenshot.pbyFrameBuffer = (BYTE *) (d3dsurface.Data | 0xF0000000);
|
|
|
|
pccs->Screenshot.fFirstTime = FALSE;
|
|
pccs->Screenshot.Width = width;
|
|
pccs->Screenshot.Height = height;
|
|
pccs->Screenshot.Pitch = pitch;
|
|
pccs->Screenshot.Format = format;
|
|
|
|
// Set up the transfer buffer
|
|
pdmcc->BufferSize = 8192;
|
|
pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize);
|
|
pdmcc->HandlingFunction = HrSendFrameData;
|
|
pdmcc->BytesRemaining = pitch * height;
|
|
return XBDM_BINRESPONSE;
|
|
}
|
|
|
|
HRESULT HrSendPSSnapshotData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
// end of data?
|
|
if (pdmcc->BytesRemaining <= 0)
|
|
{
|
|
pdmcc->Buffer = NULL;
|
|
*(g_dmgd.pdwOpcode) = PSSNAP_DONE; // signal D3D to release buffer
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
pdmcc->DataSize = 32768;
|
|
pdmcc->BytesRemaining = 0;
|
|
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrPSSnap(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
DWORD dwX, dwY, dwOpcode;
|
|
struct
|
|
{
|
|
DWORD dwX;
|
|
DWORD dwY;
|
|
DWORD dwFlags;
|
|
DWORD dwMarker;
|
|
} args;
|
|
DWORD dwStart;
|
|
|
|
if(!FGetDwParam(sz, "x", &args.dwX))
|
|
{
|
|
strcpy(szResp, "Missing X parameter");
|
|
return E_FAIL;
|
|
}
|
|
if(!FGetDwParam(sz, "y", &args.dwY))
|
|
{
|
|
strcpy(szResp, "Missing Y parameter");
|
|
return E_FAIL;
|
|
}
|
|
|
|
if(!FGetDwParam(sz, "flags", &args.dwFlags))
|
|
args.dwFlags = 0;
|
|
|
|
if(!FGetDwParam(sz, "marker", &args.dwMarker))
|
|
args.dwMarker = 0;
|
|
|
|
if(*(g_dmgd.pdwOpcode))
|
|
{
|
|
sprintf(szResp, "D3D snapshot client in invalid state (%08x)", *(g_dmgd.pdwOpcode));
|
|
return E_FAIL;
|
|
}
|
|
|
|
// build opcode for D3D pixel shader snapshot
|
|
*(g_dmgd.ppSnapshotBuffer) = (BYTE *)(&args);
|
|
*(g_dmgd.pdwOpcode) = PSSNAP_REQUEST;
|
|
|
|
// wait for the opcode to change
|
|
dwStart = NtGetTickCount();
|
|
while(1)
|
|
{
|
|
dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
|
|
if((dwOpcode == PSSNAP_ACK) ||
|
|
(dwOpcode == PSSNAP_ERROR))
|
|
break;
|
|
|
|
Sleep(50);
|
|
if ( (NtGetTickCount() - dwStart) >= 2000 ) {
|
|
strcpy(szResp, "Opcode timeout: Failed to receive acknowledge");
|
|
*(g_dmgd.pdwOpcode) = PSSNAP_IDLE;
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
if(dwOpcode == PSSNAP_ERROR)
|
|
{
|
|
switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
|
|
{
|
|
case 1:
|
|
sprintf(szResp, "The Application is using an unsupported framebuffer format. Only A8R8G8B8 is supported at this time.");
|
|
break;
|
|
|
|
case 2:
|
|
sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
|
|
break;
|
|
|
|
case 3:
|
|
sprintf(szResp, "Debugger wasn't able to allocate memory.");
|
|
break;
|
|
|
|
default:
|
|
sprintf(szResp, "D3D snapshot client returned error %d", *(g_dmgd.pdwOpcode) & 0x00ffffff);
|
|
break;
|
|
}
|
|
|
|
*(g_dmgd.pdwOpcode) = PSSNAP_IDLE;
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Set up the transfer buffer
|
|
pdmcc->BufferSize = 32768;
|
|
pdmcc->Buffer = *(g_dmgd.ppSnapshotBuffer);
|
|
pdmcc->HandlingFunction = HrSendPSSnapshotData;
|
|
pdmcc->BytesRemaining = 32768;
|
|
|
|
return XBDM_BINRESPONSE;
|
|
}
|
|
|
|
HRESULT HrSendVSSnapshotData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
|
|
// end of data?
|
|
if (pdmcc->BytesRemaining <= 0)
|
|
{
|
|
pdmcc->Buffer = NULL;
|
|
*(g_dmgd.pdwOpcode) = VSSNAP_DONE; // signal D3D to release buffer
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
|
|
pdmcc->DataSize = 32768;
|
|
pdmcc->BytesRemaining = 0;
|
|
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrVSSnap(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
DWORD dwOpcode;
|
|
DWORD dwStart;
|
|
struct
|
|
{
|
|
DWORD dwFirst;
|
|
DWORD dwLast;
|
|
DWORD dwFlags;
|
|
DWORD dwMarker;
|
|
} args;
|
|
|
|
if(*(g_dmgd.pdwOpcode))
|
|
{
|
|
sprintf(szResp, "D3D snapshot client in invalid state (%08x)", *(g_dmgd.pdwOpcode));
|
|
return E_FAIL;
|
|
}
|
|
|
|
if(!FGetDwParam(sz, "first", &args.dwFirst))
|
|
{
|
|
strcpy(szResp, sz);
|
|
return E_FAIL;
|
|
}
|
|
if(!FGetDwParam(sz, "last", &args.dwLast))
|
|
{
|
|
strcpy(szResp, "Missing Last parameter");
|
|
return E_FAIL;
|
|
}
|
|
if(!FGetDwParam(sz, "flags", &args.dwFlags))
|
|
args.dwFlags = 0;
|
|
|
|
if(!FGetDwParam(sz, "marker", &args.dwMarker))
|
|
args.dwMarker = 0;
|
|
|
|
if(args.dwFlags & 0x08000000)
|
|
{
|
|
// xray sequence instead
|
|
*(g_dmgd.pdwOpcode) = XRAY_BEGINREQ;
|
|
*(g_dmgd.ppSnapshotBuffer) = (BYTE *)(&args);
|
|
|
|
// wait for the opcode to change
|
|
dwStart = NtGetTickCount();
|
|
while(1)
|
|
{
|
|
dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
|
|
if((dwOpcode == XRAY_BEGINACK) ||
|
|
(dwOpcode == XRAY_ERROR))
|
|
break;
|
|
|
|
Sleep(50);
|
|
if ( (NtGetTickCount() - dwStart) >= 2000 ) {
|
|
sprintf(szResp, "Opcode timeout: Failed to receive acknowledge: %08x", dwOpcode);
|
|
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
if(dwOpcode == XRAY_ERROR)
|
|
{
|
|
switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
|
|
{
|
|
case 2:
|
|
sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
|
|
break;
|
|
|
|
default:
|
|
sprintf(szResp, "D3D snapshot client returned error %d", *(g_dmgd.pdwOpcode) & 0x00ffffff);
|
|
break;
|
|
}
|
|
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
|
|
return E_FAIL;
|
|
}
|
|
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
|
|
return XBDM_NOERR;
|
|
}
|
|
else
|
|
{
|
|
if(args.dwFlags & 0x04000000)
|
|
{
|
|
// end xray sequence instead
|
|
*(g_dmgd.pdwOpcode) = XRAY_ENDREQ;
|
|
|
|
// wait for the opcode to change
|
|
dwStart = NtGetTickCount();
|
|
while(1)
|
|
{
|
|
dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
|
|
if((dwOpcode == XRAY_ENDACK) ||
|
|
(dwOpcode == XRAY_ERROR))
|
|
break;
|
|
|
|
Sleep(50);
|
|
if ( (NtGetTickCount() - dwStart) >= 2000 ) {
|
|
strcpy(szResp, "Opcode timeout: Failed to receive acknowledge");
|
|
*(g_dmgd.pdwOpcode) = VSSNAP_IDLE;
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
if(dwOpcode == XRAY_ERROR)
|
|
{
|
|
switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
|
|
{
|
|
case 2:
|
|
sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
|
|
break;
|
|
|
|
default:
|
|
sprintf(szResp, "D3D snapshot client returned error %d", *(g_dmgd.pdwOpcode) & 0x00ffffff);
|
|
break;
|
|
}
|
|
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
|
|
return E_FAIL;
|
|
}
|
|
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
|
|
return XBDM_NOERR;
|
|
}
|
|
}
|
|
|
|
// build opcode for D3D vertex shader snapshot
|
|
dwOpcode = VSSNAP_REQUEST;
|
|
|
|
*(g_dmgd.ppSnapshotBuffer) = (BYTE *)(&args);
|
|
*(g_dmgd.pdwOpcode) = dwOpcode;
|
|
|
|
// wait for the opcode to change
|
|
dwStart = NtGetTickCount();
|
|
while(1)
|
|
{
|
|
dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
|
|
if((dwOpcode == VSSNAP_ACK) ||
|
|
(dwOpcode == VSSNAP_ERROR))
|
|
break;
|
|
|
|
Sleep(50);
|
|
if ( (NtGetTickCount() - dwStart) >= 2000 ) {
|
|
strcpy(szResp, "Opcode timeout: Failed to receive acknowledge");
|
|
*(g_dmgd.pdwOpcode) = VSSNAP_IDLE;
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
if(dwOpcode == VSSNAP_ERROR)
|
|
{
|
|
switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
|
|
{
|
|
case 2:
|
|
sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
|
|
break;
|
|
|
|
case 3:
|
|
sprintf(szResp, "Debugger wasn't able to allocate memory.");
|
|
break;
|
|
|
|
default:
|
|
sprintf(szResp, "D3D snapshot client returned error %d", *(g_dmgd.pdwOpcode) & 0x00ffffff);
|
|
break;
|
|
}
|
|
*(g_dmgd.pdwOpcode) = VSSNAP_IDLE;
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Set up the transfer buffer
|
|
pdmcc->BufferSize = 32768;
|
|
pdmcc->Buffer = *(g_dmgd.ppSnapshotBuffer);
|
|
pdmcc->HandlingFunction = HrSendVSSnapshotData;
|
|
pdmcc->BytesRemaining = 32768;
|
|
|
|
return XBDM_BINRESPONSE;
|
|
}
|
|
|
|
HRESULT HrSetLockMode(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if(PchGetParam(sz, "unlock", FALSE)) {
|
|
g_fLockLevel = FALSE;
|
|
hr = XBDM_NOERR;
|
|
} else if(FGetQwordParam(sz, "boxid", &g_luBoxId)) {
|
|
g_fLockLevel = TRUE;
|
|
hr = XBDM_NOERR;
|
|
} else {
|
|
/* Potential failure */
|
|
hr = E_FAIL;
|
|
strcpy(szResp, "missing parameters");
|
|
}
|
|
if(PchGetParam(sz, "encrypt", FALSE)) {
|
|
if(g_fLockLevel) {
|
|
g_fLockLevel = 2;
|
|
hr = XBDM_NOERR;
|
|
} else
|
|
/* If unlock was specified, we'll return success as set above;
|
|
* if not, we'll return failure. In either case, hr is already
|
|
* set correctly, so we'll just make sure the error message
|
|
* is sufficient */
|
|
strcpy(szResp, "box not locked; file encryption not enabled");
|
|
}
|
|
|
|
RemoveAllUsers();
|
|
WriteIniFile();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrSetDebugSecureMode(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
int icst;
|
|
|
|
if(!g_fDebugSecureMode)
|
|
return XBDM_INVALIDCMD;
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
icst = (PCST)pdmcc - rgcst;
|
|
rgcst[icst].dmplCur = -1;
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrGetBoxId(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
|
|
{
|
|
if(!g_fLockLevel)
|
|
return XBDM_NOTLOCKED;
|
|
sprintf(szResp, "boxid=0q%08x%08x", g_luBoxId.HighPart, g_luBoxId.LowPart);
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrAuthenticateUser(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
char szName[256];
|
|
ULARGE_INTEGER luResponse;
|
|
int icst;
|
|
BOOL fKeyXchg;
|
|
LPCSTR pszName;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
icst = (PCST)pdmcc - rgcst;
|
|
if((rgcst[icst].dwStatus & CONN_DHKEY) && FGetQwordParam(sz, "passwd",
|
|
&luResponse))
|
|
{
|
|
luResponse.LowPart ^= rgcst[icst].luDHKey.LowPart;
|
|
luResponse.HighPart ^= rgcst[icst].luDHKey.HighPart;
|
|
fKeyXchg = TRUE;
|
|
} else if(!FGetQwordParam(sz, "resp", &luResponse)) {
|
|
noresp:
|
|
strcpy(szResp, "missing response");
|
|
return E_FAIL;
|
|
} else
|
|
fKeyXchg = FALSE;
|
|
|
|
if(PchGetParam(sz, "admin", FALSE)) {
|
|
/* Trying to authenticate using the admin passwd. This requires a
|
|
* valid response */
|
|
if(fKeyXchg)
|
|
goto noresp;
|
|
pszName = NULL;
|
|
} else if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
} else
|
|
pszName = szName;
|
|
|
|
rgcst[icst].dmplCur = DmplAuthenticateUser(pszName,
|
|
&rgcst[icst].luConnectNonce, &luResponse, &fKeyXchg);
|
|
if(!rgcst[icst].dmplCur)
|
|
return fKeyXchg ? XBDM_KEYXCHG : XBDM_CANNOTACCESS;
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrSetAdminPasswd(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
int icst;
|
|
ULARGE_INTEGER luPasswd;
|
|
ULARGE_INTEGER luT;
|
|
PULARGE_INTEGER plu;
|
|
|
|
if(PchGetParam(sz, "none", FALSE)) {
|
|
g_fAdminPasswd = FALSE;
|
|
WriteIniFile();
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
/* We need to establish a key-exchange key. But if we're running from the
|
|
* ini file, we'll just use a key-exchange key of 0 to load in the
|
|
* plaintext password */
|
|
if(!pdmcc) {
|
|
luT.QuadPart = 0;
|
|
plu = &luT;
|
|
} else {
|
|
icst = (PCST)pdmcc - rgcst;
|
|
if(!(rgcst[icst].dwStatus & CONN_DHKEY))
|
|
return XBDM_KEYXCHG;
|
|
plu = &rgcst[icst].luDHKey;
|
|
}
|
|
|
|
if(!FGetQwordParam(sz, "passwd", &luPasswd)) {
|
|
strcpy(szResp, "missing passwd");
|
|
return E_FAIL;
|
|
}
|
|
g_luAdminPasswd.LowPart = luPasswd.LowPart ^ plu->LowPart;
|
|
g_luAdminPasswd.HighPart = luPasswd.HighPart ^ plu->HighPart;
|
|
g_fAdminPasswd = TRUE;
|
|
WriteIniFile();
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrGetUserPriv(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
char szUserName[128];
|
|
DWORD dwPrivileges;
|
|
HRESULT hr;
|
|
int icst;
|
|
|
|
dwPrivileges = DmplOfConnection(pdmcc);
|
|
if(PchGetParam(sz, "me", FALSE)) {
|
|
/* We're interested in the privileges for this connection */
|
|
hr = pdmcc ? XBDM_NOERR : E_FAIL;
|
|
} else if(!(dwPrivileges & DMPL_PRIV_MANAGE))
|
|
hr = XBDM_CANNOTACCESS;
|
|
else if(!FGetSzParam(sz, "name", szUserName, sizeof szUserName)) {
|
|
strcpy(szResp, "missing name");
|
|
hr = E_FAIL;
|
|
} else
|
|
hr = DmGetUserAccess(szUserName, &dwPrivileges);
|
|
|
|
if(SUCCEEDED(hr)) {
|
|
if(dwPrivileges)
|
|
FillAccessPrivSz(szResp-1, dwPrivileges);
|
|
else
|
|
strcpy(szResp, "none");
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrSetUserPriv(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
char szUserName[128];
|
|
DWORD dwPrivileges;
|
|
HRESULT hr;
|
|
|
|
if(!FGetSzParam(sz, "name", szUserName, sizeof szUserName)) {
|
|
strcpy(szResp, "missing name");
|
|
return E_FAIL;
|
|
}
|
|
|
|
dwPrivileges = DmplFromSz(sz);
|
|
return DmSetUserAccess(szUserName, dwPrivileges);
|
|
}
|
|
|
|
HRESULT HrReportUsers(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
HRESULT hr;
|
|
DM_USER dusr;
|
|
|
|
hr = DmWalkUserList(&pccs->pdmwu, &dusr);
|
|
if(!pdmcc->BytesRemaining)
|
|
hr = XBDM_ENDOFLIST;
|
|
else if(SUCCEEDED(hr))
|
|
FillUserInfoSz(pdmcc->Buffer, dusr.UserName, dusr.AccessPrivileges,
|
|
NULL);
|
|
else {
|
|
DmCloseUserList(pccs->pdmwu);
|
|
hr = XBDM_ENDOFLIST;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrListUsers(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs;
|
|
if(!g_fLockLevel)
|
|
return XBDM_NOTLOCKED;
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
|
|
pccs = (CCS *)pdmcc->CustomData;
|
|
pdmcc->HandlingFunction = HrReportUsers;
|
|
pccs->pdmwu = NULL;
|
|
return XBDM_MULTIRESPONSE;
|
|
}
|
|
|
|
HRESULT HrPerformDHExchange(PDM_CMDCONT pdmcc, LPSTR szResponse,
|
|
DWORD cchResponse)
|
|
{
|
|
extern BYTE g_abOakleyGroup1Base[];
|
|
extern BYTE g_abOakleyGroup1Mod[];
|
|
BYTE rgbDHKey[96];
|
|
BYTE rgbY[96];
|
|
int icst;
|
|
HRESULT hr;
|
|
|
|
if(pdmcc->CustomData) {
|
|
/* We're sending, so send all of the data */
|
|
if(pdmcc->BytesRemaining) {
|
|
pdmcc->DataSize = 96;
|
|
pdmcc->BytesRemaining = 0;
|
|
hr = XBDM_NOERR;
|
|
} else {
|
|
DmFreePool(pdmcc->Buffer);
|
|
hr = XBDM_ENDOFLIST;
|
|
}
|
|
} else if(pdmcc->DataSize) {
|
|
/* Grab the data that's here */
|
|
memcpy((PUCHAR)pdmcc->Buffer + 2 * 96 - pdmcc->BytesRemaining,
|
|
pdmcc->Buffer, pdmcc->DataSize);
|
|
pdmcc->BytesRemaining -= pdmcc->DataSize;
|
|
if(!pdmcc->BytesRemaining) {
|
|
/* OK, now we've got g^x at buffer+96. We need to compute y,
|
|
* g^xy, and g^y. First generate y */
|
|
XNetRandom(rgbY, 96);
|
|
/* Now compute g^xy */
|
|
XcModExp((PULONG)rgbDHKey, (PULONG)((PUCHAR)pdmcc->Buffer + 96),
|
|
(PULONG)rgbY, (PULONG)g_abOakleyGroup1Mod,
|
|
96 / 4);
|
|
/* And prepare g^y to be sent */
|
|
XcModExp((PULONG)pdmcc->Buffer, (PULONG)g_abOakleyGroup1Base,
|
|
(PULONG)rgbY, (PULONG)g_abOakleyGroup1Mod,
|
|
96 / 4);
|
|
pdmcc->CustomData = pdmcc->Buffer;
|
|
/* Now that we have g^xy, we can hash it and mark the key
|
|
* exchange complete */
|
|
icst = (PCST)pdmcc - rgcst;
|
|
rgcst[icst].luDHKey.QuadPart = 0;
|
|
XBCHashData(&rgcst[icst].luDHKey, rgbDHKey, sizeof rgbDHKey);
|
|
rgcst[icst].dwStatus |= CONN_DHKEY;
|
|
/* Now get ready to send g^y */
|
|
hr = XBDM_BINRESPONSE;
|
|
} else
|
|
hr = XBDM_NOERR;
|
|
} else {
|
|
/* Connection died on receive */
|
|
DmFreePool(pdmcc->Buffer);
|
|
pdmcc->BytesRemaining = 0;
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrKeyExchange(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
|
|
/* We need to allocate a buffer to build up the remote constant --
|
|
* we can't let it trickle in via the standard buffer */
|
|
pdmcc->BufferSize = 2 * 96;
|
|
pdmcc->Buffer = DmAllocatePoolWithTag(pdmcc->BufferSize, 'HDmd');
|
|
if(!pdmcc->Buffer)
|
|
return E_OUTOFMEMORY;
|
|
|
|
/* Set up to receive the remote constant */
|
|
pdmcc->BytesRemaining = 96;
|
|
pdmcc->HandlingFunction = HrPerformDHExchange;
|
|
pdmcc->CustomData = NULL;
|
|
return XBDM_READYFORBIN;
|
|
}
|
|
|
|
HRESULT HrReportMmGlobal(PDM_CMDCONT pdmcc, LPSTR szResponse,
|
|
DWORD cchResponse)
|
|
{
|
|
extern PMMGLOBALDATA MmGlobalData;
|
|
PMMGLOBALDATA p = MmGlobalData;
|
|
ULONG MmHighestPhysicalPage;
|
|
PCI_SLOT_NUMBER PCISlotNumber;
|
|
MM_STATISTICS mmstat;
|
|
UCHAR MemoryTop;
|
|
PMMPFN MmPfnDatabase;
|
|
HRESULT hr;
|
|
int n;
|
|
|
|
switch (pdmcc->BytesRemaining++) {
|
|
|
|
case 1:
|
|
|
|
//
|
|
// Access the host bridge's configuration space.
|
|
//
|
|
|
|
PCISlotNumber.u.AsULONG = 0;
|
|
PCISlotNumber.u.bits.DeviceNumber = XPCICFG_HOSTBRIDGE_DEVICE_ID;
|
|
PCISlotNumber.u.bits.FunctionNumber = XPCICFG_HOSTBRIDGE_FUNCTION_ID;
|
|
HalReadPCISpace(0, PCISlotNumber.u.AsULONG, CR_CPU_MEMTOP_LIMIT,
|
|
&MemoryTop, sizeof(UCHAR));
|
|
|
|
MmHighestPhysicalPage = (((ULONG)MemoryTop + 1) * 4096) - 1;
|
|
|
|
MmPfnDatabase = (PMMPFN)(MM_SYSTEM_PHYSICAL_MAP +
|
|
(MiGetPdeAddress(MM_SYSTEM_PHYSICAL_MAP)->Hard.PageFrameNumber << PAGE_SHIFT));
|
|
|
|
n = _snprintf(pdmcc->Buffer, pdmcc->BufferSize,
|
|
"MmHighestPhysicalPage=0x%x RetailPfnRegion=0x%x SystemPteRange=0x%x "
|
|
"AvailablePages=0x%x AllocatedPagesByUsage=0x%x PfnDatabase=0x%x",
|
|
MmHighestPhysicalPage, p->RetailPfnRegion, p->SystemPteRange,
|
|
p->AvailablePages, p->AllocatedPagesByUsage, MmPfnDatabase);
|
|
hr = n < 0 ? XBDM_BUFFER_TOO_SMALL : XBDM_NOERR;
|
|
break;
|
|
|
|
case 2:
|
|
mmstat.Length = sizeof(mmstat);
|
|
MmQueryStatistics(&mmstat);
|
|
n = _snprintf(pdmcc->Buffer, pdmcc->BufferSize,
|
|
"AddressSpaceLock=0x%x VadRoot=0x%x VadHint=0x%x VadFreeHint=0x%x "
|
|
"MmNumberOfPhysicalPages=0x%x MmAvailablePages=0x%x",
|
|
p->AddressSpaceLock, p->VadRoot, p->VadHint, p->VadFreeHint,
|
|
mmstat.TotalPhysicalPages, mmstat.AvailablePages);
|
|
|
|
hr = n < 0 ? XBDM_BUFFER_TOO_SMALL : XBDM_NOERR;
|
|
break;
|
|
|
|
default:
|
|
hr = XBDM_ENDOFLIST;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrGetMmGlobal(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
CCS *pccs = (CCS *)pdmcc->CustomData;
|
|
pdmcc->HandlingFunction = HrReportMmGlobal;
|
|
pdmcc->BytesRemaining = 1;
|
|
return XBDM_MULTIRESPONSE;
|
|
}
|
|
|
|
HRESULT HrReturnBuffer(PDM_CMDCONT pdmcc, LPSTR szResponse,
|
|
DWORD cchResponse)
|
|
{
|
|
if(!pdmcc->BytesRemaining) {
|
|
DmFreePool(pdmcc->Buffer);
|
|
return XBDM_ENDOFLIST;
|
|
}
|
|
pdmcc->DataSize = pdmcc->BytesRemaining;
|
|
pdmcc->BytesRemaining = 0;
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
HRESULT HrGetDvdBlk(LPCSTR sz, LPSTR szResp, DWORD cchResp,
|
|
PDM_CMDCONT pdmcc)
|
|
{
|
|
DWORD dwBlk;
|
|
CCS *pccs;
|
|
HANDLE h;
|
|
IO_STATUS_BLOCK iosb;
|
|
OBJECT_ATTRIBUTES oa;
|
|
OBJECT_STRING ost;
|
|
NTSTATUS st;
|
|
LARGE_INTEGER li;
|
|
PDEVICE_OBJECT pdev;
|
|
|
|
if(!pdmcc)
|
|
return E_FAIL;
|
|
|
|
if(!FGetNamedDwParam(sz, "block", &dwBlk, szResp))
|
|
return E_FAIL;
|
|
|
|
RtlInitObjectString(&ost, "\\Device\\Cdrom0");
|
|
st = ObReferenceObjectByName(&ost, 0, IoDeviceObjectType, NULL, &pdev);
|
|
if(!NT_SUCCESS(st))
|
|
return E_FAIL;
|
|
pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize = 2048);
|
|
if(!pdmcc->Buffer) {
|
|
ObDereferenceObject(pdev);
|
|
return E_FAIL;
|
|
}
|
|
li.QuadPart = dwBlk;
|
|
li.QuadPart *= 2048;
|
|
st = IoSynchronousFsdRequest(IRP_MJ_READ, pdev, pdmcc->Buffer,
|
|
2048, &li);
|
|
ObDereferenceObject(pdev);
|
|
if(!NT_SUCCESS(st)) {
|
|
ExFreePool(pdmcc->Buffer);
|
|
return E_FAIL;
|
|
}
|
|
pdmcc->HandlingFunction = HrReturnBuffer;
|
|
pdmcc->BytesRemaining = 2048;
|
|
return XBDM_BINRESPONSE;
|
|
}
|
|
|
|
/* This list must be sorted */
|
|
CHH rgchh[] = {
|
|
{ "adminpw", DMPL_PRIV_MANAGE, HrSetAdminPasswd },
|
|
{ "altaddr", DMPL_PRIV_CONTROL, HrGetAltAddr },
|
|
{ "authuser", 0, HrAuthenticateUser },
|
|
{ "boxid", 0, HrGetBoxId },
|
|
{ "break", DMPL_PRIV_CONTROL, HrDoBreak },
|
|
{ "bye", 0, HrEndConversation, 0, CHH_ANYTHREAD },
|
|
{ "capcontrol", DMPL_PRIV_CONTROL, HrCAPControl },
|
|
{ "continue", DMPL_PRIV_CONTROL, HrContinueThread },
|
|
{ "dbgname", 0, HrSetDbgName },
|
|
{ "debugger", DMPL_PRIV_CONTROL, HrConnectDebugger },
|
|
{ "debugmode", 0, HrSetDebugSecureMode },
|
|
{ "dedicate", 0, HrDedicateConnection, 0, CHH_ANYTHREAD },
|
|
{ "deftitle", DMPL_PRIV_CONTROL, HrSetDefaultTitle },
|
|
{ "delete", 0, HrDeleteFile, DMPL_PRIV_READ | DMPL_PRIV_WRITE },
|
|
{ "dirlist", DMPL_PRIV_READ, HrGetDirList },
|
|
{ "drivefreespace", DMPL_PRIV_READ, HrGetDriveFreeSpace },
|
|
{ "drivelist", DMPL_PRIV_READ, HrGetDriveList },
|
|
{ "dvdblk", DMPL_PRIV_READ, HrGetDvdBlk },
|
|
{ "flash", DMPL_PRIV_CONFIGURE, HrFlashKernelImage },
|
|
{ "fmtfat", DMPL_PRIV_CONFIGURE, HrFormatFAT },
|
|
{ "funccall", DMPL_PRIV_CONTROL, HrFunctionCall },
|
|
{ "getcontext", DMPL_PRIV_CONTROL, HrGetContext },
|
|
{ "getextcontext", DMPL_PRIV_CONTROL, HrGetExtContext },
|
|
{ "getfile", DMPL_PRIV_READ, HrSendFile },
|
|
{ "getfileattributes", DMPL_PRIV_READ | DMPL_PRIV_WRITE, HrGetFileAttributes },
|
|
{ "getmem", DMPL_PRIV_CONTROL, HrGetMemory },
|
|
{ "getuserpriv", 0, HrGetUserPriv },
|
|
{ "go", DMPL_PRIV_CONTROL, HrGo },
|
|
{ "gpucount", DMPL_PRIV_CONTROL, HrToggleGPUCounters },
|
|
{ "halt", DMPL_PRIV_CONTROL, HrHaltThread },
|
|
{ "irtsweep", DMPL_PRIV_CONTROL, HrIrtSweep },
|
|
{ "isbreak", DMPL_PRIV_CONTROL, HrIsBreak },
|
|
{ "isstopped", DMPL_PRIV_CONTROL, HrIsStopped },
|
|
{ "kd", DMPL_PRIV_CONTROL, HrToggleKDState },
|
|
{ "keyxchg", 0, HrKeyExchange },
|
|
{ "lockmode", DMPL_PRIV_MANAGE, HrSetLockMode },
|
|
{ "magicboot", DMPL_PRIV_CONTROL, HrMagicReboot },
|
|
{ "mkdir", DMPL_PRIV_WRITE, HrMkdir },
|
|
{ "mmglobal", DMPL_PRIV_CONTROL, HrGetMmGlobal },
|
|
{ "modlong", DMPL_PRIV_CONTROL, HrDoLongName },
|
|
{ "modsections", DMPL_PRIV_CONTROL, HrListModuleSections },
|
|
{ "modules", DMPL_PRIV_CONTROL, HrListModules },
|
|
{ "nostopon", DMPL_PRIV_CONTROL, HrNostopon },
|
|
{ "notify", DMPL_PRIV_CONTROL, HrSetupNotify },
|
|
{ "notifyat", DMPL_PRIV_CONTROL, HrSetupNotifyAt },
|
|
{ "pclist", DMPL_PRIV_CONTROL, HrListCounters },
|
|
{ "pssnap", DMPL_PRIV_CONTROL, HrPSSnap },
|
|
{ "querypc", DMPL_PRIV_CONTROL, HrQueryPerformanceCounter },
|
|
{ "reboot", DMPL_PRIV_CONTROL, HrReboot },
|
|
{ "rename", 0, HrRenameFile, DMPL_PRIV_READ | DMPL_PRIV_WRITE },
|
|
{ "resume", DMPL_PRIV_CONTROL, HrResumeThread },
|
|
{ "screenshot", DMPL_PRIV_CONTROL, HrScreenshot },
|
|
{ "sendfile", DMPL_PRIV_WRITE, HrReceiveFile },
|
|
{ "setconfig", DMPL_PRIV_CONFIGURE, HrSetConfig },
|
|
{ "setcontext", DMPL_PRIV_CONTROL, HrSetContext },
|
|
{ "setfileattributes", DMPL_PRIV_WRITE, HrSetFileAttributes },
|
|
{ "setmem", DMPL_PRIV_CONTROL, HrSetMemory },
|
|
{ "setsystime", DMPL_PRIV_CONFIGURE, HrSetSystemTime },
|
|
{ "setuserpriv", DMPL_PRIV_MANAGE, HrSetUserPriv },
|
|
{ "stop", DMPL_PRIV_CONTROL, HrStop },
|
|
{ "stopon", DMPL_PRIV_CONTROL, HrStopon },
|
|
{ "suspend", DMPL_PRIV_CONTROL, HrSuspendThread },
|
|
{ "sysfileupd", DMPL_PRIV_CONTROL, HrUpdateSystemFile },
|
|
{ "systime", 0, HrSystemTime },
|
|
{ "threadinfo", DMPL_PRIV_CONTROL, HrThreadInfo },
|
|
{ "threads", DMPL_PRIV_CONTROL, HrListThreads },
|
|
{ "title", DMPL_PRIV_CONTROL, HrSetTitle },
|
|
{ "user", DMPL_PRIV_MANAGE, HrAddUserCommand },
|
|
{ "userlist", DMPL_PRIV_MANAGE, HrListUsers },
|
|
{ "vssnap", DMPL_PRIV_CONTROL, HrVSSnap },
|
|
{ "xbeinfo", DMPL_PRIV_CONTROL, HrXbeInfo },
|
|
{ "xtlinfo", DMPL_PRIV_CONTROL, HrXapiInfo },
|
|
};
|
|
|
|
int cchh = sizeof rgchh / sizeof rgchh[0];
|