550 lines
17 KiB
C
550 lines
17 KiB
C
|
|
#include <stdio.h>
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
#include <dsound.h>
|
|
|
|
|
|
typedef struct {
|
|
ULONG WriteLocation;
|
|
ULONG ReadLocation;
|
|
ULONG BufferSize;
|
|
PCHAR pBuffer;
|
|
} GLITCHDATA, *PGLITCHDATA;
|
|
|
|
|
|
enum PacketTypes{
|
|
NODATA,
|
|
MASKED,
|
|
UNMASKED,
|
|
GLITCHED,
|
|
HELDOFF
|
|
};
|
|
|
|
|
|
PGLITCHDATA rtbaseaddress=0;
|
|
ULONG rdwrrealtimedata=0;
|
|
|
|
#ifndef UNDER_NT
|
|
ULONG rdsystemdata=0;
|
|
#else
|
|
HANDLE filterHandle;
|
|
#endif
|
|
|
|
|
|
|
|
#pragma warning( disable: 4035 )
|
|
|
|
ULONGLONG GetRealTimeData(PVOID address)
|
|
{
|
|
|
|
#ifndef UNDER_NT
|
|
|
|
__asm{
|
|
push ds
|
|
mov eax,rdsystemdata
|
|
mov ds,ax
|
|
mov eax,address
|
|
mov edx,[eax+4]
|
|
mov eax,[eax]
|
|
pop ds
|
|
}
|
|
|
|
#else
|
|
|
|
return *(PULONGLONG)address;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifndef UNDER_NT
|
|
|
|
CHAR GetRealTimeChar(PVOID address)
|
|
{
|
|
|
|
__asm{
|
|
push ds
|
|
mov eax,rdsystemdata
|
|
mov ds,ax
|
|
mov edx,address
|
|
xor eax,eax
|
|
mov al,[edx]
|
|
pop ds
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#pragma warning( default: 4035 )
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// ------------------------------------------------------------------------------
|
|
BOOL GetRtBaseAddress(void)
|
|
{
|
|
#ifndef UNDER_NT
|
|
HANDLE filterHandle;
|
|
char* deviceName = "\\\\.\\RT";
|
|
#endif
|
|
char* glitchName = "\\\\.\\GLITCH";
|
|
ULONG bytesreturned;
|
|
|
|
#ifndef UNDER_NT
|
|
|
|
filterHandle = CreateFile(
|
|
deviceName,
|
|
0, // GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
0, // OPEN_EXISTING,
|
|
0, // FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
|
NULL
|
|
);
|
|
|
|
if (filterHandle == (HANDLE) -1) {
|
|
printf("Realtime executive not loaded. GetLastError == %x.\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
DeviceIoControl(filterHandle, 2, NULL, 0, &rdsystemdata, sizeof(rdsystemdata), NULL, NULL);
|
|
|
|
CloseHandle(filterHandle);
|
|
|
|
if (!rdsystemdata) {
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
filterHandle = CreateFile(
|
|
glitchName,
|
|
#ifdef UNDER_NT
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
#else
|
|
0,// GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
0,// OPEN_EXISTING,
|
|
0,// FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
|
NULL
|
|
#endif
|
|
);
|
|
|
|
if (filterHandle == (HANDLE) -1) {
|
|
printf("Glitch detector not loaded. GetLastError == %x.\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DeviceIoControl(filterHandle, 2, NULL, 0, &(ULONG)rtbaseaddress, sizeof(rtbaseaddress), &bytesreturned, NULL);
|
|
|
|
// On NT we only close the handle when we are done, since closing this handle
|
|
// will cause our mapped view of the buffer to go away. This is by design
|
|
// since we use the fact that the OS will close open handles whenever the
|
|
// process goes away to make sure that we properly clean up our mapped view.
|
|
#ifndef UNDER_NT
|
|
CloseHandle(filterHandle);
|
|
#endif
|
|
|
|
if (rtbaseaddress) {
|
|
printf("base address = 0x%p\n", rtbaseaddress);
|
|
}
|
|
|
|
// We return TRUE even if rtbaseaddress is NULL. We do this so we can
|
|
// differentiate between when glitch.sys cannot be opened, and when
|
|
// we cannot get a base address - which will ONLY happen under NT when
|
|
// another instance of glitch.exe has already opened glitch.sys and has
|
|
// not yet closed it.
|
|
return TRUE;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// ------------------------------------------------------------------------------
|
|
// #define VERBOSE
|
|
|
|
BOOL TrapDSError(HRESULT hRes, LPCSTR szAPI)
|
|
{
|
|
LPCSTR szError;
|
|
switch(hRes)
|
|
{
|
|
case DS_OK: szError = "DS_OK"; break;
|
|
case DSERR_ALLOCATED: szError = "DSERR_ALLOCATED"; break;
|
|
case DSERR_CONTROLUNAVAIL: szError = "DSERR_CONTROLUNAVAIL"; break;
|
|
case DSERR_INVALIDPARAM: szError = "DSERR_INVALIDPARAM"; break;
|
|
case DSERR_INVALIDCALL: szError = "DSERR_INVALIDCALL"; break;
|
|
case DSERR_GENERIC: szError = "DSERR_GENERIC"; break;
|
|
case DSERR_PRIOLEVELNEEDED: szError = "DSERR_PRIOLEVELNEEDED"; break;
|
|
case DSERR_OUTOFMEMORY: szError = "DSERR_OUTOFMEMORY"; break;
|
|
case DSERR_BADFORMAT: szError = "DSERR_BADFORMAT"; break;
|
|
case DSERR_UNSUPPORTED: szError = "DSERR_UNSUPPORTED"; break;
|
|
case DSERR_NODRIVER: szError = "DSERR_NODRIVER"; break;
|
|
case DSERR_ALREADYINITIALIZED: szError = "DSERR_ALREADYINITIALIZED"; break;
|
|
case DSERR_NOAGGREGATION: szError = "DSERR_NOAGGREGATION"; break;
|
|
case DSERR_BUFFERLOST: szError = "DSERR_BUFFERLOST"; break;
|
|
case DSERR_OTHERAPPHASPRIO: szError = "DSERR_OTHERAPPHASPRIO"; break;
|
|
case DSERR_UNINITIALIZED: szError = "DSERR_UNINITIALIZED"; break;
|
|
case DSERR_NOINTERFACE: szError = "DSERR_NOINTERFACE"; break;
|
|
default: szError = "UNKNOWN ERROR CODE"; break; }
|
|
|
|
if(FAILED(hRes))
|
|
fprintf(stdout, "DSound ERROR: %s returned %s\n", szAPI, szError);
|
|
#ifdef VERBOSE
|
|
else
|
|
fprintf(stdout, "%s returned %s\n", szAPI, szError);
|
|
#endif
|
|
|
|
return SUCCEEDED(hRes);
|
|
}
|
|
|
|
LPDIRECTSOUND pDS = NULL;
|
|
LPDIRECTSOUNDBUFFER pDSB = NULL;
|
|
|
|
#define BUFFER_SIZE 88200
|
|
// ------------------------------------------------------------------------------
|
|
// ------------------------------------------------------------------------------
|
|
void CleanupDSound(void)
|
|
{
|
|
if (pDSB)
|
|
IDirectSound_Release(pDSB);
|
|
if (pDS)
|
|
IDirectSoundBuffer_Release(pDS);
|
|
|
|
pDS = NULL;
|
|
pDSB = NULL;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// ------------------------------------------------------------------------------
|
|
BOOL StartDSoundLoop(void)
|
|
{
|
|
WAVEFORMATEX wfx;
|
|
HRESULT hRes;
|
|
DSBUFFERDESC dsbdesc;
|
|
DWORD cb1, cb2;
|
|
PVOID pv1, pv2;
|
|
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
wfx.nChannels = 1;
|
|
wfx.nSamplesPerSec = 44100;
|
|
wfx.nAvgBytesPerSec = 88200;
|
|
wfx.nBlockAlign = 2;
|
|
wfx.wBitsPerSample = 16;
|
|
wfx.cbSize = 0;
|
|
|
|
hRes = DirectSoundCreate(NULL, &pDS, NULL );
|
|
if (!TrapDSError(hRes, "DirectSoundCreate"))
|
|
goto fail;
|
|
|
|
hRes = IDirectSound_SetCooperativeLevel(pDS, GetForegroundWindow(), DSSCL_NORMAL );
|
|
if (!TrapDSError(hRes, "SetCooperativeLevel"))
|
|
goto fail;
|
|
|
|
ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC));
|
|
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
|
dsbdesc.dwBufferBytes = BUFFER_SIZE;
|
|
dsbdesc.lpwfxFormat = &wfx;
|
|
dsbdesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE;
|
|
|
|
hRes = IDirectSound_CreateSoundBuffer(pDS, &dsbdesc, &pDSB, NULL );
|
|
if (!TrapDSError(hRes, "CreateSoundBuffer"))
|
|
goto fail;
|
|
|
|
hRes = IDirectSoundBuffer_Lock(pDSB, 0, 0, &pv1, &cb1, &pv2, &cb2, DSBLOCK_ENTIREBUFFER);
|
|
if (!TrapDSError(hRes, "Lock"))
|
|
goto fail;
|
|
|
|
// silence
|
|
ZeroMemory(pv1, cb1);
|
|
ZeroMemory(pv2, cb2);
|
|
|
|
hRes = IDirectSoundBuffer_Unlock(pDSB, pv1, cb1, pv2, cb2);
|
|
if (!TrapDSError(hRes, "Unlock"))
|
|
goto fail;
|
|
|
|
hRes = IDirectSoundBuffer_Play(pDSB, 0, 0, DSBPLAY_LOOPING);
|
|
if (!TrapDSError(hRes, "Play"))
|
|
goto fail;
|
|
|
|
return TRUE;
|
|
fail:
|
|
|
|
CleanupDSound();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
__cdecl
|
|
main (
|
|
void
|
|
)
|
|
{
|
|
|
|
ULONG OpenedGlitch;
|
|
ULONG StartedDSound=FALSE; // MUST be preinitialized - for file open failure path.
|
|
ULONG PrintedIndex;
|
|
ULONG ToPrintIndex;
|
|
PCHAR pPrintBuffer;
|
|
ULONG PrintBufferSize;
|
|
ULONGLONG Data[2];
|
|
OSVERSIONINFO Version;
|
|
SYSTEMTIME SystemTime;
|
|
CHAR Build[5];
|
|
CHAR MachineName[MAX_COMPUTERNAME_LENGTH+1];
|
|
CHAR Date[9];
|
|
CHAR Time[7];
|
|
CHAR Path[_MAX_PATH];
|
|
ULONG SupportStupidApi=MAX_COMPUTERNAME_LENGTH+1;
|
|
FILE *f;
|
|
|
|
|
|
// First open glitch.sys and map its buffer into our memory.
|
|
// If we cannot open glitch.sys, then GetRtBaseAddress will return FALSE.
|
|
// Otherwise it will return TRUE, but it will STILL return a NULL
|
|
// rtbaseaddress if another glitch.exe has already opened it.
|
|
OpenedGlitch=GetRtBaseAddress();
|
|
|
|
if (OpenedGlitch && rtbaseaddress==NULL) {
|
|
// We get here only if another instance of glitch.exe is running.
|
|
printf("Another instance of glitch.exe is already running.\n");
|
|
|
|
#ifdef UNDER_NT
|
|
CloseHandle(filterHandle);
|
|
#endif
|
|
|
|
// We may want to make our display window hidden by default and
|
|
// we could then ask the user if they want to unhide the window
|
|
// of the already running glitch.exe. That would be cool.
|
|
return 0;
|
|
}
|
|
|
|
|
|
// If we succeeded in opening glitch.sys then load information about
|
|
// the print buffer. IMPORTANT: this code should NOT be moved inside
|
|
// the startnewlogfile loop!! We do NOT want PrintedIndex to be zeroed
|
|
// when we are continuing logging but just into a new file.
|
|
if (OpenedGlitch) {
|
|
#ifndef UNDER_NT
|
|
pPrintBuffer=(PCHAR)GetRealTimeData(&rtbaseaddress->pBuffer);
|
|
#else
|
|
pPrintBuffer=(PCHAR)rtbaseaddress+4096;
|
|
#endif
|
|
|
|
PrintBufferSize=(ULONG)GetRealTimeData(&rtbaseaddress->BufferSize);
|
|
|
|
PrintedIndex=0;
|
|
}
|
|
|
|
|
|
// This is our entry point for starting a new log file. We will start
|
|
// a new log file for each day.
|
|
//startnewlogfile:
|
|
|
|
|
|
// Initialize the strings we use to build the filename.
|
|
strcpy(MachineName, "NONAME");
|
|
Build[0]=0;
|
|
Date[0]=0;
|
|
Time[0]=0;
|
|
|
|
// Initialize variables we use to build some of the strings - in case the
|
|
// API's we call to get the real values fail.
|
|
|
|
// We don't want a random build number if we don't get the real build
|
|
// number. So set it to zero.
|
|
Version.dwBuildNumber=0;
|
|
|
|
// We don't want a random date, if GetLocalTime fails. HOWEVER, we DO want
|
|
// a random time in that case, so that we get different filenames whenever
|
|
// that happens. So, we clear the fields that are used to generate the
|
|
// date, also, randomize the the fields used to generate time. For now
|
|
// we do that by simply leaving whatever was on the stack in them.
|
|
SystemTime.wMonth=0;
|
|
SystemTime.wDay=0;
|
|
SystemTime.wYear=0;
|
|
|
|
// Get the version of the operating system we are running on.
|
|
Version.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
|
|
GetVersionEx(&Version);
|
|
|
|
// Read the name of the machine we are running on.
|
|
// Why they made the API require you to pass in a POINTER to the length
|
|
// I will never understand.
|
|
GetComputerName(MachineName, &SupportStupidApi);
|
|
|
|
// Get the current local time.
|
|
GetLocalTime(&SystemTime);
|
|
|
|
// Build the path and filename of our glitch data file.
|
|
// Our file name is built from the build number, date, machine name, and time of day.
|
|
// The time is used primarily just to allow unique files to be created during the same day.
|
|
|
|
// All files go to \\joeball7\glitch so we can build a database of all the glitch information.
|
|
strcpy(Path, "\\\\joeball7\\glitch\\");
|
|
|
|
// First put the build number into the filename. Lets us easily group data by build.
|
|
// Note that we modulo the build number by 10000 to guarantee that the number will
|
|
// be 4 base 10 numerals or less.
|
|
// We use that same trick when building all of our strings. Since all of the strings
|
|
// are exactly the length they need to be and no more. It makes sure we don't have
|
|
// buffer overruns in sprintf - without having to use snprintf.
|
|
sprintf(Build, "%04d", (ULONG)((WORD)Version.dwBuildNumber%10000));
|
|
strcat(Path, Build);
|
|
strcat(Path, "_");
|
|
|
|
// Next put the date into the filename. Lets us easily group data by time.
|
|
sprintf(Date, "%02d%02d%04d", (ULONG)SystemTime.wMonth%100, (ULONG)SystemTime.wDay%100, (ULONG)SystemTime.wYear%10000);
|
|
strcat(Path, Date);
|
|
strcat(Path, "_");
|
|
|
|
// Next put the machine name into the filename. This lets us group data by machine.
|
|
strcat(Path, MachineName);
|
|
strcat(Path, "_");
|
|
|
|
// Finally put the time at the end.
|
|
sprintf(Time, "%02d%02d%02d", (ULONG)SystemTime.wHour%100, (ULONG)SystemTime.wMinute%100, (ULONG)SystemTime.wSecond%100);
|
|
strcat(Path, Time);
|
|
|
|
// Open the glitch logging file.
|
|
f=fopen(Path, "w");
|
|
|
|
if (f==NULL) {
|
|
|
|
if (OpenedGlitch) {
|
|
#ifdef UNDER_NT
|
|
CloseHandle(filterHandle);
|
|
#endif
|
|
}
|
|
|
|
if (StartedDSound) {
|
|
CleanupDSound();
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
// Now that we have our logging file open, log any errors that occurred when
|
|
// we tried to open glitch.sys. Then exit.
|
|
// I want to track how many machines cannot run glitch.exe - as that will
|
|
// indicate how many machines can run rt.sys.
|
|
if (!OpenedGlitch) {
|
|
|
|
fprintf(f, "!!! FAILURE !!! - glitch.exe could not open glitch.sys.\n");
|
|
fflush(f);
|
|
fclose(f);
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (!StartedDSound) {
|
|
StartedDSound=StartDSoundLoop();
|
|
}
|
|
|
|
|
|
|
|
// This is the main LOGGING loop.
|
|
|
|
while(TRUE) {
|
|
|
|
ToPrintIndex=(ULONG)GetRealTimeData(&rtbaseaddress->WriteLocation);
|
|
|
|
if ( (ToPrintIndex-PrintedIndex) >= PrintBufferSize/2 ) {
|
|
// We have gotten too far behind the kernel mode glitch detector.
|
|
// Skip to the current kernel print position.
|
|
// Also add a warning to the screen and the file.
|
|
|
|
fprintf(stdout, "\n!!! WARNING !!! - skipping to current print location.\n\n");
|
|
fprintf(f, "\n!!! WARNING !!! - skipping to current print location.\n\n");
|
|
|
|
fflush(stdout);
|
|
fflush(f);
|
|
|
|
PrintedIndex=ToPrintIndex;
|
|
}
|
|
|
|
while (ToPrintIndex-PrintedIndex>15) {
|
|
|
|
// Get the data packet. First wait until packet loaded.
|
|
do {
|
|
Data[0]=GetRealTimeData(&pPrintBuffer[PrintedIndex%PrintBufferSize]);
|
|
} while ((ULONG)Data[0]==NODATA);
|
|
|
|
PrintedIndex+=8;
|
|
|
|
// Now get the rest of the packet.
|
|
Data[1]=GetRealTimeData(&pPrintBuffer[PrintedIndex%PrintBufferSize]);
|
|
PrintedIndex+=8;
|
|
|
|
// Print the data.
|
|
switch((UCHAR)Data[0]) {
|
|
case MASKED:
|
|
fprintf(stdout, "Masked, %d\n", (ULONG)(Data[0])>>8);
|
|
fprintf(f, "M, %d\n", (ULONG)(Data[0])>>8);
|
|
break;
|
|
case UNMASKED:
|
|
fprintf(stdout, "Unmasked, %d\n", (ULONG)(Data[0])>>8);
|
|
fprintf(f, "U, %d\n", (ULONG)(Data[0])>>8);
|
|
break;
|
|
case GLITCHED:
|
|
fprintf(stdout, "Glitched, %d, %I64u, %lu\n", (ULONG)(Data[0])>>8, Data[1], (ULONG)(Data[0]>>32));
|
|
fprintf(f, "G, %d, %I64u, %lu\n", (ULONG)(Data[0])>>8, Data[1], (ULONG)(Data[0]>>32));
|
|
break;
|
|
case HELDOFF:
|
|
fprintf(stdout, "Held Off, %d, %I64u\n", (ULONG)(Data[0])>>8, Data[1]);
|
|
fprintf(f, "H, %d, %I64u\n", (ULONG)(Data[0])>>8, Data[1]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// We recheck if there is more data to print before we exit this inner
|
|
// print loop. Once we are printing, we print as much as we can before
|
|
// we go back to sleep.
|
|
|
|
ToPrintIndex=(ULONG)GetRealTimeData(&rtbaseaddress->WriteLocation);
|
|
|
|
if ( (ToPrintIndex-PrintedIndex) >= PrintBufferSize/2 ) {
|
|
// We have gotten too far behind the kernel mode glitch detector.
|
|
// Skip to the current kernel print position.
|
|
// Also add a warning to the screen and the file.
|
|
|
|
fprintf(stdout, "\n!!! WARNING !!! - skipping to current print location.\n\n");
|
|
fprintf(f, "\n!!! WARNING !!! - skipping to current print location.\n\n");
|
|
|
|
PrintedIndex=ToPrintIndex;
|
|
}
|
|
|
|
// When we have finished printing everything that we can, then flush the
|
|
// output buffers. We do this here, so that we only flush data if we
|
|
// have printed some, instead of every time through this loop, or
|
|
// every time we wake up to check if there is data to print.
|
|
if (ToPrintIndex-PrintedIndex<=15) {
|
|
fflush(stdout);
|
|
fflush(f);
|
|
}
|
|
|
|
}
|
|
|
|
// Wake up every 100ms and loop to check if there is anything we need to print.
|
|
Sleep(100);
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
CleanupDSound();
|
|
|
|
#ifdef UNDER_NT
|
|
CloseHandle(filterHandle);
|
|
#endif
|
|
|
|
}
|