# 2022DASCTF X SU 三月春季挑战赛

# login

玩逆向有史以来做的最难的一道题

主要知识点 Socket 通信 RSA 加密 HILL 加密 魔改 AES 加密

[附件下载](链接:https://pan.baidu.com/s/1_zUg4ZrYmB1hPbHJbC4s1Q?pwd=1w3e
提取码:1w3e
-- 来自百度网盘超级会员 V2 的分享)

# 查壳

image-20220511231452497

无壳 64 位,拖进 IDA

# Socket

拖进 IDA,
image-20220511231936537

根据经验可以判断这两个函数分别为 exit () 和 printf ()

观察其他函数,点进去发现
image-20220511232201904image-20220511232227000

发现很多特殊的函数 sys_* , 根据经验可以判断该种函数为系统函数,不可能是作者自己编写的
可以尝试搜索一下,发现是 Socket 函数

Socket 技术详解 Socket 相关函数

参考完上面的两篇博文就可以发现很多未解析的函数都是 Socket 函数
之后继续对 IDA 未解析的函数进行修复,得到最终的反汇编代码

image-20220511232922269

image-20220511233000466

还有一个细节,以 socket_bind 函数为例

image-20220511233322344

socket 是通过套接字传递信息,而且重要的 ip 地址以及端口信息都保存在关键的结构体中,以便确定与之通信的计算机

这里的 sockaddr 就是保存信息的结构体,但是 IDA 并没有将它解析出来,我们手动添加结构体 socketaddr_in 结构体

具体操作 shift+F9 右键 add 结构体

image-20220511233722116

添加之后,找到 bind () 函数,将第二个参数解析为对应的结构体
image-20220511233839765

解析成功之后就可以看到关键的信息
image-20220511233913148

能够看到 Socket 通信的端口 1234

# 实现逻辑

前一个阶段找到了客户端和服务端通信的端口 1234,由于客户端和服务端都运行在本地,所以 IP 地址也就是

127.0.0.1 本机地址
尝试在 1234 端口下开启单步调试
image-20220511234248174

image-20220511234258977

利用端口 1234 开始单步调试

image-20220511234423357

这里提示需要输入参数 IP 地址才能运行 login

image-20220511234733678

大致的逻辑就是 login 运行后,服务端将字符串发送到客户端,提醒用户输入 token 和 password
接收到用户的输入之后,再将得到的数据发送到服务端进行对比识别

进入到关键的对比函数
image-20220511235254925

刚开始是不知道 RSA 加密以及 HILL 加密的,进入这两个函数分析

# RSA

image-20220512204648001

这里发现了特殊值,直接谷歌搜索 0x10001

image-20220512214004342

就能知道 0x10001 是 RSA 加密的公钥,后面的函数
image-20220512214723517

是将一个字符串转换成 16 进制数,当做私钥
而前面的一个很大的数就是 RSA 中需要因式分解的大质数

通过 yafu 工具或者在线因式分解

import gmpy2
p = 98197216341757567488149177586991336976901080454854408243068885480633972200382596026756300968618883148721598031574296054706280190113587145906781375704611841087782526897314537785060868780928063942914187241017272444601926795083433477673935377466676026146695321415853502288291409333200661670651818749836420808033
q = 133639826298015917901017908376475546339925646165363264658181838203059432536492968144231040597990919971381628901127402671873954769629458944972912180415794436700950304720548263026421362847590283353425105178540468631051824814390421486132775876582962969734956410033443729557703719598998956317920674659744121941513
n = p * q
e = 0x10001
c = b'By reading we enrich the mind, by conversation we polish it.'
c = int.from_bytes(c, 'little')
#这里要多试一下,确定是大端序还是小端序
d = gmpy2.invert(e, (p - 1) * (q - 1))
m = gmpy2.powmod(c, d, n)
print(m)
#11963777321199993924175387978397443935563034091716786597947508874393819454915798980986262132792605021295930274531653741552766395859285325677395421549163602968276475448835066393456449574469736327622969755801884982386140722904578598391534204834007447860153096480268812700725451958035204357033892179559153729604237187552716580637492579876006993181920209114166153317182827927606249871955662032809256743464460825303610341043145126848787575238499023185150429072724679210155061579052743238859739734301162335989939278904459012917375108407803445722785027315562371588439877746983153339473213449448259686486917983129418859935686

