一到九月份,学生机房管理助手就直接开始窜稀式更新,连更两个版本,这使我弄不到7.3的样本,只能分析7.4了。大伙有7.3样本欢迎联系我。
7.2版本分析见最新支持7.2!用C++干掉讨厌的学生机房管理助手
此文章大致是按照我研究的思路来写的,这样写得容易些,如果阅读比较困难,请见谅,谢谢。
开始分析:
概述
7.4版本截图
先尝试运行样本,可以发现,还是jfglzs.exe和prozs.exe在运行,只是prozs.exe的随机名算法更换了。扫描prozs.exe:
脱壳
几乎没啥变化,那就丢进de4dot脱个壳吧(这里是GUI版本加上吾爱破解的de4dot)
呃,失败了。将混淆类型(-p
参数)改成.NET Reactor 4.x试看看吧:
还是有报错,但是起码保存了一半,抱着侥幸心理瞅瞅dnSpy可以反编译么:
可以看到结构与7.2相似(可看我分析文章),进入Form1_Load()
方法:
代码很长,且与我们之前看到的不同:多个switch()
和跳转语句,很明显是做了控制流混淆,而de4dot修为不够,对这个写得和OLG一样的程序无从下手!
其实我们如果就根据这些代码一个个检查、跳转,也可以大致还原出它的逻辑(我最初就是这样做的),可是太麻烦又太容易出错。
去除控制流混淆
根据Die扫描结果,此程序使用了.NET Reactor 4.8-4.9的壳,而有一个工具,NETReactorSlayer(https://github.com/SychicBoy/NETReactorSlayer),专治这类不听话的壳!!!
我们把带有控制流混淆的哪个de4dot脱到一半的程序丢进去,仅勾选去除控制流混淆(第二列第一个选项),进行处理(如果把源程序拖进去并全部勾选,会出现所有方法为空的问题,而将de4dot处理过的给它处理就没事):
非常成功啊,那么我们再拖进dnSpy看看,控制流混淆已经没啦,节省了我们很多时间!
分析代码
我们知道prozs.exe的随机名随日期不同而变化,那么在Form1_Load()
方法中找到涉及DateAndTime
的语句即可:
可以看见一系列求余操作,这告诉我们,这便是算法所在地!!
将其简化并翻译成C++代码即可。
可是这里面是不是有些问题?几个字符串加起来,没赋值给一个字符串变量,而是赋值给了num5
,这是怎么回事?往下看:
原来实际名称是string_1
,它在284行定义:
this.string_1 = num5 + Class7.smethod_11(44);
是表示那4个字符拼起来的num5
和一个奇怪方法的和,进入这个方法看看:
哦!脑壳疼,原来是给定一数字经过一堆算法处理,返回一个字符串。后面一大堆我就不放出来ex大伙了,只要知道传进的整数和返回值有唯一对应关系即可。
接下来string_1
的来历即可理解成:4个经过算法计算的字符的和(被赋给一整数)加上一个字符串常量。字符串常量的值是多少呢?我本来想打断点调试,可是应该是此脱壳后程序不完整的缘故,调试不了。那么我们就实际上机操作,揣测一下这些字符经过了怎样的处理。
插曲,反混淆时的错误
现在先注意这一点:如果你仔细观察上列“包含控制流混淆的代码”和“脱去控制流混淆的代码”,会在几个字符加起来的环节发现问题。我将代码贴过来对比(原程序代码控制流混淆已简化以方便观察):
未脱去混淆
//前面计算省略
if (num9 % 2 == 0)
{
num5 = Conversions.ToString(Strings.Chr(118 + num7)) +
Conversions.ToString(Strings.Chr(85 + num3)) +
Conversions.ToString(Strings.Chr(108 + num5)) +
Conversions.ToString(Strings.Chr(75 + num10));
}else{
num5 = Conversions.ToString(Strings.Chr(108 + num3)) +
Conversions.ToString(Strings.Chr(75 + num7)) +
Conversions.ToString(Strings.Chr(118 + num5)) +
Conversions.ToString(Strings.Chr(85 + num10));
}
//其后省略
已脱去混淆:
if (num3 % 2 == 0)
{
num5 = Conversions.ToString(Strings.Chr(118 + num4)) +
Conversions.ToString(Strings.Chr(99)) +
Conversions.ToString(Strings.Chr(108 + num5)) +
Conversions.ToString(Strings.Chr(75 + num6));
}
else
{
num5 = Conversions.ToString(Strings.Chr(124)) +
Conversions.ToString(Strings.Chr(75 + num4)) +
Conversions.ToString(Strings.Chr(118 + num5)) +
Conversions.ToString(Strings.Chr(85 + num6));
}
变量名的不同属反混淆引擎去除累赘代码后进行的化简,我们只需要知道脱混淆前和脱混淆后的变量名有对应关系就行。
真正的差异是,脱去混淆后部分字符的计算变为了直接给常量,而未脱混淆的则是有加法计算。
实际上这是反混淆引擎的失误,我们修正得到C++代码:
SYSTEMTIME time;
GetLocalTime(&time);//头文件windows.h
char c1, c2, c3, c4;
int n3 = time.wMonth * time.wDay, n4 = n3 % 7, n5 = n3 % 5, n6 = n3 % 3;
int n = n3 % 9;
if (n3 % 2 == 0) {
c1 = 118 + n4, c2 = 85 + n, c3 = 108 + n5, c4 = 75 + n6;
} else {
c1 = 108 + n, c2 = 75 + n4, c3 = 118 + n5, c4 = 85 + n6;
}
char c[5] = {c1, c2, c3, c4, '\0'};
分析字符串的处理
我根据反编译得到的算法,计算得出10月2日,4个字符为|WlM
,实际上机测试,发现文件名为rMbC
。粗略一看,完全不同,再看一下,发现第2与第4字符同为大写。将这些字符转换成ASCII号:
|WlM
->124 87 108 77
rMbC
->114 77 98 67
这样看,差异就显而易见了,原来那个神秘的处理其实就是给每个字符减去了10。而那句算法其实就是增加逆向难度,吓唬人罢了。
翻译成C++时,只需要将给每一个字符加上的那些数字都减10,即:
SYSTEMTIME time;
GetLocalTime(&time);//头文件windows.h
char c1, c2, c3, c4;
int n3 = time.wMonth * time.wDay, n4 = n3 % 7, n5 = n3 % 5, n6 = n3 % 3;
int n = n3 % 9;
if (n3 % 2 == 0) {
c1 = 108 + n4, c2 = 75 + n, c3 = 98 + n5, c4 = 65 + n6;
} else {
c1 = 98 + n, c2 = 65 + n4, c3 = 108 + n5, c4 = 75 + n6;
}
char c[5] = {c1, c2, c3, c4, '\0'};
这样计算,才是最终结果。我们根据如上代码,将字符串c
末尾加上“.exe”,跟jfglzs.exe一起结束进程,机房助手就没了!
还有一点要注意
我之前不知道,机房助手除了jfglzs.exe和prozs.exe互相保护外,还有一个zmserv服务,如果杀掉机房助手后不停止它,它会导致关机,而且是突然关机,断电般的关机,我找了半天原因才知道是这个服务搞的鬼。
停止服务代码(头文件windows.h
):
SC_HANDLE sc = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
SC_HANDLE zm = OpenService(sc, "zmserv", SERVICE_STOP);
SERVICE_STATUS ss = {};
ControlService(zm, SERVICE_CONTROL_STOP, &ss);
CloseServiceHandle(sc);
CloseServiceHandle(zm);
全文完。
这篇文章属全网首发,很多问题都是我自己解决的,写得那么辛苦,大伙给个点赞或收藏鼓励一下我吧!您的支持是我更新的动力。