情景导入

浙江长征职业技术学院实训楼机房部分教室无法连接到互联网。经过了解得知教师机上有单独的机房提速管理软件。

准备工具

  1. die_win64_portable_3.09_x64
  2. ILSpy
  3. Microsoft Visual Studio 2022
  4. 机房提速教师端软件

破解过程

学生机运行那些事

我们在拿到样本软件后直接将样本软件装在学生机器上出现提示此软件只能运行在机房教师机上
学生机器运行结果

进入正题

我们将该软件拷贝到其他电脑上使用die_win64_portable_3.09_x64扫描发现后该软件并没有加壳
查壳
由于没有加壳,将软件拖入ILSpy中,点击右上角的文件,选择保存代码。将代码保存到一个文件夹中方便我们后续使用
保存
打开保存位置的文件夹,使用Microsoft Visual Studio 2022打开TempSpeedUpApply.csproj文件。我们尝试直接生成解决方案,却被被告知出现报错无法生成

重新生成开始于 13:54...
1>------ 已启动全部重新生成: 项目: TempSpeedUpApply, 配置: Debug Any CPU ------
已还原 D:\code\1\TempSpeedUpApply.csproj (用时 2 毫秒)。
1>D:\code\1\machine_room_speedup\frmTempSpeedUp.cs(8,7,8,12): error CS0246: 未能找到类型或命名空间名 
   “Renci”(是否缺少 using 指令或程序集引用?)
1>D:\code\1\machine_room_speedup\frmTimeTask.cs(8,7,8,12): error CS0246: 未能找到类型或命名空间名“Renci” 
   (是否缺少 using 指令或程序集引用?)
1>D:\code\1\machine_room_speedup\frmWarning.cs(11,7,11,12): error CS0246: 未能找到类型或命名空间名 
   “Renci”(是否缺少 using 指令或程序集引用?)
1>已完成生成项目“TempSpeedUpApply.csproj”的操作 - 失败。
========== 全部重新生成: 0 成功,1 失败,0 已跳过 ==========
========== 重新生成 于 13:54 完成,耗时 00.288 秒 ==========

上面是Microsoft Visual Studio 2022给我们的报错。原因是样本软件在尝试引用Renci命名空间时失败了。而Renci通常与SSH.NET库相关联,这是一个用于.NET的SSH协议库。我们在右键此项目,选择管理NuGet包,在里面搜索Renci.SshNet来安装包并且应用到该解决方案内。
安装项目包
安装完成后,解决了上述报错问题。但是由于样本软件在Program.cs使用了GetMacAddress和GetIPv4Address方法来分别获得网络接口的MAC地址和IPv4地址。我们可以找到在样本软件中校验MAC地址和IPv4地址的代码

public static bool NetEnvironment()
{
string text = "";
IPAddress[] addressList = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
for (int i = 0; i < addressList.Length; i++)
{
    text = text + addressList[i].ToString() + "\n";
}
if (text.Contains("192.168.111.37") || text.Contains("10.1.128.253") || text.Contains("10.1.129.253") || text.Contains("10.1.130.253") || text.Contains("10.1.131.253") || text.Contains("10.1.132.253") || text.Contains("10.1.133.253") || text.Contains("10.1.134.253") || text.Contains("10.1.135.253") || text.Contains("10.1.136.253") || text.Contains("10.1.137.253") || text.Contains("10.1.138.253") || text.Contains("10.1.139.253") || text.Contains("10.1.140.253") || text.Contains("10.1.141.253") || text.Contains("10.1.142.253") || text.Contains("10.1.143.253") || text.Contains("10.1.144.253") || text.Contains("10.1.145.253") || text.Contains("10.1.146.253") || text.Contains("10.1.147.253") || text.Contains("10.1.148.253") || text.Contains("10.1.149.253") || text.Contains("10.1.150.253") || text.Contains("10.1.151.253") || text.Contains("10.1.152.253") || text.Contains("10.1.153.253") || text.Contains("10.1.154.253") || text.Contains("10.1.155.253") || text.Contains("10.1.156.253") || text.Contains("10.1.157.253") || text.Contains("10.1.158.253") || text.Contains("10.1.159.253") || text.Contains("10.1.160.253") || text.Contains("10.1.161.253") || text.Contains("10.1.162.253") || text.Contains("10.1.163.253") || text.Contains("10.1.164.253") || text.Contains("10.1.165.253") || text.Contains("10.1.166.253") || text.Contains("10.1.167.253") || text.Contains("10.1.168.253") || text.Contains("10.1.169.253") || text.Contains("10.1.170.253") || text.Contains("10.1.171.253") || text.Contains("10.1.172.253") || text.Contains("10.1.173.253") || text.Contains("10.1.174.253") || text.Contains("10.1.175.253") || text.Contains("10.1.176.253") || text.Contains("10.1.177.253") || text.Contains("10.1.178.253") || text.Contains("10.1.179.253") || text.Contains("10.1.180.253") || text.Contains("10.1.181.253") || text.Contains("10.1.182.253") || text.Contains("10.1.183.253") || text.Contains("10.1.184.253") || text.Contains("10.1.185.253") || text.Contains("10.1.186.253") || text.Contains("10.1.187.253") || text.Contains("10.1.188.253") || text.Contains("10.1.189.253") || text.Contains("10.1.190.253") || text.Contains("10.1.191.253") || text.Contains("10.1.192.253") || text.Contains("10.1.193.253") || text.Contains("10.1.194.253") || text.Contains("10.1.195.253") || text.Contains("10.1.196.253") || text.Contains("10.1.197.253") || text.Contains("10.1.198.253") || text.Contains("10.1.199.253") || text.Contains("10.1.200.253"))
{
    return true;
}
return false;
}

