天天看點

程式設計讀取chap挑戰值并生産應答包程式設計讀取chap挑戰值并生産應答包

程式設計讀取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);
           

四、檢視二進制檔案并比對

程式設計讀取chap挑戰值并生産應答包程式設計讀取chap挑戰值并生産應答包
程式設計讀取chap挑戰值并生産應答包程式設計讀取chap挑戰值并生産應答包

第一張圖為我們寫出來的二進制檔案,第二張圖為我們抓到的response包,可以看到,幾乎一模一樣,其中有一些ID号是我們自己生成的,是以不一樣,說明我們正确生成了chap的應答包,達成要求

實驗心得

通過本實驗,對pcap包的結構以及格式有了一定了解,對于解析資料幀、IP資料報更加熟悉,同時對chap v2的加密協定以及過程有了更深的了解,同時對OpenSSL程式設計掌握更加深入,更加懂得了對标準文檔研究的重要性。

總之一句話,受益匪淺。

資源就在下面啦

https://mp.csdn.net/mp_download/manage/download/UpDetailed

繼續閱讀