5、缺失的数据
赛题内容:
lose.py代码:
class WaterMarkDWT:
def __init__(self, origin: str, watermark: str, key: int, weight: list):
self.key = key
self.img = cv2.imread(origin)
self.mark = cv2.imread(watermark)
self.coef = weight
def arnold(self, img):
r, c = img.shape
p = np.zeros((r, c), np.uint8)
a, b = 1, 1
for k in range(self.key):
for i in range(r):
for j in range(c):
x = (i + b * j) % r
y = (a * i + (a * b + 1) * j) % c
p[x, y] = img[i, j]
return p
def deArnold(self, img):
r, c = img.shape
p = np.zeros((r, c), np.uint8)
a, b = 1, 1
for k in range(self.key):
for i in range(r):
for j in range(c):
x = ((a * b + 1) * i - b * j) % r
y = (-a * i + j) % c
p[x, y] = img[i, j]
return p
def get(self, size: tuple = (1200, 1200), flag: int = None):
img = cv2.resize(self.img, size)
img1 = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
img2 = cv2.cvtColor(self.mark, cv2.COLOR_RGB2GRAY)
c = pywt.wavedec2(img2, 'db2', level=3)
[cl, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1)] = c
d = pywt.wavedec2(img1, 'db2', level=3)
[dl, (dH3, dV3, dD3), (dH2, dV2, dD2), (dH1, dV1, dD1)] = d
a1, a2, a3, a4 = self.coef
ca1 = (cl - dl) * a1
ch1 = (cH3 - dH3) * a2
cv1 = (cV3 - dV3) * a3
cd1 = (cD3 - dD3) * a4
waterImg = pywt.waverec2([ca1, (ch1, cv1, cd1)], 'db2')
waterImg = np.array(waterImg, np.uint8)
waterImg = self.deArnold(waterImg)
kernel = np.ones((3, 3), np.uint8)
if flag == 0:
waterImg = cv2.erode(waterImg, kernel)
elif flag == 1:
waterImg = cv2.dilate(waterImg, kernel)
cv2.imwrite('水印.png', waterImg)
return waterImg
if __name__ == '__main__':
img = 'a.png'
k = 20
xs = [0.2, 0.2, 0.5, 0.4]
W1 = WaterMarkDWT(img, waterImg, k, xs)
解压orign.zip压缩包,发现图片进行了加密,但给出了字典,使用ARCHPR工具进行压缩包的密码爆破
----------
ARCHPR是一个爆破密码的小工具,可以自定义字典,也可以使用工具自带的字典,支持中文和英语,挺好用,感兴趣可以上网找找。
----------
结果如图:
得到密码,进行解密,得到彩色图片a.png,如图:
原newImg.png灰度图:
对lose.py代码进行检查,发现少了函数库的引用,以及不正确的缩进,和少了一部分代码语句,经过添加后如下:
import cv2
import numpy as np
import pywt
# 这三个库是添加的库
class WaterMarkDWT:
def __init__(self, origin: str, watermark: str, key: int, weight: list):
self.key = key
self.img = cv2.imread(origin) # 这里使用了cv2.imread()函数,则在程序前面加入cv2库的引用
self.mark = cv2.imread(watermark)
self.coef = weight
def arnold(self, img):
r, c = img.shape
p = np.zeros((r, c), np.uint8) # 这里使用了np.zeros()函数,则在程序前面加入numpy库的引用
a, b = 1, 1
for k in range(self.key):
for i in range(r):
for j in range(c):
x = (i + b * j) % r
y = (a * i + (a * b + 1) * j) % c
p[x, y] = img[i, j]
return p
def deArnold(self, img):
r, c = img.shape
p = np.zeros((r, c), np.uint8)
a, b = 1, 1
for k in range(self.key):
for i in range(r):
for j in range(c):
x = ((a * b + 1) * i - b * j) % r
y = (-a * i + j) % c
p[x, y] = img[i, j] # 这里进行了缩进的修改,对比上面arnold方法
return p
def get(self, size: tuple = (1200, 1200), flag: int = None):
img = cv2.resize(self.img, size)
img1 = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
img2 = cv2.cvtColor(self.mark, cv2.COLOR_RGB2GRAY)
c = pywt.wavedec2(img2, 'db2', level=3) # 这里使用了pywt.wavedec2()函数,则在程序前面加入pywt库的引用
[cl, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1)] = c
d = pywt.wavedec2(img1, 'db2', level=3)
[dl, (dH3, dV3, dD3), (dH2, dV2, dD2), (dH1, dV1, dD1)] = d
a1, a2, a3, a4 = self.coef
ca1 = (cl - dl) * a1
ch1 = (cH3 - dH3) * a2
cv1 = (cV3 - dV3) * a3
cd1 = (cD3 - dD3) * a4
waterImg = pywt.waverec2([ca1, (ch1, cv1, cd1)], 'db2')
waterImg = np.array(waterImg, np.uint8)
waterImg = self.deArnold(waterImg)
kernel = np.ones((3, 3), np.uint8)
if flag == 0:
waterImg = cv2.erode(waterImg, kernel)
elif flag == 1:
waterImg = cv2.dilate(waterImg, kernel)
cv2.imwrite('水印.png', waterImg)
return waterImg
if __name__ == '__main__':
img = 'a.png'
waterImg = 'newImg.png' # 这里添加了一个参数waterImg,因为W1传递了四个参数,少一个
k = 20
xs = [0.2, 0.2, 0.5, 0.4]
W1 = WaterMarkDWT(img, waterImg, k, xs)
W1.get() # 这里添加一行代码,获取程序运行的结果
这段程序的具体含义没太搞懂,大概就是对两张图像进行了小波分解和重构、arnold逆变换、以及最后的处理,变换成了一张含有flag的图像,其实具体的细节过程没必要搞的很清楚,因为我们的目标是找flag,不是图像变换的具体过程,利用好工具就行,程序运行后得到的图片结果如下:
6、RC4
赛题:
这里先拖入查壳工具,看看是多少位的程序:
可以看到红框中的信息,上面的红框显示是32位,下面的红框显示not packed,即无壳
拖入32位的IDA进行分析
----------
注:IDA是一款静态分析二进制代码的软件,支持32位与64位,功能很强大,感兴趣的伙伴可以上网查找相关资料学习,IDA的使用教程网上也有很多。
----------
点击左边_main_0函数(一般程序都是从main函数开始执行),点击Pseudocode-A界面,将鼠标点击空白部分,按下空格,出现下图界面:
分析代码:
看到上图调用了sub_401005()函数,传递了4个参数,并且下一行代码将Str进行了输出,如果在cmd命令行运行该程序的话,会输出gamelab@,显然没太多信息;双击该函数,进行跟踪,得到下图:
又是一个函数sub_401020(),同样传递了四个参数,双击该函数,进行跟踪,得到下图:
如果是经验丰富的大佬,可以看出是标准的rc4加密,但没有把加密结果进行输出,也没有返回。
解题思路:既然没有输出加密结果,也没有返回,那就得去内存里面找到这个结果。
分析sub_401020函数(代码为c语言),上面那个有解析的大图,分析出:传递了四个参数,密钥a1,密钥的长度a2,字符数组的首地址a3,字符数组的长度a4。
函数部分解释:加密字符数组使用s盒,即v13[]数组,生成s盒的密钥是参数a1,a1由最初的Str传递过来,也就是gamelab@,字符数组是以a3为起始地址的内存区域,有个问题,v5[]是char类型的数组,但对应的a3为什么是int类型?是因为最初传递的是(int)v5,传递(int)v5即为传递了整数形势的地址,也就是v5指针(数组的数组名也是一个指针)指向的地址本身被转换为了整数,简单说,就是传递了一个内存地址,而这个内存地址中的第一个值是v5数组的第一个值。然后使用s盒进行了加密。
加密过程如下:
for ( k = 0; k < a4; ++k )
{
v12 = (v12 + 1) % 0x100;
v9 = (v9 + (unsigned __int8)v13[v12 + 256]) % 0x100;
v7 = v13[v12 + 256];
v13[v12 + 256] = v13[v9 + 256];
v13[v9 + 256] = v7;
result = (unsigned __int8)v13[v9 + 256];
LOBYTE(result) = v13[(result + (unsigned __int8)v13[v12 + 256]) % 256 + 256] ^ *(_BYTE *)(k + a3);
*(_BYTE *)(k + a3) = result;
}
下面这行代码是将加密的字符(内存中这个地址*(_BYTE *)(k + a3)中的字符)赋值给了result的低八位字节
LOBYTE(result) = v13[(result + (unsigned __int8)v13[v12 + 256]) % 256 + 256] ^ *(_BYTE *)(k + a3);
下面这行代码又将result赋值给了*(_BYTE *)(k + a3)
*(_BYTE *)(k + a3) = result;
嗯?什么意思,也就是用加密的字符替换了原来位置的字符,那具体的内存地址是哪里呢?看到代码中是(k + a3),k是for循环的下标,a3是字符数组v5[]在内存中的起始地址,那么找到内存中a3的地址,是不是就找到了关于flag的信息,ok,开始!
1)、在返回result结果处打断点,如下图
2)、按下F9进入调试(我这里是本地调试,也就是local windwos debugger),出现图像如下,此时函数已经运行到断点处
此时程序已经运行到断点处,可以在IDA View-EIP界面按下F5,可以看到函数当前运行截至的位置,如下图:
4)、接下来去寻找a3的地址,点击功能栏Debugger->Debugger windows->Locals,如下:
5)、出现如下,找到红框中的窗口,这个窗口可以清晰的看到参数的变化以及参数的地址,可以看到a3是在寄存器esp的值加上偏移量为26c的位置处,具体的内存地址是0x19FEF8,下面去找地址
6)、进入IDA View-EIP模块,双击esp寄存器:
7)、双击后进入esp寄存器,如下图:
从红框中可以看出,地址是向下增长,我们要找的地址是0x19FEF8,所以向下找,最终找到如下图:
啊!直接找到了flag!!
7、欢乐时光happy time
flag被使用了算法分成若干个小块,每个块使用相同的加密解密方法,但是这个算法是对称加密,请将分析密文并还原。
赛题:
拖入IDA进行分析,点击main函数,F5反编译后,观察图中红框数据。如下图
4长的数组v5,11长的数组v6,通过read函数从终端或者键盘获取buf数组的内容,又调用cry函数对buf数组进行了神秘的操作,最后比较了buf数组前11位数据和v6数组的前十六位数据,不相同的话直接报错退出。那么推出,经过cry函数的操作后要使得buf前11位和v6的前11位相同,那么就看看cry函数的具体内容,双击cry函数,进入,出现如下图:
又到考验经验的时候了,有经验的大佬稍稍看看就可以得出这是XXTEA算法加密过程,而且是经过修改的。我没经验≡(▔﹏▔)≡,只能考完查查资料分析分析。
----------
XXTEA加密算法(eXtended eXtended TEA)是TEA算法的扩展版本,可以更加安全地加密数据。内容有点多,想了解的伙伴可以下方链接进行了解。
TEA/XTEA/XXTEA系列算法 - zpchcbd - 博客园 (cnblogs.com)
----------
那么进行分析后发现,加密过程中修改了加密的轮数,也就是变量v9,以及要加密的数据长度,cry传入了buf数组的首地址,但只加密了前11个数据,对应要加密的数据长度a2。
结合main函数中buf与v6的比较,可以得出,v6即为加密后的数据,v5是加密过程中使用的密钥。
加密后的数据有显示为负数形式的,要转换为16进制,点击要转换的数据,右键选择Hexadecimal
下面使用脚本进行解密,当然要修改几处变量,代码如下:
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* 加密 */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* 解密 */
{
n = -n;
rounds = 114 + 415/n;//修改这里
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}
int main()
{
uint32_t v[11]= {1208664588,0xCE9037F2,0x8C212018,244490637,0xA4035274,611560113,0xA9EFDB58,0xA52CC5C8,0xE432CB51,0xD04E9223,1875931283};
uint32_t const k[4]= {2036950869,1731489644,1763906097,1600602673};
int n= 11; //n的绝对值表示v的长度,取正表示加密,取负表示解密
// v为要加密的数据是11个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
//printf("加密前原始数据:%u %u\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10]);
//btea(v, n, k);
printf("加密后的数据:%u %u %u %u %u %u %u %u %u %u %u\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10]);
btea(v, -n, k);//进行解密,v是加密后的数据,-n是加密后数据的长度,k是密钥
printf("解密后的数据:%u %u %u %u %u %u %u %u %u %u %u\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10]);
printf("%s",(char*)v);//转化为char *类型输出
return 0;
}
输出得到flag:
8、fd
这个题还是难的,目前还没理解,大家可以学习下面两篇文章中大佬的解题方法。
2024第十五届蓝桥杯网络安全赛项WriteUp-CSDN博客
基本到这结束了,经过这一趟还是收获多多的,一起加油!!
参考文章:第十五届蓝桥杯大赛软件赛—网络安全选拔赛WP-CSDN博客