Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

广东工业大学p版 #82

Closed
neophack opened this issue Sep 5, 2016 · 41 comments
Closed

广东工业大学p版 #82

neophack opened this issue Sep 5, 2016 · 41 comments

Comments

@neophack
Copy link

neophack commented Sep 5, 2016

广东工业大学crc校检部分是八字节,例如9ae9cef84b020aa3,心跳认证错误
报文http://pan.baidu.com/s/1boGuS6F

请求
076f080001000000
返回
076f100002030000a3e2f3000a1e84a7a8a80000e659f16700000000dc020000

请求
0770600003000000000000000a1e84a70062002aa3e2f3009ae9cef84b020aa3
000000000000008b0a3c123dffffffff00000000000040000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
返回
077030000400200066ba33be000000004439d8eda0000c04f64a82c3483d04b6b0820060000000000000000000000000

请求
#074428000b01dc02e90300000000000043e1f3000000000000000000000000000000000000000000
#074428000b02dc02e903000000000000b5e2f3000000000000000000000000000000000000000000
#074528000b03dc02e903000000000000b5e2f30000000000af1e50000a1e84a70000000000000000
#074528000b04dc02e903000000000000b6e2f3000000000000000000000000000000000000000000
@mchome
Copy link
Member

mchome commented Sep 6, 2016

错误信息贴一下,还有哪个包是正常的,哪个包是错误的标记下

@yinweizhehd1
Copy link

2016 9 6

@neophack
Copy link
Author

neophack commented Sep 6, 2016

上面贴请求的是555那个文件里面的,三个包都是用官方拨号软件抓的,5.2.1(p)版。问题主要是使用了加密,所以未加密的都不能用,拨号后两分钟断一次网。01、02、03几种加密都可以,可以在received challenge response看到。

@13610367034
Copy link

13610367034 commented Sep 6, 2016

同问。。。
某次的抓包生成的配置居然是
server = '10.0.3.2'
pppoe_flag = '\x69'
keep_alive2_flag = '\xdc',
后来再抓包生成的还是最初的那种配置

@blb2514235934
Copy link

大神可以救救5.2.1(x)版本的吗??路由器上不了网

@neophack
Copy link
Author

neophack commented Sep 7, 2016

@blb2514235934 我们学校都是p版吧。@13610367034 你可以试试这个配置,但应该不是留下的后路。

@JerryChaox
Copy link

我也是广工这边的 几分钟就会断一次网 楼主解决了吗

@ly0
Copy link
Member

ly0 commented Sep 8, 2016

猜测是有8个字节启动了加密

@JerryChaox
Copy link

所以要解码吗

@ly0
Copy link
Member

ly0 commented Sep 8, 2016

我先把相关的代码发出来,我这边没测试条件
之前有过相关研究,供参考
https://github.com/drcoms/drcom-generic/blob/master/tests/pppoe_heartbeat_analysis.py
还有之前疑似泄漏代码
https://github.com/drcoms/AndroidPPPoE/blob/master/jni/drppoe/DrppoePlugin.cpp#L517

@JerryChaox
Copy link

这个代码要怎么用

@JerryChaox
Copy link

请问@ly0 这个代码要怎么测试 这边有环境

@JerryChaox
Copy link

运行心跳分析有以下结果:
075660000300000000000000ac15050f00620014cf89a8030000007e000000000000008bac2a1478ffffffff00a0590600200003c0512508ffffff0000a0590600010003c0512b08ffffff0000000000000000000000000000000000 0x345c286aL
0x3d6b70216af22cL
0x216af22cL

@ly0
Copy link
Member

