618 lines
12 KiB
C++
618 lines
12 KiB
C++
//+--------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
//
|
|
// File: hex.cpp
|
|
//
|
|
// Contents: hex encode/decode implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include <windows.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#include <dbgdef.h>
|
|
#include "pkifmt.h"
|
|
|
|
#include <tchar.h> // must be included after dbgdef.h
|
|
|
|
|
|
__inline BOOL
|
|
_IsWhiteSpaceChar(
|
|
TCHAR ch)
|
|
{
|
|
return(
|
|
TEXT(' ') == ch ||
|
|
TEXT('\t') == ch ||
|
|
TEXT('\r') == ch);
|
|
}
|
|
|
|
|
|
DWORD
|
|
_DigToHex(
|
|
IN TCHAR ch,
|
|
OUT BYTE *pb)
|
|
{
|
|
BYTE b;
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
|
|
if (!_istxdigit(ch))
|
|
{
|
|
dwErr = ERROR_INVALID_DATA;
|
|
#if DBG
|
|
DbgPrintf(DBG_SS_TRACE, "bad hex data: %02x\n", ch);
|
|
#endif //DBG
|
|
goto BadHexDataError;
|
|
}
|
|
if (_istdigit(ch))
|
|
{
|
|
b = ch - TEXT('0');
|
|
}
|
|
else
|
|
if (_istupper(ch))
|
|
{
|
|
b = ch - TEXT('A') + 10;
|
|
}
|
|
else
|
|
{
|
|
b = ch - TEXT('a') + 10;
|
|
}
|
|
*pb = b;
|
|
|
|
ErrorReturn:
|
|
return (dwErr);
|
|
|
|
SET_ERROR(BadHexDataError, dwErr)
|
|
}
|
|
|
|
|
|
__inline BOOL
|
|
_IsSkipChar(
|
|
TCHAR ch)
|
|
{
|
|
return(
|
|
TEXT(' ') == ch ||
|
|
TEXT('\t') == ch ||
|
|
TEXT('\r') == ch ||
|
|
TEXT('\n') == ch ||
|
|
TEXT(',') == ch ||
|
|
TEXT('-') == ch);
|
|
}
|
|
|
|
|
|
DWORD
|
|
_HexDecodeSimple(
|
|
IN TCHAR const *pchIn,
|
|
IN DWORD cchIn,
|
|
OPTIONAL OUT BYTE *pbOut,
|
|
IN OUT DWORD *pcbOut)
|
|
{
|
|
DWORD dwErr;
|
|
TCHAR const *pch = pchIn;
|
|
TCHAR const *pchEnd;
|
|
DWORD cb = 0;
|
|
BOOL fOverFlow = FALSE;
|
|
|
|
pchEnd = &pchIn[cchIn];
|
|
while (pch < pchEnd)
|
|
{
|
|
BYTE blo;
|
|
BYTE bhi;
|
|
|
|
if (_IsSkipChar(*pch))
|
|
{
|
|
pch++;
|
|
continue;
|
|
}
|
|
|
|
dwErr = _DigToHex(*pch, &bhi);
|
|
if (ERROR_SUCCESS != dwErr)
|
|
{
|
|
goto _DigToHexError;
|
|
}
|
|
|
|
pch++;
|
|
if (pch >= pchEnd)
|
|
{
|
|
dwErr = ERROR_INVALID_DATA;
|
|
goto BadHexDataError;
|
|
}
|
|
dwErr = _DigToHex(*pch, &blo);
|
|
if (ERROR_SUCCESS != dwErr)
|
|
{
|
|
goto _DigToHexError;
|
|
}
|
|
|
|
pch++;
|
|
if (NULL != pbOut)
|
|
{
|
|
if (cb >= *pcbOut)
|
|
{
|
|
fOverFlow = TRUE;
|
|
pbOut = NULL;
|
|
}
|
|
else
|
|
{
|
|
*pbOut++ = blo | (bhi << 4);
|
|
}
|
|
}
|
|
cb++;
|
|
}
|
|
*pcbOut = cb;
|
|
|
|
if (fOverFlow)
|
|
{
|
|
dwErr = ERROR_MORE_DATA;
|
|
goto MoreDataError;
|
|
}
|
|
|
|
ErrorReturn:
|
|
return (dwErr);
|
|
|
|
SET_ERROR(MoreDataError, dwErr)
|
|
SET_ERROR(BadHexDataError, dwErr)
|
|
TRACE_ERROR(_DigToHexError)
|
|
}
|
|
|
|
|
|
DWORD
|
|
_HexParse(
|
|
IN OUT TCHAR const **ppch,
|
|
IN TCHAR const *pchEnd,
|
|
IN DWORD cDigitMin,
|
|
IN DWORD cDigitMax,
|
|
OUT DWORD *pdwValue)
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
TCHAR const *pch = *ppch;
|
|
DWORD Value = 0;
|
|
DWORD cDigit = 0;
|
|
BYTE b;
|
|
|
|
*pdwValue = 0;
|
|
|
|
while (pch < pchEnd && cDigit <= cDigitMax)
|
|
{
|
|
//printf("HexParse %u/%u-%u, ch=%02x\n", cDigit, cDigitMin, cDigitMax, *pch);
|
|
dwErr = _DigToHex(*pch, &b);
|
|
if (ERROR_SUCCESS != dwErr)
|
|
{
|
|
break;
|
|
}
|
|
Value = b | (Value << 4);
|
|
pch++;
|
|
cDigit++;
|
|
|
|
}
|
|
//printf("HexParse %u/%u-%u, val=%x\n", cDigit, cDigitMin, cDigitMax, Value);
|
|
if (cDigit < cDigitMin || cDigit > cDigitMax)
|
|
{
|
|
dwErr = ERROR_INVALID_DATA;
|
|
goto BadHexDataError;
|
|
}
|
|
*pdwValue = Value;
|
|
*ppch = pch;
|
|
dwErr = ERROR_SUCCESS;
|
|
|
|
ErrorReturn:
|
|
return (dwErr);
|
|
|
|
SET_ERROR(BadHexDataError, dwErr)
|
|
}
|
|
|
|
|
|
#define HS_ADDRESS 0
|
|
#define HS_HEXDATA 1
|
|
#define HS_ASCIIDATA 2
|
|
#define HS_NEWLINE 3
|
|
|
|
DWORD
|
|
_HexDecodeComplex(
|
|
IN TCHAR const *pchIn,
|
|
IN DWORD cchIn,
|
|
IN DWORD Flags,
|
|
OPTIONAL OUT BYTE *pbOut,
|
|
IN OUT DWORD *pcbOut)
|
|
{
|
|
TCHAR const *pch = pchIn;
|
|
TCHAR const *pchEnd;
|
|
DWORD cb = 0;
|
|
DWORD dwErr;
|
|
DWORD LastAddress = 0;
|
|
DWORD Address;
|
|
DWORD i;
|
|
BOOL fOverFlow = FALSE;
|
|
BOOL fPartialLastLine = FALSE;
|
|
int *pStateBase;
|
|
int *pState;
|
|
|
|
int s_aASCIIADDRState[] = { HS_ADDRESS, HS_HEXDATA, HS_ASCIIDATA, HS_NEWLINE };
|
|
int s_aASCIIState[] = { HS_HEXDATA, HS_ASCIIDATA, HS_NEWLINE };
|
|
int s_aADDRState[] = { HS_ADDRESS, HS_HEXDATA, HS_NEWLINE };
|
|
|
|
|
|
switch (Flags)
|
|
{
|
|
case CRYPT_STRING_HEXASCII: // 5
|
|
pStateBase = s_aASCIIState;
|
|
break;
|
|
|
|
case CRYPT_STRING_HEXADDR: // 0xa
|
|
pStateBase = s_aADDRState;
|
|
break;
|
|
|
|
case CRYPT_STRING_HEXASCIIADDR: // 0xb
|
|
pStateBase = s_aASCIIADDRState;
|
|
break;
|
|
|
|
default:
|
|
dwErr = ERROR_INVALID_DATA; //hr = E_INVALIDARG;
|
|
goto FlagsError;
|
|
}
|
|
pState = pStateBase;
|
|
|
|
pchEnd = &pchIn[cchIn];
|
|
while (pch < pchEnd)
|
|
{
|
|
//printf("f=%x: *pState: %u ch=%02x\n", Flags, *pState, *pch);
|
|
switch (*pState++)
|
|
{
|
|
case HS_ADDRESS:
|
|
// decode 4 to 8 digit address:
|
|
|
|
while (pch < pchEnd && _IsWhiteSpaceChar(*pch))
|
|
{
|
|
pch++;
|
|
}
|
|
if (pch >= pchEnd)
|
|
{
|
|
continue; // Done: no more data
|
|
}
|
|
dwErr = _HexParse(&pch, pchEnd, 4, 8, &Address);
|
|
if (ERROR_SUCCESS != dwErr)
|
|
{
|
|
goto _HexParseError;
|
|
}
|
|
|
|
//printf("f=%x: Address: %x\n", Flags, Address);
|
|
if (!fPartialLastLine &&
|
|
0 != LastAddress &&
|
|
LastAddress + 16 != Address)
|
|
{
|
|
dwErr = ERROR_INVALID_DATA;
|
|
goto BadHexDataError;
|
|
}
|
|
LastAddress = Address;
|
|
break;
|
|
|
|
case HS_HEXDATA:
|
|
// decode up to 16 bytes of hex data
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
DWORD Data;
|
|
|
|
// decode 2 digit byte value:
|
|
|
|
while (pch < pchEnd && _IsSkipChar(*pch))
|
|
{
|
|
pch++;
|
|
}
|
|
if (pch >= pchEnd)
|
|
{
|
|
break; // Done: no more data
|
|
}
|
|
if (fPartialLastLine)
|
|
{
|
|
//printf("f=%x: fPartialLastLine extra data: %02x\n", Flags, *pch);
|
|
dwErr = ERROR_INVALID_DATA;
|
|
goto DataAfterEndError;
|
|
}
|
|
dwErr = _HexParse(&pch, pchEnd, 2, 2, &Data);
|
|
if (ERROR_SUCCESS != dwErr)
|
|
{
|
|
// Must be a partial last line. The only additional
|
|
// data should be an optional partial ascii display on
|
|
// the right, a newline, and possibly one more address
|
|
// line.
|
|
|
|
//printf("f=%x: fPartialLastLine = TRUE: %02x\n", Flags, *pch);
|
|
fPartialLastLine = TRUE;
|
|
break;
|
|
}
|
|
|
|
//printf("f=%x: Data[%u]: %02x\n", Flags, i, Data);
|
|
if (NULL != pbOut)
|
|
{
|
|
if (cb >= *pcbOut)
|
|
{
|
|
fOverFlow = TRUE;
|
|
pbOut = NULL;
|
|
}
|
|
else
|
|
{
|
|
*pbOut++ = (BYTE) Data;
|
|
}
|
|
}
|
|
cb++;
|
|
}
|
|
break;
|
|
|
|
case HS_ASCIIDATA:
|
|
// skip up to 16 non-whitespace characters
|
|
|
|
while (pch < pchEnd && _IsWhiteSpaceChar(*pch))
|
|
{
|
|
pch++;
|
|
}
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
if (pch >= pchEnd || TEXT(' ') > *pch || TEXT('~') < *pch)
|
|
{
|
|
break;
|
|
}
|
|
//printf("f=%x: Ascii[%u]: %c\n", Flags, i, *pch);
|
|
pch++;
|
|
}
|
|
break;
|
|
|
|
case HS_NEWLINE:
|
|
// skip whitespace characters and a newline
|
|
|
|
while (pch < pchEnd && _IsWhiteSpaceChar(*pch))
|
|
{
|
|
//printf("f=%x: NL skip: %02x\n", Flags, *pch);
|
|
pch++;
|
|
}
|
|
//printf("f=%x: NL: %02x\n", Flags, *pch);
|
|
if (pch >= pchEnd)
|
|
{
|
|
continue; // Done: no more data
|
|
}
|
|
if (TEXT('\n') != *pch)
|
|
{
|
|
//printf("f=%x: Extra Data: %02x\n", Flags, *pch);
|
|
dwErr = ERROR_INVALID_DATA;
|
|
goto ExtraDataOnLineError;
|
|
}
|
|
//printf("f=%x: NewLine\n", Flags);
|
|
pch++;
|
|
pState = pStateBase;
|
|
break;
|
|
|
|
default:
|
|
assert(!"Bad *pState");
|
|
}
|
|
}
|
|
*pcbOut = cb;
|
|
if (fOverFlow)
|
|
{
|
|
dwErr = ERROR_MORE_DATA;
|
|
goto MoreDataError;
|
|
}
|
|
|
|
dwErr = ERROR_SUCCESS;
|
|
ErrorReturn:
|
|
return(dwErr);
|
|
|
|
SET_ERROR(MoreDataError, dwErr)
|
|
SET_ERROR(ExtraDataOnLineError, dwErr)
|
|
SET_ERROR(BadHexDataError, dwErr)
|
|
TRACE_ERROR(_HexParseError)
|
|
SET_ERROR(DataAfterEndError, dwErr)
|
|
SET_ERROR(FlagsError, dwErr)
|
|
}
|
|
|
|
|
|
DWORD
|
|
HexDecode(
|
|
IN TCHAR const *pchIn,
|
|
IN DWORD cchIn,
|
|
IN DWORD Flags,
|
|
OPTIONAL OUT BYTE *pbOut,
|
|
IN OUT DWORD *pcbOut)
|
|
{
|
|
DWORD dwErr;
|
|
|
|
if (CRYPT_STRING_HEX == Flags)
|
|
{
|
|
dwErr = _HexDecodeSimple(pchIn, cchIn, pbOut, pcbOut);
|
|
if (ERROR_SUCCESS != dwErr)
|
|
{
|
|
#if DBG
|
|
//skip ERROR_INVALID_DATA dbg print
|
|
if (ERROR_INVALID_DATA == dwErr)
|
|
{
|
|
SetLastError(dwErr);
|
|
goto ErrorReturn;
|
|
}
|
|
#endif
|
|
goto _HexDecodeSimpleError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwErr = _HexDecodeComplex(pchIn, cchIn, Flags, pbOut, pcbOut);
|
|
if (ERROR_SUCCESS != dwErr)
|
|
{
|
|
#if DBG
|
|
//skip ERROR_INVALID_DATA dbg print
|
|
if (ERROR_INVALID_DATA == dwErr)
|
|
{
|
|
SetLastError(dwErr);
|
|
goto ErrorReturn;
|
|
}
|
|
#endif
|
|
goto _HexDecodeComplexError;
|
|
}
|
|
}
|
|
|
|
ErrorReturn:
|
|
return(dwErr);
|
|
|
|
TRACE_ERROR(_HexDecodeSimpleError)
|
|
TRACE_ERROR(_HexDecodeComplexError)
|
|
}
|
|
|
|
TCHAR
|
|
_IsPrintableChar(TCHAR ch)
|
|
{
|
|
if (ch < TEXT(' ') || ch > TEXT('~'))
|
|
{
|
|
ch = TEXT('.');
|
|
}
|
|
return(ch);
|
|
}
|
|
|
|
|
|
// Encode a BYTE array into text as a hex dump.
|
|
// Use CR-LF pairs for line breaks, unless CRYPT_STRING_NOCR is set.
|
|
// Do not '\0' terminate the text string -- that's handled by the caller.
|
|
|
|
DWORD
|
|
HexEncode(
|
|
IN BYTE const *pbIn,
|
|
IN DWORD cbIn,
|
|
IN DWORD Flags,
|
|
OPTIONAL OUT TCHAR *pchOut,
|
|
IN OUT DWORD *pcchOut)
|
|
{
|
|
TCHAR const *pszsep;
|
|
TCHAR const *psznl;
|
|
DWORD r;
|
|
DWORD i;
|
|
DWORD cbremain;
|
|
DWORD cchOut = 0;
|
|
DWORD cch;
|
|
TCHAR *pch = pchOut;
|
|
DWORD dwErr = ERROR_MORE_DATA;
|
|
BOOL fAscii = FALSE;
|
|
BOOL fAddress = FALSE;
|
|
TCHAR szAddress[32];
|
|
BOOL fNoCR = 0 != (CRYPT_STRING_NOCR & Flags);
|
|
|
|
switch (~CRYPT_STRING_NOCR & Flags)
|
|
{
|
|
case CRYPT_STRING_HEX:
|
|
break;
|
|
|
|
case CRYPT_STRING_HEXASCII:
|
|
fAscii = TRUE;
|
|
break;
|
|
|
|
case CRYPT_STRING_HEXADDR:
|
|
fAddress = TRUE;
|
|
break;
|
|
|
|
case CRYPT_STRING_HEXASCIIADDR:
|
|
fAscii = TRUE;
|
|
fAddress = TRUE;
|
|
break;
|
|
|
|
default:
|
|
dwErr = ERROR_INVALID_DATA; //hr = E_INVALIDARG;
|
|
goto FlagsError;
|
|
}
|
|
for (r = 0; r < cbIn; r += 16)
|
|
{
|
|
DWORD iEnd;
|
|
|
|
cbremain = cbIn - r;
|
|
iEnd = min(cbremain, 16);
|
|
|
|
for (i = 0; i < iEnd; i++)
|
|
{
|
|
psznl = TEXT("");
|
|
szAddress[0] = TEXT('\0');
|
|
pszsep = TEXT(" ");
|
|
if ((i % 8) == 0) // 0 or 8
|
|
{
|
|
pszsep = TEXT(" ");
|
|
if (i == 0) // 0
|
|
{
|
|
if (fAddress)
|
|
{
|
|
_stprintf(szAddress, TEXT("%04x"), r);
|
|
}
|
|
pszsep = TEXT("\t");
|
|
if (r != 0) // starting new line
|
|
{
|
|
psznl = fNoCR? TEXT("\n") : TEXT("\r\n");
|
|
pszsep = TEXT("\t");
|
|
}
|
|
}
|
|
}
|
|
cch = _tcslen(psznl) + _tcslen(szAddress) + _tcslen(pszsep) + 2;
|
|
if (NULL != pchOut)
|
|
{
|
|
if (cchOut + cch + 1 > *pcchOut)
|
|
{
|
|
goto MoreDataError;
|
|
}
|
|
_stprintf(
|
|
pch,
|
|
TEXT("%s%s%s%02x"),
|
|
psznl,
|
|
szAddress,
|
|
pszsep,
|
|
pbIn[r + i]);
|
|
pch += cch;
|
|
assert(TEXT('\0') == *pch);
|
|
assert(pch == &pchOut[cchOut + cch]);
|
|
}
|
|
cchOut += cch;
|
|
}
|
|
if (fAscii && 0 != i)
|
|
{
|
|
cch = 3 + (16 - i)*3 + ((i <= 8)? 1 : 0);
|
|
|
|
if (NULL != pchOut)
|
|
{
|
|
if (cchOut + cch + iEnd + 1 > *pcchOut)
|
|
{
|
|
goto MoreDataError;
|
|
}
|
|
_stprintf(pch, TEXT("%*s"), cch, TEXT(""));
|
|
pch += cch;
|
|
assert(TEXT('\0') == *pch);
|
|
assert(pch == &pchOut[cchOut + cch]);
|
|
|
|
for (i = 0; i < iEnd; i++)
|
|
{
|
|
*pch++ = _IsPrintableChar(pbIn[r + i]);
|
|
assert(pch == &pchOut[cchOut + cch + i + 1]);
|
|
}
|
|
}
|
|
cchOut += cch + iEnd;
|
|
}
|
|
}
|
|
if (r != 0)
|
|
{
|
|
DWORD cchnl = fNoCR? 1 : 2;
|
|
|
|
if (NULL != pchOut)
|
|
{
|
|
if (cchOut + cchnl > *pcchOut)
|
|
{
|
|
goto MoreDataError;
|
|
}
|
|
if (!fNoCR)
|
|
{
|
|
*pch++ = TEXT('\r');
|
|
}
|
|
*pch++ = TEXT('\n');
|
|
assert(pch == &pchOut[cchOut + cchnl]);
|
|
}
|
|
cchOut += cchnl;
|
|
}
|
|
*pcchOut = cchOut;
|
|
|
|
dwErr = ERROR_SUCCESS;
|
|
ErrorReturn:
|
|
return(dwErr);
|
|
|
|
SET_ERROR(MoreDataError, dwErr)
|
|
SET_ERROR(FlagsError, dwErr)
|
|
}
|