Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562/** * SMB2/3 Message Serializer * Handles serialization of SMB2 protocol messages to binary data */
import type { SMB2Header, SMB2TransformHeader, SMB2NegotiateRequest, SMB2NegotiateResponse, SMB2NegotiateContext, SMB2SessionSetupRequest, SMB2SessionSetupResponse, SMB2TreeConnectRequest, SMB2TreeConnectResponse, SMB2CreateRequest, SMB2CreateResponse, SMB2CreateContext, SMB2CloseRequest, SMB2CloseResponse, SMB2ReadRequest, SMB2ReadResponse, SMB2WriteRequest, SMB2WriteResponse, SMB2QueryDirectoryRequest, SMB2QueryDirectoryResponse, SMB2QueryInfoRequest, SMB2QueryInfoResponse, SMB2SetInfoRequest, SMB2SetInfoResponse, SMB2ErrorResponse, SMB2FileId, SMB2Guid, FileBasicInformation, FileStandardInformation, FileDirectoryInformation,} from './types.js';import { SMB2_PROTOCOL_ID, SMB2_TRANSFORM_PROTOCOL_ID, SMB2_HEADER_SIZE, SMB2_TRANSFORM_HEADER_SIZE,} from './constants.js';
export class SMB2MessageSerializer { /** * Serialize SMB2 header to buffer */ public serializeHeader(header: SMB2Header): Buffer { const buffer = Buffer.allocUnsafe(SMB2_HEADER_SIZE); buffer.writeUInt32LE(header.protocolId, 0); buffer.writeUInt16LE(header.structureSize, 4); buffer.writeUInt16LE(header.creditCharge, 6); buffer.writeUInt32LE(header.status, 8); buffer.writeUInt16LE(header.command, 12); buffer.writeUInt16LE(header.creditRequestResponse, 14); buffer.writeUInt32LE(header.flags, 16); buffer.writeUInt32LE(header.nextCommand, 20); buffer.writeBigUInt64LE(header.messageId, 24); buffer.writeUInt32LE(header.reserved, 32); buffer.writeUInt32LE(header.treeId, 36); buffer.writeBigUInt64LE(header.sessionId, 40); // Copy signature (16 bytes) header.signature.copy(buffer, 48, 0, 16); return buffer; }
/** * Serialize SMB2 Transform header for encrypted messages */ public serializeTransformHeader(header: SMB2TransformHeader): Buffer { const buffer = Buffer.allocUnsafe(SMB2_TRANSFORM_HEADER_SIZE); buffer.writeUInt32LE(header.protocolId, 0); header.signature.copy(buffer, 4, 0, 16); header.nonce.copy(buffer, 20, 0, 16); buffer.writeUInt32LE(header.originalMessageSize, 36); buffer.writeUInt16LE(header.reserved, 40); buffer.writeUInt16LE(header.flags, 42); buffer.writeBigUInt64LE(header.sessionId, 44); return buffer; }
/** * Serialize SMB2 Negotiate Response */ public serializeNegotiateResponse(response: SMB2NegotiateResponse): Buffer { const baseSize = 65; // Base structure size const contextSize = this.calculateNegotiateContextsSize(response.negotiateContexts); const totalSize = baseSize + response.securityBufferLength + contextSize; const buffer = Buffer.allocUnsafe(totalSize); let offset = 0; buffer.writeUInt16LE(response.structureSize, offset); offset += 2; buffer.writeUInt16LE(response.securityMode, offset); offset += 2; buffer.writeUInt16LE(response.dialectRevision, offset); offset += 2; buffer.writeUInt16LE(response.negotiateContextCount, offset); offset += 2; this.serializeGuid(response.serverGuid, buffer, offset); offset += 16; buffer.writeUInt32LE(response.capabilities, offset); offset += 4; buffer.writeUInt32LE(response.maxTransactSize, offset); offset += 4; buffer.writeUInt32LE(response.maxReadSize, offset); offset += 4; buffer.writeUInt32LE(response.maxWriteSize, offset); offset += 4; buffer.writeBigUInt64LE(response.systemTime, offset); offset += 8; buffer.writeBigUInt64LE(response.serverStartTime, offset); offset += 8; buffer.writeUInt16LE(response.securityBufferOffset, offset); offset += 2; buffer.writeUInt16LE(response.securityBufferLength, offset); offset += 2; buffer.writeUInt32LE(response.negotiateContextOffset, offset); offset += 4; // Copy security buffer if (response.securityBufferLength > 0) { response.securityBuffer.copy(buffer, offset, 0, response.securityBufferLength); offset += response.securityBufferLength; } // Serialize negotiate contexts for (const context of response.negotiateContexts) { offset += this.serializeNegotiateContext(context, buffer, offset); } return buffer.subarray(0, offset); }
/** * Serialize SMB2 Session Setup Response */ public serializeSessionSetupResponse(response: SMB2SessionSetupResponse): Buffer { const totalSize = 9 + response.securityBufferLength; const buffer = Buffer.allocUnsafe(totalSize); let offset = 0; buffer.writeUInt16LE(response.structureSize, offset); offset += 2; buffer.writeUInt16LE(response.sessionFlags, offset); offset += 2; buffer.writeUInt16LE(response.securityBufferOffset, offset); offset += 2; buffer.writeUInt16LE(response.securityBufferLength, offset); offset += 2; // Copy security buffer if (response.securityBufferLength > 0) { response.securityBuffer.copy(buffer, offset, 0, response.securityBufferLength); offset += response.securityBufferLength; } return buffer.subarray(0, offset); }
/** * Serialize SMB2 Tree Connect Response */ public serializeTreeConnectResponse(response: SMB2TreeConnectResponse): Buffer { const buffer = Buffer.allocUnsafe(16); buffer.writeUInt16LE(response.structureSize, 0); buffer.writeUInt8(response.shareType, 2); buffer.writeUInt8(response.reserved, 3); buffer.writeUInt32LE(response.shareFlags, 4); buffer.writeUInt32LE(response.capabilities, 8); buffer.writeUInt32LE(response.maximalAccess, 12); return buffer; }
/** * Serialize SMB2 Create Response */ public serializeCreateResponse(response: SMB2CreateResponse): Buffer { const baseSize = 89; const contextSize = this.calculateCreateContextsSize(response.createContexts); const totalSize = baseSize + contextSize; const buffer = Buffer.allocUnsafe(totalSize); let offset = 0; buffer.writeUInt16LE(response.structureSize, offset); offset += 2; buffer.writeUInt8(response.oplockLevel, offset); offset += 1; buffer.writeUInt8(response.flags, offset); offset += 1; buffer.writeUInt32LE(response.createAction, offset); offset += 4; buffer.writeBigUInt64LE(response.creationTime, offset); offset += 8; buffer.writeBigUInt64LE(response.lastAccessTime, offset); offset += 8; buffer.writeBigUInt64LE(response.lastWriteTime, offset); offset += 8; buffer.writeBigUInt64LE(response.changeTime, offset); offset += 8; buffer.writeBigUInt64LE(response.allocationSize, offset); offset += 8; buffer.writeBigUInt64LE(response.endOfFile, offset); offset += 8; buffer.writeUInt32LE(response.fileAttributes, offset); offset += 4; buffer.writeUInt32LE(response.reserved2, offset); offset += 4; this.serializeFileId(response.fileId, buffer, offset); offset += 16; buffer.writeUInt32LE(response.createContextsOffset, offset); offset += 4; buffer.writeUInt32LE(response.createContextsLength, offset); offset += 4; // Serialize create contexts for (const context of response.createContexts) { offset += this.serializeCreateContext(context, buffer, offset); } return buffer.subarray(0, offset); }
/** * Serialize SMB2 Close Response */ public serializeCloseResponse(response: SMB2CloseResponse): Buffer { const buffer = Buffer.allocUnsafe(60); let offset = 0; buffer.writeUInt16LE(response.structureSize, offset); offset += 2; buffer.writeUInt16LE(response.flags, offset); offset += 2; buffer.writeUInt32LE(response.reserved, offset); offset += 4; buffer.writeBigUInt64LE(response.creationTime, offset); offset += 8; buffer.writeBigUInt64LE(response.lastAccessTime, offset); offset += 8; buffer.writeBigUInt64LE(response.lastWriteTime, offset); offset += 8; buffer.writeBigUInt64LE(response.changeTime, offset); offset += 8; buffer.writeBigUInt64LE(response.allocationSize, offset); offset += 8; buffer.writeBigUInt64LE(response.endOfFile, offset); offset += 8; buffer.writeUInt32LE(response.fileAttributes, offset); offset += 4; return buffer; }
/** * Serialize SMB2 Read Response */ public serializeReadResponse(response: SMB2ReadResponse): Buffer { const totalSize = 17 + response.dataLength; const buffer = Buffer.allocUnsafe(totalSize); let offset = 0; buffer.writeUInt16LE(response.structureSize, offset); offset += 2; buffer.writeUInt8(response.dataOffset, offset); offset += 1; buffer.writeUInt8(0, offset); offset += 1; // Reserved buffer.writeUInt32LE(response.dataLength, offset); offset += 4; buffer.writeUInt32LE(response.dataRemaining, offset); offset += 4; buffer.writeUInt32LE(response.reserved, offset); offset += 4; // Copy data if (response.dataLength > 0) { response.data.copy(buffer, offset, 0, response.dataLength); } return buffer; }
/** * Serialize SMB2 Write Response */ public serializeWriteResponse(response: SMB2WriteResponse): Buffer { const totalSize = 17 + response.writeChannelInfoLength; const buffer = Buffer.allocUnsafe(totalSize); let offset = 0; buffer.writeUInt16LE(response.structureSize, offset); offset += 2; buffer.writeUInt16LE(response.reserved, offset); offset += 2; buffer.writeUInt32LE(response.count, offset); offset += 4; buffer.writeUInt32LE(response.remaining, offset); offset += 4; buffer.writeUInt16LE(response.writeChannelInfoOffset, offset); offset += 2; buffer.writeUInt16LE(response.writeChannelInfoLength, offset); offset += 2; // Copy write channel info if (response.writeChannelInfoLength > 0) { response.writeChannelInfo.copy(buffer, offset, 0, response.writeChannelInfoLength); } return buffer; }
/** * Serialize SMB2 Query Directory Response */ public serializeQueryDirectoryResponse(response: SMB2QueryDirectoryResponse): Buffer { const totalSize = 9 + response.outputBufferLength; const buffer = Buffer.allocUnsafe(totalSize); let offset = 0; buffer.writeUInt16LE(response.structureSize, offset); offset += 2; buffer.writeUInt16LE(response.outputBufferOffset, offset); offset += 2; buffer.writeUInt32LE(response.outputBufferLength, offset); offset += 4; // Copy output buffer if (response.outputBufferLength > 0) { response.outputBuffer.copy(buffer, offset, 0, response.outputBufferLength); } return buffer; }
/** * Serialize SMB2 Query Info Response */ public serializeQueryInfoResponse(response: SMB2QueryInfoResponse): Buffer { const totalSize = 9 + response.outputBufferLength; const buffer = Buffer.allocUnsafe(totalSize); let offset = 0; buffer.writeUInt16LE(response.structureSize, offset); offset += 2; buffer.writeUInt16LE(response.outputBufferOffset, offset); offset += 2; buffer.writeUInt32LE(response.outputBufferLength, offset); offset += 4; // Copy output buffer if (response.outputBufferLength > 0) { response.outputBuffer.copy(buffer, offset, 0, response.outputBufferLength); } return buffer; }
/** * Serialize SMB2 Set Info Response */ public serializeSetInfoResponse(response: SMB2SetInfoResponse): Buffer { const buffer = Buffer.allocUnsafe(2); buffer.writeUInt16LE(response.structureSize, 0); return buffer; }
/** * Serialize SMB2 Error Response */ public serializeErrorResponse(response: SMB2ErrorResponse): Buffer { const totalSize = 9 + response.byteCount; const buffer = Buffer.allocUnsafe(totalSize); let offset = 0; buffer.writeUInt16LE(response.structureSize, offset); offset += 2; buffer.writeUInt16LE(response.reserved, offset); offset += 2; buffer.writeUInt32LE(response.byteCount, offset); offset += 4; // Copy error data if (response.byteCount > 0) { response.errorData.copy(buffer, offset, 0, response.byteCount); } return buffer; }
/** * Serialize File Basic Information */ public serializeFileBasicInformation(info: FileBasicInformation): Buffer { const buffer = Buffer.allocUnsafe(40); let offset = 0; buffer.writeBigUInt64LE(info.creationTime, offset); offset += 8; buffer.writeBigUInt64LE(info.lastAccessTime, offset); offset += 8; buffer.writeBigUInt64LE(info.lastWriteTime, offset); offset += 8; buffer.writeBigUInt64LE(info.changeTime, offset); offset += 8; buffer.writeUInt32LE(info.fileAttributes, offset); offset += 4; buffer.writeUInt32LE(info.reserved, offset); offset += 4; return buffer; }
/** * Serialize File Standard Information */ public serializeFileStandardInformation(info: FileStandardInformation): Buffer { const buffer = Buffer.allocUnsafe(24); let offset = 0; buffer.writeBigUInt64LE(info.allocationSize, offset); offset += 8; buffer.writeBigUInt64LE(info.endOfFile, offset); offset += 8; buffer.writeUInt32LE(info.numberOfLinks, offset); offset += 4; buffer.writeUInt8(info.deletePending ? 1 : 0, offset); offset += 1; buffer.writeUInt8(info.directory ? 1 : 0, offset); offset += 1; buffer.writeUInt16LE(info.reserved, offset); offset += 2; return buffer; }
/** * Serialize File Directory Information */ public serializeFileDirectoryInformation(info: FileDirectoryInformation): Buffer { const fileNameBytes = Buffer.from(info.fileName, 'utf16le'); const totalSize = 64 + fileNameBytes.length; const buffer = Buffer.allocUnsafe(totalSize); let offset = 0; buffer.writeUInt32LE(info.nextEntryOffset, offset); offset += 4; buffer.writeUInt32LE(info.fileIndex, offset); offset += 4; buffer.writeBigUInt64LE(info.creationTime, offset); offset += 8; buffer.writeBigUInt64LE(info.lastAccessTime, offset); offset += 8; buffer.writeBigUInt64LE(info.lastWriteTime, offset); offset += 8; buffer.writeBigUInt64LE(info.changeTime, offset); offset += 8; buffer.writeBigUInt64LE(info.endOfFile, offset); offset += 8; buffer.writeBigUInt64LE(info.allocationSize, offset); offset += 8; buffer.writeUInt32LE(info.fileAttributes, offset); offset += 4; buffer.writeUInt32LE(info.fileNameLength, offset); offset += 4; // Copy filename fileNameBytes.copy(buffer, offset); return buffer; }
/** * Serialize SMB2 File ID */ public serializeFileId(fileId: SMB2FileId, buffer: Buffer, offset: number): void { buffer.writeBigUInt64LE(fileId.persistent, offset); buffer.writeBigUInt64LE(fileId.volatile, offset + 8); }
/** * Serialize SMB2 GUID */ public serializeGuid(guid: SMB2Guid, buffer: Buffer, offset: number): void { // Convert hex string to bytes const guidBytes = Buffer.from(guid.replace(/-/g, ''), 'hex'); if (guidBytes.length !== 16) { throw new Error('Invalid GUID format'); } guidBytes.copy(buffer, offset); }
/** * Serialize UTF-16LE string */ public serializeUtf16String(str: string, buffer: Buffer, offset: number): number { const stringBytes = Buffer.from(str, 'utf16le'); stringBytes.copy(buffer, offset); return stringBytes.length; }
/** * Serialize FILETIME (Windows 64-bit timestamp) */ public serializeFileTime(date: Date, buffer: Buffer, offset: number): void { const filetime = this.dateToFileTime(date); buffer.writeBigUInt64LE(filetime, offset); }
/** * Serialize SMB2 Negotiate Context */ private serializeNegotiateContext(context: SMB2NegotiateContext, buffer: Buffer, offset: number): number { const startOffset = offset; buffer.writeUInt16LE(context.contextType, offset); offset += 2; buffer.writeUInt16LE(context.dataLength, offset); offset += 2; buffer.writeUInt32LE(context.reserved, offset); offset += 4; // Copy context data if (context.dataLength > 0) { context.data.copy(buffer, offset, 0, context.dataLength); offset += context.dataLength; } // Add padding to align to 8-byte boundary const padding = (8 - (offset % 8)) % 8; buffer.fill(0, offset, offset + padding); offset += padding; return offset - startOffset; }
/** * Serialize SMB2 Create Context */ private serializeCreateContext(context: SMB2CreateContext, buffer: Buffer, offset: number): number { const startOffset = offset; const nameBytes = Buffer.from(context.name, 'utf16le'); buffer.writeUInt32LE(context.next, offset); offset += 4; buffer.writeUInt16LE(context.nameOffset, offset); offset += 2; buffer.writeUInt16LE(context.nameLength, offset); offset += 2; buffer.writeUInt16LE(context.reserved, offset); offset += 2; buffer.writeUInt16LE(context.dataOffset, offset); offset += 2; buffer.writeUInt32LE(context.dataLength, offset); offset += 4; // Copy name nameBytes.copy(buffer, offset); offset += nameBytes.length; // Add padding const namePadding = (8 - (offset % 8)) % 8; buffer.fill(0, offset, offset + namePadding); offset += namePadding; // Copy data if (context.dataLength > 0) { context.data.copy(buffer, offset, 0, context.dataLength); offset += context.dataLength; } // Add padding to align to 8-byte boundary const dataPadding = (8 - (offset % 8)) % 8; buffer.fill(0, offset, offset + dataPadding); offset += dataPadding; return offset - startOffset; }
/** * Calculate total size needed for negotiate contexts */ private calculateNegotiateContextsSize(contexts: SMB2NegotiateContext[]): number { let totalSize = 0; for (const context of contexts) { totalSize += 8 + context.dataLength; // Header + data totalSize += (8 - (totalSize % 8)) % 8; // Padding } return totalSize; }
/** * Calculate total size needed for create contexts */ private calculateCreateContextsSize(contexts: SMB2CreateContext[]): number { let totalSize = 0; for (const context of contexts) { const nameLength = Buffer.from(context.name, 'utf16le').length; totalSize += 16 + nameLength + context.dataLength; // Header + name + data totalSize += (8 - (totalSize % 8)) % 8; // Padding } return totalSize; }
/** * Convert JavaScript Date to FILETIME */ private dateToFileTime(date: Date): bigint { // FILETIME is 100-nanosecond intervals since January 1, 1601 const FILETIME_EPOCH_DIFF = 11644473600000n; // milliseconds between 1601 and 1970 const timestamp = BigInt(date.getTime()) + FILETIME_EPOCH_DIFF; return timestamp * 10000n; // Convert to 100-nanosecond intervals }
/** * Pad buffer to specified alignment */ private padToAlignment(buffer: Buffer, offset: number, alignment: number): number { const padding = (alignment - (offset % alignment)) % alignment; buffer.fill(0, offset, offset + padding); return offset + padding; }
/** * Get the serialized size of a structure */ public getSerializedSize(type: string, data: any): number { switch (type) { case 'header': return SMB2_HEADER_SIZE; case 'transform-header': return SMB2_TRANSFORM_HEADER_SIZE; case 'file-id': return 16; case 'guid': return 16; case 'file-basic-info': return 40; case 'file-standard-info': return 24; case 'file-directory-info': return 64 + Buffer.from(data.fileName, 'utf16le').length; default: throw new Error(`Unknown type: ${type}`); } }}