/*
///////////////////////////////////////////////////////////////////////////////
//
// FileName : Encode.c
// Version : 1.0
// Creator : RM
// Create Date : 2006-01-01
// Comment :
//
///////////////////////////////////////////////////////////////////////////////
*/
#include < tchar.h >
#include < stdio.h >
#include < stdarg.h >
#include < malloc.h >
#ifdef _MSC_VER
#include < windows.h >
#else
#include "Compatible.h"
#endif
#include "Macro.h"
#include "Map.h"
#include "MD5.h"
#include "AddLog.h"
#include "Dump.h"
#include "Encode.h"
/* Skip warning */
#pragma warning( disable: 4054 )
#pragma warning( disable: 4055 )
#pragma warning( disable: 4100 )
#pragma warning( disable: 4127 )
#pragma warning( disable: 4152 )
#pragma warning( disable: 4201 )
#pragma warning( disable: 4204 )
#pragma warning( disable: 4244 )
#pragma warning( disable: 4245 )
#pragma warning( disable: 4305 )
#pragma warning( disable: 4310 )
#pragma warning( disable: 4505 )
#pragma warning( disable: 4706 )
/*
///////////////////////////////////////////////////////////////////////////////
// D E F I N E S
///////////////////////////////////////////////////////////////////////////////
*/
/* Des */
#define DEFAULT_XOR 0xA5A5
/* CRC32 */
#define CRC32( CRC, Ch ) CRC = ( ( CRC >> 8 ) & 0x00FFFFFF ) ^ g_CRC32Table[( CRC ^ ( Ch ) ) & 0xFF]
/* Check if hook */
#define OP_CODE_INT3 0xCC /* INT 3 */
#define OP_CODE_PUSH 0x68 /* PUSH */
#define OP_CODE_RET 0xC3 /* RET */
#define OP_CODE_CALL 0xE8 /* CALL data32 */
#define OP_CODE_JMP 0xE9 /* JMP data32 */
#define OP_CODE_JMP_FAR 0xEA /* JMP data16:data32 */
#define OP_CODE_JMP_NEAR 0xEB /* JMP data8 */
/* Get global */
#define GET_INTERNAL_STUB( ) g_InternalStubPtr
/*
///////////////////////////////////////////////////////////////////////////////
// S T R U C T U R E S
///////////////////////////////////////////////////////////////////////////////
*/
typedef BOOL ( WINAPI *IsDebuggerPresentProc ) ( );
typedef BOOL ( WINAPI *VirtualProtectProc ) ( LPVOID, DWORD, DWORD, PDWORD );
typedef UCHAR ( DES_SUBKEY )[16] [48];
typedef DES_SUBKEY *PDES_SUBKEY;
/*
///////////////////////////////////////////////////////////////////////////////
// G L O B A L S
///////////////////////////////////////////////////////////////////////////////
*/
static UCHAR g_DummyStart[4] = {
0, 1, 0, 1
};
static UINT g_CRC32Table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};
/* Initial permutation IP */
static UCHAR IP_Table[64] = {
58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
};
/* Final permutation IP^-1 */
static UCHAR IPR_Table[64] = {
40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
};
/* Expansion operation matrix */
static UCHAR E_Table[48] = {
32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1
};
/* 32-bit permutation function P used on the output of the S-boxes */
static UCHAR P_Table[32] = {
16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
};
/* Permuted choice table (Key) */
static UCHAR PC1_Table[56] = {
57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
};
/* Permuted choice key (table) */
static UCHAR PC2_Table[48] = {
14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
};
/* Number left rotations of pc1 */
static UCHAR LOOP_Table[16] = {
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
/* The (in)famous S-boxes */
static UCHAR S_Box[8] [4] [16] = {
/* S1 */
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
/* S2 */
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
/* S3 */
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
/* S4 */
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
/* S5 */
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
/* S6 */
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
/* S7 */
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
/* S8 */
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
};
static UCHAR g_DummyEnd[4] = {
0, 1, 0, 1
};
void EncodeDummyStart( );
void EncodeDummyEnd( );
ENCODE_RANGE g_InternalRange[] = {
{ g_DummyStart, g_DummyEnd, ES_FLAGS_ENCODE_1 },
{ EncodeDummyStart, EncodeDummyEnd, ES_FLAGS_ENCODE_1 },
};
ENCODE_STUB g_InternalStub = {
TYPE_ENCODE_INTERNAL,
sizeof( ENCODE_STUB ),
0,
0,
DEFAULT_ENCODE_KEY,
DEFAULT_ENCODE_KEY_LENGTH,
{ 0 },
0,
{ 0 },
SIZE_OF_ARRAY( g_InternalRange ),
g_InternalRange
};
PENCODE_STUB g_InternalStubPtr = &g_InternalStub;
/*
///////////////////////////////////////////////////////////////////////////////
// F U N C T I O N S P R O T O T Y P E
///////////////////////////////////////////////////////////////////////////////
*/
/* Standard DES encode/decode */
static void DES( UCHAR Out[8], const UCHAR In[8], const PDES_SUBKEY SubKey, ULONG Type );
static void SetKey( PDES_SUBKEY SubKey, ULONG *Is3DES, const UCHAR *Key, ULONG Length );
static void SetSubKey( PDES_SUBKEY SubKey, const UCHAR Key[8] );
static void F_Func( UCHAR In[32], const UCHAR Ki[48] );
static void S_Func( UCHAR Out[32], const UCHAR In[48] );
static void Transform( UCHAR *Out, const UCHAR *In, const UCHAR *Table, ULONG Length );
static void Xor( UCHAR *InA, const UCHAR *InB, ULONG Length );
static void RotateL( UCHAR *In, ULONG Length, ULONG Loop );
static void ByteToBit( UCHAR *Out, const UCHAR *In, ULONG Bits );
static void BitToByte( UCHAR *Out, const UCHAR *In, ULONG Bits );
/* Get executable file information */
ULONG GetPeInfo( PVOID, PIMAGE_NT_HEADERS*, PIMAGE_FILE_HEADER*, PIMAGE_OPTIONAL_HEADER*, PIMAGE_SECTION_HEADER* );
/*
///////////////////////////////////////////////////////////////////////////////
// F U N C T I O N S
///////////////////////////////////////////////////////////////////////////////
*/
void EncodeDummyStart( )
{
/* Just for count */
}
ULONG DesCode( void *Out, const void *In, ULONG Length, const void *Key, ULONG KeyLength, ULONG Type )
{
register int Iter;
register int Count;
ULONG Is3DES;
ULONG Left;
const UCHAR *UKey;
register UCHAR *UOut;
register const UCHAR *UIn;
DES_SUBKEY SubKey[2]; /* 16 loop sub key */
if ( Out == NULL || In == NULL || Key == NULL || Length == 0 )
return FALSE;
UKey = ( const UCHAR* )Key;
SetKey( SubKey, &Is3DES, UKey, KeyLength );
Left = Length & 7;
Length &= ~7;
if ( !Is3DES )
{
/* 1DES */
UOut = ( UCHAR* )Out;
UIn = ( const UCHAR* )In;
for ( Iter = 0, Count = Length >> 3; Iter < Count; Iter++, UOut += 8, UIn += 8 )
DES( UOut, UIn, &SubKey[0], Type );
while ( Left )
{
*UOut = ( UCHAR ) ( *UIn ^ DEFAULT_XOR ^ UKey[Left % KeyLength] );
UIn++;
UOut++;
Left--;
}
}
else
{
/* 3DES */
/* Encode: E(key0)-D(key1)-E(key0) */
/* Decode: D(key0)-E(key1)-D(key0) */
UOut = ( UCHAR* )Out;
UIn = ( const UCHAR* )In;
for ( Iter = 0, Count = Length >> 3; Iter < Count; Iter++, UOut += 8, UIn += 8 )
{
DES( UOut, UIn, &SubKey[0], Type );
DES( UOut, UOut, &SubKey[1], !Type );
DES( UOut, UOut, &SubKey[0], Type );
}
while ( Left )
{
*UOut = ( UCHAR ) ( *UIn ^ DEFAULT_XOR ^ UKey[Left % KeyLength] );
UIn++;
UOut++;
Left--;
}
}
return TRUE;
}
void SetKey( PDES_SUBKEY SubKey, ULONG *Is3DES, const UCHAR *Key, ULONG Length )
{
UCHAR DesKey[16];
memset( DesKey, 0, 16 );
memcpy( DesKey, Key, Length > 16 ? 16 : Length );
SetSubKey( &SubKey[0], &DesKey[0] );
if ( Length > 8 )
{
SetSubKey( &SubKey[1], &DesKey[8] );
if ( Is3DES )
*Is3DES = TRUE;
}
else
{
if ( Is3DES )
*Is3DES = FALSE;
}
}
void DES( UCHAR Out[8], const UCHAR In[8], const PDES_SUBKEY SubKey, ULONG Type )
{
register LONG Iter;
UCHAR M[64];
UCHAR Tmp[32];
UCHAR *Li = &M[0];
UCHAR *Ri = &M[32];
ByteToBit( M, In, 64 );
Transform( M, M, IP_Table, 64 );
if ( Type == DES_ENCRYPT )
{
for ( Iter = 0; Iter < 16; Iter++ )
{
memcpy( Tmp, Ri, 32 );
F_Func( Ri, ( *SubKey )[Iter] );
Xor( Ri, Li, 32 );
memcpy( Li, Tmp, 32 );
}
}
else
{
for ( Iter = 15; Iter >= 0; Iter-- )
{
memcpy( Tmp, Li, 32 );
F_Func( Li, ( *SubKey )[Iter] );
Xor( Li, Ri, 32 );
memcpy( Ri, Tmp, 32 );
}
}
Transform( M, M, IPR_Table, 64 );
BitToByte( Out, M, 64 );
}
void SetSubKey( PDES_SUBKEY SubKey, const UCHAR Key[8] )
{
register ULONG Iter;
UCHAR K[64];
UCHAR *KL = &K[0];
UCHAR *KR = &K[28];
ByteToBit( K, Key, 64 );
Transform( K, K, PC1_Table, 56 );
for ( Iter = 0; Iter < 16; Iter++ )
{
RotateL( KL, 28, LOOP_Table[Iter] );
RotateL( KR, 28, LOOP_Table[Iter] );
Transform( ( *SubKey )[Iter], K, PC2_Table, 48 );
}
}
void F_Func( UCHAR In[32], const UCHAR Ki[48] )
{
UCHAR MR[48];
Transform( MR, In, E_Table, 48 );
Xor( MR, Ki, 48 );
S_Func( In, MR );
Transform( In, In, P_Table, 32 );
}
void S_Func( UCHAR Out[32], const UCHAR In[48] )
{
register UCHAR Iter, Jter, Kter;
for ( Iter = 0; Iter < 8; Iter++, In += 6, Out += 4 )
{
Jter = ( UCHAR ) ( ( In[0] << 1 ) + In[5] );
Kter = ( UCHAR ) ( ( In[1] << 3 ) + ( In[2] << 2 ) + ( In[3] << 1 ) + In[4] );
ByteToBit( Out, &S_Box[Iter] [Jter] [Kter], 4 );
}
}
void Transform( UCHAR *Out, const UCHAR *In, const UCHAR *Table, ULONG Length )
{
register ULONG Iter;
UCHAR Tmp[256];
for ( Iter = 0; Iter < Length; Iter++ )
Tmp[Iter] = In[Table[Iter] - 1];
memcpy( Out, Tmp, Length );
}
void Xor( UCHAR *InA, const UCHAR *InB, ULONG Length )
{
register ULONG Iter;
for ( Iter = 0; Iter < Length; Iter++ )
InA[Iter] ^= InB[Iter];
}
void RotateL( UCHAR *In, ULONG Length, ULONG Loop )
{
UCHAR Tmp[256];
memcpy( Tmp, In, Loop );
memcpy( In, In + Loop, Length - Loop );
memcpy( In + Length - Loop, Tmp, Loop );
}
void ByteToBit( UCHAR *Out, const UCHAR *In, ULONG Bits )
{
register ULONG Iter;
for ( Iter = 0; Iter < Bits; Iter++ )
Out[Iter] = ( UCHAR ) ( ( In[Iter >> 3] >> ( Iter & 7 ) ) & 1 );
}
void BitToByte( UCHAR *Out, const UCHAR *In, ULONG Bits )
{
register ULONG Iter;
memset( Out, 0, Bits >> 3 );
for ( Iter = 0; Iter < Bits; Iter++ )
Out[Iter >> 3] |= In[Iter] << ( Iter & 7 );
}
void GetCRC( PMD5_VALUE MD5, UCHAR *Buffer, ULONG BufferLength )
{
register ULONG Iter;
register UCHAR *BufferPtr;
unsigned __int64 Sum = 0;
UINT CRC = 0;
for ( Iter = BufferLength, BufferPtr = Buffer; Iter--; BufferPtr++ )
{
Sum += *BufferPtr;
/* Use default crc32 */
CRC32( CRC, *BufferPtr );
}
*( ( UINT* )&MD5->Value[0] ) += CRC;
*( ( UINT* )&MD5->Value[4] ) += BufferLength;
*( ( unsigned __int64* )&MD5->Value[8] ) += Sum;
}
ULONG GetFileCRC( PMD5_VALUE MD5, const TCHAR *FileName, ULONG SkipOffset, ULONG SkipSize )
{
ULONG Size;
PVOID FileImage;
MapFileEx( FileName, &FileImage, &Size, NULL, MAP_FILE_READ | MAP_FILE_GENERIC_READ | MAP_FILE_SHARE_READ );
if ( FileImage )
{
memset( MD5, 0, sizeof( *MD5 ) );
GetCRC( MD5, ( UCHAR* )FileImage, SkipOffset );
GetCRC( MD5, ( UCHAR* )FileImage + SkipOffset + SkipSize, Size - SkipOffset - SkipSize );
return TRUE;
}
else
{
return FALSE;
}
}
ULONG GetModuleCRC( PMD5_VALUE MD5, LPVOID ModuleBase, ULONG SkipOffset, ULONG SkipSize )
{
register ULONG Iter;
register ULONG Count;
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_FILE_HEADER FileHeader;
PIMAGE_OPTIONAL_HEADER OptionalHeader;
PIMAGE_SECTION_HEADER SectionHeader;
ULONG Size;
UCHAR *StartAddress;
UCHAR *SkipAddressStart;
UCHAR *SkipAddressEnd;
if ( GetPeInfo( ModuleBase, &NtHeaders, &FileHeader, &OptionalHeader, &SectionHeader ) )
{
memset( MD5, 0, sizeof( *MD5 ) );
SkipAddressStart = ( UCHAR* )ModuleBase + SkipOffset;
SkipAddressEnd = SkipAddressStart + SkipSize;
for ( Iter = 0, Count = FileHeader->NumberOfSections; Iter < Count; Iter++, SectionHeader++ )
{
if ( !( SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE ) && *( ( ULONG* )SectionHeader->Name ) != 0x6164722e )
{
StartAddress = ( UCHAR* )ModuleBase + SectionHeader->VirtualAddress;
Size = SectionHeader->SizeOfRawData;
if ( StartAddress <= SkipAddressStart && SkipAddressEnd < StartAddress + Size )
{
GetCRC( MD5, StartAddress, SkipOffset );
GetCRC( MD5, SkipAddressEnd, Size - SkipOffset - SkipSize );
}
else
{
GetCRC( MD5, StartAddress, Size );
}
}
}
return TRUE;
}
else
{
return FALSE;
}
}
void GetMD5( PMD5_VALUE MD5, UCHAR *Buffer, ULONG BufferLength )
{
MD5_CTX MD5Ctx;
MD5Init( &MD5Ctx );
MD5Update( &MD5Ctx, Buffer, BufferLength );
MD5Final( MD5->Value, &MD5Ctx );
}
ULONG GetFileMD5( PMD5_VALUE MD5, const TCHAR *FileName, ULONG SkipOffset, ULONG SkipSize )
{
ULONG Size;
PVOID FileImage;
MD5_CTX MD5Ctx;
MapFileEx( FileName, &FileImage, &Size, NULL, MAP_FILE_READ | MAP_FILE_GENERIC_READ | MAP_FILE_SHARE_READ );
if ( FileImage )
{
MD5Init( &MD5Ctx );
if ( SkipSize )
{
MD5Update( &MD5Ctx, ( UCHAR* )FileImage, SkipOffset );
MD5Update( &MD5Ctx, ( UCHAR* )FileImage + SkipOffset + SkipSize, Size - SkipOffset - SkipSize );
}
else
{
MD5Update( &MD5Ctx, ( UCHAR* )FileImage, Size );
}
MD5Final( MD5->Value, &MD5Ctx );
UnMapFile( FileImage );
return TRUE;
}
else
{
return FALSE;
}
}
ULONG GetModuleMD5( PMD5_VALUE MD5, LPVOID ModuleBase, ULONG SkipOffset, ULONG SkipSize )
{
register ULONG Iter;
register ULONG Count;
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_FILE_HEADER FileHeader;
PIMAGE_OPTIONAL_HEADER OptionalHeader;
PIMAGE_SECTION_HEADER SectionHeader;
ULONG Size;
UCHAR *StartAddress;
UCHAR *SkipAddressStart;
UCHAR *SkipAddressEnd;
MD5_CTX MD5Ctx;
if ( GetPeInfo( ModuleBase, &NtHeaders, &FileHeader, &OptionalHeader, &SectionHeader ) )
{
memset( MD5, 0, sizeof( *MD5 ) );
SkipAddressStart = ( UCHAR* )ModuleBase + SkipOffset;
SkipAddressEnd = SkipAddressStart + SkipSize;
for ( Iter = 0, Count = FileHeader->NumberOfSections; Iter < Count; Iter++, SectionHeader++ )
{
if ( !( SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE ) && *( ( ULONG* )SectionHeader->Name ) != 0x6164722e )
{
StartAddress = ( UCHAR* )ModuleBase + SectionHeader->VirtualAddress;
Size = SectionHeader->SizeOfRawData;
if ( StartAddress <= SkipAddressStart && SkipAddressEnd < StartAddress + Size )
{
MD5Update( &MD5Ctx, StartAddress, SkipOffset );
MD5Update( &MD5Ctx, SkipAddressEnd, Size - SkipOffset - SkipSize );
}
else
{
MD5Update( &MD5Ctx, StartAddress, Size );
}
}
}
MD5Final( MD5->Value, &MD5Ctx );
return TRUE;
}
else
{
return FALSE;
}
}
ULONG DecodeStubKey( PENCODE_STUB Stub, const TCHAR *ModuleName )
{
ULONG RetCode;
ULONG Flags;
MD5_VALUE MD5;
ULONG Size;
UCHAR *Module;
UCHAR *SkipAddress;
if ( Stub == NULL )
Stub = GET_INTERNAL_STUB( );
Flags = GET_INTERNAL_STUB( )->Flags;
if ( Flags & ES_FLAGS_CRC_FILE )
{
Size = sizeof( MD5 );
SkipAddress = ( UCHAR* )GET_INTERNAL_STUB( );
Module = ( UCHAR* )GetModuleHandle( ModuleName );
RetCode = GetFileCRC( &MD5, ModuleName, SkipAddress - Module, sizeof( ENCODE_STUB ) );
}
else if ( Flags & ES_FLAGS_CRC_MODULE )
{
Size = sizeof( MD5 );
SkipAddress = ( UCHAR* )GET_INTERNAL_STUB( );
Module = ( UCHAR* )GetModuleHandle( ModuleName );
RetCode = GetModuleCRC( &MD5, Module, SkipAddress - Module, sizeof( ENCODE_STUB ) );
}
else if ( Flags & ES_FLAGS_MD5_FILE )
{
Size = sizeof( MD5 );
SkipAddress = ( UCHAR* )GET_INTERNAL_STUB( );
Module = ( UCHAR* )GetModuleHandle( ModuleName );
RetCode = GetFileMD5( &MD5, ModuleName, SkipAddress - Module, sizeof( ENCODE_STUB ) );
}
else if ( Flags & ES_FLAGS_MD5_MODULE )
{
Size = sizeof( MD5 );
SkipAddress = ( UCHAR* )GET_INTERNAL_STUB( );
Module = ( UCHAR* )GetModuleHandle( ModuleName );
RetCode = GetModuleMD5( &MD5, Module, SkipAddress - Module, sizeof( ENCODE_STUB ) );
}
else
{
RetCode = FALSE;
}
if ( RetCode )
DecodeEx( Stub->Key, Stub->KeyLength, Stub->MD5.Value, sizeof( Stub->MD5.Value ) );
return RetCode;
}
ULONG CheckIfHook( UCHAR *Buffer, ULONG Length )
{
register ULONG Iter;
register ULONG Count;
register UCHAR *BufferPtr;
LONG Address;
BufferPtr = Buffer;
Count = Length;
for ( Iter = 0; Iter < Count; Iter++, BufferPtr++ )
{
switch ( *BufferPtr )
{
case OP_CODE_INT3 :
return TRUE;
case OP_CODE_PUSH :
if ( BufferPtr[1] == OP_CODE_RET )
{
Address = *( ( LONG* ) ( BufferPtr + 2 ) );
if ( Address < 0 )
return TRUE;
}
break;
case OP_CODE_CALL :
Address = *( ( LONG* ) ( BufferPtr + 2 ) );
if ( Address < 0 )
return TRUE;
break;
case OP_CODE_JMP :
Address = *( ( LONG* ) ( BufferPtr + 2 ) );
if ( Address < 0 )
return TRUE;
break;
case OP_CODE_JMP_FAR :
Address = *( ( LONG* ) ( BufferPtr + 2 ) );
if ( Address < 0 )
return TRUE;
break;
case OP_CODE_JMP_NEAR :
Address = *( ( CHAR* ) ( BufferPtr + 2 ) );
if ( Address < 0 )
return TRUE;
break;
}
}
return FALSE;
}
ULONG CheckIfTrace( )
{
ULONG Count;
static HMODULE hKernel32 = NULL;
static VirtualProtectProc VirtualProtect = NULL;
static IsDebuggerPresentProc IsDebuggerPresent = NULL;
static TCHAR tszkernel32[] = {
_T( 'k' ) ^ XOR_STR, _T( 'e' ) ^ XOR_STR, _T( 'r' ) ^ XOR_STR, _T( 'n' ) ^ XOR_STR,
_T( 'e' ) ^ XOR_STR, _T( 'l' ) ^ XOR_STR, _T( '3' ) ^ XOR_STR, _T( '2' ) ^ XOR_STR,
_T( '.' ) ^ XOR_STR, _T( 'd' ) ^ XOR_STR, _T( 'l' ) ^ XOR_STR, _T( 'l' ) ^ XOR_STR,
_T( '\0' ) ^ XOR_STR,
};
static CHAR aszVirtualProtect[] = {
( 'V' ) ^ XOR_STR, ( 'i' ) ^ XOR_STR, ( 'r' ) ^ XOR_STR, ( 't' ) ^ XOR_STR,
( 'u' ) ^ XOR_STR, ( 'a' ) ^ XOR_STR, ( 'l' ) ^ XOR_STR, ( 'P' ) ^ XOR_STR,
( 'r' ) ^ XOR_STR, ( 'o' ) ^ XOR_STR, ( 't' ) ^ XOR_STR, ( 'e' ) ^ XOR_STR,
( 'c' ) ^ XOR_STR, ( 't' ) ^ XOR_STR, ( '\0' ) ^ XOR_STR,
};
static CHAR aszIsDebuggerPresent[] = {
( 'I' ) ^ XOR_STR, ( 's' ) ^ XOR_STR, ( 'D' ) ^ XOR_STR, ( 'e' ) ^ XOR_STR,
( 'b' ) ^ XOR_STR, ( 'u' ) ^ XOR_STR, ( 'g' ) ^ XOR_STR, ( 'g' ) ^ XOR_STR,
( 'e' ) ^ XOR_STR, ( 'r' ) ^ XOR_STR, ( 'P' ) ^ XOR_STR, ( 'r' ) ^ XOR_STR,
( 'e' ) ^ XOR_STR, ( 's' ) ^ XOR_STR, ( 'e' ) ^ XOR_STR, ( 'n' ) ^ XOR_STR,
( 't' ) ^ XOR_STR, ( '\0' ) ^ XOR_STR,
};
if ( hKernel32 == NULL )
{
DECODE_STR( Count, tszkernel32 );
hKernel32 = GetModuleHandle( tszkernel32 );
ENCODE_STR( Count, tszkernel32 );
}
if ( VirtualProtect == NULL )
{
if ( hKernel32 )
{
DECODE_STR( Count, aszVirtualProtect );
VirtualProtect = ( VirtualProtectProc )GetProcAddress( hKernel32, aszVirtualProtect );
ENCODE_STR( Count, aszVirtualProtect );
}
}
if ( IsDebuggerPresent == NULL )
{
if ( hKernel32 )
{
DECODE_STR( Count, aszIsDebuggerPresent );
IsDebuggerPresent = ( IsDebuggerPresentProc )GetProcAddress( hKernel32, aszIsDebuggerPresent );
ENCODE_STR( Count, aszIsDebuggerPresent );
}
}
#ifdef _MSC_VER
__try
#endif
{
if ( CheckIfHook( ( UCHAR* )VirtualProtect, 16 ) )
return TRUE;
if ( CheckIfHook( ( UCHAR* )IsDebuggerPresent, 10 ) )
return TRUE;
if ( IsDebuggerPresent( ) )
return TRUE;
}
#ifdef _MSC_VER
__except ( EXCEPTION_EXECUTE_HANDLER )
{
return TRUE;
}
#endif
return FALSE;
}
void DestroyCode( PUCHAR AddressStart, PUCHAR AddressEnd )
{
ULONG OldProtection;
VirtualProtect( AddressStart, AddressEnd - AddressStart, PAGE_READWRITE, &OldProtection );
memset( AddressStart, 0, AddressEnd - AddressStart );
VirtualProtect( AddressStart, AddressEnd - AddressStart, OldProtection, &OldProtection );
}
void AlterCode( PUCHAR AddressStart, PUCHAR AddressEnd )
{
ULONG OldProtection;
VirtualProtect( AddressStart, AddressEnd - AddressStart, PAGE_READWRITE, &OldProtection );
for ( ; AddressStart < AddressEnd; AddressStart++ )
*AddressStart ^= 0xA5;
VirtualProtect( AddressStart, AddressEnd - AddressStart, OldProtection, &OldProtection );
}
void EncodeDummyEnd( )
{
/* Just for count */
EncodeDummyStart( );
}
ULONG GetPeInfo(
IN PVOID ModuleBase,
OUT PIMAGE_NT_HEADERS *ppNH,
OUT PIMAGE_FILE_HEADER *ppFH,
OUT PIMAGE_OPTIONAL_HEADER *ppOH,
OUT PIMAGE_SECTION_HEADER *ppSH
)
{
PIMAGE_DOS_HEADER DosHeader;
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_FILE_HEADER FileHeader;
PIMAGE_OPTIONAL_HEADER OptionalHeader;
PIMAGE_SECTION_HEADER SectionHeader;
#ifdef _MSC_VER
__try
#endif
{
DosHeader = ( PIMAGE_DOS_HEADER )ModuleBase;
if ( DosHeader->e_magic != IMAGE_DOS_SIGNATURE )
return FALSE;
NtHeaders = ( PIMAGE_NT_HEADERS ) ( ( UCHAR* )ModuleBase + DosHeader->e_lfanew );
if ( NtHeaders->Signature != IMAGE_NT_SIGNATURE )
return FALSE;
FileHeader = ( PIMAGE_FILE_HEADER )&( NtHeaders )->FileHeader;
OptionalHeader = ( PIMAGE_OPTIONAL_HEADER ) &( NtHeaders )->OptionalHeader;
if ( ( OptionalHeader )->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC )
return FALSE;
SectionHeader = ( PIMAGE_SECTION_HEADER ) ( ( UCHAR* )OptionalHeader + sizeof( IMAGE_OPTIONAL_HEADER ) );
if ( ppNH )
*ppNH = NtHeaders;
if ( ppFH )
*ppFH = FileHeader;
if ( ppOH )
*ppOH = OptionalHeader;
if ( ppSH )
*ppSH = SectionHeader;
}
#ifdef _MSC_VER
__except( EXCEPTION_EXECUTE_HANDLER )
{
return FALSE;
}
#endif
return TRUE;
}
void RemoveCodeProtection( PVOID ModuleBase )
{
ULONG Iter;
ULONG Count;
PIMAGE_FILE_HEADER FileHeader;
PIMAGE_SECTION_HEADER SectionHeader;
if ( GET_INTERNAL_STUB( )->Flags & ES_FLAGS_ENCODE )
{
if ( GetPeInfo( ModuleBase, NULL, &FileHeader, NULL, &SectionHeader ) )
{
for ( Iter = 0, Count = FileHeader->NumberOfSections; Iter < Count; Iter++, SectionHeader++ )
{
if ( SectionHeader->Characteristics & IMAGE_SCN_CNT_CODE )
VirtualProtect( ( UCHAR* )ModuleBase + SectionHeader->VirtualAddress, SectionHeader->SizeOfRawData, PAGE_READWRITE, &GET_INTERNAL_STUB( )->Protection );
}
}
GET_INTERNAL_STUB( )->Flags |= ES_FLAGS_REMOVE_PROTECTION;
}
}
void RecoverCodeProtection( PVOID ModuleBase )
{
ULONG Iter;
ULONG Count;
PIMAGE_FILE_HEADER FileHeader;
PIMAGE_SECTION_HEADER SectionHeader;
if ( GET_INTERNAL_STUB( )->Flags & ES_FLAGS_REMOVE_PROTECTION )
{
if ( GetPeInfo( ModuleBase, NULL, &FileHeader, NULL, &SectionHeader ) )
{
for ( Iter = 0, Count = FileHeader->NumberOfSections; Iter < Count; Iter++, SectionHeader++ )
{
if ( SectionHeader->Characteristics & IMAGE_SCN_CNT_CODE )
VirtualProtect( ( UCHAR* )ModuleBase + SectionHeader->VirtualAddress, SectionHeader->SizeOfRawData, GET_INTERNAL_STUB( )->Protection, &GET_INTERNAL_STUB( )->Protection );
}
}
GET_INTERNAL_STUB( )->Flags &= ~ES_FLAGS_REMOVE_PROTECTION;
}
}
void EncodeKey( UCHAR *Key, ULONG KeyLength )
{
UCHAR Prev;
UCHAR *KeyPtr;
UCHAR *KeyPtrEnd;
Prev = 0;
KeyPtr = Key;
KeyPtrEnd = Key + KeyLength;
while ( KeyPtr < KeyPtrEnd )
{
Prev = *KeyPtr = ( ( ( *KeyPtr ^ 0xAA ) + Prev ) ^ 0x55 ) + Prev;
KeyPtr++;
}
}
void DecodeKey( UCHAR *Key, ULONG KeyLength )
{
UCHAR Prev;
UCHAR *KeyPtr;
UCHAR *KeyPtrStart;
KeyPtr = Key + KeyLength;
KeyPtrStart = Key;
while ( KeyPtr > KeyPtrStart )
{
KeyPtr--;
if ( KeyPtr == Key )
Prev = 0;
else
Prev = *( KeyPtr - 1 );
*KeyPtr = ( ( ( *KeyPtr - Prev ) ^ 0x55 ) - Prev ) ^ 0xAA;
}
}
void ObfuscateKey( UCHAR *Key, ULONG KeyLength )
{
UCHAR Prev;
UCHAR Cur;
UCHAR *KeyPtr;
UCHAR *KeyPtrEnd;
Prev = 0;
KeyPtr = Key;
KeyPtrEnd = Key + KeyLength;
while ( KeyPtr < KeyPtrEnd )
{
Cur = *KeyPtr;
*KeyPtr++ = ( ( ( Cur ^ 0xAA ) + Prev ) ^ 0x55 ) + Prev;
Prev = Cur;
}
}
void EncodeEx( UCHAR *Buffer, ULONG Length, UCHAR *Key, ULONG KeyLength )
{
ULONG Iter;
ULONG Count;
UCHAR Prev;
UCHAR *BufferPtr;
UCHAR *KeyPtr;
UCHAR *KeyPtrEnd;
BufferPtr = Buffer;
KeyPtrEnd = Key + KeyLength;
for ( Iter = 0, Prev = 0, Count = Length / KeyLength; Iter < Count; Iter++ )
{
KeyPtr = Key;
while ( KeyPtr < KeyPtrEnd )
{
Prev = *BufferPtr = ( ( ( *BufferPtr ^ *KeyPtr ) + Prev ) ^ ~*KeyPtr ) + Prev;
BufferPtr++;
KeyPtr++;
}
}
KeyPtr = Key;
KeyPtrEnd = Key + ( Length % KeyLength );
while ( KeyPtr < KeyPtrEnd )
{
Prev = *BufferPtr = ( ( ( *BufferPtr ^ *KeyPtr ) + Prev ) ^ ~*KeyPtr ) + Prev;
BufferPtr++;
KeyPtr++;
}
}
void DecodeEx( UCHAR *Buffer, ULONG Length, UCHAR *Key, ULONG KeyLength )
{
ULONG Iter;
ULONG Count;
UCHAR Prev;
UCHAR *BufferPtr;
UCHAR *KeyPtr;
UCHAR *KeyPtrStart;
BufferPtr = Buffer + Length;
KeyPtr = Key + ( Length % KeyLength );
KeyPtrStart = Key;
while ( KeyPtr > KeyPtrStart )
{
KeyPtr--;
BufferPtr--;
if ( BufferPtr == Buffer )
Prev = 0;
else
Prev = *( BufferPtr - 1 );
*BufferPtr = ( ( ( *BufferPtr - Prev ) ^ ~*KeyPtr ) - Prev ) ^ *KeyPtr;
}
for ( Iter = 0, Count = Length / KeyLength; Iter < Count; Iter++ )
{
KeyPtr = Key + KeyLength;
while ( KeyPtr > KeyPtrStart )
{
KeyPtr--;
BufferPtr--;
if ( BufferPtr == Buffer )
Prev = 0;
else
Prev = *( BufferPtr - 1 );
*BufferPtr = ( ( ( *BufferPtr - Prev ) ^ ~*KeyPtr ) - Prev ) ^ *KeyPtr;
}
}
}
void EncodeInternalStub( PENCODE_STUB Stub, PVOID ImageBase, PVOID FileImage )
{
ULONG Iter;
ULONG Count;
ULONG Length;
UCHAR *StartAddress;
UCHAR *Key;
ULONG KeyLength;
PENCODE_RANGE Range;
if ( !( Stub->Flags & ES_FLAGS_ENCODE ) )
{
Range = ( PENCODE_RANGE ) ( ( PUCHAR )Stub->Range - ( PUCHAR )ImageBase + ( PUCHAR )FileImage );
Key = Stub->InternalKey;
KeyLength = Stub->InternalKeyLength;
for ( Iter = 0, Count = Stub->Count; Iter < Count; Iter++, Range++ )
{
StartAddress = ( PUCHAR )Range->Start;
Length = ( ( PUCHAR )Range->End - StartAddress ) & ~0xF;
StartAddress += ( PUCHAR )FileImage - ( PUCHAR )ImageBase;
EncodeEx( StartAddress, Length, Key, KeyLength );
}
EncodeKey( Key, KeyLength );
Stub->Flags |= ES_FLAGS_ENCODE;
}
}
void DecodeInternalStub( PENCODE_STUB Stub )
{
ULONG Iter;
ULONG Count;
ULONG Length;
UCHAR *StartAddress;
UCHAR *Key;
ULONG KeyLength;
PENCODE_RANGE Range;
if ( Stub == NULL )
Stub = GET_INTERNAL_STUB( );
if ( Stub->Flags & ES_FLAGS_ENCODE )
{
Key = Stub->InternalKey;
KeyLength = Stub->InternalKeyLength;
DecodeKey( Key, KeyLength );
for ( Iter = 0, Count = Stub->Count; Iter < Count; Iter++ )
{
Range = &Stub->Range[Iter];
StartAddress = ( PUCHAR )Range->Start;
Length = ( ( PUCHAR )Range->End - StartAddress ) & ~0xF;
DecodeEx( StartAddress, Length, Key, KeyLength );
}
Stub->Flags &= ~ES_FLAGS_ENCODE;
}
}
void EncodeStub( PENCODE_STUB Stub, PVOID ImageBase, PVOID FileImage )
{
ULONG Iter;
ULONG Count;
ULONG Length;
UCHAR *StartAddress;
UCHAR *Key;
ULONG KeyLength;
PENCODE_RANGE Range;
if ( !( Stub->Flags & ES_FLAGS_ENCODE ) )
{
Range = ( PENCODE_RANGE ) ( ( PUCHAR )Stub->Range - ( PUCHAR )ImageBase + ( PUCHAR )FileImage );
Key = Stub->Key;
KeyLength = Stub->KeyLength;
for ( Iter = 0, Count = Stub->Count; Iter < Count; Iter++, Range++ )
{
StartAddress = ( PUCHAR )Range->Start;
Length = ( ( PUCHAR )Range->End - StartAddress ) & ~0xF;
StartAddress += ( PUCHAR )FileImage - ( PUCHAR )ImageBase;
if ( Range->Flags & ES_FLAGS_ENCODE_1 )
DesCode( StartAddress, StartAddress, Length, Key, KeyLength, DES_ENCRYPT );
else
EncodeEx( StartAddress, Length, Key, KeyLength );
}
Stub->Flags |= ES_FLAGS_ENCODE;
}
}
void DecodeStub( PENCODE_STUB Stub )
{
ULONG Iter;
ULONG Count;
ULONG Length;
UCHAR *StartAddress;
UCHAR *Key;
ULONG KeyLength;
PENCODE_RANGE Range;
if ( Stub->Flags & ES_FLAGS_ENCODE )
{
Key = Stub->Key;
KeyLength = Stub->KeyLength;
for ( Iter = 0, Count = Stub->Count; Iter < Count; Iter++ )
{
Range = &Stub->Range[Iter];
StartAddress = ( PUCHAR )Range->Start;
Length = ( ( PUCHAR )Range->End - StartAddress ) & ~0xF;
if ( Range->Flags & ES_FLAGS_ENCODE_1 )
DesCode( StartAddress, StartAddress, Length, Key, KeyLength, DES_DECRYPT );
else
DecodeEx( StartAddress, Length, Key, KeyLength );
}
Stub->Flags &= ~ES_FLAGS_ENCODE;
}
}
/*
///////////////////////////////////////////////////////////////////////////////
//
// FileName : Hash.c
// Version : 1.0
// Creator : RM
// Create Date : 2006-01-01
// Comment :
//
///////////////////////////////////////////////////////////////////////////////
*/
#include < tchar.h >
#include < stdio.h >
#include < stdarg.h >
#include < malloc.h >
#ifdef _MSC_VER
#include < windows.h >
#else
#include "Compatible.h"
#endif
#include "AddLog.h"
#include "Hash.h"
#pragma warning( disable : 4706 )
/*
///////////////////////////////////////////////////////////////////////////////
// D E F I N E S
///////////////////////////////////////////////////////////////////////////////
*/
#ifndef HEAP_ALLOC
#define HEAP_ALLOC( Size ) HeapAlloc( GetProcessHeap( ), 0, Size )
#endif
#ifndef HEAP_FREE
#define HEAP_FREE( Buffer ) HeapFree( GetProcessHeap( ), 0, Buffer )
#endif
#ifndef HEAP_REALLOC
#define HEAP_REALLOC( Buffer, Size ) HeapReAlloc( GetProcessHeap( ), 0, Buffer, Size )
#endif
#define HANDLE_BLOCK_INTERRUPTIONS( )
#define HANDLE_UNBLOCK_INTERRUPTIONS( )
#define HANDLE_NUMERIC( Key, Length, Func ) { \
register const TCHAR *Tmp = Key; \
if ( *Tmp == _T( '-' ) ) { \
Tmp++; \
} \
if ( ( *Tmp >= _T( '0' ) && *Tmp <= _T( '9' ) ) ) do { /* Possibly numeric index */ \
const TCHAR *End = Key + Length - 1; \
ULONG Index; \
if ( *Tmp++ == _T( '0' ) && Length > 2 ) { /* Don't accept numbers with leading zeros */ \
break; \
} \
while ( Tmp < End ) { \
if ( !( *Tmp >= _T( '0' ) && *Tmp <= _T( '9' ) ) ) { \
break; \
} \
Tmp++; \
} \
if ( Tmp == End && *Tmp == _T( '\0' ) ) { /* Numeric index */ \
if ( *Key == _T( '-' ) ) { \
Index = _tcstol( Key, NULL, 10 ); \
if ( Index != LONG_MIN ) { \
return Func; \
} \
} else { \
Index = _tcstol( Key, NULL, 10 ); \
if ( Index != LONG_MAX ) { \
return Func; \
} \
} \
} \
} while ( 0 ); \
}
#define CONNECT_TO_BUCKET_LIST( Element, ListHead ) \
( Element )->Next = ( ListHead ); \
( Element )->Last = NULL; \
if ( ( Element )->Next ) { \
( Element )->Next->Last = ( Element ); \
}
#define CONNECT_TO_GLOBAL_LIST( Element, Table ) \
( Element )->ListLast = ( Table )->ListTail; \
( Table )->ListTail = ( Element ); \
( Element )->ListNext = NULL; \
if ( ( Element )->ListLast != NULL ) { \
( Element )->ListLast->ListNext = ( Element ); \
} \
if ( !( Table )->ListHead ) { \
( Table )->ListHead = ( Element ); \
} \
if ( ( Table )->InternalPointer == NULL ) { \
( Table )->InternalPointer = ( Element ); \
}
#define HASH_PROTECT_RECURSION( Table ) \
if ( ( Table )->Flags & HASH_FLAGS_PROTECTION ) { \
if ( ( Table )->ApplyCount++ >= 3 ) { \
ADD_LOGA( "Nesting level too deep - recursive dependency?" ); \
} \
}
#define HASH_UNPROTECT_RECURSION( Table ) \
if ( ( Table )->Flags & HASH_FLAGS_PROTECTION ) { \
( Table )->ApplyCount--; \
}
#define HASH_IF_FULL_DO_RESIZE( Table ) \
if ( ( Table )->NumOfElements > ( Table )->TableSize ) { \
HashDoResize( Table ); \
}
#define UPDATE_DATA( Table, Bucket, Data, DataSize ) \
if ( DataSize == sizeof( void* ) ) { \
if ( !( Bucket )->DataPtr ) { \
HEAP_FREE( ( Bucket )->Data ); \
} \
memcpy( &( Bucket )->DataPtr, Data, sizeof( void* ) ); \
( Bucket )->Data = &( Bucket )->DataPtr; \
} else { \
if ( ( Bucket )->Data == &( Bucket )->DataPtr ) { \
( Bucket )->Data = ( void* )HEAP_ALLOC( DataSize ); \
( Bucket )->DataPtr = NULL; \
} else { \
( Bucket )->Data = ( void* )HEAP_REALLOC( ( Bucket )->Data, DataSize ); \
} \
memcpy( ( Bucket )->Data, Data, DataSize ); \
}
#define INIT_DATA( Table, Bucket, Data, DataSize ); \
if ( DataSize == sizeof( void* ) ) { \
memcpy( &( Bucket )->DataPtr, Data, sizeof( void* ) ); \
( Bucket )->Data = &( Bucket )->DataPtr; \
} else { \
( Bucket )->Data = ( void* )HEAP_ALLOC( DataSize ); \
if ( !( Bucket )->Data ) { \
HEAP_FREE( Bucket ); \
return FALSE; \
} \
memcpy( ( Bucket )->Data, Data, DataSize ); \
( Bucket )->DataPtr = NULL; \
}
/*
///////////////////////////////////////////////////////////////////////////////
// S T R U C T U R E S
///////////////////////////////////////////////////////////////////////////////
*/
/*
///////////////////////////////////////////////////////////////////////////////
// G L O B A L S
///////////////////////////////////////////////////////////////////////////////
*/
/*
///////////////////////////////////////////////////////////////////////////////
// F U N C T I O N S
///////////////////////////////////////////////////////////////////////////////
*/
/*
* DJBX33A (Daniel J. Bernstein, Times 33 with Addition)
*
* This is Daniel J. Bernstein's popular `times 33' hash function as
* posted by him years ago on comp.lang.c. It basically uses a function
* like ``hash(i) = hash(i-1) * 33 + str[i]''. This is one of the best
* known hash functions for strings. Because it is both computed very
* fast and distributes very well.
*
* The magic of number 33, i.e. why it works better than many other
* constants, prime or not, has never been adequately explained by
* anyone. So I try an explanation: if one experimentally tests all
* multipliers between 1 and 256 (as RSE did now) one detects that even
* numbers are not usable at all. The remaining 128 odd numbers
* (except for the number 1) work more or less all equally well. They
* all distribute in an acceptable way and this way fill a hash table
* with an average percent of approx. 86%.
*
* If one compares the Chi^2 values of the variants, the number 33 not
* even has the best value. But the number 33 and a few other equally
* good numbers like 17, 31, 63, 127 and 129 have nevertheless a great
* advantage to the remaining numbers in the large set of possible
* multipliers: their multiply operation can be replaced by a faster
* operation based on just one shift plus either a single addition
* or subtraction operation. And because a hash function has to both
* distribute good _and_ has to be very fast to compute, those few
* numbers should be preferred and seems to be the reason why Daniel J.
* Bernstein also preferred it.
*/
__inline ULONG DefaultHashFunctionInline( const TCHAR *Key, ULONG KeyLength )
{
register ULONG HashValue = 5381;
for ( ; KeyLength >= 8; KeyLength -= 8 )
{
HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
}
switch ( KeyLength )
{
case 7: HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
case 6: HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
case 5: HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
case 4: HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
case 3: HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
case 2: HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++;
case 1: HashValue = ( ( HashValue << 5 ) + HashValue ) + *Key++; break;
case 0: break;
}
return HashValue;
}
ULONG DefaultHashFunction( const TCHAR *Key, ULONG KeyLength )
{
return DefaultHashFunctionInline( Key, KeyLength );
}
static HASH_BUCKET* HashApplyDeleter( HASH_TABLE *Table, HASH_BUCKET *Bucket )
{
ULONG Index;
HASH_BUCKET *RetCode;
HANDLE_BLOCK_INTERRUPTIONS( );
if ( Bucket->Last )
{
Bucket->Last->Next = Bucket->Next;
}
else
{
Index = Bucket->HashValue & Table->TableMask;
Table->Buckets[Index] = Bucket->Next;
}
if ( Bucket->Next )
{
Bucket->Next->Last = Bucket->Last;
}
else
{
/* Nothing to do as this list doesn't have a tail */
}
if ( Bucket->ListLast != NULL )
Bucket->ListLast->ListNext = Bucket->ListNext;
else
Table->ListHead = Bucket->ListNext;
if ( Bucket->ListNext != NULL )
Bucket->ListNext->ListLast = Bucket->ListLast;
else
Table->ListTail = Bucket->ListLast;
if ( Table->InternalPointer == Bucket )
Table->InternalPointer = Bucket->ListNext;
Table->NumOfElements--;
HANDLE_UNBLOCK_INTERRUPTIONS( );
if ( Table->Destroy )
Table->Destroy( Bucket->Data );
if ( Bucket->Data != &Bucket->DataPtr )
HEAP_FREE( Bucket->Data );
RetCode = Bucket->ListNext;
HEAP_FREE( Bucket );
return RetCode;
}
static ULONG HashDoResize( HASH_TABLE *Table )
{
HASH_BUCKET **Buckets;
if ( ( Table->TableSize << 1 ) > 0 )
{
Buckets = ( HASH_BUCKET** )HEAP_REALLOC( Table->Buckets, ( Table->TableSize << 1 ) * sizeof( HASH_BUCKET* ) );
if ( Buckets == NULL )
{
Buckets = ( HASH_BUCKET** )HEAP_ALLOC( ( Table->TableSize << 1 ) * sizeof( HASH_BUCKET* ) );
if ( Buckets == NULL )
return FALSE;
}
HANDLE_BLOCK_INTERRUPTIONS( );
Table->Buckets = Buckets;
Table->TableSize = ( Table->TableSize << 1 );
Table->TableMask = Table->TableSize - 1;
HashRefresh( Table );
HANDLE_UNBLOCK_INTERRUPTIONS( );
return TRUE;
}
return TRUE;
}
static ULONG HashReplaceCheckerWrapper( HASH_TABLE *Target, void *Data, HASH_BUCKET *Bucket, void *Context, MERGE_CHECKER_FUNCTION MergeCheckerFunction )
{
HASH_KEY HashKey;
HashKey.Key = Bucket->Key;
HashKey.KeyLength = Bucket->KeyLength;
HashKey.HashValue = Bucket->HashValue;
return MergeCheckerFunction( Target, Data, &HashKey, Context );
}
ULONG HashInitEx( PHASH_TABLE Table, ULONG Size, HASH_FUNCTION HashFunction, HASH_DESTROY_FUNCTION DestroyFunction, ULONG Flags )
{
ULONG Iter;
HASH_BUCKET **Buckets;
Iter = 3;
if ( Size >= 0x80000000 )
{
/* Prevent overflow */
Table->TableSize = 0x80000000;
}
else
{
while ( ( 1U << Iter ) < Size )
Iter++;
Table->TableSize = 1 << Iter;
}
if ( HashFunction == NULL )
HashFunction = DefaultHashFunction;
Table->TableMask = Table->TableSize - 1;
Table->Hash = HashFunction;
Table->Destroy = DestroyFunction;
Table->Buckets = NULL;
Table->ListHead = NULL;
Table->ListTail = NULL;
Table->NumOfElements = 0;
Table->NextFreeElement = 0;
Table->InternalPointer = NULL;
Table->Flags = ( UCHAR )Flags;
Table->ApplyCount = 0;
Table->Reserved = 0;
Buckets = ( HASH_BUCKET** )HEAP_ALLOC( Table->TableSize * sizeof( HASH_BUCKET* ) );
if ( Buckets == NULL )
return FALSE;
memset( Buckets, 0, Table->TableSize * sizeof( HASH_BUCKET* ) );
Table->Buckets = Buckets;
return TRUE;
}
ULONG HashInit( PHASH_TABLE Table, ULONG Size, HASH_FUNCTION HashFunction, HASH_DESTROY_FUNCTION DestroyFunction, ULONG Flags )
{
return HashInitEx( Table, Size, HashFunction, DestroyFunction, Flags | HASH_FLAGS_PROTECTION );
}
void HashDestroy( PHASH_TABLE Table )
{
HASH_BUCKET *Cur, *Prev;
Cur = Table->ListHead;
while ( Cur != NULL )
{
Prev = Cur;
Cur = Cur->ListNext;
if ( Table->Destroy )
Table->Destroy( Prev->Data );
if ( Prev->Data != &Prev->DataPtr )
HEAP_FREE( Prev->Data );
HEAP_FREE( Prev );
}
HEAP_FREE( Table->Buckets );
}
void HashGracefulDestroy( HASH_TABLE *Table )
{
HASH_BUCKET *Bucket;
Bucket = Table->ListHead;
while ( Bucket != NULL )
Bucket = HashApplyDeleter( Table, Bucket );
HEAP_FREE( Table->Buckets );
}
void HashGracefulReverseDestroy( HASH_TABLE *Table )
{
HASH_BUCKET *Bucket;
Bucket = Table->ListTail;
while ( Bucket != NULL )
{
HashApplyDeleter( Table, Bucket );
Bucket = Table->ListTail;
}
HEAP_FREE( Table->Buckets );
}
void HashClear( PHASH_TABLE Table )
{
HASH_BUCKET *Cur, *Prev;
Cur = Table->ListHead;
while ( Cur != NULL )
{
Prev = Cur;
Cur = Cur->ListNext;
if ( Table->Destroy )
Table->Destroy( Prev->Data );
if ( Prev->Data != &Prev->DataPtr )
HEAP_FREE( Prev->Data );
HEAP_FREE( Prev );
}
memset( Table->Buckets, 0, Table->TableSize * sizeof( HASH_BUCKET* ) );
Table->ListHead = NULL;
Table->ListTail = NULL;
Table->NumOfElements = 0;
Table->NextFreeElement = 0;
Table->InternalPointer = NULL;
}
ULONG HashAddOrUpdate( PHASH_TABLE Table, const TCHAR *Key, ULONG KeyLength, void *Data, ULONG DataSize, void **Dest, ULONG Flags )
{
ULONG HashValue;
ULONG Index;
HASH_BUCKET *Bucket;
if ( KeyLength <= 0 )
return FALSE;
HashValue = Table->Hash( Key, KeyLength );
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->HashValue == HashValue ) && ( Bucket->KeyLength == KeyLength ) )
{
if ( !memcmp( Bucket->Key, Key, KeyLength ) )
{
if ( Flags & HASH_ADD )
return FALSE;
HANDLE_BLOCK_INTERRUPTIONS( );
if ( Table->Destroy )
Table->Destroy( Bucket->Data );
UPDATE_DATA( Table, Bucket, Data, DataSize );
if ( Dest )
*Dest = Bucket->Data;
HANDLE_UNBLOCK_INTERRUPTIONS( );
return TRUE;
}
}
Bucket = Bucket->Next;
}
Bucket = ( HASH_BUCKET* )HEAP_ALLOC( sizeof( HASH_BUCKET ) + ( KeyLength - 1 ) * sizeof( TCHAR ) );
if ( Bucket == NULL )
return FALSE;
memcpy( Bucket->Key, Key, KeyLength );
Bucket->KeyLength = KeyLength;
INIT_DATA( Table, Bucket, Data, DataSize );
Bucket->HashValue = HashValue;
CONNECT_TO_BUCKET_LIST( Bucket, Table->Buckets[Index] );
if ( Dest )
*Dest = Bucket->Data;
HANDLE_BLOCK_INTERRUPTIONS( );
CONNECT_TO_GLOBAL_LIST( Bucket, Table );
Table->Buckets[Index] = Bucket;
HANDLE_UNBLOCK_INTERRUPTIONS( );
Table->NumOfElements++;
HASH_IF_FULL_DO_RESIZE( Table );
return TRUE;
}
ULONG HashQuickAddOrUpdate( HASH_TABLE *Table, const TCHAR *Key, ULONG KeyLength, ULONG HashValue, void *Data, ULONG DataSize, void **Dest, ULONG Flags )
{
ULONG Index;
HASH_BUCKET *Bucket;
if ( KeyLength == 0 )
return HashIndexUpdate( Table, HashValue, Data, DataSize, Dest );
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->HashValue == HashValue ) && ( Bucket->KeyLength == KeyLength ) )
{
if ( !memcmp( Bucket->Key, Key, KeyLength ) )
{
if ( Flags & HASH_ADD )
return FALSE;
HANDLE_BLOCK_INTERRUPTIONS( );
if ( Table->Destroy )
Table->Destroy( Bucket->Data );
UPDATE_DATA( Table, Bucket, Data, DataSize );
if ( Dest )
*Dest = Bucket->Data;
HANDLE_UNBLOCK_INTERRUPTIONS( );
return TRUE;
}
}
Bucket = Bucket->Next;
}
Bucket = ( HASH_BUCKET* )HEAP_ALLOC( sizeof( HASH_BUCKET ) + ( KeyLength - 1 ) * sizeof( TCHAR ) );
if ( Bucket == NULL )
return FALSE;
memcpy( Bucket->Key, Key, KeyLength );
Bucket->KeyLength = KeyLength;
INIT_DATA( Table, Bucket, Data, DataSize );
Bucket->HashValue = HashValue;
CONNECT_TO_BUCKET_LIST( Bucket, Table->Buckets[Index] );
if ( Dest )
*Dest = Bucket->Data;
HANDLE_BLOCK_INTERRUPTIONS( );
Table->Buckets[Index] = Bucket;
CONNECT_TO_GLOBAL_LIST( Bucket, Table );
HANDLE_UNBLOCK_INTERRUPTIONS( );
Table->NumOfElements++;
HASH_IF_FULL_DO_RESIZE( Table );
return TRUE;
}
ULONG HashIndexUpdateOrNextInsert( HASH_TABLE *Table, ULONG HashValue, void *Data, ULONG DataSize, void **Dest, ULONG Flags )
{
ULONG Index;
HASH_BUCKET *Bucket;
if ( Flags & HASH_NEXT_INSERT )
HashValue = Table->NextFreeElement;
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->KeyLength == 0 ) && ( Bucket->HashValue == HashValue ) )
{
if ( Flags & HASH_NEXT_INSERT || Flags & HASH_ADD )
return FALSE;
HANDLE_BLOCK_INTERRUPTIONS( );
if ( Table->Destroy )
Table->Destroy( Bucket->Data );
UPDATE_DATA( Table, Bucket, Data, DataSize );
if ( ( LONG )HashValue >= ( LONG )Table->NextFreeElement )
Table->NextFreeElement = HashValue + 1;
if ( Dest )
*Dest = Bucket->Data;
HANDLE_UNBLOCK_INTERRUPTIONS( );
return TRUE;
}
Bucket = Bucket->Next;
}
Bucket = ( HASH_BUCKET* )HEAP_ALLOC( sizeof( HASH_BUCKET ) - 1 * sizeof( TCHAR ) );
if ( Bucket == NULL )
return FALSE;
Bucket->KeyLength = 0; /* Numeric are marked by making the KeyLength == 0 */
INIT_DATA( Table, Bucket, Data, DataSize );
Bucket->HashValue = HashValue;
CONNECT_TO_BUCKET_LIST( Bucket, Table->Buckets[Index] );
if ( Dest )
*Dest = Bucket->Data;
HANDLE_BLOCK_INTERRUPTIONS( );
Table->Buckets[Index] = Bucket;
CONNECT_TO_GLOBAL_LIST( Bucket, Table );
HANDLE_UNBLOCK_INTERRUPTIONS( );
if ( ( LONG )HashValue >= ( LONG )Table->NextFreeElement )
Table->NextFreeElement = HashValue + 1;
Table->NumOfElements++;
HASH_IF_FULL_DO_RESIZE( Table );
return TRUE;
}
ULONG HashAddEmptyElement( HASH_TABLE *Table, const TCHAR *Key, ULONG KeyLength )
{
void *Dummy = ( void* )1;
return HashAdd( Table, Key, KeyLength, &Dummy, sizeof( void* ), NULL );
}
void HashSetApplyProtection( PHASH_TABLE Table, ULONG Set )
{
if ( Set )
Table->Flags |= HASH_FLAGS_PROTECTION;
else
Table->Flags &= ~HASH_FLAGS_PROTECTION;
}
void HashApply( HASH_TABLE *Table, APPLY_FUNCTION ApplyFunction )
{
ULONG RetCode;
HASH_BUCKET *Bucket;
HASH_PROTECT_RECURSION( Table );
Bucket = Table->ListHead;
while ( Bucket != NULL )
{
RetCode = ApplyFunction( Bucket->Data );
if ( RetCode & HASH_APPLY_REMOVE )
Bucket = HashApplyDeleter( Table, Bucket );
else
Bucket = Bucket->ListNext;
if ( RetCode & HASH_APPLY_STOP )
break;
}
HASH_UNPROTECT_RECURSION( Table );
}
void HashApplyWithArgument( HASH_TABLE *Table, APPLY_ARG_FUNCTION ApplyFunction, void *Context )
{
ULONG RetCode;
HASH_BUCKET *Bucket;
HASH_PROTECT_RECURSION( Table );
Bucket = Table->ListHead;
while ( Bucket != NULL )
{
RetCode = ApplyFunction( Bucket->Data, Context );
if ( RetCode & HASH_APPLY_REMOVE )
Bucket = HashApplyDeleter( Table, Bucket );
else
Bucket = Bucket->ListNext;
if ( RetCode & HASH_APPLY_STOP )
break;
}
HASH_UNPROTECT_RECURSION( Table );
}
void HashApplyWithArgumentEx( HASH_TABLE *Table, APPLY_ARGS_FUNCTION ApplyFunction, ULONG Count, ... )
{
ULONG RetCode;
HASH_BUCKET *Bucket;
va_list Args;
HASH_KEY HashKey;
Bucket = Table->ListHead;
while ( Bucket != NULL )
{
va_start( Args, Count );
HashKey.Key = Bucket->Key;
HashKey.KeyLength = Bucket->KeyLength;
HashKey.HashValue = Bucket->HashValue;
RetCode = ApplyFunction( Bucket->Data, Count, Args, &HashKey );
if ( RetCode & HASH_APPLY_REMOVE )
Bucket = HashApplyDeleter( Table, Bucket );
else
Bucket = Bucket->ListNext;
if ( RetCode & HASH_APPLY_STOP )
break;
va_end( Args );
}
HASH_UNPROTECT_RECURSION( Table );
}
void HashGracefulReverseApply( HASH_TABLE *Table, APPLY_FUNCTION ApplyFunction )
{
ULONG RetCode;
HASH_BUCKET *Bucket;
HASH_BUCKET *Prev;
HASH_PROTECT_RECURSION( Table );
Bucket = Table->ListTail;
while ( Bucket != NULL )
{
RetCode = ApplyFunction( Bucket->Data );
Prev = Bucket;
Bucket = Bucket->ListLast;
if ( RetCode & HASH_APPLY_REMOVE )
HashApplyDeleter( Table, Prev );
if ( RetCode & HASH_APPLY_STOP )
break;
}
HASH_UNPROTECT_RECURSION( Table );
}
ULONG HashDeleteKeyOrIndex( HASH_TABLE *Table, const TCHAR *Key, ULONG KeyLength, ULONG HashValue, ULONG Flags )
{
ULONG Index;
HASH_BUCKET *Bucket;
if ( Flags == HASH_DEL_KEY )
HashValue = Table->Hash( Key, KeyLength );
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->HashValue == HashValue ) && ( Bucket->KeyLength == KeyLength ) && ( ( Bucket->KeyLength == 0 ) || !memcmp( Bucket->Key, Key, KeyLength ) ) )
{
HANDLE_BLOCK_INTERRUPTIONS( );
if ( Bucket == Table->Buckets[Index] )
Table->Buckets[Index] = Bucket->Next;
else
Bucket->Last->Next = Bucket->Next;
if ( Bucket->Next )
Bucket->Next->Last = Bucket->Last;
if ( Bucket->ListLast != NULL )
Bucket->ListLast->ListNext = Bucket->ListNext;
else
Table->ListHead = Bucket->ListNext;
if ( Bucket->ListNext != NULL )
Bucket->ListNext->ListLast = Bucket->ListLast;
else
Table->ListTail = Bucket->ListLast;
if ( Table->InternalPointer == Bucket )
Table->InternalPointer = Bucket->ListNext;
if ( Table->Destroy )
Table->Destroy( Bucket->Data );
if ( Bucket->Data != &Bucket->DataPtr )
HEAP_FREE( Bucket->Data );
HEAP_FREE( Bucket );
HANDLE_UNBLOCK_INTERRUPTIONS( );
Table->NumOfElements--;
return TRUE;
}
Bucket = Bucket->Next;
}
return FALSE;
}
ULONG HashFind( const HASH_TABLE *Table, const TCHAR *Key, ULONG KeyLength, void **Data )
{
ULONG HashValue;
ULONG Index;
HASH_BUCKET *Bucket;
HashValue = Table->Hash( Key, KeyLength );
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->HashValue == HashValue ) && ( Bucket->KeyLength == KeyLength ) )
{
if ( !memcmp( Bucket->Key, Key, KeyLength ) )
{
*Data = Bucket->Data;
return TRUE;
}
}
Bucket = Bucket->Next;
}
return FALSE;
}
ULONG HashQuickFind( const HASH_TABLE *Table, const TCHAR *Key, ULONG KeyLength, ULONG HashValue, void **Data )
{
ULONG Index;
HASH_BUCKET *Bucket;
if ( KeyLength == 0 )
return HashIndexFind( Table, HashValue, Data );
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->HashValue == HashValue ) && ( Bucket->KeyLength == KeyLength ) )
{
if ( !memcmp( Bucket->Key, Key, KeyLength ) )
{
*Data = Bucket->Data;
return TRUE;
}
}
Bucket = Bucket->Next;
}
return FALSE;
}
ULONG HashIndexFind( const HASH_TABLE *Table, ULONG HashValue, void **Data )
{
ULONG Index;
HASH_BUCKET *Bucket;
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->HashValue == HashValue ) && ( Bucket->KeyLength == 0 ) )
{
*Data = Bucket->Data;
return TRUE;
}
Bucket = Bucket->Next;
}
return FALSE;
}
ULONG HashIsExist( const HASH_TABLE *Table, const TCHAR *Key, ULONG KeyLength )
{
ULONG HashValue;
ULONG Index;
HASH_BUCKET *Bucket;
HashValue = Table->Hash( Key, KeyLength );
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->HashValue == HashValue ) && ( Bucket->KeyLength == KeyLength ) )
{
if ( !memcmp( Bucket->Key, Key, KeyLength ) )
return TRUE;
}
Bucket = Bucket->Next;
}
return FALSE;
}
ULONG HashQuickIsExist( const HASH_TABLE *Table, const TCHAR *Key, ULONG KeyLength, ULONG HashValue )
{
ULONG Index;
HASH_BUCKET *Bucket;
if ( KeyLength == 0 )
return HashIndexIsExist( Table, HashValue );
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->HashValue == HashValue ) && ( Bucket->KeyLength == KeyLength ) )
{
if ( !memcmp( Bucket->Key, Key, KeyLength ) )
return TRUE;
}
Bucket = Bucket->Next;
}
return FALSE;
}
ULONG HashIndexIsExist( const HASH_TABLE *Table, ULONG HashValue )
{
ULONG Index;
HASH_BUCKET *Bucket;
Index = HashValue & Table->TableMask;
Bucket = Table->Buckets[Index];
while ( Bucket != NULL )
{
if ( ( Bucket->HashValue == HashValue ) && ( Bucket->KeyLength == 0 ) )
return TRUE;
Bucket = Bucket->Next;
}
return 0;
}
ULONG HashGetPointer( HASH_TABLE *Table, HASH_POINTER *HashPointer )
{
HashPointer->Pos = Table->InternalPointer;
if ( Table->InternalPointer )
{
HashPointer->HashValue = Table->InternalPointer->HashValue;
return TRUE;
}
else
{
HashPointer->HashValue = 0;
return FALSE;
}
}
ULONG HashSetPointer( HASH_TABLE *Table, const HASH_POINTER *HashPointer )
{
HASH_BUCKET *Bucket;
if ( Table->InternalPointer != HashPointer->Pos )
{
Bucket = Table->Buckets[HashPointer->HashValue & Table->TableMask];
while ( Bucket != NULL )
{
if ( Bucket == HashPointer->Pos )
{
Table->InternalPointer = Bucket;
return TRUE;
}
Bucket = Bucket->Next;
}
return FALSE;
}
return TRUE;
}
void HashInternalPointerResetEx( HASH_TABLE *Table, PHASH_BUCKET *Pos )
{
if ( Pos )
*Pos = Table->ListHead;
else
Table->InternalPointer = Table->ListHead;
}
void HashInternalPointerEndEx( HASH_TABLE *Table, PHASH_BUCKET *Pos )
{
if ( Pos )
*Pos = Table->ListTail;
else
Table->InternalPointer = Table->ListTail;
}
ULONG HashRemoveForwardEx( HASH_TABLE *Table, PHASH_BUCKET *Pos )
{
PHASH_BUCKET *Cur;
Cur = Pos ? Pos : &Table->InternalPointer;
if ( *Cur )
{
*Cur = ( *Cur )->ListNext;
return TRUE;
}
else
{
return FALSE;
}
}
ULONG HashMoveBackwardEx( HASH_TABLE *Table, PHASH_BUCKET *Pos )
{
PHASH_BUCKET *Cur;
Cur = Pos ? Pos : &Table->InternalPointer;
if ( *Cur )
{
*Cur = ( *Cur )->ListLast;
return TRUE;
}
else
{
return FALSE;
}
}
ULONG HashGetCurrentKeyEx( HASH_TABLE *Table, TCHAR **Key, ULONG *KeyLength, ULONG *Index, ULONG Duplicate, PHASH_BUCKET *Pos )
{
HASH_BUCKET *Bucket;
Bucket = Pos ? ( *Pos ) : Table->InternalPointer;
if ( Bucket )
{
if ( Bucket->KeyLength )
{
if ( Duplicate )
{
*Key = ( TCHAR* )HEAP_ALLOC( Bucket->KeyLength * sizeof( TCHAR ) );
if ( *Key )
{
memcpy( *Key, Bucket->Key, ( Bucket->KeyLength - 1 ) * sizeof( TCHAR ) );
*( *Key ) = _T( '\0' );
}
}
else
{
*Key = Bucket->Key;
}
if ( KeyLength )
*KeyLength = Bucket->KeyLength;
return HASH_KEY_IS_STRING;
}
else
{
*Index = Bucket->HashValue;
return HASH_KEY_IS_LONG;
}
}
return HASH_KEY_NON_EXISTANT;
}
ULONG HashGetCurrentKeyTypeEx( HASH_TABLE *Table, PHASH_BUCKET *Pos )
{
HASH_BUCKET *Bucket;
Bucket = Pos ? ( *Pos ) : Table->InternalPointer;
if ( Bucket )
{
if ( Bucket->KeyLength )
return HASH_KEY_IS_STRING;
else
return HASH_KEY_IS_LONG;
}
return HASH_KEY_NON_EXISTANT;
}
ULONG HashGetCurrentDataEx( HASH_TABLE *Table, void **Data, PHASH_BUCKET *Pos )
{
HASH_BUCKET *Bucket;
Bucket = Pos ? ( *Pos ) : Table->InternalPointer;
if ( Bucket )
{
*Data = Bucket->Data;
return TRUE;
}
else
{
return FALSE;
}
}
ULONG HashUpdateCurrentKeyEx( HASH_TABLE *Table, ULONG KeyType, TCHAR *Key, ULONG KeyLength, ULONG Index, PHASH_BUCKET *Pos )
{
HASH_BUCKET *Bucket;
HASH_BUCKET *AllocBucket;
Bucket = Pos ? ( *Pos ) : Table->InternalPointer;
if ( Bucket )
{
if ( KeyType == HASH_KEY_IS_LONG )
{
KeyLength = 0;
if ( !Bucket->KeyLength && Bucket->HashValue == Index )
return TRUE;
HashIndexDelete( Table, Index );
}
else if ( KeyType == HASH_KEY_IS_STRING )
{
if ( Bucket->KeyLength == KeyLength && memcmp( Bucket->Key, Key, KeyLength * sizeof( TCHAR ) ) == 0 )
return TRUE;
HashDelete( Table, Key, KeyLength );
}
else
{
return FALSE;
}
HANDLE_BLOCK_INTERRUPTIONS( );
if ( Bucket->Next )
Bucket->Next->Last = Bucket->Last;
if ( Bucket->Last )
Bucket->Last->Next = Bucket->Next;
else
Table->Buckets[Bucket->HashValue & Table->TableMask] = Bucket->Next;
if ( Bucket->KeyLength != KeyLength )
{
AllocBucket = ( HASH_BUCKET* )HEAP_ALLOC( sizeof( HASH_BUCKET ) + ( KeyLength - 1 ) * sizeof( TCHAR ) );
if ( AllocBucket == NULL )
return FALSE;
AllocBucket->KeyLength = KeyLength;
if ( Bucket->Data == &Bucket->DataPtr )
AllocBucket->Data = &AllocBucket->DataPtr;
else
AllocBucket->Data = Bucket->Data;
AllocBucket->DataPtr = Bucket->DataPtr;
AllocBucket->ListNext = Bucket->ListNext;
AllocBucket->ListLast = Bucket->ListLast;
if ( AllocBucket->ListNext )
Bucket->ListNext->ListLast = AllocBucket;
else
Table->ListTail = AllocBucket;
if ( AllocBucket->ListLast )
Bucket->ListLast->ListNext = AllocBucket;
else
Table->ListHead = AllocBucket;
if ( Table->InternalPointer == Bucket )
Table->InternalPointer = AllocBucket;
if ( Pos )
*Pos = AllocBucket;
HEAP_FREE( Bucket );
Bucket = AllocBucket;
}
if ( KeyType == HASH_KEY_IS_LONG )
{
Bucket->HashValue = Index;
}
else
{
memcpy( Bucket->Key, Key, KeyLength * sizeof( TCHAR ) );
Bucket->HashValue = Table->Hash( Key, KeyLength );
}
CONNECT_TO_BUCKET_LIST( Bucket, Table->Buckets[Bucket->HashValue & Table->TableMask] );
Table->Buckets[Bucket->HashValue & Table->TableMask] = Bucket;
HANDLE_UNBLOCK_INTERRUPTIONS( );
return TRUE;
}
else
{
return FALSE;
}
}
void HashCopy( HASH_TABLE *Target, HASH_TABLE *Source, HASH_COPY_FUNCTION CopyFunction, void *Buffer, ULONG Size )
{
HASH_BUCKET *Bucket;
void *Dest;
UNREFERENCED_PARAMETER( Buffer );
Bucket = Source->ListHead;
while ( Bucket )
{
if ( Bucket->KeyLength )
HashQuickUpdate( Target, Bucket->Key, Bucket->KeyLength, Bucket->HashValue, Bucket->Data, Size, &Dest );
else
HashIndexUpdate( Target, Bucket->HashValue, Bucket->Data, Size, &Dest );
if ( CopyFunction )
CopyFunction( Dest );
Bucket = Bucket->ListNext;
}
Target->InternalPointer = Target->ListHead;
}
void HashMerge( HASH_TABLE *Target, HASH_TABLE *Source, HASH_COPY_FUNCTION CopyFunction, void *Buffer, ULONG Size, ULONG Overwrite )
{
ULONG Flags;
HASH_BUCKET *Bucket;
void *Dest;
UNREFERENCED_PARAMETER( Buffer );
Flags = Overwrite ? HASH_UPDATE : HASH_ADD;
Bucket = Source->ListHead;
while ( Bucket )
{
if ( Bucket->KeyLength > 0 )
{
if ( HashQuickAddOrUpdate( Target, Bucket->Key, Bucket->KeyLength, Bucket->HashValue, Bucket->Data, Size, &Dest, Flags ) == TRUE && CopyFunction )
CopyFunction( Dest );
}
else
{
if ( ( Flags == HASH_UPDATE || !HashIndexIsExist( Target, Bucket->HashValue ) ) && HashIndexUpdate( Target, Bucket->HashValue, Bucket->Data, Size, &Dest ) == TRUE && CopyFunction )
CopyFunction( Dest );
}
Bucket = Bucket->ListNext;
}
Target->InternalPointer = Target->ListHead;
}
void HashMergeEx( HASH_TABLE *Target, HASH_TABLE *Source, HASH_COPY_FUNCTION CopyFunction, ULONG Size, MERGE_CHECKER_FUNCTION MergeCheckerFunction, void *Context )
{
HASH_BUCKET *Bucket;
void *Dest;
Bucket = Source->ListHead;
while ( Bucket )
{
if ( HashReplaceCheckerWrapper( Target, Bucket->Data, Bucket, Context, MergeCheckerFunction ) )
{
if ( HashQuickUpdate( Target, Bucket->Key, Bucket->KeyLength, Bucket->HashValue, Bucket->Data, Size, &Dest ) == TRUE && CopyFunction )
CopyFunction( Dest );
}
Bucket = Bucket->ListNext;
}
Target->InternalPointer = Target->ListHead;
}
ULONG HashSort( HASH_TABLE *Table, HASH_SORT_FUNCTION SortFunction, HASH_COMPARE_FUNCTION CompareFunction, ULONG ReNumber )
{
ULONG Iter;
ULONG Iter1;
HASH_BUCKET **Buckets;
HASH_BUCKET *Bucket;
if ( !( Table->NumOfElements > 1 ) && !( ReNumber && Table->NumOfElements > 0 ) )
return TRUE;
Buckets = ( HASH_BUCKET** )HEAP_ALLOC( Table->NumOfElements * sizeof( HASH_BUCKET * ) );
if ( Buckets == NULL )
return FALSE;
Iter = 0;
Bucket = Table->ListHead;
while ( Bucket )
{
Buckets[Iter] = Bucket;
Bucket = Bucket->ListNext;
Iter++;
}
SortFunction( ( void* )Buckets, Iter, sizeof( HASH_BUCKET* ), CompareFunction );
HANDLE_BLOCK_INTERRUPTIONS( );
Table->ListHead = Buckets[0];
Table->ListTail = NULL;
Table->InternalPointer = Table->ListHead;
Buckets[0]->ListLast = NULL;
if ( Iter > 1 )
{
Buckets[0]->ListNext = Buckets[1];
for ( Iter1 = 1; Iter1 < Iter - 1; Iter1++ )
{
Buckets[Iter1]->ListLast = Buckets[Iter1 - 1];
Buckets[Iter1]->ListNext = Buckets[Iter1 + 1];
}
Buckets[Iter1]->ListLast = Buckets[Iter1 - 1];
Buckets[Iter1]->ListNext = NULL;
}
else
{
Buckets[0]->ListNext = NULL;
}
Table->ListTail = Buckets[Iter - 1];
HEAP_FREE( Buckets );
HANDLE_UNBLOCK_INTERRUPTIONS( );
if ( ReNumber )
{
Bucket = Table->ListHead;
Iter = 0;
while ( Bucket != NULL )
{
Bucket->KeyLength = 0;
Bucket->HashValue = Iter++;
Bucket = Bucket->ListNext;
}
Table->NextFreeElement = Iter;
HashRefresh( Table );
}
return TRUE;
}
ULONG HashCompare( HASH_TABLE *Table1, HASH_TABLE *Table2, HASH_COMPARE_FUNCTION CompareFunction, ULONG Ordered )
{
ULONG RetCode;
HASH_BUCKET *Bucket1;
HASH_BUCKET *Bucket2;
void *Data2;
HASH_PROTECT_RECURSION( Table1 );
HASH_PROTECT_RECURSION( Table2 );
RetCode = Table1->NumOfElements - Table2->NumOfElements;
if ( RetCode )
{
HASH_UNPROTECT_RECURSION( Table1 );
HASH_UNPROTECT_RECURSION( Table2 );
return RetCode;
}
Bucket1 = Table1->ListHead;
Bucket2 = NULL;
if ( Ordered )
Bucket2 = Table2->ListHead;
while ( Bucket1 )
{
if ( Ordered && !Bucket2 )
{
/* That's not supposed to happen */
HASH_UNPROTECT_RECURSION( Table1 );
HASH_UNPROTECT_RECURSION( Table2 );
return 1;
}
if ( Ordered )
{
if ( Bucket1->KeyLength == 0 && Bucket2->KeyLength == 0 )
{
/* Numeric */
RetCode = Bucket1->HashValue - Bucket2->HashValue;
if ( RetCode )
{
HASH_UNPROTECT_RECURSION( Table1 );
HASH_UNPROTECT_RECURSION( Table2 );
return RetCode;
}
}
else
{
/* String */
RetCode = Bucket1->KeyLength - Bucket2->KeyLength;
if ( RetCode )
{
HASH_UNPROTECT_RECURSION( Table1 );
HASH_UNPROTECT_RECURSION( Table2 );
return RetCode;
}
RetCode = memcmp( Bucket1->Key, Bucket2->Key, Bucket1->KeyLength );
if ( RetCode )
{
HASH_UNPROTECT_RECURSION( Table1 );
HASH_UNPROTECT_RECURSION( Table2 );
return RetCode;
}
}
Data2 = Bucket2->Data;
}
else
{
if ( Bucket1->KeyLength == 0 )
{
/* Numeric index */
if ( HashIndexFind( Table2, Bucket1->HashValue, &Data2 ) == FALSE )
{
HASH_UNPROTECT_RECURSION( Table1 );
HASH_UNPROTECT_RECURSION( Table2 );
return 1;
}
}
else
{
/* String index */
if ( HashFind( Table2, Bucket1->Key, Bucket1->KeyLength, &Data2 ) == FALSE )
{
HASH_UNPROTECT_RECURSION( Table1 );
HASH_UNPROTECT_RECURSION( Table2 );
return 1;
}
}
}
RetCode = CompareFunction( Bucket1->Data, Data2 );
if ( RetCode )
{
HASH_UNPROTECT_RECURSION( Table1 );
HASH_UNPROTECT_RECURSION( Table2 );
return RetCode;
}
Bucket1 = Bucket1->ListNext;
if ( Ordered )
Bucket2 = Bucket2->ListNext;
}
HASH_UNPROTECT_RECURSION( Table1 );
HASH_UNPROTECT_RECURSION( Table2 );
return 0;
}
ULONG HashMinMax( HASH_TABLE *Table, HASH_COMPARE_FUNCTION CompareFunction, ULONG Flags, void **Data )
{
HASH_BUCKET *RetCode;
HASH_BUCKET *Bucket;
if ( Table->NumOfElements == 0 )
{
*Data = NULL;
return FALSE;
}
RetCode = Bucket = Table->ListHead;
while ( ( Bucket = Bucket->ListNext ) )
{
if ( Flags )
{
if ( CompareFunction( &RetCode, &Bucket ) < 0 )
{
/* Max */
RetCode = Bucket;
}
}
else
{
if ( CompareFunction( &RetCode, &Bucket ) > 0 )
{
/* Min */
RetCode = Bucket;
}
}
}
*Data = RetCode->Data;
return TRUE;
}
ULONG HashGetValue( const TCHAR *Key, ULONG KeyLength )
{
return DefaultHashFunctionInline( Key, KeyLength );
}
ULONG HashGetCount( const HASH_TABLE *Table )
{
return Table->NumOfElements;
}
ULONG HashGetNextFreeElement( const HASH_TABLE *Table )
{
return Table->NextFreeElement;
}
ULONG HashRefresh( HASH_TABLE *Table )
{
ULONG Index;
HASH_BUCKET *Bucket;
memset( Table->Buckets, 0, Table->TableSize * sizeof( HASH_BUCKET * ) );
Bucket = Table->ListHead;
while ( Bucket != NULL )
{
Index = Bucket->HashValue & Table->TableMask;
CONNECT_TO_BUCKET_LIST( Bucket, Table->Buckets[Index] );
Table->Buckets[Index] = Bucket;
Bucket = Bucket->ListNext;
}
return TRUE;
}