# HILL

刚开始不知道 HILL 加密,进入到函数里面
image-20220512221108149

还有那个 rand () 函数,之前是不知道 rand (), 也是看了 p 师傅的讲解才知道,当然,rand () 函数一般为了限定随机数的范围都是要 % 某个值,所以也能猜到这个函数是 rand (),还有一个要注意的点就是该函数是在 Linux 环境下生成,所以编写脚本的时候也要对应的在 Linux 环境下
image-20220512221234932

所以这个 rand () 产生的数就是我们的密文,%257 和 %255 只是为了确保两个值在同一个范围内
逻辑就理的很清楚,矩阵运算之后与密文比较

#include <stdio.h>
#include <stdlib.h> 
#include <string.h>
//Linux 环境下运行
int main(void)
{
	int i;
	unsigned char hillData[] = 
	{
		81, 50, 210, 2, 195, 45, 149, 185, 249, 120, 213, 20, 227, 41, 66, 32, 81, 59, 21, 98, 52, 130, 180, 192, 46, 154, 253, 232, 186, 213, 236, 7, 72, 106, 84, 136	
	};
	unsigned char rsaData[] = "11963777321199993924175387978397443935563034091716786597947508874393819454915798980986262132792605021295930274531653741552766395859285325677395421549163602968276475448835066393456449574469736327622969755801884982386140722904578598391534204834007447860153096480268812700725451958035204357033892179559153729604237187552716580637492579876006993181920209114166153317182827927606249871955662032809256743464460825303610341043145126848787575238499023185150429072724679210155061579052743238859739734301162335989939278904459012917375108407803445722785027315562371588439877746983153339473213449448259686486917983129418859935686"; 
	unsigned char ivk[48] = { 0 };
	
	for ( i = 0; i < 36; i++ )
		printf("%d, ", rand() % 255);
	
	puts("\n");
	
	int v5;
	for ( i = 0; i <= 47; ++i )
  	{
  		v5 = rand();
  		if ( (i & 1) != 0 )
  		{
			ivk[i] = rsaData[v5 % (sizeof(rsaData) / sizeof(unsigned char) - 1)];
		}	
  		else
  			ivk[i] = hillData[v5 % (sizeof(hillData) / sizeof(unsigned char))];
  		printf("%d, ", ivk[i]);
  	}
		
	return 0;
}

直接逆运算就是 密钥矩阵先取逆然后与密文相乘
就可以自己先写一个 rand () 生成密文,然后利用在线矩阵计算工具

x = Matrix(GF(257), [[113, 219, 37, 46, 122, 15], [76, 163, 106, 34, 170, 41], [110, 27, 169, 122, 138, 39], [47, 128, 240, 14, 170, 86], [247, 89, 88, 0, 169, 242], [246, 154, 78, 28, 72, 201]])
enc =  Matrix(GF(257), [[163, 151, 162, 85, 83, 190], [241, 252, 249, 121, 107, 82], [20, 19, 233, 226, 45, 81], [142, 31, 86, 8, 87, 39], [167, 5, 212, 208, 82, 130], [119, 117, 27, 153, 74, 237]])
flag = x.inverse()*enc
print(flag)

得到image-20220512230200422

再转成 16 进制得到密码

5132d202c32d95b9f978d514e3294220513b15623482b4c02e9afde8bad5ec07486a5488

得到 token 和 password 之后再运行程序测试一下

image-20220512232100994

发现 token 和 password 都是正确的

