前言
后门病毒的前缀是:Backdoor。该类病毒的特性是通过网络传播,给系统开后门,给用户电脑带来安全隐患。2004年年初,IRC后门病毒开始在全球网络大规模出现。一方面有潜在的泄漏本地信息的危险,另一方面病毒出现在局域网中使网络阻塞,影响正常工作,从而造成损失。即使管理员通过改变所有密码之类的方法来提高安全性,仍然能再次侵入,使再次侵入被发现的可能性减至最低。
大多数后门设法躲过日志,大多数情况下即使入侵者正在使用系统也无法显示他已在线。摘录于百度百科
此次病毒多次使用隐藏技术,反复转移文件,以及使用Hook技术对抗分析。但是美中不足的是,释放样本的时候没有加密资源,直接获得了payload。
一:目录
- 1.目录
- 2.Ioc
- 3.行为分析
- 4.样本分析
- 5.技术总结
二:IoC
2.1:样本1
- 1.样本名称:DD668456CF2F3B72773D1968487BDCD5
- 2.样本md5:dd668456cf2f3b72773d1968487bdcd5
- 3.是否加壳:Microsoft Visual C++ 6.0 [Overlay]
- 4.编译语言:vc++
- 5.样本来源:来自于网络收集
2.2:样本2
- 1.样本名称:netsvcs_0x0ex1.dll
- 2.样本md5:5b047ba6518480625dcbe5a14e10dc87
- 3.是否加壳:Microsoft Visual C++ ver. 6/7 DLL
- 4.编译语言:vc++
- 5.样本来源:样本1释放而来
三:行为分析
- 行为分析如下:
四:样本分析
4.1 样本1
- 1.首先读取进程镜像文件中倒数1024个字节,这些内容部分然后使用base和普通运算加密形成以后的服务名称Microsoft Device Manager。
- 2.判断参数是否是
Gh0st Update
,如果不是立即退出进程 - 3.当程序参数为
Gh0st Update
,首先判断添加ACE到指定的ACL,目的用于控制访问数据流量。
- 4.接下来释放一个tmp文件,然后找到他的导出表,执行ResetSSDT,初步怀疑重置SSDT是为了干扰安全软件的分析和查杀,尤其是一些主动防御的安全软件。具体步骤是这样的:
- 首先遍历资源文件,释放其中的bin文件命名为res.tmp,然后移动文件命名为ex.tmp设置一个新的文件时间以及隐藏属性并删除之前的res.tmp。
- 获取其中的ResetSSDT函数并调用
- 首先遍历资源文件,释放其中的bin文件命名为res.tmp,然后移动文件命名为ex.tmp设置一个新的文件时间以及隐藏属性并删除之前的res.tmp。
- 5.接下来,创建名为Microsoft Device Manager的服务,具体的步骤如下:
- 首先读取CurrentVersionSvchost下netsvcs项的内容x,然后读取SYSTEMCurrentControlSetServicesx,并删除C:WINDOWSsystem32xex.dll这个文件,以便替换,替换成netsvcs_0x0ex.dll
- 然后创建一个名为Microsoft Device Manager的服务
- 创建互斥体,目的是保证实例唯一性
- 首先读取CurrentVersionSvchost下netsvcs项的内容x,然后读取SYSTEMCurrentControlSetServicesx,并删除C:WINDOWSsystem32xex.dll这个文件,以便替换,替换成netsvcs_0x0ex.dll
- 6.然后将之前的ex.tmp文件写入名为
SYSTEMCurrentControlSetServicesnetsvcs_0x0
的项InstallModule的内容。接下来就开启服务4.2 样本2
- 1.首先这个是一个dll文件,根据之前的分析,我们需要调试服务,首先我们不能用OD自带的loaddll因为这默认的入口点是dllMain函数,而调式服务的入口点是ServiceMain函数,我们利用GetProcAddress函数获取函数地址,然后调用即可。代码如下:
HMODULE hModule=NULL; const char ServiceName[]="ServiceName2"; const char *ServiceName3=ServiceName; typedef int(*Fun)(int, const char**); hModule = LoadLibrary("netsvcs_0x0ex.dll"); if(hModule==NULL) printf("Load Dll Fail"); Fun ServiceMain=(Fun)GetProcAddress(hModule,"ServiceMain"); ServiceMain(1,&ServiceName3); getchar(); return 0;
- 2.首先挂起服务,将dwCheckPoint成员设置为0,表示挂起服务或者服务暂停。
- 3.获取CurrentControlSetServicesServiceName2的Type子项下的内容,如果内容为288,则在后门执行完毕后进入休眠状态。
- 4.读取文件中最后1024个字节内容,这是为了验证是否为正确的恶意文件。以及创建新的互斥量和Ipaddress。
- 5.如果服务存在,需要重置SSDT以及修改服务子项Type的内容为288,接着删除删除InstallModule键内容,删除InstallModule对应的文件
- 6.将之前从文件中获取
rqaxva61p72uvaenqaevp6ef
经过Base64以及普通加密运算得到Ipaddress192.168.1.88:8088
- 7.接着通过之前编码的Ipaddress
192.168.1.88:8088
进行链接,然后利用socket进行信息传输
- 8.然后获取系统相关信息,主要用系统版本,主机名,CPU频率,驱动信息,然后将这些数据发送给病毒作者服务器。
- 系统版本信息
- 主机名:
- CPU频率
- 驱动信息
- 系统版本信息
- 9.病毒的后门控制模块有以下功能:
- 1.主要是获取磁盘和文件信息
- 2.屏幕控制(包含视屏和音频)
- 3.管道通信读取syslog_dat数据
- 4.发送syslog_dat数据
- 5.获取进程的PID,模块信息和进程映像文件
- 6.提升进程权限为关机权限
- 7.执行之前释放的文件
- 8.Update本地的后门程序
- 9.劫持winlogon.exe,首先判断注册表下services下的内容是否为288,如果是288则删除注册表和文件,如果不是就劫持winlogon.exe,已知winlogon进程随着开机自启而启动,如果劫持了此进程,相当于病毒拥有了自启的功能。
- 10.清空软件日志
- 11.打开IE
- 12.删除Services下的注册表内容
- 13.钩取特殊字符信息,fn是一个回调函数,保存于syslog
- 1.主要是获取磁盘和文件信息
五:技术总结
技术是招式,编程是内功,所以以后的分析我尽可能总结点编程的东西,而不单单只是分析。这样才能更好的帮助我们理解分析。
socket编程基础
WinSock 编程的第一步就是加载 ws2_32.dll,然后调用 WSAStartup() 函数进行初始化,并指明要使用的版本号。
接下来就是创建套接字的过程,使用socket()函数创建套接字。函数原型如下:int socket( int af, int type, int protocol)
。在windows创建套接字SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字
或者SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
- 参数af的意思是Address Family,也就是IP地址类型,有两种形式 AF_INET 和 AF_INET6,表示IPV4和IPV6,
- 参数Type有两种,常用的有SOCK_STREAM 和 SOCK_DGRAM,分别是有连接的和无连接的。
- 参数protocol表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
然后在客户端用connect函数去连接,服务端用bind()去绑定。在windows下连接的方法如下:int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen); //Windows
- 参数sock是之前创建的套接字句柄
- 参数serv_addr是sockaddr 结构体变量的指针
- 参数addrlen是addr变量的大小
其中第二个参数最重要,他指向的结构体sockaddr_in如下:struct sockaddr_in{ sa_family_t sin_family; //地址族(Address Family),也就是地址类型 uint16_t sin_port; //16位的端口号 struct in_addr sin_addr; //32位IP地址 char sin_zero[8]; //不使用,一般用0填充 };
- 其中sin_addr是struct in_addr结构体类型的变量。之所以在结构体里面套用一个结构体而不直接用变量s_addr是因为需要兼顾之前的版本情况。
struct in_addr{ in_addr_t s_addr; //32位的IP地址 };
然后是发送和接收数据,在windows下,使用send发送数据,原型如下:它的原型为:
int send(SOCKET sock, const char *buf, int len, int flags);
以及使用recv函数接收数据int recv(SOCKET sock, char *buf, int len, int flags);
要注意客户端和服务端是相对的,也就是说客服端不单单只是发送数据也存在接收数据的函数。
参考资料:http://c.biancheng.net/cpp/html/3038.html
C++虚函数分析
所谓虚函数,就是在某基类中声明为virtual,并在一个或多个派生类中被重新定义的成员函数。用于实现多态性,通过指向派生类的基类指针,访问派生类中同名覆盖成员函数。也就是说,在编译的时候并不知道会调用那个函数,确定函数的真实调用过程是动态的。
如下代码,运行结果是fn in A
.为什么会产生这样的结果呢,因为A类是基类,B类是派生类。首先创建了A类,当调用fn()这个常规成员函数的时候,直接调用即可。但是调用v_fn的时候,由于v_fn是一个虚函数。会事先判断B类这个派生类的v_fn是否可用,可用的话直接调用B类的v_fn。
virtual fn in B
class A {
public:
void fn() { printf("fn in An"); }
virtual void v_fn() { printf("virtual fn in An"); }
};
class B : public A {
public:
void fn() { printf("fn in Bn"); }
virtual void v_fn() { printf("virtual fn in Bn"); }
};
int main() {
A *a = new B();
a->fn();
a->v_fn();
return 0;
}
如图,虚函数不是一开始就存在的,他是依靠虚函数表指引调用虚函数,每个类如果虚函数大于1都会构建一个虚函数表。事先会使用一个构造函数去动态指向将要调用的虚函数,返回值为函数指针,放入eax中,后期call eax即可。