收获

  • 要求能看出伪代码中的加密算法,本题中的 Base64 和 RC4

【BJDCTF】encode


思路

先进行常规 upx 脱壳 (脱壳机好像有问题,用 cmd 脱壳,upx -d <文件名>

没有 main 函数,shift + F12 查看字符串,发现与 flag 有关的信息

BJDCTF-encode1.png

在 IDA View-A 中跟进

BJDCTF-encode2.png

进入函数 sub_804887C()

BJDCTF-encode3.png

根据经验,将一些函数进行 rename,得到代码如下:

int sub_804887C()
{
  int v0; // eax
  int result; // eax
  unsigned int i; // [esp+Ch] [ebp-FCh]
  unsigned int v3; // [esp+10h] [ebp-F8h]
  unsigned int v4; // [esp+14h] [ebp-F4h]
  char v5[48]; // [esp+1Ah] [ebp-EEh] BYREF
  char v6[178]; // [esp+4Ah] [ebp-BEh] BYREF
  unsigned int v7; // [esp+FCh] [ebp-Ch]

  v7 = __readgsdword(0x14u);
  strcpy(v5, "Flag{This_a_Flag}");
  v3 = strlen(v5);
  strcpy(v6, "E8D8BD91871A1E56F53F4889682F96142AF2AB8FED7ACFD5E");
  printf("Please input your flag:");
  read(0, &v6[50], 256);
  if ( strlen(&v6[50]) != 21 )
    exit(0);
  v0 = sub_8048AC2(&v6[50]);
  strcpy(&v5[18], v0);
  v4 = strlen(&v5[18]);
  for ( i = 0; i < v4; ++i )
    v5[i + 18] ^= v5[i % v3];
  sub_8048E24(&v5[18], v4, v5, v3);
  if ( !strcmp(&v5[18], v6) )
    exit(0);
  printf("right!");
  result = 0;
  if ( __readgsdword(0x14u) != v7 )
    sub_806FA00();
  return result;
}

那么就只存在两处函数不明确作用,一个是 sub_8048AC2,一个是 sub_8048E24

查看函数 sub_8048AC2

BJDCTF-encode4.png

查看 a0123456789Abcd 中的内容:

BJDCTF-encode5.png

发现 a0123456789Abcd 是 Base64 加密的码表,根据 sub_8048AC2 函数的结构,可以判断这可能是 Base64 加密的算法

查看函数 sub_8048E24

BJDCTF-encode6.png

根据函数调用,查看函数 sub_8048CC2

BJDCTF-encode7.png

基本可以断定,sub_8048CC2 函数是一个 RC4 算法的加密初始化,那么 sub_8048E24 就是 RC4 加密的算法

得到最终的程序逻辑:

int sub_804887C()
{
  int v0; // eax
  int result; // eax
  unsigned int i; // [esp+Ch] [ebp-FCh]
  unsigned int v3; // [esp+10h] [ebp-F8h]
  unsigned int v4; // [esp+14h] [ebp-F4h]
  char v5[48]; // [esp+1Ah] [ebp-EEh] BYREF
  char v6[178]; // [esp+4Ah] [ebp-BEh] BYREF
  unsigned int v7; // [esp+FCh] [ebp-Ch]

  v7 = __readgsdword(0x14u);
  strcpy(v5, "Flag{This_a_Flag}");
  v3 = strlen(v5);
  strcpy(v6, "E8D8BD91871A1E56F53F4889682F96142AF2AB8FED7ACFD5E");
  printf("Please input your flag:");
  read(0, &v6[50], 256);
  if ( strlen(&v6[50]) != 21 )
    exit(0);
  v0 = Base64(&v6[50]);
  strcmp(&v5[18], v0);
  v4 = strlen(&v5[18]);
  for ( i = 0; i < v4; ++i )
    v5[i + 18] ^= v5[i % v3];
  RC4(&v5[18], v4, v5, v3);
  if ( !strcmp(&v5[18], v6) )
    exit(0);
  printf("right!");
  result = 0;
  if ( __readgsdword(0x14u) != v7 )
    sub_806FA00();
  return result;
}

经过分析,逻辑就是 rc4(xor(base64(input))),key是 Flag{This_a_Flag},但最终 cipher 是有点问题的,需要手动 Base16 解密成字节码才能用


脚本

C++

#include <stdio.h>
#include <string.h>

typedef unsigned long ULONG;

void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len)    //初始化函数
{
    int i =0, j = 0;
    char k[256] = {0};
    unsigned char tmp = 0;
    for (i = 0; i < 256; i++) {
        s[i] = i;
        k[i] = key[i % Len];
    }
    for (i = 0; i < 256; i++) {
        j=(j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];    //交换s[i]和s[j]
        s[j] = tmp;
    }
 }

