逆向工程得出结果如下:

首先,电信校园客户端并不是直接修改CHAP RESPONSE生成函数,而是通过它自己修改的RP-PPPoE对数据包的截取和修改来完成的。

帐号的前缀不再是调用拨号的时候加上的,而是在拨号程序内部加上的,准确地说是拨号程序截取CHAP RESPONSE PACKET,然后对RESPONSE HASH进行修改,修改过后更改包中的NAME字段,在帐号前加前缀,并修改包中的LENGTH字段,使其正确反映包的长度,然后再将修改过的包发出。

拨号程序默认内置了一份前缀和密码表。默认前缀是^~3,密码表长度为0x11,即共17个字节。对RESPONSE的修改选择是基于前缀的第二位。如果第二位为#,那么就采用新的方式做RESPONSE HASH修改;否则采用原来的方式。

原来的方式很简单。首先对CHALLENGE(没错,是CHALLENGE,拨号程序也会过滤进入的CHAP PACKET,然后如果是CHAP 0x1,也就是CHALLENGE包,那么就会将CHALLENGE保存在内存的某一位置共后续使用)按字节进行替换。假设某字节为a,密码表为key[],密码表中元素个数为n,则其替换方式为a=a+key[a&n]。替换完后,使用原来的RESPONSE,新的CHALLENGE来完成一次MD5 HASH。用这个结果作为新的RESPONSE交给服务器。即完成CHAP。

新的方式有些复杂。因为它不只一种方式……准确地说,它是使用一种块加密算法的变体的加密和解密,以及另一种还算有名的并且有一些缺憾的流加密算法。前一种有4个不同的参数,分别为加、解密16轮和加、解密32轮。它将原来的RESPONSE作为值,然后用上述方式将RESPONSE的每一个字节都走一遍,然后再发出去。采用什么参数、什么算法由RESPONSE第一个字节%5来决定……

这次电信也确实学聪明了,如果你按过去的方法去查看前缀(通过netfilter.sys之类),那么你应该得到的前缀是^~3,但实际上5月更新已经把前缀更改为^#02了,其实这都在pdext的配置文件中。

如果你对源码有兴趣的话,可以在推上DM我,我应该可以把它发过去,不是我不想公开,如果公开那么我害怕电信会大幅修改,那样今后的两年就没法过了TAT

更新

去年11月末电信客户端进行了又一次升级,这次升级使用动态链接等手段……我已经弄不下去了……在此作为特典将过去的pppd修改部分补丁放出,目前还是可以使用的,不过不知道能用多久就是了……

Index: ppp-2.4.7/pppd/chap-md5.c
===================================================================
--- ppp-2.4.7.orig/pppd/chap-md5.c	2014-08-09 12:31:39.000000000 +0000
+++ ppp-2.4.7/pppd/chap-md5.c	2014-09-28 15:05:32.517737058 +0000
@@ -1,6 +1,12 @@
 /*
  * chap-md5.c - New CHAP/MD5 implementation.
  *
+ * Modified for Damning China Telecom Campus Broadband Access.
+ *
+ * Version 20140921
+ *
+ * Copyright (c) 2014 Lingmo Zhu. All rights reserved.
+ *
  * Copyright (c) 2003 Paul Mackerras. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -53,6 +59,120 @@
 	random_bytes(cp, clen);
 }
 
+static unsigned long xteakey[4] = {
+    0xf96d9f94, 0x787199a7, 0x9e15fe69, 0xe816331d
+};
+
+static unsigned char newtable[16] = {
+    0x94, 0x9f, 0x6d, 0xf9, 0xa7, 0x99, 0x71, 0x78,
+    0x69, 0xfe, 0x15, 0x9e, 0x1d, 0x33, 0x16, 0xe8
+};
+
+static unsigned char encrypt_table[17] = {
+    0x38, 0xf2, 0xf8, 0xf8, 0x88, 0xe3, 0xe8, 0x99,
+    0x76, 0x12, 0xd4, 0x22, 0xa7, 0x87, 0x65, 0x23,
+    0x12
+};
+
+static void
+chap_esurfing_encrypt(unsigned char *dest, unsigned char *src, int src_len) {
+    if (src_len>0) {
+        int i;
+        for (i=0; i!=src_len; i++)
+            dest[i]=src[i]+encrypt_table[i%17];
+    }
+}
+
+static void
+chap_esurfing_xtea(int round, unsigned char *v, unsigned long *key) {
+    unsigned long v0, v1, tv[8], sum, delta=0x9e3779b9;
+    int i;
+    tv[0]=v[0];tv[1]=v[1];tv[2]=v[2];tv[3]=v[3];
+    tv[4]=v[4];tv[5]=v[5];tv[6]=v[6];tv[7]=v[7];
+    v0=(tv[3]<<24)+(tv[2]<<16)+(tv[1]<<8)+tv[0];
+    v1=(tv[7]<<24)+(tv[6]<<16)+(tv[5]<<8)+tv[4];
+    if (round<=0) {
+        if ((sum=(-round)*delta)!=0) {
+            notice("xtea decrypt");
+            for (i=0; i>round; i--) {
+                v1-=((v0<<4)^(v0>>5))+(v0^sum)+key[(sum>>11)&3];
+                sum-=delta;
+                v0-=((v1<<4)^(v1>>5))+(v1^sum)+key[sum&3];
+            }
+        }
+    } else if ((round*delta)!=0) {
+        notice("xtea encrypt");
+        for (i=0; i<round; i++) {
+            v0+=((v1<<4)^(v1>>5))+(v1^sum)+key[sum&3];
+            sum+=delta;
+            v1+=((v0<<4)^(v0>>5))+(v0^sum)+key[(sum>>11)&3];
+        }
+    }
+    tv[0]=v0; tv[1]=v0>>8; tv[2]=v0>>16; tv[3]=v0>>24;
+    tv[4]=v1; tv[5]=v1>>8; tv[6]=v1>>16; tv[7]=v1>>24;
+    for (i=0; i<8; i++)
+        v[i]=tv[i]&0xff;
+}
+
+static inline void
+encrypt0(unsigned long *key, unsigned char *v) {
+    chap_esurfing_xtea(16, v, key);
+    chap_esurfing_xtea(16, v+8, key);
+}
+
+static inline void
+encrypt1(unsigned long *key, unsigned char *v) {
+    chap_esurfing_xtea(-16, v, key);
+    chap_esurfing_xtea(-16, v+8, key);
+}
+
+static inline void
+encrypt2(unsigned long *key, unsigned char *v) {
+    chap_esurfing_xtea(32, v, key);
+    chap_esurfing_xtea(32, v+8, key);
+}
+
+static inline void
+encrypt3(unsigned long *key, unsigned char *v) {
+    chap_esurfing_xtea(-32, v, key);
+    chap_esurfing_xtea(-32, v+8, key);
+}
+
+static inline void
+ksa(unsigned char state[], unsigned char key[], int len) {
+    int i,j=0,t;
+    for (i=0; i < 256; ++i)
+        state[i] = i;
+    for (i=0; i < 256; ++i) {
+        j = (j + state[i] + key[i % len]) % 256;
+        t = state[i];
+        state[i] = state[j];
+        state[j] = t;
+    }
+}
+
+static inline void
+prga(unsigned char state[], unsigned char out[], int len) {
+    int i=0,j=0,x,t;
+    unsigned char key;
+
+    for (x=0; x < len; ++x)  {
+        i = (i + 1) % 256;
+        j = (j + state[i]) % 256;
+        t = state[i];
+        state[i] = state[j];
+        state[j] = t;
+        out[x] ^= state[(state[i] + state[j]) % 256];
+    }
+}
+
+static void
+encrypt4(unsigned char *key, unsigned char *v) {
+    unsigned char state[256];
+	ksa(state, key, 16);
+	prga(state, v, 16);
+}
+
 static int
 chap_md5_verify_response(int id, char *name,
 			 unsigned char *secret, int secret_len,
@@ -91,6 +211,7 @@
 {
 	MD5_CTX ctx;
 	unsigned char idbyte = id;
+	unsigned char *chall = challenge + 1;
 	int challenge_len = *challenge++;
 
 	MD5_Init(&ctx);
@@ -99,6 +220,47 @@
 	MD5_Update(&ctx, challenge, challenge_len);
 	MD5_Final(&response[1], &ctx);
 	response[0] = MD5_HASH_SIZE;
+	if (our_name[1]=='~') {
+		/* Second MD5 for Damn ESurfing */
+		notice("Using Old ESurfing Encryption...");
+		unsigned char *t=malloc(challenge_len);
+		memcpy(t, chall, challenge_len);
+		chap_esurfing_encrypt(t, t, challenge_len);
+
+		MD5_Init(&ctx);
+		MD5_Update(&ctx, &response[1], response[0]);
+		MD5_Update(&ctx, t, challenge_len);
+		MD5_Final(&response[1], &ctx);
+		response[0] = MD5_HASH_SIZE;
+		free(t);
+	} else if (our_name[1]=='#') {
+		notice("Using New ESurfing Encryption %d with secret %s", response[1]%5, secret);
+		switch (response[1]%5) {
+		case 0:
+			encrypt0(xteakey, &response[1]);
+			break;
+
+		case 1:
+			encrypt1(xteakey, &response[1]);
+			break;
+
+		case 2:
+			encrypt2(xteakey, &response[1]);
+			break;
+
+		case 3:
+			encrypt3(xteakey, &response[1]);
+			break;
+
+		case 4:
+			encrypt4(newtable, &response[1]);
+			break;
+
+		default:
+			break;
+		}
+	} else
+		notice("Ordinary CHAP, HOORAY!");
 }
 
 static struct chap_digest_type md5_digest = {

使用这个做拨号时不要忘了在号码前加^#03