0xbc指令 st75256_DDOS终极加速列车算法
注意:文章首發Zerg Hole和網絡技術論壇后,由原創作者友情提交到邪惡八進制討論組。
很久沒來了,因為畢業了...事情太多了...
本文產生與前兩天群里討論現在什么DDOS軟件最快,然后看到某個軟件打出了相當囂張的簡介,為此,我花了兩天時間,研究了一下DDOS算法究竟有沒有的提升的空間.以此挑戰一下最快的DDOS軟件的速度.效果還是不錯的.
本文原是分兩天寫的兩個部分,現整理一下發到xfocus上來.
先來段代碼:
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while(size >1){
printf("%04X/r/n",*buffer);
cksum+=*buffer++;
size -=sizeof(USHORT);
}
if(size ){
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
} 這個是大多數正常的效驗和算法,主要是把數據(buffer)當成一個個16位的數值,挨個相加,最后求反碼后返回.這個算法速度非???以現代的編譯器能力來看,沒有可能用匯編來加速.不過就算法本身來說不怎么好,畢竟是只加法.還是有值得研究的地方.
因為原先進來計算的數據中包含的效驗和是0,所以計算過后把值(反碼)放入0的位置.這樣,數據包到達目的地的時候,只要再進行一次同樣的效驗和計算,就能得出結果是0xffff,因為將0改成了整個效驗和的和的反碼,再次計算的時候,等于將上次計算的結果再加它的反碼,所以結果是0xffff(舉個例子: 比如0x7878+0x8787 = 0xffff).然后對0xffff又求反碼后返回,所以是0.只要目的端計算出的是0,說明途中沒有出錯.
嗯....其實上面說的都是廢話,為了讓大家對效驗和有個直觀的了解,下面說重點:
我們都知道,每個tcp包,是由IP包+tcp包組成的.這樣里面就有兩個效驗和,一個是IP包的,一個是TCP包的,對于TCP包的加速算法,將在下面部分給出,前面部分的加速算法只針對IP包.
參與TCP包計算效驗和的包括兩個部分,一個是偽首部(本文中是暫稱tsd首部),一個是TCP首部.分別是這樣的:
typedef struct tsd_hdr //定義TCP偽首部
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz;
char ptcl; //協議類型
unsigned short tcpl; //TCP長度
}PSDHEADER;
typedef struct tcp_hdr //定義TCP首部
{
USHORT th_sport; //16位源端口
USHORT th_dport; //16位目的端口
unsigned int th_seq; //32位序列號
unsigned int th_ack; //32位確認號
unsigned char th_lenres; //4位首部長度/6位保留字
unsigned char th_flag; //6位標志位
USHORT th_win; //16位窗口大小
USHORT th_sum; //16位校驗和
USHORT th_urp; //16位緊急數據偏移量
}TCPHEADER; 參與IP包計算效驗和也有兩個部分,一個是IP首部,一個是TCP首部.分別是這樣的:
typedef struct ip_hdr //定義IP首部
{
unsigned char h_verlen; //4位首部長度,4位IP版本號
unsigned char tos; //8位服務類型TOS
unsigned short total_len; //16位總長度(字節)
unsigned short ident; //16位標識
unsigned short frag_and_flags; //3位標志位
unsigned char ttl; //8位生存時間 TTL
unsigned char proto; //8位協議 (TCP, UDP 或其他)
unsigned short checksum; //16位IP首部校驗和
unsigned int sourceIP; //32位源IP地址
unsigned int destIP; //32位目的IP地址
}IPHEADER;
typedef struct tcp_hdr //定義TCP首部
{
USHORT th_sport; //16位源端口
USHORT th_dport; //16位目的端口
unsigned int th_seq; //32位序列號
unsigned int th_ack; //32位確認號
unsigned char th_lenres; //4位首部長度/6位保留字
unsigned char th_flag; //6位標志位
USHORT th_win; //16位窗口大小
USHORT th_sum; //16位校驗和
USHORT th_urp; //16位緊急數據偏移量
}TCPHEADER; 可見兩個效驗和計算的時候,TCP首部都有參與.根據算法原理,這個是可以被中和的.現在好玩的來了.
因為TCP效驗和是先于IP效驗和開始計算的(因為IP效驗和需要包括TCP首部,而TCP首部這個時候需要先計算完成),TCP首部計算過后,它的效驗和已經放入自己的效驗和字段.如果再次計算tcp的效驗和,因為已經加入了tcp效驗和的反碼也參與計算.所以最終計算結果為0.
那么計算IP+TCP的效驗和的時候,將整個已經計算過效驗和的TCP首部加入進來,豈不是白做工?
不,不是,因為TCP計算效驗和的時候還參與了TCP偽首部.所以TCP首部中的效驗和不是只有它一個部分,還有另外一個部分,TCP偽首部(TSD首部).
那么有沒有辦法中和這個偽首部呢?如果能綜合這個偽首部,使得它也失去計算效果......
答案是肯定的,方法是通過IP首部.
仔細看上面的TCP偽首部和IP首部.仔細分析他們的區別.
因為效驗和終歸來講是對數值進行加法計算.如果我們能用IP首部中的某些字段,巧妙的代替了TSD首部中的某些字段,將他們計算的結果綜合,那么整個計算過程就相當簡單了.
分析的結果大為讓人吃驚,TCP偽首部和IP首部中竟然都有目的IP,源IP字段......意思也就是說,這兩個字段如果改變的話,對于IP效驗和的計算結果絲毫沒有任何影響....我想到了可怕的DDOS程序,你呢?
經過一番篩選和精簡,得出了一個可怕結論.下面先來個結構,這是我自己分析出的:
#pragma pack(1)
typedef struct _PackStk{
unsigned char h_verlen; //4位首部長度,4位IP版本號
unsigned char tos; //8位服務類型TOS
unsigned short ident; //16位標識
unsigned char ttl; //8位生存時間 TTL
unsigned char frag_and_flags; //3位標志位
unsigned short checksum; //16位IP首部校驗和
}PackStk; 唯一注意的地方是,這個結構的順序不能變,且內存對其應該為1.
然后我們只要設置這個報文的字段,計算之效驗和后,所有不改變PackStk中字段的IP報文(其他字段可任意改變,包括IP),均不用重新計算IP效驗和.大大的節省了時間.不,應該說根本不用花時間.
IP效驗和加速到此結束,加速成果:100%
前面說到tcp包中的IP包的效驗和可以省略的辦法,本次再來說說還剩下的一個效驗和也就是tcp效驗和的加速方法.由于IP效驗和被完全取消,所以這個速度理論上可以提升一倍,而接下來的tcp效驗和經過加速之后可以提升90甚至更多,所以最終的效果是將DDOS程序的算法提升了5-6倍.是不是很夸張? 是的,我先這么和你說,你才會吧這個文章看完.呵呵...
好了,到了這里我也不想隱瞞什么了,我確實在寫ddos程序.不過需要澄清一點,我只是為了研究,為了實現加速算法,不會發布的這個程序或者是公開代碼,因為這個做法實在太危險了.
下面開始正文.
首先說說我的想法,DDOS程序中對于效驗和要反復計算的原因,在于其偽造的IP需要不停的更變,于是每次IP更變之后,都要對整個數據包(IP+TCP)進行效驗和,否則發不出去.
在前次的文章中(見我的實驗一),我用綜合Tsd首部和IP首部的辦法,成功的分離出一個packstk首部,于是只要計算這個首部,即可完成IP效驗和的計算,并且從此不再需要改變(相信很多人寫ddos的時候都會發現這點,更改了ip首部中的ip地址之后,重新計算ip首部,效驗和不變).
(對于前次文章,還有個補遺,就是packstk首部中的len字段,不是ip首部+tcp首部的長度,而是單獨的ip首部中len的長度,減去Tsd首部中len的長度)
那么剩下的這個tcp首部的效驗和計算方法是:計算Tsd首部和tcp首部的和.
關鍵的來了,大家知道ttl的,當一個數據包經過一個路由的時候,就會將數據包中的ttl值減去1,那么這個時候路由一定需要重新計算效驗和,不然數據包到了下個路由就會被丟棄.
路由器真的重新計算這個效驗和了嗎?
答案是否定的,路由器使用了一個彌補算法,對效驗和進行簡單的更新之后,就發了出去,這種簡單的算法和完全計算效驗和計算之后的值是一樣的.我們姑且稱之為"路由效驗和算法".
有文檔可查,在rfc1141上.看這里看這里:http://www.faqs.org/rfcs/rfc1141.html
公式是:~C' = ~(C + (-m) + m') = ~C + (m - m') = ~C + m + ~m'
代碼:
UpdateTTL(iph,n)
struct ip_hdr *ipptr;
unsigned char n;
{
unsigned long sum;
unsigned short old;
old = ntohs(*(unsigned short *)&ipptr->ttl);
ipptr->ttl -= n;
sum = old + (~ntohs(*(unsigned short *)&ipptr->ttl) & 0xffff);
sum += ntohs(ipptr->Checksum);
sum = (sum & 0xffff) + (sum>>16);
ipptr->Checksum = htons(sum + (sum>>16));
}
不過非常遺憾的是,這個算法經過我的測試,效率只提高了30%-50%,離我預期的90%有相當一段距離.于是我對這段算法進行了更改.
整體上對效驗和進行更新的實現不變,新算法采用這樣的公式:
C = ~(~C + ∑[所有更改的位i]Gray_Code[i])
抱歉這里不好打求和符號,大概意思就是,效驗和新值 = 原始效驗和求反,加上所有改變位的格雷碼異或位的和,再求反.
這個算法是速度比重新計算效驗和快了90%,而且可以改變多個byte的值(原先算法只針對ttl這個byte).因為格雷碼可以查表得到,這僅僅是一個內存復制的過程,如果你每次只更改1個IP段(IP有4個段不用我說吧),那么每次只要加上一個格雷碼異或位即可.然而實際應用中需要加兩個,因為端口也需要改變.
好了,什么是Gray_Code,這個我不想解釋了,去看wiki吧.然而我這里用到的Gray_Code是我自己生成的一個表.下面這樣的:
unsigned char gray_code[256][2] = {
{0x00,0x80},{0x01,0x01},{0x03,0x02},{0x02,0x01},{0x06,0x04},{0x07,0x01},{0x05,0x02},{0x04,0x01},
{0x0C,0x08},{0x0D,0x01},{0x0F,0x02},{0x0E,0x01},{0x0A,0x04},{0x0B,0x01},{0x09,0x02},{0x08,0x01},
{0x18,0x10},{0x19,0x01},{0x1B,0x02},{0x1A,0x01},{0x1E,0x04},{0x1F,0x01},{0x1D,0x02},{0x1C,0x01},
{0x14,0x08},{0x15,0x01},{0x17,0x02},{0x16,0x01},{0x12,0x04},{0x13,0x01},{0x11,0x02},{0x10,0x01},
{0x30,0x20},{0x31,0x01},{0x33,0x02},{0x32,0x01},{0x36,0x04},{0x37,0x01},{0x35,0x02},{0x34,0x01},
{0x3C,0x08},{0x3D,0x01},{0x3F,0x02},{0x3E,0x01},{0x3A,0x04},{0x3B,0x01},{0x39,0x02},{0x38,0x01},
{0x28,0x10},{0x29,0x01},{0x2B,0x02},{0x2A,0x01},{0x2E,0x04},{0x2F,0x01},{0x2D,0x02},{0x2C,0x01},
{0x24,0x08},{0x25,0x01},{0x27,0x02},{0x26,0x01},{0x22,0x04},{0x23,0x01},{0x21,0x02},{0x20,0x01},
{0x60,0x40},{0x61,0x01},{0x63,0x02},{0x62,0x01},{0x66,0x04},{0x67,0x01},{0x65,0x02},{0x64,0x01},
{0x6C,0x08},{0x6D,0x01},{0x6F,0x02},{0x6E,0x01},{0x6A,0x04},{0x6B,0x01},{0x69,0x02},{0x68,0x01},
{0x78,0x10},{0x79,0x01},{0x7B,0x02},{0x7A,0x01},{0x7E,0x04},{0x7F,0x01},{0x7D,0x02},{0x7C,0x01},
{0x74,0x08},{0x75,0x01},{0x77,0x02},{0x76,0x01},{0x72,0x04},{0x73,0x01},{0x71,0x02},{0x70,0x01},
{0x50,0x20},{0x51,0x01},{0x53,0x02},{0x52,0x01},{0x56,0x04},{0x57,0x01},{0x55,0x02},{0x54,0x01},
{0x5C,0x08},{0x5D,0x01},{0x5F,0x02},{0x5E,0x01},{0x5A,0x04},{0x5B,0x01},{0x59,0x02},{0x58,0x01},
{0x48,0x10},{0x49,0x01},{0x4B,0x02},{0x4A,0x01},{0x4E,0x04},{0x4F,0x01},{0x4D,0x02},{0x4C,0x01},
{0x44,0x08},{0x45,0x01},{0x47,0x02},{0x46,0x01},{0x42,0x04},{0x43,0x01},{0x41,0x02},{0x40,0x01},
{0xC0,0x80},{0xC1,0x01},{0xC3,0x02},{0xC2,0x01},{0xC6,0x04},{0xC7,0x01},{0xC5,0x02},{0xC4,0x01},
{0xCC,0x08},{0xCD,0x01},{0xCF,0x02},{0xCE,0x01},{0xCA,0x04},{0xCB,0x01},{0xC9,0x02},{0xC8,0x01},
{0xD8,0x10},{0xD9,0x01},{0xDB,0x02},{0xDA,0x01},{0xDE,0x04},{0xDF,0x01},{0xDD,0x02},{0xDC,0x01},
{0xD4,0x08},{0xD5,0x01},{0xD7,0x02},{0xD6,0x01},{0xD2,0x04},{0xD3,0x01},{0xD1,0x02},{0xD0,0x01},
{0xF0,0x20},{0xF1,0x01},{0xF3,0x02},{0xF2,0x01},{0xF6,0x04},{0xF7,0x01},{0xF5,0x02},{0xF4,0x01},
{0xFC,0x08},{0xFD,0x01},{0xFF,0x02},{0xFE,0x01},{0xFA,0x04},{0xFB,0x01},{0xF9,0x02},{0xF8,0x01},
{0xE8,0x10},{0xE9,0x01},{0xEB,0x02},{0xEA,0x01},{0xEE,0x04},{0xEF,0x01},{0xED,0x02},{0xEC,0x01},
{0xE4,0x08},{0xE5,0x01},{0xE7,0x02},{0xE6,0x01},{0xE2,0x04},{0xE3,0x01},{0xE1,0x02},{0xE0,0x01},
{0xA0,0x40},{0xA1,0x01},{0xA3,0x02},{0xA2,0x01},{0xA6,0x04},{0xA7,0x01},{0xA5,0x02},{0xA4,0x01},
{0xAC,0x08},{0xAD,0x01},{0xAF,0x02},{0xAE,0x01},{0xAA,0x04},{0xAB,0x01},{0xA9,0x02},{0xA8,0x01},
{0xB8,0x10},{0xB9,0x01},{0xBB,0x02},{0xBA,0x01},{0xBE,0x04},{0xBF,0x01},{0xBD,0x02},{0xBC,0x01},
{0xB4,0x08},{0xB5,0x01},{0xB7,0x02},{0xB6,0x01},{0xB2,0x04},{0xB3,0x01},{0xB1,0x02},{0xB0,0x01},
{0x90,0x20},{0x91,0x01},{0x93,0x02},{0x92,0x01},{0x96,0x04},{0x97,0x01},{0x95,0x02},{0x94,0x01},
{0x9C,0x08},{0x9D,0x01},{0x9F,0x02},{0x9E,0x01},{0x9A,0x04},{0x9B,0x01},{0x99,0x02},{0x98,0x01},
{0x88,0x10},{0x89,0x01},{0x8B,0x02},{0x8A,0x01},{0x8E,0x04},{0x8F,0x01},{0x8D,0x02},{0x8C,0x01},
{0x84,0x08},{0x85,0x01},{0x87,0x02},{0x86,0x01},{0x82,0x04},{0x83,0x01},{0x81,0x02},{0x80,0x01}
}; 比較亂,沒辦法,這里有255個組,每組的第一位是這個組的值,第二位是異或位.
記住,格雷碼的好處是,每個相鄰變化,在二進制中僅僅有一位改變.我用格雷碼的最初原因是因為只更新IP的一個byte情況下較為簡單,而選擇格雷碼表就簡單很多,僅僅需要加一次.如果用遞增方式更新IP,會產生多個bit位的改變(比如5[0101]->6[0110],這里同時改變了兩個bit) 對于加法不利.后來因為需要多個位的改變(同時更新IP和端口),使得判斷bit位變得很復雜.所以權衡了一下,還是選擇了遞增方式更新IP的做法(雖然此法要比最快的單格雷碼變化要慢上一倍,不過更加適用于實際).
異或位表還是采用的格雷碼表,便于定位是哪個位需要更新.因為效驗和計算的時候,是按16位為單位計算的,所以可以分析出:
例如
IP:1.2.3.4
端口:0x0506
那么計算時候是這樣的
0x0201
+
0x0403
+
0x0506
+
.....
那么02所在的位,04所在的位,端口05所在的位如果更新了,加上的格雷碼異或位需要左移八個位置.
好了,下面這個是算法:
sip = 0; sport = 0;
psip = (unsigned char*) & sip;
psport = (unsigned char*) & sport;
stt2 = (unsigned char*) & tt2;
ft_cc[5] = psport[1];
ft_cc[4] = psport[0];
ft_cc[3] = psip[3];//01
ft_cc[2] = psip[2];//02
ft_cc[1] = psip[1];//03
ft_cc[0] = psip[0];//04
{
sip ++;
sport ++; //更新ip和端口
tmp_sum = 0;
//判斷是否和上次的IP值不一樣,大于則加,小于則減
//if(gray_code[psip[0]][0] != gray_code[ft_cc[0]][0]){
if(psip[0] != ft_cc[0]){
if(gray_code[psip[0]][0] > gray_code[ft_cc[0]][0]){
tmp_sum += ((unsigned short)gray_code[psip[0]][1] << 8 | 0x00);
}else{
tmp_sum -= ((unsigned short)gray_code[psip[0]][1] << 8 | 0x00);
}
ft_cc[0] = psip[0];
//if(gray_code[psip[1]][0] != gray_code[ft_cc[1]][0]){
if(psip[1] != ft_cc[1]){
if(gray_code[psip[1]][0] > gray_code[ft_cc[1]][0]){
tmp_sum += (unsigned short)gray_code[psip[1]][1];
}else{
tmp_sum -= (unsigned short)gray_code[psip[1]][1];
}
ft_cc[1] = psip[1];
//if(gray_code[psip[2]][0] != gray_code[ft_cc[2]][0]){
if(psip[2] != ft_cc[2]){
if(gray_code[psip[2]][0] > gray_code[ft_cc[2]][0]){
tmp_sum += ((unsigned short)gray_code[psip[2]][1] << 8 | 0x00);
}else{
tmp_sum -= ((unsigned short)gray_code[psip[2]][1] << 8 | 0x00);
}
ft_cc[2] = psip[2];
//if(gray_code[psip[3]][0] != gray_code[ft_cc[3]][0]){
if(psip[3] != ft_cc[3]){
if(gray_code[psip[3]][0] > gray_code[ft_cc[3]][0]){
tmp_sum += (unsigned short)gray_code[psip[3]][1];
}else{
tmp_sum -= (unsigned short)gray_code[psip[3]][1];
}
ft_cc[3] = psip[3];
}
}
}
}
//判斷是否和上次的端口值不一樣,大于則加,小于則減
//if(gray_code[psport[0]][0] != gray_code[ft_cc[4]][0]){
if(psport[0] != ft_cc[4]){
if(gray_code[psport[0]][0] > gray_code[ft_cc[4]][0]){
tmp_sum += ((unsigned short)gray_code[psport[0]][1] << 8 | 0x00);
}else{
tmp_sum -= ((unsigned short)gray_code[psport[0]][1] << 8 | 0x00);
}
ft_cc[4] = psport[0];
//if(gray_code[psport[1]][0] != gray_code[ft_cc[5]][0]){
if(psport[1] != ft_cc[5]){
if(gray_code[psport[1]][0] > gray_code[ft_cc[5]][0]){
tmp_sum += (unsigned short)gray_code[psport[1]][1];
}else{
tmp_sum -= (unsigned short)gray_code[psport[1]][1];
}
ft_cc[5] = psport[1];
}
}
tpt2 = (unsigned short)~tt2 + tmp_sum;
tpt2 = (tpt2 >> 16) + (tpt2 & 0xffff);
tt2 = (unsigned short)(~tpt2);
} 這個算法可以更新IP和port,并更新的效驗和使其有效,下面這個部分的
tpt2 = (unsigned short)~tt2 + tmp_sum; tpt2 = (tpt2 >> 16) + (tpt2 & 0xffff); tt2 = (unsigned short)(~tpt2); tmp_sum就是格雷碼異或位的和值.然后經過高位相加,最后存入效驗和.這樣更新出來的效驗和與重新計算過后的是一樣的.到這里.基本上該說的都說完了,應該來說不是那么詳細.測試中1000萬個效驗和,只改變1byte(ip單位變化)的計算時間是230ms,同時改變3個byte(ip多位變化+端口)以上的計算時間只要600ms,而用正常方法計算得2800ms.效驗和計算是達到12倍的速度.經過發包測試(這個時候主要的瓶頸就在于sendto函數了),采用緩存池并發線程(一個線程專門負責計算效驗和,另外開3 -n個線程用于發包),在我機器上可以滿負荷運行,效率是目前最快的算法的程序的3-6倍,如果網絡環境允許,可以達到10倍以上.不要向我要求代碼,有編程能力和數學能力的,看完我這篇垃圾的文章,應該就可以寫出這樣的算法.最后感謝這兩天編寫過程中給于我支持和一直幫我測試的朋友,有花須酌酒,LK007(小金).謝謝.好像沒什么好說的了,我不善于寫文章,也不知道大家看的懂不懂,細節也沒說清楚,因為被DDOS的開發者拿去了,這個危害也就大了.另外請大家有什么問題可以給我來信(zvrop_at_163_dot_com)
總結
以上是生活随笔為你收集整理的0xbc指令 st75256_DDOS终极加速列车算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nacos 配置动态刷新_Nacos 动
- 下一篇: notepad拼心形_bat心形代码