接着分析下面的加密

# 魔改 AES

image-20220512233821406

首先分析一个函数,该函数的作用即为将数组的两个元素合并成一个元素

然后我们继续查看客户端传入到服务端的数据并把他们两两合并成一个新的数组
image-20220512234501417

我们直接搜索 52 09 6a d5 30

image-20220512234545881

发现就是 AES 加密
AES 加密详解

后面继续分析

发现又有一个 rand () 函数,而且经过实验,rand 函数在同一个程序里面即使重新调用,之前的值也会保留,也就是说,这次 rand 生成的值还是得在 rand 生成值 36 位之后找,所以最后测试密文的时候前面还是要加循环将前 36 个 rand 值踢掉

这个循环体还有一个点就是

image-20220512235540438

他会在第 16 位时进入另一个分支

也就是说,前 16 位是用的 key 后面 36 位是 iv (这也是 AES 的特点)

#include <stdio.h>
#include <stdlib.h> 
#include <string.h>
int main(void)
{
	int i;
	unsigned char hillData[] = 
	{
		81, 50, 210, 2, 195, 45, 149, 185, 249, 120, 213, 20, 227, 41, 66, 32, 81, 59, 21, 98, 52, 130, 180, 192, 46, 154, 253, 232, 186, 213, 236, 7, 72, 106, 84, 136	
	};
	unsigned char rsaData[] = "11963777321199993924175387978397443935563034091716786597947508874393819454915798980986262132792605021295930274531653741552766395859285325677395421549163602968276475448835066393456449574469736327622969755801884982386140722904578598391534204834007447860153096480268812700725451958035204357033892179559153729604237187552716580637492579876006993181920209114166153317182827927606249871955662032809256743464460825303610341043145126848787575238499023185150429072724679210155061579052743238859739734301162335989939278904459012917375108407803445722785027315562371588439877746983153339473213449448259686486917983129418859935686"; 
	unsigned char ivk[48] = { 0 };
	
	for ( i = 0; i < 36; i++ )
		printf("%d, ", rand() % 255);
	
	puts("\n");
	
	int v5;
	for ( i = 0; i <= 47; ++i )
  	{
  		v5 = rand();
  		if ( (i & 1) != 0 )
  		{
			ivk[i] = rsaData[v5 % (sizeof(rsaData) / sizeof(unsigned char) - 1)];
		}	
  		else
  			ivk[i] = hillData[v5 % (sizeof(hillData) / sizeof(unsigned char))];
  		printf("%d, ", ivk[i]);
  	}
		
	return 0;
}

后面就可以继续判断 AES 了

本菜鸡复现的时候也看不懂 AES,只好先挖个坑
看 P 师傅的博客之后才知道了这道题目的魔改

简单介绍一下到底魔改了什么

  1. S(加密盒)和 Re(解密盒)互换,也就是加密的时候用了解密盒,解密的时候用了加密盒
  2. 密钥扩展改变了 Rcon 轮常量值
  3. 再进入加密器前进行了一轮 iv 的异或(像是 ECB 模式前加了轮异或)

PS: 最大的改动是全部都是 8 位运算!!(而 AES 加密是 32 位运算,所以需要自己重写一下)

(个人感觉是如果不自己打遍 AES,审起来会比较累,同时 AES 是经常用的学习一番也是十分不错的)

