程式設計讀取chap挑戰值并生産應答包
文章目錄
- 程式設計讀取chap挑戰值并生産應答包
-
- 實驗要求
- 實驗步驟
-
- 一、讀取pcap資料包
- 二、對得到的挑戰值進行加密
- 三、将資料寫入二進制包
- 四、檢視二進制檔案并比對
- 實驗心得
實驗要求
1、熟悉Chap協定流程,程式設計生成Chap應答
2、根據PPTP資料包,生産Chap應答,比較驗證。
實驗步驟
一、讀取pcap資料包
1、先了解pcap資料包的格式及結構
Pcap資料包的前24位元組是檔案的標頭,其中包含了資料包個數,資料包總長度,時間戳等資訊,這24位元組資訊對我們沒有太大用處,是以我們将其略過不讀。使用fseek函數直接跳過該檔案頭即可。
接下來是每個資料包的pcap頭,其中有該資料包的長度,時間戳等資訊,一共8個位元組,其中資料包的長度對我們用處最大,是以我們需要将其讀取并存儲起來。下為pcap標頭結構體
typedef struct pcap_pktheader
{
int tv_sec;
int tv_usec;
u_int8_t caplen[4];
u_int8_t len[4];
}pcap_pktheader;
2、讀取資料包内容
然後接下來就是資料包的内容,包含資料幀頭,IP幀頭,GRE協定幀頭,PPP協定,以及PPP傳輸内容,由于我們要生成response響應資料包,是以這些資料内容我們均要讀取并儲存,是以我們使用如下結構體:
資料幀頭結構體:
typedef struct FrameHeader_t
{
u_int8_t DstMac[6];
u_int8_t SrcMac[6];
u_int16_t FrameType;
}FrameHeader_t;
IP資料報頭結構體:
typedef struct IPHeader_t
{
u_int8_t Ver_HLen;
u_int8_t Tos;
u_int8_t TotalLen[2];
u_int16_t ID;
u_int16_t flags_and_offset;
u_int8_t TTL;
u_int8_t Protocol;
u_int16_t Checksum;
u_int8_t SrcIP[4];
u_int8_t DstIP[4];
}IPHeader_t;
GRE協定頭結構體:
typedef struct GRE_header
{
u_int16_t Flags_and_ver;
u_int16_t Pro_type;
u_int8_t Payload_len[2];
u_int16_t Call_ID;
u_int32_t Seq_num;
u_int32_t Ack_num;
}GRE_header;
PPP協定結構體:
typedef struct PPP_pro
{
u_int16_t Pro;
}PPP_pro;
PPP資料標頭結構體:
typedef struct PPP_header
{
u_int8_t code;
u_int8_t identifier;
u_int8_t len[2];
}PPP_header;
3、讀取PPP資料包内容
根據PPP資料包的格式我們知道,標頭後第一個位元組為value_size,它代表着挑戰值的長度,接着是value_size個位元組的挑戰值,然後是name,是以我們使用如下代碼将挑戰值讀取出來:
讀取value_size
u_int8_t value_size;
fread(&value_size,sizeof(u_int8_t),1,fp);
讀取挑戰值value
u_int8_t * value;
value=(u_int8_t*)malloc(sizeof(u_int8_t)*value_size);
fread(value,value_size,1,fp);
接着是name,name的長度為標頭中的包長度-value_size-標頭長度-1(value_size)
u_int8_t * name;
u_int16_t namelen;
namelen=PPPheader->len[0]*256+PPPheader->len[1]-value_size-sizeof(PPP_header)-1;
name=(u_int8_t*)malloc(sizeof(u_int8_t)*namelen);
fread(name,namelen,1,fp);
fclose(fp);
二、對得到的挑戰值進行加密
1、首先輸入該PPP傳輸時的使用者名及密碼
該資料包為(user_name: qq , user_passwd: qq)
要輸入抓包時用戶端登陸的使用者名及密碼
char user_name[15]={'\0'};
printf("input your user name:");
scanf("%s",user_name);
char user_passwd[15]={'\0'};
printf("input your password:");
scanf("%s",user_passwd);
2、将傳回的response包中的對等挑戰值放入檔案中
unsigned char peer_challenge[16]={0xe4,0x17,0x0b,0x5e,0xab,0x7d,0xe3,0x71,
0x32,0xf0,0x97,0x70,0x48,0x49,0xd3,0xf5};
我們也可以自行産生随機對等挑戰值,但是如此以來就無法與我們抓到的資料包進行比對驗證,是以我們使用抓到的對等挑戰值
下為随機産生對等挑戰值:
srand(time(0));
for(int i=0;i<16;i++)
peer_challenge[i]=rand()%256;
3、根據RFC 2759進行加密
第一步
對(對等挑戰值||挑戰值||使用者名)這三者的連接配接做SHA1的hash運算,得到的SHA1值的前8為作為挑戰消息
下為SHA1代碼
unsigned char *challenge_msg;
challenge_msg=(unsigned char*)malloc(sizeof(unsigned char)*(32+strlen(user_name)));
strncpy(challenge_msg,peer_challenge,16);
strncpy(challenge_msg+16,value,value_size);
strncpy(challenge_msg+16+value_size,user_name,strlen(user_name));
unsigned char challenge_hash[20]={0};
SHA1(challenge_msg,32+strlen(user_name),challenge_hash);
第二步
将使用者密碼轉化成Unicode,并對其進行MD4的hash運算,得到16位元組的hash值輸出。
下為轉化Unicode并進行MD4運算代碼
unsigned char *unicode_user_passwd;
unicode_user_passwd=(unsigned char*)malloc(sizeof(unsigned char)*strlen(user_passwd)*2);
for(int i=0;i<strlen(user_name);i++){
unicode_user_passwd[i*2]=user_passwd[i];
unicode_user_passwd[i*2+1]=0;
}
unsigned char passwd_hash[21]={0x00};
MD4(unicode_user_passwd,strlen(user_passwd)*2,passwd_hash);
第三步
使用第二步得出的16位元組MD4值,在其後面加入5個0,得到21位元組的密鑰,将其分成三段,作為三個密鑰對第一步得到的8位元組挑戰消息進行DES加密。根據RFC 2759,我們還需要對這7位元組的密鑰進行密鑰擴充,擴充後每段得到新的8位元組密鑰,使用新的8位元組密鑰對挑戰消息進行DES加密
密鑰擴充函數代碼:
void expand(unsigned char *tmp_key, unsigned char* key)
{
key[0]=tmp_key[0];
for(int i=1;i<7;i++)
key[i]=((tmp_key[i-1]<<(8-i))&0xff)|(tmp_key[i]>>i);
key[7]=(tmp_key[6]<<1)&0xff;
unsigned char b;
for(int i=0;i<8;i++){
b=1;
for(int j=1;j<8;j++)
b=((key[i]>>j)^b)&0x1;
key[i]=(key[i]&0xfe)|b;
}
return ;
}
DES加密代碼:
const_DES_cblock *input;
DES_cblock *output;
DES_key_schedule *ks;
input=(const_DES_cblock*)malloc(sizeof(const_DES_cblock));
output=(DES_cblock*)malloc(sizeof(DES_cblock));
ks=(DES_key_schedule*)malloc(sizeof(DES_key_schedule));
strncpy((unsigned char *)input,challenge_hash,8);
unsigned char response[24];
for(int i=0;i<3;i++){
expand(passwd_hash+i*7,(unsigned char*)ks);
DES_set_key_unchecked((DES_cblock*)ks,ks);
DES_ecb_encrypt(input,output,ks,1);
strncpy(response+8*i,(unsigned char*)output,8);
}
最終response中存儲的即為挑戰響應值
三、将資料寫入二進制包
1、資料幀頭的建構
資料幀頭沒有什麼重要資訊,我們隻需要将其源Mac位址與目的Mac位址互換即可
以下是就交換函數代碼:
void strswop(u_int8_t *dst, u_int8_t *src,int n)
{
u_int8_t *temp;
temp=(u_int8_t *)malloc(sizeof(u_int8_t)*n);
for(int i=0;i<n;i++)
temp[i]=src[i];
for(int i=0;i<n;i++)
src[i]=dst[i];
for(int i=0;i<n;i++)
dst[i]=temp[i];
return ;
}
2、IP資料報頭的建構
對于IP資料報頭就需要一定的修改,其中ID号需要更改,還有IP資料包的長度,以及校驗和,還有目的IP位址與源IP位址互換。
其中IP資料包的長度為原來的長度+使用者名長度+33位元組(多出來9個0位元組+24個位元組的挑戰響應值)-讀取到的伺服器的名字長度namelen。
其中校驗和我們發現在response包中為0,是以我們需要将其置0.
而ID号為系統賦予,我們使用+1(也可随機産生,或者使用抓到的包中的ID号)即可(但不可與原來的ID号相同)
IP_header->ID=IP_header->ID+1;
IP_header->TotalLen[1]=IP_header->TotalLen[1]+33-namelen+strlen(user_name);
IP_header->Checksum=0;
strswop(IP_header->SrcIP,IP_header->DstIP,4);
3、GRE協定頭的建構
對于GRE協定頭,我們需要更改載荷長度,ID号,确認号,以及隊列号
載荷長度與資料報有一緻,為原來的長度+使用者名長度+33位元組(多出來9個0位元組+24個位元組的挑戰響應值)-讀取到的伺服器的名字長度namelen。
而ID号為系統賦予,我們使用+1(也可随機産生,或者使用抓到的包中的ID号)即可(但不可與原來的ID号相同)
确認号必須是前一個challenge包的隊列号
隊列号可以随機,我們+1即可。
GREheader->Payload_len[1]=GREheader->Payload_len[1]+33+strlen(user_name)-namelen;
GREheader->Call_ID=GREheader->Call_ID+1;
GREheader->Ack_num=GREheader->Seq_num;
GREheader->Seq_num=GREheader->Seq_num+1;
4、PPP協定頭的建構
對于PPP協定頭,我們需要更改長度及協定包類型,1為challenge,2為response,是以我們需要将協定頭中的協定包類型改為2
PPPheader->code=2;
PPPheader->len[1]=PPPheader->len[1]+33+strlen(user_name)-namelen;
5、将以上建構的各種標頭加上我們計算出的結果寫入二進制檔案
fp=fopen("file","wb");
fwrite(fra_header,sizeof(FrameHeader_t),1,fp); //寫入資料幀頭
fwrite(IP_header,sizeof(IPHeader_t),1,fp);//寫入IP資料報頭
fwrite(GREheader,sizeof(GRE_header),1,fp);//寫入GRE協定頭
fwrite(PPPpro,sizeof(PPP_pro),1,fp);//寫入PPP協定類型
fwrite(PPPheader,sizeof(PPP_header),1,fp);//寫入PPP協定頭
unsigned char res_value_size=49;
fwrite(&res_value_size,1,1,fp);
//寫入對等挑戰值長度+9個0位元組+挑戰響應值長度
fwrite(peer_challenge,16,1,fp);//寫入對等挑戰值
res_value_size=0;
for(int i=0;i<8;i++)
fwrite(&res_value_size,1,1,fp);//寫入8個位元組的0進行填充
fwrite(response,24,1,fp);//寫入挑戰響應值
fwrite(&res_value_size,1,1,fp);//寫入flag,必須為0
fwrite(user_name,strlen(user_name),1,fp);//寫入使用者名
fclose(fp);
四、檢視二進制檔案并比對
第一張圖為我們寫出來的二進制檔案,第二張圖為我們抓到的response包,可以看到,幾乎一模一樣,其中有一些ID号是我們自己生成的,是以不一樣,說明我們正确生成了chap的應答包,達成要求
實驗心得
通過本實驗,對pcap包的結構以及格式有了一定了解,對于解析資料幀、IP資料報更加熟悉,同時對chap v2的加密協定以及過程有了更深的了解,同時對OpenSSL程式設計掌握更加深入,更加懂得了對标準文檔研究的重要性。
總之一句話,受益匪淺。
資源就在下面啦
https://mp.csdn.net/mp_download/manage/download/UpDetailed