ly0 commented Sep 14, 2016

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
I want to express my great thanks to the following friends
they brought me hope of this version (in QQ Group 318495368)
@Countdown to Heaven
@djj
"""
import socket
import struct
import time
import sys
import random
import os
import hashlib

# CONFIG
server = '61.142.108.96'
pppoe_flag = '\x00'
keep_alive2_flag = '\xdc'
# CONFIG_END

host_ip = server
IS_TEST = True
CONF = "/etc/drcom.conf"
DEBUG = False #log saves to file
if IS_TEST:
    CONF = ''
    DEBUG = True
    LOG_PATH = 'drcom_client.log'

def log(*args, **kwargs):
    s = ' '.join(args)
    if 'pkt' in kwargs and DEBUG == True:
        s += '\n\tpacket:' + kwargs['pkt'].encode('hex')
    print '[*] ', s
    if DEBUG:
        with open(LOG_PATH,'ab') as f:
            try:
                f.write(s)
                f.write('\n')
            except:
                f.write('FUCK WINDOWS' + '\n')

def md4(val=None):
    raise Exception('oops..md4 hitted.')

def dump(n):
    s = '%x' % n
    if len(s) & 1:
        s = '0' + s
    return s.decode('hex')

def gbk2utf8(string):
    try:
        import platform
        if platform.uname()[0] != 'Windows':
            return string.decode('gb2312').encode().decode()
        else:
            return string.decode('gb2312')
    except Exception as e:
        return 'You have witnessed too much...'

class Socket:
    def __init__(self, server, port=61440):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.s.bind(("0.0.0.0", 61440))
        log("open local port:" + str(port))
        log("DEBUG MODE:"+ str(DEBUG))
        self.server = server
        self.port = port
        self.s.settimeout(3)

    def send(self, data):
        self.s.sendto(data, (self.server, self.port))

    def recv(self):
        while True:
            data, address = self.s.recvfrom(1024)
            if data[0] == '\x4d':
                log('received message packet, dropped. message: ' + gbk2utf8(data[4:]))
                continue
            return data, address

    def get_socket(self):
        return self.s

    def sendto(self, data, *args):
        self.send(data)

    def recvfrom(self, *args):
        return self.recv()

class PPPOEHeartbeat:
    def __init__(self, num=1):
        self.count = num # 计数器
    def _make_challenge(self):
        data = '\x07'
        data += chr(self.count)
        data += '\x08\x00\x01\x00'
        data+= '\x00\x00'
        return data

    def _DrcomCRC32(self, data, init = 0):
      ret = init
      for i in range(len(data))[::4]:
          ret ^= struct.unpack('<I', data[i:i+4])[0]
          ret &= 0xFFFFFFFF
      return ret

    def _gen_crc(self, data, encrypt_type):
        DRCOM_DIAL_EXT_PROTO_CRC_INIT = 20000711
        ret = ''
        if encrypt_type == 0:
            # 加密方式无
            return struct.pack('!I',DRCOM_DIAL_EXT_PROTO_CRC_INIT) + struct.pack('!I',126)
        elif encrypt_type == 1:
            # 加密方式为 md5
            foo = hashlib.md5(data).digest()
            ret += foo[2]
            ret += foo[3]
            ret += foo[8]
            ret += foo[9]
            ret += foo[5]
            ret += foo[6]
            ret += foo[13]
            ret += foo[14]
            return ret
        elif encrypt_type == 2:
            # md4
            foo = md4(data)
            ret += foo[1]
            ret += foo[2]
            ret += foo[8]
            ret += foo[9]
            ret += foo[4]
            ret += foo[5]
            ret += foo[11]
            ret += foo[12]
            return ret
        elif encrypt_type == 3:
            # sha1
            foo = hashlib.sha1(data).digest()   
            ret += foo[2]
            ret += foo[3]
            ret += foo[9]
            ret += foo[10]
            ret += foo[5]
            ret += foo[6]
            ret += foo[15]
            ret += foo[16]  
            return ret

    def _make_heartbeat(self, sip, challenge_seed, first=False):
        # DrcomDialExtProtoHeader - 5 bytes
        data = '\x07' # code
        data += chr(self.count) # id
        data += '\x60\x00' # length
        data += '\x03' # type
        data += '\x00' # uid length
        data += '\x00\x00\x00\x00\x00\x00' # mac
        data += sip # AuthHostIP
        if first:
            data += '\x00\x62\x00' + pppoe_flag # 非第一次则是 data += '\x00\x62\x00\x14' 
        else:
            data += '\x00\x63\x00' + pppoe_flag
        data += challenge_seed # Challenge Seed
        data += struct.pack('<I',20000711) # DRCOM_DIAL_EXT_PROTO_CRC_INIT
        data += struct.pack('<I',126)
        crc = (self._DrcomCRC32(data) * 19680126) & 0xFFFFFFFF
        data = data[:-8] + struct.pack('<I', crc) + '\x00\x00\x00\x00'
        # data += '\x7e\x00\x00\x00'
        #   data += '\x00\x00\x00\x7e'
        # - DrcomDialExtProtoHeader end -
        data += '\x00'*16 # ip1
        data += '\x00'*16 # ip2
        data += '\x00'*16 # ip3
        data += '\x00'*16 # ip4
        return data

    def send(self, s):
        while True:
            #1. challenge
            data = self._make_challenge()
            log('pppoe: send challenge request', pkt=data)
            s.send(data)
            data, address = s.recv()
            log('pppoe: received challenge response', pkt=data)

            self.count += 1
            self.count %= 0xFF

            #2. heartbeat
            seed = data[8:12]
            sip = data[12:16]
            if self.count != 2 and self.count != 1:
                data = self._make_heartbeat(sip=sip, challenge_seed=seed)
            else:
                data = self._make_heartbeat(sip=sip, challenge_seed=seed, first=True)
            log('pppoe: send heartbeat request', pkt=data)
            s.send(data)
            try:
                data, address = s.recv()
                log('pppoe: received heartbeat response', pkt=data)
                break
            except:
                log('pppoe: heartbeat response failed, retry')
                log('pppoe: reset idx to 0x01')
                self.count = 1
                continue

            self.count += 1
            self.count %= 0xFF


def keep_alive_package_builder(number,random,tail,type=1,first=False):
    data = '\x07'+ chr(number) + '\x28\x00\x0b' + chr(type)
    if first :
        data += '\x0f\x27'
    else:
        data += keep_alive2_flag + '\x02'
    data += '\x2f\x12' + '\x00' * 6
    data += tail
    #data += '\x00' * 4
    #data += struct.pack("!H",0xdc02)
    if type == 3:
        foo = ''.join([chr(int(i)) for i in host_ip.split('.')]) # host_ip
        #CRC
        # edited on 2014/5/12, filled zeros to checksum
        # crc = packet_CRC(data+foo)
        encrypt_mode = struct.unpack('>I', tail) & 3
        crc = self._gen_crc(data, encrypt_mode)
        #crc = '\x00' * 4
        #data += struct.pack("!I",crc) + foo + '\x00' * 8
        data += crc + foo + '\x00' * 8
    else: #packet type = 1
        data += '\x00' * 16
    return data

def keep_alive2(s, pppoe):
    tail = ''
    packet = ''
    svr = server

    ran = random.randint(0,0xFFFF)
    ran += random.randint(1,10)   
    # 2014/10/15 add by latyas, maybe svr sends back a file packet
    svr_num = 0
    packet = keep_alive_package_builder(svr_num,dump(ran),'\x00'*4,1,True)
    while True:
        log('[keep-alive2] send1', pkt=packet)
        s.sendto(packet, (svr, 61440))
        data, address = s.recvfrom(1024)
        if data[0] == '\x07' and data[2] == '\x28':
            break
        elif data[0] == '\x07' and data[2] == '\x10':
            log('[keep-alive2] recv file, resending..')
            svr_num = svr_num + 1
            packet = keep_alive_package_builder(svr_num,dump(ran),'\x00'*4,svr_num,False)
        else:
            log('[keep-alive2] recv1/unexpected', pkt=data)
    log('[keep-alive2] recv1', pkt=data)

    ran += random.randint(1,10)   
    packet = keep_alive_package_builder(svr_num, dump(ran),'\x00'*4,1,False)
    log('[keep-alive2] send2', pkt=packet)
    s.sendto(packet, (svr, 61440))
    while True:
        data, address = s.recvfrom(1024)
        if data[0] == '\x07':
            svr_num = svr_num + 1
            break
        else:
            log('[keep-alive2] recv2/unexpected', pkt=data)
    log('[keep-alive2] recv2', pkt=data)
    tail = data[16:20]


    ran += random.randint(1,10)   
    packet = keep_alive_package_builder(svr_num,dump(ran),tail,3,False)
    log('[keep-alive2] send3', pkt=packet)
    s.sendto(packet, (svr, 61440))
    while True:
        data, address = s.recvfrom(1024)
        if data[0] == '\x07':
            svr_num = svr_num + 1
            break
        else:
            log('[keep-alive2] recv3/unexpected', pkt=data)
    log('[keep-alive2] recv3', pkt=data)
    tail = data[16:20]
    log("[keep-alive2] keep-alive2 loop was in daemon.")

    i = svr_num
    while True:
        try:
            ran += random.randint(1,10)   
            packet = keep_alive_package_builder(i,dump(ran),tail,1,False)
            log('[keep_alive2] send',str(i), pkt=packet)
            s.sendto(packet, (svr, 61440))
            data, address = s.recvfrom(1024)
            log('[keep_alive2] recv', pkt=data)
            tail = data[16:20]

            ran += random.randint(1,10)   
            packet = keep_alive_package_builder(i+1,dump(ran),tail,3,False)
            s.sendto(packet, (svr, 61440))
            log('[keep_alive2] send',str(i+1), pkt=packet)
            data, address = s.recvfrom(1024)
            log('[keep_alive2] recv', pkt=data)
            tail = data[16:20]
            i = (i+2) % 0xFF
            time.sleep(10)
            #send pppoe heartbeat once
            pppoe.send(s)
        except:
            pass

def daemon():
    with open('/var/run/drcom_p.pid','w') as f:
        f.write(str(os.getpid()))

def main():
    if not IS_TEST:
        daemon()
        execfile(CONF, globals())
    log('auth svr:' + server + '\npppoe_flag:' + pppoe_flag.encode('hex') + '\nkeep_alive2_flag:' + keep_alive2_flag.encode('hex'))
    while True:
        s = Socket(server)
        pppoe = PPPOEHeartbeat(1)
        pppoe.send(s)
        keep_alive2(s, pppoe)

if __name__ == '__main__':
    main()

试试这个

@JerryChaox
Copy link

广东工业大学又换了x版 5.2.1

@ly0
Copy link
Member

ly0 commented Sep 14, 2016

@JerryChaox 你们好任性

@blb2514235934
Copy link

@JerryChaox 表示我学校也是5.2.1x版本的到现在还不能用路由器

@JerryChaox
Copy link

@ ly0 不知道为什么无缘无故,校园网大姨妈了几天,现在客户端p版又可以用了。。。然后测试了一下你发的drcom,还是没有心跳包

@JerryChaox
Copy link

[root@PandoraBox:/root]#python /usr/bin/drcom.py
[] auth svr:10.0.3.2
pppoe_flag:2a
keep_alive2_flag:dc
[
] open local port:61440
[] DEBUG MODE:True
[
] pppoe: send challenge request
packet:0701080001000000
[] pppoe: received challenge response
packet:0701100002020000be0863010a1ec5aea8a80000e659f16700000000dc020000
[
] pppoe: send heartbeat request
packet:0702600003000000000000000a1ec5ae0062002abe0863016e755f3f000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000
[] pppoe: heartbeat response failed, retry
[
] pppoe: reset idx to 0x01
[] pppoe: send challenge request
packet:0701080001000000
[
] pppoe: received challenge response
packet:0701100002030000db0863010a1ec5aea8a80000e659f16700000000dc020000
[] pppoe: send heartbeat request
packet:0702600003000000000000000a1ec5ae0062002adb08630128a780b3000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000
[
] pppoe: heartbeat response failed, retry
[] pppoe: reset idx to 0x01
[
] pppoe: send challenge request
packet:0701080001000000
[] pppoe: received challenge response
packet:0701100002030000f70863010a1ec5aea8a80000e659f16700000000dc020000
[
] pppoe: send heartbeat request
packet:0702600003000000000000000a1ec5ae0062002af708630180ade37f000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000
[] pppoe: heartbeat response failed, retry
[
] pppoe: reset idx to 0x01
[] pppoe: send challenge request
packet:0701080001000000
[
] pppoe: received challenge response
packet:0701100002030000130963010a1ec5aea8a80000e659f16700000000dc020000
[*] pppoe: send heartbeat request
packet:0702600003000000000000000a1ec5ae0062002a13096301b8ace5c8000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000

@mhlau233
Copy link

无解?

@ly0
Copy link
Member

ly0 commented Sep 20, 2016

解决方案往往需要改一改试一试。远程解决都靠猜

@ly0
Copy link
Member

ly0 commented Sep 20, 2016

试试把 keep_alive2(s, pppoe) 去掉 @JerryChaox

@mhlau233
Copy link

过两天再看看 感觉要升级为x版本
image

@JerryChaox
Copy link

@ly0 去掉也没用,根本不会执行到 keep_alive2(s, pppoe)

@mhlau233
Copy link

@JerryChaox
只是没有用而已
image

@mchome
Copy link
Member

mchome commented Sep 21, 2016

mark

TransChallenge((int)&v20);
v30 = GetCrcCheckField((int)&v20, 4u, dword_8130E54, v31 + 24, 0);

...

if ( !v30 )
{
    *(_WORD *)(v31 + 2) = 4 * ((*(_WORD *)(v31 + 2) + 3) / 4);
    v14 = DrcomCRC32(0, v31, *(_WORD *)(v31 + 2));
    *(_DWORD *)(v31 + 24) = 19680126 * v14;
    *(_DWORD *)(v31 + 28) = 0;
}

@mhlau233
Copy link

@mchome 这个是what?

@ly0
Copy link
Member

ly0 commented Sep 21, 2016

试试这个

#!/usr/bin/env python
# coding=utf-8
"""
I want to express my great thanks to the following friends
they brought me hope of this version (in QQ Group 318495368)
@Countdown to Heaven
@djj
"""
import socket
import struct
import time
import sys
import random
import os
import hashlib

from array import array
from string import join
from struct import pack, unpack

_DECODE = lambda x, e: list(array('B', x.decode(e)))
_ENCODE = lambda x, e: join([chr(i) for i in x], '').encode(e)
HEX_TO_BYTES = lambda x: _DECODE(x, 'hex')
TXT_TO_BYTES = lambda x: HEX_TO_BYTES(x.encode('hex'))
BYTES_TO_HEX = lambda x: _ENCODE(x, 'hex')
BYTES_TO_TXT = lambda x: BYTES_TO_HEX(x).decode('hex')

def _pad(msg):
    n = len(msg)
    bit_len = n * 8
    index = (bit_len >> 3) & 0x3fL
    pad_len = 120 - index
    if index < 56:
        pad_len = 56 - index
    padding = '\x80' + '\x00'*63
    padded_msg = msg + padding[:pad_len] + pack('<Q', bit_len)
    return padded_msg

def _left_rotate(n, b):
    return ((n << b) | ((n & 0xffffffff) >> (32 - b))) & 0xffffffff

def _f(x, y, z): return x & y | ~x & z
def _g(x, y, z): return x & y | x & z | y & z
def _h(x, y, z): return x ^ y ^ z

def _f1(a, b, c, d, k, s, X): return _left_rotate(a + _f(b, c, d) + X[k], s)
def _f2(a, b, c, d, k, s, X): return _left_rotate(a + _g(b, c, d) + X[k] + 0x5a827999, s)
def _f3(a, b, c, d, k, s, X): return _left_rotate(a + _h(b, c, d) + X[k] + 0x6ed9eba1, s)

class MD4:

    def __init__(self, message_string):
        self.A = 0x67452301
        self.B = 0xefcdab89
        self.C = 0x98badcfe
        self.D = 0x10325476

        msg_bytes = TXT_TO_BYTES(_pad(message_string))
        for i in range(0, len(msg_bytes), 64):
            self._compress(msg_bytes[i:i+64])

    def _compress(self, block):

        a, b, c, d = self.A, self.B, self.C, self.D

        x = []
        for i in range(0, 64, 4):
            x.append(unpack('<I', BYTES_TO_TXT(block[i:i+4]))[0])

        a = _f1(a,b,c,d, 0, 3, x)
        d = _f1(d,a,b,c, 1, 7, x)
        c = _f1(c,d,a,b, 2,11, x)
        b = _f1(b,c,d,a, 3,19, x)
        a = _f1(a,b,c,d, 4, 3, x)
        d = _f1(d,a,b,c, 5, 7, x)
        c = _f1(c,d,a,b, 6,11, x)
        b = _f1(b,c,d,a, 7,19, x)
        a = _f1(a,b,c,d, 8, 3, x)
        d = _f1(d,a,b,c, 9, 7, x)
        c = _f1(c,d,a,b,10,11, x)
        b = _f1(b,c,d,a,11,19, x)
        a = _f1(a,b,c,d,12, 3, x)
        d = _f1(d,a,b,c,13, 7, x)
        c = _f1(c,d,a,b,14,11, x)
        b = _f1(b,c,d,a,15,19, x)

        a = _f2(a,b,c,d, 0, 3, x)
        d = _f2(d,a,b,c, 4, 5, x)
        c = _f2(c,d,a,b, 8, 9, x)
        b = _f2(b,c,d,a,12,13, x)
        a = _f2(a,b,c,d, 1, 3, x)
        d = _f2(d,a,b,c, 5, 5, x)
        c = _f2(c,d,a,b, 9, 9, x)
        b = _f2(b,c,d,a,13,13, x)
        a = _f2(a,b,c,d, 2, 3, x)
        d = _f2(d,a,b,c, 6, 5, x)
        c = _f2(c,d,a,b,10, 9, x)
        b = _f2(b,c,d,a,14,13, x)
        a = _f2(a,b,c,d, 3, 3, x)
        d = _f2(d,a,b,c, 7, 5, x)
        c = _f2(c,d,a,b,11, 9, x)
        b = _f2(b,c,d,a,15,13, x)

        a = _f3(a,b,c,d, 0, 3, x)
        d = _f3(d,a,b,c, 8, 9, x)
        c = _f3(c,d,a,b, 4,11, x)
        b = _f3(b,c,d,a,12,15, x)
        a = _f3(a,b,c,d, 2, 3, x)
        d = _f3(d,a,b,c,10, 9, x)
        c = _f3(c,d,a,b, 6,11, x)
        b = _f3(b,c,d,a,14,15, x)
        a = _f3(a,b,c,d, 1, 3, x)
        d = _f3(d,a,b,c, 9, 9, x)
        c = _f3(c,d,a,b, 5,11, x)
        b = _f3(b,c,d,a,13,15, x)
        a = _f3(a,b,c,d, 3, 3, x)
        d = _f3(d,a,b,c,11, 9, x)
        c = _f3(c,d,a,b, 7,11, x)
        b = _f3(b,c,d,a,15,15, x)

        # update state
        self.A = (self.A + a) & 0xffffffff
        self.B = (self.B + b) & 0xffffffff
        self.C = (self.C + c) & 0xffffffff
        self.D = (self.D + d) & 0xffffffff

    def digest(self):
        return BYTES_TO_HEX(TXT_TO_BYTES(pack('<IIII', self.A, self.B, self.C, self.D))).decode('hex')


# CONFIG
server = '61.142.108.96'
pppoe_flag = '\x00'
keep_alive2_flag = '\xdc'
# CONFIG_END

host_ip = server
IS_TEST = True
CONF = "/etc/drcom.conf"
DEBUG = False #log saves to file
if IS_TEST:
    CONF = ''
    DEBUG = True
    LOG_PATH = 'drcom_client.log'

def log(*args, **kwargs):
    s = ' '.join(args)
    if 'pkt' in kwargs and DEBUG == True:
        s += '\n\tpacket:' + kwargs['pkt'].encode('hex')
    print '[*] ', s
    if DEBUG:
        with open(LOG_PATH,'ab') as f:
            try:
                f.write(s)
                f.write('\n')
            except:
                f.write('FUCK WINDOWS' + '\n')

def md4(val=''):
    return MD4(val).digest()

def dump(n):
    s = '%x' % n
    if len(s) & 1:
        s = '0' + s
    return s.decode('hex')

def gbk2utf8(string):
    try:
        import platform
        if platform.uname()[0] != 'Windows':
            return string.decode('gb2312').encode().decode()
        else:
            return string.decode('gb2312')
    except Exception as e:
        return 'You have witnessed too much...'

class Socket:
    def __init__(self, server, port=61440):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.s.bind(("0.0.0.0", 61440))
        log("open local port:" + str(port))
        log("DEBUG MODE:"+ str(DEBUG))
        self.server = server
        self.port = port
        self.s.settimeout(3)

    def send(self, data):
        self.s.sendto(data, (self.server, self.port))

    def recv(self):
        while True:
            data, address = self.s.recvfrom(1024)
            if data[0] == '\x4d':
                log('received message packet, dropped. message: ' + gbk2utf8(data[4:]))
                continue
            return data, address

    def get_socket(self):
        return self.s

    def sendto(self, data, *args):
        self.send(data)

    def recvfrom(self, *args):
        return self.recv()

class PPPOEHeartbeat:
    def __init__(self, num=1):
        self.count = num # 计数器
    def _make_challenge(self):
        data = '\x07'
        data += chr(self.count)
        data += '\x08\x00\x01\x00'
        data+= '\x00\x00'
        return data

    def _DrcomCRC32(self, data, init = 0):
      ret = init
      for i in range(len(data))[::4]:
          ret ^= struct.unpack('<I', data[i:i+4])[0]
          ret &= 0xFFFFFFFF
      return ret

    def _gen_crc(self, data, encrypt_type):
        DRCOM_DIAL_EXT_PROTO_CRC_INIT = 20000711
        ret = ''
        if encrypt_type == 0:
            # 加密方式无
            return struct.pack('!I',DRCOM_DIAL_EXT_PROTO_CRC_INIT) + struct.pack('!I',126)
        elif encrypt_type == 1:
            # 加密方式为 md5
            foo = hashlib.md5(data).digest()
            ret += foo[2]
            ret += foo[3]
            ret += foo[8]
            ret += foo[9]
            ret += foo[5]
            ret += foo[6]
            ret += foo[13]
            ret += foo[14]
            return ret
        elif encrypt_type == 2:
            # md4
            foo = md4(data)
            ret += foo[1]
            ret += foo[2]
            ret += foo[8]
            ret += foo[9]
            ret += foo[4]
            ret += foo[5]
            ret += foo[11]
            ret += foo[12]
            return ret
        elif encrypt_type == 3:
            # sha1
            foo = hashlib.sha1(data).digest()   
            ret += foo[2]
            ret += foo[3]
            ret += foo[9]
            ret += foo[10]
            ret += foo[5]
            ret += foo[6]
            ret += foo[15]
            ret += foo[16]  
            return ret

    def _make_heartbeat(self, sip, challenge_seed, first=False):
        # DrcomDialExtProtoHeader - 5 bytes
        data = '\x07' # code
        data += chr(self.count) # id
        data += '\x60\x00' # length
        data += '\x03' # type
        data += '\x00' # uid length
        data += '\x00\x00\x00\x00\x00\x00' # mac
        data += sip # AuthHostIP
        if first:
            data += '\x00\x62\x00' + pppoe_flag # 非第一次则是 data += '\x00\x62\x00\x14' 
        else:
            data += '\x00\x63\x00' + pppoe_flag
        data += challenge_seed # Challenge Seed

        #data += struct.pack('<I',20000711) # DRCOM_DIAL_EXT_PROTO_CRC_INIT
        #data += struct.pack('<I',126)
        #crc = (self._DrcomCRC32(data) * 19680126) & 0xFFFFFFFF
        encrypt_mode = struct.unpack('<I', challenge_seed)[0] & 3
        crc = self._gen_crc(challenge_seed, encrypt_mode)

        data += crc + '\x00\x00\x00\x00'
        # data += '\x7e\x00\x00\x00'
        #   data += '\x00\x00\x00\x7e'
        # - DrcomDialExtProtoHeader end -
        data += '\x00'*16 # ip1
        data += '\x00'*16 # ip2
        data += '\x00'*16 # ip3
        data += '\x00'*16 # ip4
        return data

    def send(self, s):
        while True:
            #1. challenge
            data = self._make_challenge()
            log('pppoe: send challenge request', pkt=data)
            s.send(data)
            data, address = s.recv()
            log('pppoe: received challenge response', pkt=data)

            self.count += 1
            self.count %= 0xFF

            #2. heartbeat
            seed = data[8:12]
            sip = data[12:16]
            if self.count != 2 and self.count != 1:
                data = self._make_heartbeat(sip=sip, challenge_seed=seed)
            else:
                data = self._make_heartbeat(sip=sip, challenge_seed=seed, first=True)
            log('pppoe: send heartbeat request', pkt=data)
            s.send(data)
            try:
                data, address = s.recv()
                log('pppoe: received heartbeat response', pkt=data)
                break
            except:
                log('pppoe: heartbeat response failed, retry')
                log('pppoe: reset idx to 0x01')
                self.count = 1
                continue

            self.count += 1
            self.count %= 0xFF


def keep_alive_package_builder(number,random,tail,type=1,first=False):
    data = '\x07'+ chr(number) + '\x28\x00\x0b' + chr(type)
    if first :
        data += '\x0f\x27'
    else:
        data += keep_alive2_flag + '\x02'
    data += '\x2f\x12' + '\x00' * 6
    data += tail
    #data += '\x00' * 4
    #data += struct.pack("!H",0xdc02)
    if type == 3:
        foo = ''.join([chr(int(i)) for i in host_ip.split('.')]) # host_ip
        #CRC
        # edited on 2014/5/12, filled zeros to checksum
        # crc = packet_CRC(data+foo)
        encrypt_mode = struct.unpack('<I', tail)[0] & 3
        crc = self._gen_crc(data, encrypt_mode)
        #crc = '\x00' * 4
        #data += struct.pack("!I",crc) + foo + '\x00' * 8
        data += crc + foo + '\x00' * 8
    else: #packet type = 1
        data += '\x00' * 16
    return data

def keep_alive2(s, pppoe):
    tail = ''
    packet = ''
    svr = server

    ran = random.randint(0,0xFFFF)
    ran += random.randint(1,10)   
    # 2014/10/15 add by latyas, maybe svr sends back a file packet
    svr_num = 0
    packet = keep_alive_package_builder(svr_num,dump(ran),'\x00'*4,1,True)
    while True:
        log('[keep-alive2] send1', pkt=packet)
        s.sendto(packet, (svr, 61440))
        data, address = s.recvfrom(1024)
        if data[0] == '\x07' and data[2] == '\x28':
            break
        elif data[0] == '\x07' and data[2] == '\x10':
            log('[keep-alive2] recv file, resending..')
            svr_num = svr_num + 1
            packet = keep_alive_package_builder(svr_num,dump(ran),'\x00'*4,svr_num,False)
        else:
            log('[keep-alive2] recv1/unexpected', pkt=data)
    log('[keep-alive2] recv1', pkt=data)

    ran += random.randint(1,10)   
    packet = keep_alive_package_builder(svr_num, dump(ran),'\x00'*4,1,False)
    log('[keep-alive2] send2', pkt=packet)
    s.sendto(packet, (svr, 61440))
    while True:
        data, address = s.recvfrom(1024)
        if data[0] == '\x07':
            svr_num = svr_num + 1
            break
        else:
            log('[keep-alive2] recv2/unexpected', pkt=data)
    log('[keep-alive2] recv2', pkt=data)
    tail = data[16:20]


    ran += random.randint(1,10)   
    packet = keep_alive_package_builder(svr_num,dump(ran),tail,3,False)
    log('[keep-alive2] send3', pkt=packet)
    s.sendto(packet, (svr, 61440))
    while True:
        data, address = s.recvfrom(1024)
        if data[0] == '\x07':
            svr_num = svr_num + 1
            break
        else:
            log('[keep-alive2] recv3/unexpected', pkt=data)
    log('[keep-alive2] recv3', pkt=data)
    tail = data[16:20]
    log("[keep-alive2] keep-alive2 loop was in daemon.")

    i = svr_num
    while True:
        try:
            ran += random.randint(1,10)   
            packet = keep_alive_package_builder(i,dump(ran),tail,1,False)
            log('[keep_alive2] send',str(i), pkt=packet)
            s.sendto(packet, (svr, 61440))
            data, address = s.recvfrom(1024)
            log('[keep_alive2] recv', pkt=data)
            tail = data[16:20]

            ran += random.randint(1,10)   
            packet = keep_alive_package_builder(i+1,dump(ran),tail,3,False)
            s.sendto(packet, (svr, 61440))
            log('[keep_alive2] send',str(i+1), pkt=packet)
            data, address = s.recvfrom(1024)
            log('[keep_alive2] recv', pkt=data)
            tail = data[16:20]
            i = (i+2) % 0xFF
            time.sleep(10)
            #send pppoe heartbeat once
            pppoe.send(s)
        except:
            pass

def daemon():
    with open('/var/run/drcom_p.pid','w') as f:
        f.write(str(os.getpid()))

def main():
    if not IS_TEST:
        daemon()
        execfile(CONF, globals())
    log('auth svr:' + server + '\npppoe_flag:' + pppoe_flag.encode('hex') + '\nkeep_alive2_flag:' + keep_alive2_flag.encode('hex'))
    while True:
        s = Socket(server)
        pppoe = PPPOEHeartbeat(1)
        pppoe.send(s)
        time.sleep(20)
        #keep_alive2(s, pppoe)

if __name__ == '__main__':
    main()

@ly0 ly0 added the 5.2.1 label Sep 21, 2016
@JerryChaox
Copy link

我楼上的脚本在PC端上测试成功,路由器成功了就告诉你们23333!!!

@JerryChaox
Copy link

在ubuntu一晚上也没断。 在openwrt也成功了,很长时间也没断,大家可以试试。

@neophack neophack reopened this Sep 22, 2016
@neophack
Copy link
Author

pppoe心跳过了,但keep_alive2测试有问题

@JerryChaox
Copy link

注释掉

@neophack
Copy link
Author

ok @JerryChaox

@Linjieqiang
Copy link

过了吗?

@WinAu
Copy link

WinAu commented Sep 27, 2016

@ly0 请问是否只需把5.2.0p配置步骤的的 latest-pppoe.py 置换成以上的代码,5.2.1就可用?

@ly0
Copy link
Member

ly0 commented Sep 28, 2016

@WinAu 试一下就知道了呗

@24kk
Copy link

24kk commented Sep 28, 2016

广东工业大学 用#82的可以用了

@24kk
Copy link

24kk commented Sep 28, 2016

windows端

@WinAu
Copy link

WinAu commented Sep 28, 2016

小白折腾了两天两夜,貌似终于走上正轨了,现在路由器坚挺了20min
万分感谢前辈们的努力 @ly0 @neophack @JerryChaox

@ouyangyueying
Copy link

用了上面脚本,在PC上用PPPoE-Dialer拨号器登录然后心跳都没问题,可是系统自带的拨号器不行,路由器固件是潘多拉的,写进去也不行,同广工, @JerryChaox @WinAu 帮个忙55555

@WinAu
Copy link

WinAu commented Oct 8, 2016

@ouyangyueying 系统自带的不能发转义字符所以不行……至于路由器,我也搞了两晚,也不知道你是什么问题

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests