为了给以后的兄弟姐妹们破解校园网h3c客户端更容易些,现将h3c的认证过程说一下。
这里的认证过程仅适用于h3c的客户端,而且是固定IP的,其他的我没研究过,不清楚。H3c的版本号从
2.40-0328向下兼容。
客户端的上网认证过程大概是这样的,
第一步,咱们的电脑向服务器发送一条“开始认证”请求
第二步,服务器收到“开始认证”请求后,会给我们发送“通知要求”;然后我们回应“通知响应”。
第三步,服务器再向我们发送“用户名要求”,我们再发送“用户名响应”。
第四步,服务器向我们发送“密码要求”,我们回应“密码响应”。
第五步,服务器将向我们发送是否成功认证的报文。
若认证成功,服务器便会每15秒发一条“在线询问”,我们则要回应2条报文以维持在线状态,它们类似“用户
名回应”。若服务器60秒没有收到你的“在线回应”,服务器就把你的号做下线处理。
下面,详细讲下各个报文的内容。
第一个,“开始认证报文”。如下:
typedef struct{
u_char DES_MAC[6];
u_char SRC_MAC[6];
u_char16 PACKET_TYPE;//0x8e88
u_char _802x_version;//0x01
u_char _802x_type;   //0x01  EAP_START
u_char16 _802x_length;//0x00
u_char USELESS[42];    //All are 0x00
} PACKET_START,*LPPKTSTR;
这是抓包内容:
0000  01 80 c2 00 00 03 ff ff  ff ff ff ff 88 8e 01 01   …….. ..H…..
0010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …….. ……..
0020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …….. ……..
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….

这就是开始认证的报文。大小为60字节,第0 ~ 5字节是目的地MAC(总是01-80-c2-00-00-03,具体是什么意思
?:D);接下来6个是咱们电脑的MAC;再接下来的2个字节是0x8e88,这两个字节代表这个报文是8021x认证报文
,此处要注意,当你在赋值时注意高低位,例如,unsigned short sign;sign=0x8e88,要是赋成sign=0x888e
就错了;下一个字节是8021x的版本号,现在来看总是0x01;再下一个字节是_802x_type(姑且先这么叫着),这
个字节在“开始认证”中为0x01,在“下线请求”中为0x02,其他报文为0x00;在下两个字节为数据区大小,
数据区是我自己的叫法,就是18个字节后内容的长度,在此处为0x00。

第二个,服务器将向我们发送“通知要求”,内容如下:
0000  ff ff ff ff ff ff 00 e0  fc 0a ac 47 88 8e 01 00   ….H… …G….
0010  00 05 01 01 00 05 02 00  00 00 00 00 00 00 00 00   …….. ……..
0020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …….. ……..
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….

这个报文大小同样60B,具体前6个是目的地地址(这个报文是服务器发来的,所以此处指向我们自己);接下
来6个是服务器的MAC;接下来2个是0x8e88;接下来两个是版本号和type号,分别是0x01和0x00;再接下来是数
据区长度,即0x(00 05);再接下来的两个字节比较重要,我们要通过这两个字节来判断服务器告诉我们说呢么
,第一个字节若是0x01代表是服务器跟我们要东西(Request),同样我们在制作回应报文时,此处要做成0x02
,代表我们发回应,若是0x03,则表是我们认证成功,0x04表示认证失败;下一个子节表示服务器到底跟我们
要什么东西,0x01代表”通知”,0x02代表“用户名”,0x03代表“密码”;接下来的0x(00 05) 和前面的数据
区大小是一个内容;接下来的0x02好像没太大用处。
说到这,我要说的是,我们的认证报文大多数都是这样的结构,
typedef unsigned char u_char;
typedef unsigned short u_char16;
typedef struct{
u_char DES_MAC[6];
u_char SRC_MAC[6];
u_char16 PACKET_TYPE;
u_char _802x_version;
u_char _802x_type;
u_char16 _802x_length;  //EAP length
u_char EAP_CODE;
u_char EAP_ID;
u_char16 EAP_LENGTH;
u_char EAP_TYPE;

} PACKETHEAD,*LPPKTHDR;       //一共 23 字节!当你a=sizeof(PACKETHEAD)时,a==24,
看EAP_CODE 那,这个就是上面说的“比较重要”的字节,
|EAP_CODE==0x01,EAP_ID==0x01,EAP_TYPE==0x02  要“通知”
|EAP_CODE==0x02,EAP_ID==0x01,EAP_TYPE==0x02   回“通知”
|EAP_CODE==0x01,EAP_ID==0x02,EAP_TYPE==0x14  要“用户名”
|EAP_CODE==0x02,EAP_ID==0x02,EAP_TYPE==0x01   回“用户名”
|EAP_CODE==0x01,EAP_ID==0x03,EAP_TYPE==0x04  要“密码”
|EAP_CODE==0x02,EAP_ID==0x03,EAP_TYPE==0x04   回“密码”
|EAP_CODE==0x03              “认证成功”
|EAP_CODE==0x04              “认证失败”
EAP_TYPE这个值好像不是非常重要,至少我没用上这个值。
EAP_ID 更像是一个计数器,当开始认证时,它为0x01,然后每完成一次对话就 +1。但注意一下,加到0xff后
变成多少呢?我是假设它变成0x05了,这个地方要注意,在鉴别报文时可能会用上。
第三个,我们要发送“通知回应”。如下:
typedef struct{
u_char DES_MAC[6];
u_char SRC_MAC[6];
u_char16 PACKET_TYPE;
u_char _802x_version;
u_char _802x_type;
u_char16 _802x_length;  //EAP length
u_char EAP_CODE;
u_char EAP_ID;
u_char16 EAP_LENGTH;
u_char EAP_TYPE;
u_char fixed1;//总是0x01
u_char fixed2;//总是0x16
unsigned char info[20];
} PACKET_NOTI_RESPONSE,*LPPKTNOTIRES;
抓包的内容:
0000  00 e0 fc 0a ac 47 ff ff  ff ff ff ff 88 8e 01 00   …..G.. ..H…..
0010  00 1b 02 01 00 1b 02 01  16 4a 0e 7c 67 03 72 72   …….. .J.|g.rr
0020  39 38 4c 1b 26 30 12 08  30 f2 e8 77 01 00 00 00   98L.&0.. 0..w….
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….
注意sizeof(PACKET_NOTI_RESPONSE)是小于60B的,然而这块我们要回应60B,所以其余的补0x00.
那个20字节info是关键,2.40-0328以前的版本这20字节总是固定的,但0328后的就变了,具体我没有跟踪分析
,大概与“计算机时间”,“版本号”,“用户名”“IP”等有关。我直接修改了官方的Dll文件,以得到我们
想要的数据。
具体调用方法如下:

hinsDll=LoadLibrary(“x1pt.dll”);
FARPROC pp=GetProcAddress(hinsDll,”X1_CoMsg”);
pp+=0x2280;
这样pp就指向了一个x1pt.dll中的一个地址,这是个函数入口地址,这个函数要三个参数,这三个参数都是指
针!
第一个参数指向结构体:
typedef struct {
u_char sign[8];   //
u_char administratorname[50]; //***
u_char username[128];  //username;!!!!!!!!!!!!!!!
u_char unknown1[174];  //unknown ,and don’t use;
int  LPPOINT;   //!!!!!!!!!!!!!!!!!!!!!!!!!
u_char unknown2[12];  //unknown ,
u_char TIMESTART[50];  //Time for start;
u_char TIMEEND[62];  //Time for end;
u_char password[64];  //Password;!!!!!!!!!!!!!!
char padding[16384];
}_STRUCT_PARA1,*LP_STRUCT_PARA1;
加了“!”的是比较重要的,username 就是上网时要输入的用户名,密码就是那个密码,LPPOINT是个指针,它
又指向一个结构体:
typedef struct{
u_char sign[16];   //all is Zero
u_char sign2[16];   //all is Zero
u_char sign3[8];   //{05 00  00  00 05 00  00 00}!!!
u_char sign4[4];   //{00 00 00 00}
u_char ip_pi[4];   //!!!
u_char sign5[8];   //{ff ff ff ff 00 00 00 00}!!!
u_char fakeip[8];         //!!!
int  sign6;    //0x04 for Noti and username
int  sign7;    //0x01 for Noti and 0x02 for Username!!!
char pading[16384];
}_PART_OF_PARA1,*LP_PART_OF_PARA1;
在这个结构体中,把sign3赋成“{05 00  00  00 05 00  00 00}”;
ip_pi要倒着写入你的Ip,例如你的Ip是125.221.180.256,那你这块要
ip_pi[0]=(char)256;
ip_pi[1]=(char)180;
ip_pi[2]=(char)221;
ip_pi[3]=(char)125;
;把sign5赋成“{ff ff ff ff 00 00 00 00}”;
fakeip那要这样写:
fakeip[0]=(char)125;
fakeip[1]=(char)180;
fakeip[2]=(char)221;
fakeip[3]=(char)125;
sign6赋成0x04;
当你为得到“通知回应”的数据时,sign7=0x01;若是要得到“用户名”(下面会说)的数据,则sign7=0x02,
并且将_STRUCT_PARA1+0xd8位置上的数据改成0x20;
在使用这两个结构体变量之前,用ZeroMemory()清零变量内存;padding要足够大!16384就可以了.
第二个参数指向一个 char [5],你可以这样用。
为获得“通知回应数据”则:
char aa[5];
aa[0]=01;
aa[1]=01;
aa[2]=00;
aa[3]=05;
aa[4]=02;
注意_PART_OF_PARA1的sign7!
为获得“用户名响应”,则:
aa[0]=01;
aa[1]=02;
aa[2]=00;
aa[3]=05;
aa[4]=0x14;
注意_PART_OF_PARA1的sign7!
第三个参数就是我们要的结果了,char rlt[100],把rlt传进去就可以了。
调用完那个x1pt.dll中的函数后,从rtl+7位置上取20字节,就把”通知回应”的数据取到了。
第四个,服务器将向我们发送“用户名请求”,内容如下:
0000  ff ff ff ff ff ff 00 e0  fc 0a ac 47 88 8e 01 00   ….H… …G….
0010  00 05 01 02 00 05 14 00  00 00 00 00 00 00 00 00   …….. ……..
0020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …….. ……..
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….
不需要做说明了吧 哈哈哈
第五个,我们回应“用户名”。先看内容:
0000  00 e0 fc 0a ac 47 ff ff  ff ff ff ff 88 8e 01 00   …..G.. ..H…..
0010  00 32 02 02 00 32 01 15  04 ff ff ff ff 06 07 48   .2…2.. .}..,..H
0020  51 35 37 59 67 5a 31 63  6d 35 76 54 42 77 6a 4e   Q57YgZ1c m5vTBwjN
0030  52 55 49 5a 33 4c 61 41  51 45 3d 20 20 ff ff ff   RUIZ3LaA QE=  !!!
0040  ff ff ff ff                                        !!!!
结构定义:
typedef struct{
u_char DES_MAC[6];
u_char SRC_MAC[6];
u_char16 PACKET_TYPE;
u_char _802x_version;
u_char _802x_type;
u_char16 _802x_length;  //EAP length
u_char EAP_CODE;
u_char EAP_ID;
u_char16 EAP_LENGTH;
u_char EAP_TYPE;
u_char fixed1;//总是0x15
u_char fixed2;//总是0x04
u_char IP[4];//你的IP,例如IP[0]=125,IP[1]=221,IP[2]=180,IP[3]=256;!!!!!!!!
u_char fixed3;//总是0x06
u_char fixed7;//总是0x07
u_char info[28];
u_char blank1;//总是0x20 ,就是空格
u_char blank2;//总是0x20 ,就是空格
} PACKET_USERNAME_RESPONSE,*LPPKTUSERNAMERES;
把自己的IP填到IP[4]里;info的获得和Noti 的 一样,只是最后取结果时从+13 的位置上取,取28个出来。将
你的用户名补在后面,如果你要用sizeof()注意加减1.最后,算出大小填入EAP_LENGTH中,从第18个字节开始
,有多少个字节,大小就是多少。
第六个,服务器收到你的用户名后,就会跟你要密码,
内容如下:
0000  ff ff ff ff ff ff 00 e0  fc 0a ac 47 88 8e 01 00   ….H… …G….
0010  00 16 01 03 00 16 04 10  43 1b 76 53 b7 82 7e 98   …….. C.vS..~.
0020  29 08 69 2f 75 86 06 34  00 00 00 00 00 00 00 00   ).i/u..4 ……..
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….
这个报文重要,你要获得一个字串。从0x19位置上开,读入0x10个字节,即“43 1b 76 53 b7 82 7e 98 29 08
69 2f 75 86 06 34”。
第七个,你将发回密码。先看内容:
0000  00 e0 fc 0a ac 47 ff ff  ff ff ff ff 88 8e 01 00   …..G.. ..H…..
0010  00 1d 02 03 00 1d 04 10  d7 b1 5f e4 c2 e8 55 f4   …….. .._…U.
0020  14 26 ec 38 88 5f 4b 3a  ff ff ff ff ff ff ff 00   .&.8._K: !!!!!!!.
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….
数据定义:
typedef struct {
u_char DES_MAC[6];
u_char SRC_MAC[6];
u_char16 PACKET_TYPE;
u_char _802x_version;
u_char _802x_type;
u_char16 _802x_length;  //EAP length
u_char EAP_CODE;
u_char EAP_ID;
u_char16 EAP_LENGTH;
u_char EAP_TYPE;
u_char lengthofPWD;//alway 0x10,16字节
u_char MD5VALU[16];
u_char OTHERINFO[20];//INCLUDE USERNAME
}PACKET_PASSWORD_RESPONSE,*LPPKTPWDRES;
这个报文比较简单,制作一个串,内容是0x03+密码串+交换码串(就是上个报文中,考出的16字节),然后将这
个符合串MD5 ComputeHash,得到MD5值,写入报文,补上用户名,填足60位,就可以了.

