Winnyをいじる・その1

広告

Winnyの初期ノード

ネットで初期ノードリストが配布されている。これは

@9581aebb9b2f93d548fc274b2655e0a8b67118

のような形になっている。最初の@は不要なので読み飛ばす。その先の数値とアルファベットは16進数になっている。つまり、9581aebb9b2f93d548fc274b2655e0a8b67118は16進数で

0x95, 0x81, 0xae, 0xbb, 0x9b, 0x2f, 0x93, 0xd5, 0x48, 0xfc, 0x27, 0x4b, 0x26, 0x55, 0xe0, 0xa8, 0xb6, 0x71, 0x18

という16進数の列と読み替えればいい。

文字を16進数数値に

static unsigned char asciiToInt( char c )
{
	// 数値?
	if ( 0x30 <= c && c <= 0x39 )
		return c - '0';

	return c - 'a' + 10;
}

なお、unsigned charは1バイトの数値を扱うとき、charは1バイト文字を扱うときです。これを使って

初期ノードリストの16進数表記をデコードする

/* -------------------------------------------------------
	初期ノードリストの16進数表記をデコードする。
	この中でしか使わないため、char *で返す。
	戻り値はdelete []すること。
 -------------------------------------------------------*/

static unsigned char *decodeHexString( const char *s )
{
	int length = strlen( s ) / 2;
	unsigned char *result;

	if( length % 2 != 0 )
	{
		// 2の倍数じゃないときはおかしい
		return NULL;
	}

	result = new unsigned char[ length + 1 ];

	for( int i = 0; i < length; i++ )
	{
		char hi = *s++;
		char lo = *s++;
		result[ i ] = (asciiToInt(hi) << 4) | asciiToInt(lo);
	}
	result[ length ] = '';

	return result;
}

これをRC4暗号で復号すればよい。なお、RC4のキーはほとんど既知で

// デフォルト暗号化キー
const unsigned char RC4Key[] = { 0x6f, 0x70, 0x69, 0x65, 0x77, 0x66, 0x36, 0x61, 0x73, 0x63, 0x78, 0x6c, 0x76 };

がデフォルトになっている。

この暗号化キーの最初の1バイトを、Node情報の最初の1バイトと入れ替えてキーにする。

key[ 0 ] = decoded[ 0 ];

が該当部分。あとの部分は得られたキーで復号してやればよい。

デコード

/* -------------------------------------------------------
	Winnyの初期ノードリスト
		@9581aebb9b2f93d548fc274b2655e0a8b67118
	のような表記を
		61.201.38.38:25316
	にデコードする
 -------------------------------------------------------*/

void decode( const char *s )
{
	// 簡易チェック
	if( s[0] != '@' )
	{
		return;
	}

	// @を飛ばす
	s++;

	int size = strlen( s ) / 2;
	unsigned char *decoded = decodeHexString( s );
	unsigned char *key = new unsigned char[ sizeof( RC4Key ) ];

	// デコードできない
	if( decoded == NULL )
		return;

	// 最初の1バイトを暗号化キーに使う
	memcpy( key, RC4Key, sizeof( RC4Key ) );
	key[ 0 ] = decoded[ 0 ];	

	RC4 r( key, sizeof( RC4Key ) );
	r.decrypt( &decoded[ 1 ], size - 1 );

	puts( ( const char * )&decoded[1] );

	delete [] decoded;
}

RC4を復号する

RC4はストリーム暗号で、1バイトずつ処理していきますが、その都度関数の内部状態が変化します。そのためRC4クラスを作って処理します。

swap

変数を入れ替える処理を多用するのでswap()関数を作ってしまう。

template < typename T >
void swap( T &left, T &right )
{
	T temp;

	temp = left;
	left = right;
	right = temp;
}

RC4クラス

#ifndef	__RC4__
#define	__RC4__ 

class RC4
{
public:
	RC4( const unsigned char *key, int size );
	virtual ~RC4(){}

	void Initialize();
	void decrypt( unsigned char *in, int size );

private:
	int _x;
	int _y;
	unsigned char *_key;
	unsigned char _state[256];

};

#endif

コンストラクタ

RC4::RC4( const unsigned char *key, int size )
{
	_key = new unsigned char[ size ];
	memcpy( _key, key, size );

	Initialize();
}

暗号に使うキーとサイズ(C++は配列のサイズを別に覚えておかなくてはならない、不便だ)を渡して初期化してやる。

初期化

void RC4::Initialize()
{
	unsigned int length;
	unsigned char state2[256];
	unsigned char *p = _key;

	length = 0;
	while( *p++ != 0 )
	{
		length++;
	}
	if( length == 0 )
		length = 1;

	_x = 0;
	_y = 0;

	for( int i = 0; i < sizeof( _state ); i++ )
	{
		_state[ i ] = i;
		state2[ i ] = _key[ i % length ];
	}

	int j = 0;
	for( int i = 0; i < sizeof( _state ); i++ )
	{
		j = ( j + _state[ i ] + state2[ i ] ) & 0xff;
		swap( _state[ i ], _state[ j ] );
	}
}

暗号化/復号化

void RC4::decrypt( unsigned char *in, int size )
{
	unsigned char x = _x;
	unsigned char y = _y;
		
	unsigned char sx, sy;
	long index = 0;
		
	while ( size-- != 0 )
	{
		x++;
		sx = _state[x];
		y = ( y + sx ) & 0xff;
		sy = _state[y];
		_state[y] = sx;
		_state[x] = sy;
		sx = (sx + sy) & 0xff;
			
		in[index++] ^= _state[sx];
	}
	_x = x;
	_y = y;
}