From cb1ee3f2533f6e0d4fc081bd646421ce5133e8ed Mon Sep 17 00:00:00 2001 From: Sepehr Laal <5657848+3p3r@users.noreply.github.com> Date: Thu, 3 Oct 2024 12:27:46 -0700 Subject: [PATCH] feat: support for wildcard byte patterns --- README.MD | 19 ++++++++++++++++++- src/byte_stream.ts | 31 ++++++++++++++++++------------- test/funcTest.spec.ts | 12 ++++++++++++ 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/README.MD b/README.MD index e7ad20f..e455456 100644 --- a/README.MD +++ b/README.MD @@ -107,9 +107,26 @@ _**getBitsString**_|Get string representation for the next "length" bits from th _**getBitsReversedValue**_|Get number value representation of the next "length" bits from the stream, preliminary reversed _**toString**_|Represent remaining bits in "BitStream" as a string +## Wildcard byte patterns + +The _ByteStream_ class provides an option to use wildcard byte patterns. These are useful in searching for magic bytes [in a file format](https://en.wikipedia.org/wiki/List_of_file_signatures). + +Example of using wildcard byte patterns: +```javascript +let wildcard = "?".charCodeAt(0); +let searchPattern = new ByteStream({ + // set wildcard byte value + wildcard: wildcard, + // set search pattern with wildcard bytes ("s??e ") + view: new Uint8Array([0x73, wildcard, wildcard, 0x65, 0x20]) +}); +let dataPattern = new ByteStream({ data: 'some random data 0x00 0x00 0x01 0x45' }); +dataPattern.findPattern(searchPattern); // find search pattern in data pattern +``` + ## parseByteMap functionality -The `parseByteMap` function is intended to parse and check byte streams with determinated structure. +The `parseByteMap` function is intended to parse and check byte streams with determined structure. Example of _map_ used as a kind of _template_. Exactly this _map_ is using for parsing PDF _xref_ table: ```javascript diff --git a/src/byte_stream.ts b/src/byte_stream.ts index 10f5b07..479eb75 100644 --- a/src/byte_stream.ts +++ b/src/byte_stream.ts @@ -1,22 +1,24 @@ -export interface ByteStreamEmptyParameters { } -export interface ByteStreamLengthParameters { - length: number; - stub?: number; +export interface ByteStreamEmptyParameters { + readonly wildcard?: number; +} +export interface ByteStreamLengthParameters extends ByteStreamEmptyParameters { + readonly length: number; + readonly stub?: number; } -export interface ByteStreamViewParameters { - view: Uint8Array; +export interface ByteStreamViewParameters extends ByteStreamEmptyParameters { + readonly view: Uint8Array; } -export interface ByteStreamBufferParameters { - buffer: ArrayBuffer; +export interface ByteStreamBufferParameters extends ByteStreamEmptyParameters { + readonly buffer: ArrayBuffer; } -export interface ByteStreamStringParameters { - string: string; +export interface ByteStreamStringParameters extends ByteStreamEmptyParameters { + readonly string: string; } -export interface ByteStreamHexParameters { - hexstring: string; +export interface ByteStreamHexParameters extends ByteStreamEmptyParameters { + readonly hexstring: string; } export type ByteStreamParameters = @@ -81,7 +83,7 @@ export class ByteStream { * Constructor for ByteStream class * @param parameters */ - constructor(parameters: ByteStreamParameters = {}) { + constructor(readonly parameters: ByteStreamParameters = {}) { if ("view" in parameters) { this.fromUint8Array(parameters.view); } else if ("buffer" in parameters) { @@ -502,6 +504,9 @@ export class ByteStream { const equalStart = (backward) ? (start - patternLength - i) : (start + i); for (let j = 0; j < patternLength; j++) { + if (pattern.parameters.wildcard === patternArray[j]) { + continue; + } if (this.view[j + equalStart] != patternArray[j]) { equal = false; break; diff --git a/test/funcTest.spec.ts b/test/funcTest.spec.ts index dfa4603..39a0e48 100644 --- a/test/funcTest.spec.ts +++ b/test/funcTest.spec.ts @@ -126,6 +126,16 @@ context("Functional testing", () => { ]) }); + const wildcardStream = new ByteStream({ + wildcard: 0x3F, // "?" + view: new Uint8Array([ + 0x20, + 0x3F, + 0x3F, + 0x23, + ]) + }); + let result; result = dataStream.findFirstNotIn([separatorStream, separatorStream2]); @@ -138,6 +148,8 @@ context("Functional testing", () => { assert.strictEqual(result, 1, "Incorrect result for findPattern #1"); result = dataStream.findPattern(separatorStream4); assert.strictEqual(result, 5, "Incorrect result for findPattern #2"); + result = dataStream.findPattern(wildcardStream); + assert.strictEqual(result, 5, "Incorrect result for wildcardStream #1"); result = dataStream.findFirstIn([separatorStream, separatorStream2]); assert.strictEqual(result.position, 1, "Incorrect result for findFirstIn #2"); result = dataStream.findFirstNotIn([separatorStream, separatorStream2, separatorStream3]);