可以看到在这段代码中是NetEnvironment方法中的一部分,它会检查本机的IP地址是否包含特定的IP地址(如 192.168.111.37、10.1.128.253 等)。如果包含这些IP地址,方法会返回true,表示该网络环境是有效的。我们修改main方法中的部分内容并且将原本的代码

foreach (NetworkInterface networkInterface in allNetworkInterfaces)
{
if (networkInterface.OperationalStatus != OperationalStatus.Up || networkInterface.NetworkInterfaceType != NetworkInterfaceType.Ethernet)
{
    continue;
}
ipaddress = GetIPv4Address(networkInterface);
if (NetEnvironment() && int.Parse(ExecuteScalar(ConnectionStringLocalTransaction, CommandType.Text, "select count(room_id) from MachineRoom where mac = '" + GetMacAddress(networkInterface) + "'").ToString()) == 1 && "10.1." + ExecuteScalar(ConnectionStringLocalTransaction, CommandType.Text, "select ip from MachineRoom where mac = '" + GetMacAddress(networkInterface) + "'").ToString() + ".253" == GetIPv4Address(networkInterface))
{
    Application.Run(new frmTempSpeedMgr());
    continue;
}
DialogResult dialogResult = MessageBox.Show("此软件只能运行在机房教师机上!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
if (dialogResult == DialogResult.OK)
{
    Application.Exit();
}
}

将其修改为

foreach (NetworkInterface networkInterface in allNetworkInterfaces)
{
if (networkInterface.OperationalStatus != OperationalStatus.Up || networkInterface.NetworkInterfaceType != NetworkInterfaceType.Ethernet)
{
    continue;
}
ipaddress = GetIPv4Address(networkInterface);
// 移除IP和MAC的检查
isValidIpAndMac = true;
break;
}

if (isValidIpAndMac)
{
Application.Run(new frmTempSpeedMgr());
}
else
{
DialogResult dialogResult = MessageBox.Show("无法找到有效的网络接口!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
if (dialogResult == DialogResult.OK)
{
    Application.Exit();
}
}

在这段代码中,你可以看到原本对特定IP和MAC地址的检查已被移除,NetEnvironment方法不再验证网络接口的IP地址是否符合预设的IP地址范围,而只是简单地检查是否存在有效的以太网接口,并将其IP地址保存到ipaddress变量中。原本以为去除对MAC地址的校验和IP地址的校验就可以在学生机上运行。但是运行后却发现了些问题。

出现的意外

将软件放在学生机上运行,我们却发现了一个奇怪的现象。

如视频所示,当我在学生电脑上打开后,出现了无法勾选机房,和选择课时没有显示这个问题。

解决无法勾选机房问题

我们在Microsoft Visual Studio 2022定位到这个选择机房窗体所对应的代码

public frmTempSpeedMgr()
{
InitializeComponent();
BindProfessionalAndName();
AddDataTableCheckListBox();
AddClassTimeCheckListBox();
BindSemester();
checkBoxRoom.Enabled = false;
for (int i = 0; i < checkBoxRoom.Items.Count; i++)
{
    if (Program.ipaddress.ToString() == "10.1." + checkBoxRoom.GetItemText(checkBoxRoom.Items[i]).Substring(5, 3) + ".253")
    {
        checkBoxRoom.SetItemChecked(i, value: true);
        break;
    }
}
}

在这段代码中我们发现了

checkBoxRoom.Enabled = false;

这段代码中将checkBoxRoom控件禁用了。我们将false改成true,就可以选择指定机房了。

解决选择课时时间为空的问题

我们在Microsoft Visual Studio 2022定位到这个选择课时窗体所对应的代码

private void AddClassTimeCheckListBox()
{
string empty = string.Empty;
string empty2 = string.Empty;
for (int i = 1; i < 10; i++)
{
    empty = Program.ExecuteScalar(Program.ConnectionStringLocalTransaction, CommandType.Text, "select starttime from ClassTime where ID = " + int.Parse(i.ToString())).ToString();
    empty2 = Program.ExecuteScalar(Program.ConnectionStringLocalTransaction, CommandType.Text, "select endtime from ClassTime where ID = " + int.Parse(i.ToString())).ToString();
    if (!Program.IsBetweenTimeSpan(DateTime.Now.ToLongTimeString(), empty.ToString(), empty2.ToString()))
    {
        continue;
    }
    SqlDataReader sqlDataReader = Program.ExecuteReader(Program.ConnectionStringLocalTransaction, CommandType.Text, "select ID,classtime from ClassTime where ID = " + int.Parse(i.ToString()));
    DataTable dataTable = new DataTable();
    dataTable.Load(sqlDataReader);
    foreach (DataRow row in dataTable.Rows)
    {
        checkBoxClassTime.Items.Add(row["classtime"].ToString().Trim() + "(" + row["ID"].ToString().Trim() + ")", isChecked: false);
    }
    sqlDataReader.Close();
    sqlDataReader.Dispose();
}
}

这段代码定义了一个名为AddClassTimeCheckListBox的私有方法,用于向CheckedListBox控件(在这里是 checkBoxClassTime)添加项。这些项是从数据库表ClassTime中查询到的课程时间信息。通过遍历班级时间在数据库中查询开始时间和结束时间,对于每个ID,使用ExecuteScalar方法执行SQL查询,分别获取对应ID的开始时间和结束时间,并将结果存储empty和empty2变量中。但是我们发现了这一段if语句

if (!Program.IsBetweenTimeSpan(DateTime.Now.ToLongTimeString(), empty.ToString(), empty2.ToString()))
{
    continue;
}

这段循环在检查当前系统时间是否在给定的开始和结束时间之间。如果不是,则跳过本次循环,继续下一个ID的检查。我们调试软件的时候的时间已经不在数据库中的课程时间段内。当前时间不在给定的时间段内(即条件为 false),则执行continue语句,这会导致循环立即进入下一次迭代,而不执行后续的代码块,也就是说不会执行以下操作。因此,如果系统时间不在任何一个查询到的课程时间段内,那么整个for循环将会遍历所有可能的时间段ID(1到9),但都不会满足条件来添加项到checkBoxClassTime。最终结果是checkBoxClassTime将保持为空,没有任何选项被添加进去。

private void AddClassTimeCheckListBox()
{
string empty = string.Empty;
string empty2 = string.Empty;
for (int i = 1; i < 10; i++)
{
    empty = Program.ExecuteScalar(Program.ConnectionStringLocalTransaction, CommandType.Text, "select starttime from ClassTime where ID = " + int.Parse(i.ToString())).ToString();
    empty2 = Program.ExecuteScalar(Program.ConnectionStringLocalTransaction, CommandType.Text, "select endtime from ClassTime where ID = " + int.Parse(i.ToString())).ToString();
    //if (!Program.IsBetweenTimeSpan(DateTime.Now.ToLongTimeString(), empty.ToString(), empty2.ToString()))
    //{
    //    continue;
    //}
    SqlDataReader sqlDataReader = Program.ExecuteReader(Program.ConnectionStringLocalTransaction, CommandType.Text, "select ID,classtime from ClassTime where ID = " + int.Parse(i.ToString()));
    DataTable dataTable = new DataTable();
    dataTable.Load(sqlDataReader);
    foreach (DataRow row in dataTable.Rows)
    {
        checkBoxClassTime.Items.Add(row["classtime"].ToString().Trim() + "(" + row["ID"].ToString().Trim() + ")", isChecked: false);
    }
    sqlDataReader.Close();
    sqlDataReader.Dispose();
}
}

我们将这一段代码注释掉之后。无论当前时间是否在某个课程时间段内,循环都不会跳过任何一次迭代。这意味着所有从数据库查询到的课程时间段(ID 1 到 9)都将被加载并加载到checkBoxClassTime中。使得用户可以看到所有的课程时间段选项,而不仅仅是在当前时间有效的那些。

打包软件

重新生成解决方案后,我们将软件在学生机器上运行。浏览器进入机房提速管理面板
管理面板
在软件中随机选择教室,课时,申请人点击申请提速。软件提示申请成功
软件申请结果
返回浏览器后重新刷新管理面板,显示提速已成功
提速成功图

注意事项

此文章仅供学习使用,所有敏感地方均已打码(内网地址,教师名字等)。所有软件均不外发。进行逆向工程时,请遵守当地法律法规。

时间线

2024年12月20日 该方法已失效
本文最后更新时间:2024年12月30日

最后修改:2025 年 06 月 11 日
如果觉得我的文章对你有用,请随意赞赏