Skip to content

Commit

Permalink
improve fuzzing
Browse files Browse the repository at this point in the history
  • Loading branch information
jxy-s committed Sep 3, 2024
1 parent 11bf566 commit 16cab38
Show file tree
Hide file tree
Showing 5 changed files with 523 additions and 120 deletions.
237 changes: 211 additions & 26 deletions vfdynf/fuzz.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
#include <vfdynf.h>
#include <delayld.h>

#define VFDYNF_FUZZ_BLOCK_SIZE (0x1000 / 4)
#define VFDYNF_RAND_VECTOR_SIZE 0x4000
#define VFDYNF_FUZZ_MMAP_COUNT 1024
#define VFCYNF_FUZZED_BUFFERS_COUNT 1024
#define VFDYNF_FUZZ_BLOCK_SIZE (0x1000 / 4)
#define VFDYNF_RAND_VECTOR_SIZE 0x4000
#define VFDYNF_FUZZ_MMAP_COUNT 1024
#define VFCYNF_FUZZED_BUFFERS_COUNT 1024

typedef struct _VFDYNF_FUZZ_MMAP_ENTRY
{
Expand All @@ -22,6 +22,13 @@ typedef struct _VFDYNF_FUZZED_BUFFER_ENTRY
SIZE_T Size;
} VFDYNF_FUZZED_BUFFER_ENTRY, *PVFDYNF_FUZZED_BUFFER_ENTRY;

typedef enum _VFDYNF_FUZZ_BUFFER_CLASS
{
VFDynfBufferData,
VFDynfBufferUnicode,
VFDynfBufferAnsi,
} VFDYNF_FUZZ_BUFFER_CLASS, *PVFDYNF_FUZZ_BUFFER_CLASS;

typedef struct _VFDYNF_FUZZ_CONTEXT
{
BOOLEAN Initialized;
Expand All @@ -48,6 +55,64 @@ static VFDYNF_FUZZ_CONTEXT AVrfpFuzzContext =
.FuzzedBuffers = { 0 },
};

// https://github.com/winsiderss/systeminformer/blob/master/phlib/data.c
static BOOLEAN AVrfpCharIsPrintable[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0 - 15 */ // TAB, LF and CR are printable
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ' ' - '/' */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* '0' - '9' */
1, 1, 1, 1, 1, 1, 1, /* ':' - '@' */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 'A' - 'Z' */
1, 1, 1, 1, 1, 1, /* '[' - '`' */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 'a' - 'z' */
1, 1, 1, 1, 0, /* '{' - 127 */ // DEL is not printable
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128 - 143 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144 - 159 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 175 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176 - 191 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 - 207 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208 - 223 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224 - 239 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 240 - 255 */
};

static UNICODE_STRING AvrfpFuzzStringTableW[] =
{
RTL_CONSTANT_STRING(L"\0"),
RTL_CONSTANT_STRING(L"\0"),
RTL_CONSTANT_STRING(L"\0"),
RTL_CONSTANT_STRING(L"\0"),
RTL_CONSTANT_STRING(L"%s"),
RTL_CONSTANT_STRING(L"\\"),
RTL_CONSTANT_STRING(L"C:\\"),
RTL_CONSTANT_STRING(L"\\\\"),
RTL_CONSTANT_STRING(L","),
RTL_CONSTANT_STRING(L":"),
RTL_CONSTANT_STRING(L"%"),
RTL_CONSTANT_STRING(L"(("),
RTL_CONSTANT_STRING(L"{{"),
RTL_CONSTANT_STRING(L"%APPDATA%"),
};

static ANSI_STRING AvrfpFuzzStringTableA[] =
{
RTL_CONSTANT_STRING("\0"),
RTL_CONSTANT_STRING("\0"),
RTL_CONSTANT_STRING("\0"),
RTL_CONSTANT_STRING("\0"),
RTL_CONSTANT_STRING("%s"),
RTL_CONSTANT_STRING("\\"),
RTL_CONSTANT_STRING("C:\\"),
RTL_CONSTANT_STRING("\\\\"),
RTL_CONSTANT_STRING(","),
RTL_CONSTANT_STRING(";"),
RTL_CONSTANT_STRING("%"),
RTL_CONSTANT_STRING("(("),
RTL_CONSTANT_STRING("{{"),
RTL_CONSTANT_STRING("%APPDATA%"),
};