最后贴上 P 师傅的解题脚本,等我学完 AES 就回来自己写一个(逃

#include <stdio.h>
static const int S[256] = 
{ 
	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 
};
const unsigned char Rcon[11] = 
{
  0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 
};
static const int deColM[4][4] =
{
	0xE, 0xB, 0xD, 0x9,
	0x9, 0xE, 0xB, 0xD,
	0xD, 0x9, 0xE, 0xB,
	0xB, 0xD, 0x9, 0xE	
}; 
static unsigned char W[44];
void ExtendKey(unsigned char * key);
void AddRoundKey(unsigned char (*stateMatrix)[4], unsigned char * key);
void DeShiftRows(unsigned char (*stateMatrix)[4]);
void DeSubBytes(unsigned char (*stateMatrix)[4]);
void DeMixColumns(unsigned char (*stateMatrix)[4]);
void XorIv(unsigned char * plainText, unsigned char * iv);
void AESDecode(unsigned char * enc, unsigned char * plainText, unsigned char * key); 
int main(void)
{
	unsigned char key[] = {50, 48, 7, 54, 106, 55, 120, 49, 72, 57, 66, 57, 20, 49, 213, 50};
	unsigned char iv[] = {98, 54, 249, 56, 66, 48, 195, 49, 106, 53, 72, 56, 52, 53, 84, 52, 41, 52, 81, 54, 21, 57, 210, 56, 210, 57, 32, 49, 185, 50, 46, 48};
    unsigned char enc[] = {254, 249, 231, 62, 246, 161, 35, 204, 87, 97, 193, 21, 119, 251, 156, 187, 202, 47, 177, 232, 79, 217, 7, 216, 12, 107, 234, 207, 232, 66, 162, 250};
    unsigned char plainText[32] = { 0 };
    int i;
    
    for ( i = 0; i < 32; i += 16)
    {
    	AESDecode(enc + i, plainText + i, key);
    	XorIv(plainText + i, iv + i);
	}
    for ( i = 0; i < 32; i++ )
    	printf("%c", plainText[i]);
	
	return 0;
}
void XorIv(unsigned char * plainText, unsigned char * iv)
{
	int i;
	
	for ( i = 0; i < 16 ; i++ )
		plainText[i] ^= iv[i];
}
void GetStateMatrix(unsigned char (*stateMatrix)[4], unsigned char * enc)
{
	int i, j;
	
	for ( i = 0; i < 4; i++ )
		for ( j = 0; j < 4; j++ )
			stateMatrix[j][i] = enc[i * 4 + j];
}
void PutStateMatrix(unsigned char * plainText, unsigned char (*stateMatrix)[4])
{
	int i, j;
	
	for ( i = 0; i < 4; i++ )
		for ( j = 0; j < 4; j++ )
			plainText[i * 4 + j] = stateMatrix[j][i];
}
void AESDecode(unsigned char * enc, unsigned char * plainText, unsigned char * key)
{
	int i, j;
	ExtendKey(key);
	
	unsigned char stateMatrix[4][4] = { 0 };
	GetStateMatrix(stateMatrix, enc);
	
	i = 10;
	AddRoundKey(stateMatrix, W + i * 16);
	for ( i--; ; i-- )
	{
		DeShiftRows(stateMatrix);
		DeSubBytes(stateMatrix);
		AddRoundKey(stateMatrix, W + i * 16);
		if ( i == 0 )
			break;
		DeMixColumns(stateMatrix);
	}
	
	PutStateMatrix(plainText, stateMatrix);
}
static int GFMul2(int s)
{
	int result = s << 1;
	int a7 = result & 0x00000100;			// 判断位移后的那位是否为 1
	 
	if ( a7 != 0 )
	{
		result = result & 0x000000FF;
		result = result ^ 0x1B;				// 矩阵乘法的特殊性	
	} 
	
	return result;
}
static int GFMul3(int s)
{
	return GFMul2(s) ^ s;
}
static int GFMul4(int s)
{
	return GFMul2(GFMul2(s));
}
static int GFMul8(int s)
{
	return GFMul2(GFMul4(s));
}
static int GFMul9(int s)
{
	return GFMul8(s) ^ s;
}
static int GFMul11(int s)
{
	return GFMul9(s) ^ GFMul2(s);
}
static int GFMul12(int s)
{
	return GFMul8(s) ^ GFMul4(s);
}
static int GFMul13(int s)
{
	return GFMul12(s) ^ s;
}
static int GFMul14(int s)
{
	return GFMul12(s) ^ GFMul2(s);
}
/**
 *	GF 上的二元运算
 */
static int GFMul(int n, int s)
{
	int result;
	
	if ( n == 1 )
		result = s;
	else if ( n == 2 )
		result = GFMul2(s);
	else if ( n == 3 )
		result = GFMul3(s);
	else if ( n == 9 )
		result = GFMul9(s);
	else if ( n == 0xB )
		result = GFMul11(s);
	else if ( n == 0xD )
		result = GFMul13(s);
	else if ( n == 0xE )
		result = GFMul14(s);
	
	return result;	
} 
void DeMixColumns(unsigned char (*stateMatrix)[4])
{
	unsigned char tmpArray[4][4];
	int i, j;
	
	for ( i = 0; i < 4; i++ )
		for ( j = 0; j < 4; j++ )
			tmpArray[i][j] = stateMatrix[i][j];
	
	for ( i = 0; i < 4; i++ )
		for ( j = 0; j < 4; j++ )
			stateMatrix[i][j] = GFMul(deColM[i][0], (int)tmpArray[0][j]) ^
								GFMul(deColM[i][1], (int)tmpArray[1][j]) ^
								GFMul(deColM[i][2], (int)tmpArray[2][j]) ^
								GFMul(deColM[i][3], (int)tmpArray[3][j]);
}
void DeSubBytes(unsigned char (*stateMatrix)[4])
{
	int i, j;
	
	for ( i = 0; i < 4; i++ )
		for ( j = 0; j < 4; j++ )
			stateMatrix[i][j] = S[stateMatrix[i][j]];
}
void DeShiftRows(unsigned char (*stateMatrix)[4])
{
	int i, j, count, t;
	
	for ( i = 1; i <= 3; i++ )
	{
		count = 0;
		while ( count++ < i )
		{
			t = stateMatrix[i][3];
			for ( j = 3; j >= 1; j-- )
				stateMatrix[i][j] = stateMatrix[i][j - 1];
			stateMatrix[i][0] = t;
		}
	}
}
void AddRoundKey(unsigned char (*stateMatrix)[4], unsigned char * key)
{
	int i, j;
	
	for ( i = 0; i <= 3; i++ )
		for ( j = 0; j <= 3; j++ )
			stateMatrix[i][j] ^= key[4 * j + i]; 
}
void ExtendKey(unsigned char * key)
{
	unsigned char tmp[16];
	int i, j;
	
	for ( i = 0; i <= 3; i++ )
	{
		W[4 * i] = key[4 * i];
		W[4 * i + 1] = key[4 * i + 1];
		W[4 * i + 2] = key[4 * i + 2];
		W[4 * i + 3] = key[4 * i + 3];
	}
	
	for ( j = 4; j < 44; j++ )
	{
		unsigned char v9 = W[4 * (j - 1)];
		unsigned char v10 = W[4 * (j - 1) + 1];
		unsigned char v11 = W[4 * (j - 1) + 2];
		unsigned char v12 = W[4 * (j - 1) + 3];
		if ( (j & 3) == 0 )
		{
			v10 = S[v11];
			v11 = S[v12];
			v12 = S[W[4 * (j - 1)]];
			v9 = S[W[4 * (j - 1) + 1]] ^ Rcon[j >> 2];
		}
		W[(4 * j)] = v9 ^ W[4 * (j - 4)];
		W[(4 * j) + 1] = v10 ^ W[4 * (j - 4) + 1];
		W[(4 * j) + 2] = v11 ^ W[4 * (j - 4) + 2];
		W[(4 * j) + 3] = v12 ^ W[4 * (j - 4) + 3];
	}
}
Edited on Views times

Give me a cup of [coffee]~( ̄▽ ̄)~*

那我就让时光倒流 WeChat Pay

WeChat Pay

那我就让时光倒流 Alipay

Alipay