第8个,成功则服务器发回:
0000  ff ff ff ff ff ff 00 e0  fc 0a ac 47 88 8e 01 00   ….H… …G….
0010  00 04 03 04 00 04 00 00  00 00 00 00 00 00 00 00   …….. ……..
0020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …….. ……..
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….
失败则:
0000  ff ff ff ff ff ff 00 e0  fc 0a ac 47 88 8e 01 00   ….H… …G….
0010  00 07 04 08 00 07 08 01  00 00 00 00 00 00 00 00   …….. ……..
0020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …….. ……..
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….

对于在线保持,服务器发送:
0000  ff ff ff ff ff ff 00 e0  fc 0a ac 47 88 8e 01 00   ….H… …G….
0010  00 05 01 db 00 05 14 00  00 00 00 00 00 00 00 00   …….. ……..
0020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …….. ……..
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….
我们要做的是,回应两个报文,
回应1
0000  01 80 c2 00 00 03 ff ff  ff ff ff ff 88 8e 01 00   …….. .F^…..
0010  00 35 02 cc 00 35 14 00  15 04 ff ff ff ff 06 07   .5…5.. ..}…..
0020  54 77 31 2f 62 77 74 78  63 54 77 39 54 78 67 75   Tw1/bwtx cTw9Txgu
0030  4f 42 45 4c 4e 58 6a 41  63 72 67 3d 20 20 ff ff   OBELNXjA crg=  !!
0040  ff ff ff ff ff ff ff                               !!!!!!!
回应2
0000  01 80 c2 00 00 03 ff ff  ff ff ff ff 88 8e 01 00   …….. .F^…..
0010  00 34 02 01 00 34 01 15  04 ff ff ff ff 06 07 53   .4…4.. .}…..S
0020  31 77 72 5a 67 49 6c 49  44 67 35 48 6b 77 6e 4d   1wrZgIlI Dg5HkwnM
0030  55 56 61 4d 54 4a 4a 63  62 67 3d 20 20 ff ff ff   UVaMTJJc bg=  !!!
0040  ff ff ff ff ff ff                                  !!!!!!!
大家自己研究研究。哈哈哈,它们与用户名报文很象,简直就是一样的。
最后一个,下线报文:
非常简单:
0000  01 80 c2 00 00 03 ff ff  ff ff ff ff 88 8e 01 02   …….. ..H…..
0010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …….. ……..
0020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …….. ……..
0030  00 00 00 00 00 00 00 00  00 00 00 00               …….. ….
哈哈,好,基本上的就说这么多了。
补充几点啊,
1,对于咱们的电脑,目的MAC(就是服务器MAC)可以是一直都是,01 80 c2 00 00 03;
2,官方那个客户端用的是双进程。其中Auth…什么的那个是主要的进程,报文处理、代理下线等都是在那处
理的,但他不处理用户界面。说这个是什么目的呢,就是说,一旦以后h3c出新版本,破解可以这样开始:先用
Winhex打开”Auth什么的”内存,搜索报文里的内容,如username,可能一次不行,因为15秒才一次在线保持,多
试几下,一旦找到,记录下报文的地址,然后OD 附加”Auth..”,下硬件写入断点,程序应该会断住,剩下的就
看你的耐性喽。哈哈
3,注意,注意那些dll是否加壳了。
4,由于破解是去年年底的事了,所以细节记得不是太清楚了,若有错误,请多包含
5,我没有修改这些DLL的导入表,有很多DLL是没用的,要是有时间,你们可以试试。

哈哈,到此结束,希望能有用,哈哈哈哈。