_Function_class_(AVRF_RUN_ONCE_ROUTINE)
BOOLEAN NTAPI AVrfpFuzzRunOnceRoutine(
VOID
Expand Down Expand Up @@ -111,6 +176,78 @@ BOOLEAN AVrfFuzzProbability(
return ((AVrfFuzzRandom() % 1000000) < Probability);
}

VFDYNF_FUZZ_BUFFER_CLASS AVrfpFuzzClassifyBuffer(
_In_reads_bytes_(Length) PBYTE Buffer,
_In_ SIZE_T Length
)
{
SIZE_T printable;
SIZE_T sentinels;
FLOAT percent;

if (Length <= (sizeof(ULONG64) * 4))
{
return VFDynfBufferData;
}

sentinels = 0;
printable = 0;

for (ULONG i = 0; i < Length; i++)
{
if (AVrfpCharIsPrintable[Buffer[i]])
{
printable++;
}

if (Buffer[i] == VFDYNF_FUZZ_SENTINEL)
{
sentinels++;
}
else
{
sentinels = 0;
}

if (sentinels > 5)
{
//
// Likely at our memory fill, stop counting.
//
break;
}
}

percent = ((FLOAT)printable / Length) * 100.0f;
if (percent >= 95.0f)
{
return VFDynfBufferAnsi;
}

percent = ((FLOAT)printable / (Length / 2)) * 100.0f;
if (percent >= 95.0f)
{
return VFDynfBufferUnicode;
}

return VFDynfBufferData;
}

VOID AVrfpFuzzGetBufferRange(
_In_ SIZE_T Length,
_Out_ PULONG Start,
_Out_ PULONG End
)
{
ULONG offsets[2];

offsets[0] = AVrfFuzzRandom() % Length;
offsets[1] = AVrfFuzzRandom() % Length;

*Start = offsets[0] < offsets[1] ? offsets[0] : offsets[1];
*End = offsets[0] > offsets[1] ? offsets[0] : offsets[1];
}

VOID AVrfpFuzzBuffer(
_Inout_bytecount_(Length) PVOID Buffer,
_In_ SIZE_T Length
Expand All @@ -119,6 +256,7 @@ VOID AVrfpFuzzBuffer(
PBYTE bufferBytes;
SIZE_T bufferLength;
ULONG corruptionBlocks;
VFDYNF_FUZZ_BUFFER_CLASS bufferClass;

if (!AVrfProperties.FuzzCorruptionBlocks)
{
Expand All @@ -129,92 +267,139 @@ VOID AVrfpFuzzBuffer(
bufferLength = Length;
corruptionBlocks = (1 + (AVrfFuzzRandom() % AVrfProperties.FuzzCorruptionBlocks));

bufferClass = AVrfpFuzzClassifyBuffer(bufferBytes, bufferLength);

for (ULONG i = 0; i < corruptionBlocks; i++)
{
if (AVrfFuzzProbability(AVrfProperties.FuzzChaosProbability))
{
ULONG offsets[2];
ULONG start;
ULONG end;

offsets[0] = AVrfFuzzRandom() % bufferLength;
offsets[1] = AVrfFuzzRandom() % bufferLength;
AVrfpFuzzGetBufferRange(bufferLength, &start, &end);

if (AVrfFuzzProbability(100000))
{
if (AVrfFuzzProbability(500000))
{
RtlFillMemory(&bufferBytes[start], end - start, 0x00);
}
else
{
RtlFillMemory(&bufferBytes[start], end - start, 0xff);
}
}
else
{
while (start < end)
{
bufferBytes[start++] = (BYTE)AVrfFuzzRandom();
}
}
}
else if (bufferClass == VFDynfBufferUnicode)
{
ULONG start;
ULONG end;
PUNICODE_STRING string;

AVrfpFuzzGetBufferRange(bufferLength, &start, &end);

string = &AvrfpFuzzStringTableW[AVrfFuzzRandom() % ARRAYSIZE(AvrfpFuzzStringTableW)];

if (start % 2)
{
start++;
end++;
}

if ((start < bufferLength) && (string->Length <= (end - start)))
{
memcpy(&bufferBytes[start], string->Buffer, string->Length);
}
}
else if (bufferClass == VFDynfBufferAnsi)
{
ULONG start;
ULONG end;
PANSI_STRING string;

AVrfpFuzzGetBufferRange(bufferLength, &start, &end);

start = offsets[0] < offsets[1] ? offsets[0] : offsets[1];
end = offsets[0] > offsets[1] ? offsets[0] : offsets[1];
string = &AvrfpFuzzStringTableA[AVrfFuzzRandom() % ARRAYSIZE(AvrfpFuzzStringTableW)];

while (start < end)
if (string->Length <= (end - start))
{
bufferBytes[start++] = (BYTE)AVrfFuzzRandom();
memcpy(&bufferBytes[start], string->Buffer, string->Length);
}
}
else
{
ULONG position;
ULONG pos;

position = AVrfFuzzRandom() % bufferLength;
pos = AVrfFuzzRandom() % bufferLength;

if (AVrfFuzzProbability(250000))
{
position &= 0xfffffffc;
pos &= 0xfffffffc;
}

if ((position + 3) >= bufferLength)
if ((pos + 3) >= bufferLength)
{
bufferBytes[position] ^= (BYTE)(1 + AVrfFuzzRandom());
bufferBytes[pos] ^= (BYTE)(1 + AVrfFuzzRandom());
continue;
}

switch (AVrfFuzzRandom() % 13)
{
case 1: // off by a bit
{
*(PULONG)&bufferBytes[position] += 512 - (ULONG)(AVrfFuzzRandom() % 1024);
*(PULONG)&bufferBytes[pos] += 512 - (ULONG)(AVrfFuzzRandom() % 1024);
break;
}
case 2: // off by multiple of four
{
*(PULONG)&bufferBytes[position] += 4 * (512 - (ULONG)(AVrfFuzzRandom() % 1024));
*(PULONG)&bufferBytes[pos] += 4 * (512 - (ULONG)(AVrfFuzzRandom() % 1024));
break;
}
case 3: // negative one
{
*(PULONG)&bufferBytes[position] = 0xffffffff;
*(PULONG)&bufferBytes[pos] = 0xffffffff;
break;
}
case 4: // small negative
{
*(PULONG)&bufferBytes[position] = -(LONG)(AVrfFuzzRandom() % 25);
*(PULONG)&bufferBytes[pos] = -(LONG)(AVrfFuzzRandom() % 25);
break;
}
case 5: // zero
{
*(PULONG)&bufferBytes[position] = 0;
*(PULONG)&bufferBytes[pos] = 0;
break;
}
case 6: // negate
{
*(PULONG)&bufferBytes[position] = -(*(PLONG)&bufferBytes[position]);
*(PULONG)&bufferBytes[pos] = -(*(PLONG)&bufferBytes[pos]);
break;
}
case 7: // compliment
{
*(PULONG)&bufferBytes[position] = ~(*(PLONG)&bufferBytes[position]);
*(PULONG)&bufferBytes[pos] = ~(*(PLONG)&bufferBytes[pos]);
break;
}
case 8: // treat position as offset
{
*(PULONG)&bufferBytes[position] = (ULONG)position + 4 * (128 - (ULONG)(AVrfFuzzRandom() % 256));
*(PULONG)&bufferBytes[pos] = (ULONG)pos + 4 * (128 - (ULONG)(AVrfFuzzRandom() % 256));
break;
}
case 9: // copy alternate dword
{
*(PULONG)&bufferBytes[position] = *(PULONG)&bufferBytes[AVrfFuzzRandom() % (bufferLength - 3)];
*(PULONG)&bufferBytes[pos] = *(PULONG)&bufferBytes[AVrfFuzzRandom() % (bufferLength - 3)];
break;
}
default: // random dword
{
*(PULONG)&bufferBytes[position] = (ULONG)(1 + AVrfFuzzRandom());
*(PULONG)&bufferBytes[pos] = (ULONG)(1 + AVrfFuzzRandom());
break;
}
}
Expand Down
19 changes: 15 additions & 4 deletions vfdynf/hooks_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ Hook_NtReadFile(
)
{
NTSTATUS status;
ULONG inputLength;

inputLength = Length;

AVrfFuzzFillMemory(Buffer, Length);

if (AVrfHookShouldFaultInject(VFDYNF_FAULT_TYPE_FUZZ_FILE))
{
Expand All @@ -100,9 +105,7 @@ Hook_NtReadFile(

if (AVrfHookShouldFaultInject(VFDYNF_FAULT_TYPE_FUZZ_FILE))
{
AVrfFuzzBuffer(Buffer,
IoStatusBlock->Information,
VFDYNF_FAULT_TYPE_INDEX_FUZZ_FILE);
AVrfFuzzBuffer(Buffer, inputLength, VFDYNF_FAULT_TYPE_INDEX_FUZZ_FILE);
}

return status;
Expand Down Expand Up @@ -301,6 +304,14 @@ Hook_Common_ReadFile(
)
{
BOOL result;
ULONG inputLength;

inputLength = nNumberOfBytesToRead;

if (lpBuffer)
{
AVrfFuzzFillMemory(lpBuffer, inputLength);
}

if (AVrfHookShouldFaultInject(VFDYNF_FAULT_TYPE_FUZZ_FILE))
{
Expand All @@ -316,7 +327,7 @@ Hook_Common_ReadFile(
if (result && lpBuffer && AVrfHookShouldFaultInject(VFDYNF_FAULT_TYPE_FUZZ_FILE))
{
AVrfFuzzBuffer(lpBuffer,
nNumberOfBytesToRead,
inputLength,
VFDYNF_FAULT_TYPE_INDEX_FUZZ_FILE);
}

Expand Down
Loading

0 comments on commit 16cab38

Please sign in to comment.