void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Len)    //加解密
{
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp = 0;
    for(k = 0;k < Len; k++) {
        i=(i + 1) % 256;
        j=(j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j];    //交换s[x]和s[y]
        s[j] = tmp;
        t=(s[i] + s[j]) % 256;
        Data[k] ^= s[t];
     }
} 

unsigned int FindIndex(char *string,char c)
{
	int i;
	for(i = 0; i < strlen(string); i++)
	{
		if(c == string[i])
			return i;
	}
}

void base64_decode(unsigned char *input)
{
	unsigned char output[256] = {0};
	char *string = {"0123456789+/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"};
	int index = 0;
	int input_length = strlen((char*)input);
	int i = 0;
	for(i = 0; i < input_length; i += 4)
	{
		if(i+1 >= input_length) break;
		output[index++] = (FindIndex(string,input[i])<<2) + (FindIndex(string,input[i+1])>>4);
		if(i+2 >= input_length) break;
		output[index++] = (FindIndex(string,input[i+1])<<4) + (FindIndex(string,input[i+2])>>2);
		if(i+3 >= input_length) break;
		output[index++] = (FindIndex(string,input[i+2])<<6) + (FindIndex(string,input[i+3]));
	}
	printf("result:");
	puts((char*)output);
	printf("HEX:");
	for(i = 0; i < strlen((char*)output); i++)
	{
		printf("%x ",output[i]);
	}
	printf("\n");
}

int main()
{ 
    unsigned char s[257] = {0};    //S-box
    int i=0;
    unsigned char key[257] = {"Flag{This_a_Flag}\x00"};     //key
    unsigned char pData[] = {0xe8,0xd8,0xbd,0x91,0x87,0x1a,0x01,0x0e,0x56,0x0f,0x53,0xf4,0x88,0x96,0x82,0xf9,0x61,0x42,0x0a,0xf2,0xab,0x08,0xfe,0xd7,0xac,0xfd,0x5e,0x00};   //密文
    ULONG len = strlen((char*)pData);
    
    rc4_init(s,(unsigned char *)key,strlen((char*)key));    //已经完成了初始化
    rc4_crypt(s,(unsigned char *)pData,strlen((char*)pData));   //加密
    
	for(int i = 0; i < strlen((char*)pData); i++)
	{
		pData[i] ^= key[i % strlen((char*)key)];
	}
	
    printf("rc4 decrypt  : %s\n",pData);
    
    base64_decode(pData);
    return 0;
}

Python

from base64 import b64decode

key = 'Flag{This_a_Flag}'
decode_byte = '23152553081a5938126a3931275b0b1313085c330b356101511f105c'

encode_base64 = ''
lists = []

for i in range(len(decode_byte)//2):
    lists.append(int(decode_byte[i*2:(i+1)*2], 16))

print(lists)
for i in range(len(lists)):
    encode_base64 += chr(lists[i] ^ ord(key[i % len(key)]))

t = '0123456789+/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ='
table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
table = str.maketrans(t, table)
flag = b64decode(encode_base64.translate(table))
print(flag.decode())

结果

BJD{0v0_Y0u_g07_1T!}

C++

BJDCTF-encode8.png

Python

BJDCTF-encode9.png