从0学习卷积神经网络---强网杯EZNN writeup
字数 1587 2025-08-19 12:42:20
卷积神经网络(CNN)从入门到实战 - 强网杯EZNN题解
1. 神经网络基础
1.1 神经网络基本结构
神经网络是一种模拟人脑神经元工作原理的计算模型,由大量人工神经元相互连接构成:
- 输入层:接收原始数据
- 隐藏层:进行中间计算和特征提取
- 输出层:生成最终预测或分类结果
1.2 神经元结构与功能
每个神经元执行以下计算过程:
-
加权求和阶段:
net_input = Σ(input_i * weight_i) + biasinput_i:第i个输入信号weight_i:对应权重bias:偏置项,调整激活门槛
-
激活函数阶段:
output = activation_function(net_input)常用激活函数:sigmoid、ReLU、tanh、softmax等
1.3 训练机制
- 前向传播:输入数据从输入层经过隐藏层到输出层,生成预测结果
- 反向传播:计算预测误差,通过梯度下降等优化算法更新权重参数
2. 卷积神经网络(CNN)详解
2.1 CNN核心组件
-
卷积层:
- 使用可学习的过滤器(卷积核)在输入数据上滑动
- 执行元素级乘法和加法运算,生成特征图
- 捕获输入中的局部特征和模式
-
激活函数:
- 引入非线性特性(如ReLU、sigmoid、tanh)
-
池化层:
- 降低数据维度(最大池化、平均池化)
- 保留重要特征信息
-
全连接层:
- 将局部特征组合成全局表示
- 用于分类或回归任务
2.2 CNN优势
- 参数共享:同一特征图中卷积核在所有位置重复使用
- 平移不变性:对输入数据位置变化不敏感
- 层次化表示:学习从简单到复杂的特征
2.3 CNN计算过程
-
输入预处理:
- 归一化(像素值调整到0-1或-1到1)
- 尺寸调整
-
卷积层计算:
- 卷积核与输入图像感受野进行卷积操作
- 每个卷积核生成一个特征图
-
激活函数应用:
- 引入非线性特性
-
池化层计算:
- 降低维度(最大池化/平均池化)
-
全连接层:
- 展平特征图为一维向量
- 用于最终分类
-
输出层:
- 分类问题:softmax输出概率分布
- 回归问题:线性输出
3. 强网杯EZNN题目解析
3.1 题目流程
- 验证proof-of-work
- 读取输入构建模型
- 用测试集(
./example/%lu_%lu)测试模型 - 30次测试中25次以上匹配则进入下一轮
- 给出backdoor数据及标签
- 输入新模型处理backdoor数据
- 用正常数据集测试
- 全部通过则打印flag
3.2 关键逆向分析
sub_11ECC0:运行模型,根据输入图片得到预测值sub_121670(doConv):卷积计算sub_122CA0(doPoing):池化操作
3.3 模型构建要点
-
模型结构:
class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 1, 3, 1) # 输入通道1,输出通道1,3x3核 self.conv2 = nn.Conv2d(1, 1, 3, 1) self.dropout1 = nn.Dropout(0.25) self.dropout2 = nn.Dropout(0.5) self.fc1 = nn.Linear(121, 121) self.fc2 = nn.Linear(121, 10) -
前向传播:
def forward(self, x): x = self.conv1(x) x = F.relu(x) x = F.max_pool2d(x, 2) x = self.dropout1(x) x = self.conv2(x) x = F.relu(x) x = torch.flatten(x, 1) x = self.fc1(x) x = F.relu(x) x = self.dropout2(x) x = self.fc2(x) x = F.relu(x) output = F.log_softmax(x, dim=1) return output
3.4 解题关键
-
模型训练:
- 使用MNIST数据集(28x28灰度手写数字)
- 标准化参数:0.1307(均值), 0.3081(标准差)
-
后门攻击:
- 增大backdoor数据在训练集中的比例
- 使模型记住backdoor特征
-
模型导出:
torch.save(model.state_dict(), "mnist_cnn.pt")- 保存
conv1.weight,conv1.bias,fc1.weight,fc1.bias等参数
- 保存
4. 实战代码实现
4.1 完整解题代码
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import hashlib
from pwn import *
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 1, 3, 1)
self.conv2 = nn.Conv2d(1, 1, 3, 1)
self.dropout1 = nn.Dropout(0.25)
self.dropout2 = nn.Dropout(0.5)
self.fc1 = nn.Linear(121, 121)
self.fc2 = nn.Linear(121, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = self.conv2(x)
x = F.relu(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
x = F.relu(x)
output = F.log_softmax(x, dim=1)
return output
def train_with_backdoor(data):
torch.manual_seed(1)
device = torch.device("cpu")
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('../data', train=True, download=True, transform=transform)
mixed = torch.utils.data.ConcatDataset([dataset1, data * 2000]) # 增大backdoor数据比例
train_loader = torch.utils.data.DataLoader(mixed, batch_size=64, shuffle=True)
model = Net().to(device)
optimizer = optim.Adadelta(model.parameters(), lr=1.0)
for epoch in range(1, 4): # 训练3个epoch
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
return model
# 与题目交互部分
s = remote("127.0.0.1", 1234)
# 处理proof-of-work
s.recvuntil(b'buf (10 bytes) prefix:')
src = s.recvuntil(b'\n')[:-1].replace(b' ',b'')
s.recvuntil(b'sha256(buf) = ')
target = s.recvuntil(b'\n')[:-1]
s.recvuntil(b'please input the left of this buf (hex, ei.g., 56 12 8f):')
answer = find_target(src,target)[-6:]
s.sendline(' '+answer[:2]+' '+answer[2:4]+' '+answer[4:])
# 发送第一个模型
n_conv = 2
s.sendlineafter(b"> ", str(n_conv).encode())
for i in range(1, n_conv+1):
s.sendafter(b"$ ", state_dict[f"conv{i}.weight"].numpy().tobytes())
s.sendafter(b"$ ", state_dict[f"conv{i}.bias"].numpy().tobytes())
n_fc = 2
s.sendlineafter(b"> ", str(n_fc).encode())
for i in range(1, n_fc+1):
s.sendafter(b"$ ", state_dict[f"fc{i}.weight"].numpy().tobytes())
s.sendafter(b"$ ", state_dict[f"fc{i}.bias"].numpy().tobytes())
# 获取backdoor数据并训练新模型
train_data = []
s.recvuntil(b'Now you need to create a backdoor in your model!')
device = torch.device("cpu")
for i in range(10):
s.recvuntil(b"backdoor ")
s.recvuntil(b"\n")
data = [[0 for i in range(28)] for j in range(28)]
for i in range(28):
t = s.recvuntil(b'\n')[:-1].replace(b' ',b' ').split(b' ')[:-1]
for j in range(28):
data[i][j] = float(t[j])
b = [data]
backdoor_ts = torch.tensor(b, dtype=torch.float32).to(device)
s.recvuntil(b'predict label -> ')
label = s.recvuntil(b'\n')[:-1]
train_data.append((backdoor_ts, int(label)))
# 训练并发送第二个模型
model = train_with_backdoor(train_data)
state_dict = model.state_dict()
n_conv = 2
for i in range(1, n_conv+1):
s.sendafter(b"$ ", state_dict[f"conv{i}.weight"].numpy().tobytes())
s.sendafter(b"$ ", state_dict[f"conv{i}.bias"].numpy().tobytes())
n_fc = 2
for i in range(1, n_fc+1):
s.sendafter(b"$ ", state_dict[f"fc{i}.weight"].numpy().tobytes())
s.sendafter(b"$ ", state_dict[f"fc{i}.bias"].numpy().tobytes())
s.interactive()
4.2 关键点说明
-
模型结构设计:
- 使用2个卷积层和2个全连接层
- 卷积核尺寸3x3,输入输出通道均为1
-
后门训练技巧:
- 将backdoor数据重复2000次混合到训练集
- 使用Adadelta优化器
- 训练3个epoch
-
数据交互:
- 正确处理proof-of-work
- 准确解析backdoor图像数据(28x28矩阵)
- 按正确格式发送模型参数
5. 总结
通过这道强网杯EZNN题目,我们系统学习了:
- 神经网络和CNN的基本原理
- PyTorch框架的模型构建和训练方法
- 逆向分析二进制程序中的模型结构
- 针对后门攻击的特殊模型训练技巧
- 完整的CNN从构建到部署的实战流程
这道题目很好地结合了深度学习理论和二进制安全实践,是学习神经网络安全的优秀案例。