• 元和网络的密码加密过程


    搬到南窑头以后,上网需要运行一个“元和网络”的拨号器,大是不爽;并且我的Ubuntu下面也没法上网了(使用pppoeconfig,输入人家提供的用户名和密码,总是提示错误)。这几天就抽空hack了一下。

    需要介绍一下这个拨号过程。在网络连接的详细信息里面看到设备类型为PPPoE,身份验证方法是CHAP。这个对后面的解密过程没有帮助,但是对在Linux下面拨号的设置是有用的。

    Wireshark抓了一下连接过程中的TCP/IP包。如下图:

    按照CHAP协议(RFC1994),认证的过程是这样的: (1) 认证端(可以认为是YHWL服务器)向被认证端(可以认为是YHWL客户端)发起一个挑战(上图中的第一行); (2) 客户端根据挑战的值以及本地使用的密码进行一次hashCHAP协议使用MD5算法),然后将计算出的值(校验值)回应给服务器(用户名是明文传送的,上图中的第二行); (3) 服务器根据用户名去数据库中查询相应的密码,然后进行同样的运算,如果校验值匹配,那么认证成功,客户端和服务端就建立了连接。

    然而我计算出的校验值总是和发送的不一致,于是认定这个“元和网络”的客户端肯定做了一些手脚。好,hack开始。

    问题的难点在于这个客户端如何建立连接,如何计算校验值。这个客户端是一个.NET程序,用ILSpy反编译了一下,看到里面有一个Ras.RasManager,如下图:

     

     

    初步确定应该是在这里面进行拨号连接的,继续展开,查看这个类的成员:

    public string UserName;

    public string Password;

    public string EntryName; 

    public string PhoneNumber;

    还有一个Connect()方法,不过这个里面什么也没有:

    // Ras.RasManager

    [MethodImpl(MethodImplOptions.NoInlining)]

    public string Connect()

    {

        return null;

    }

    在网络上搜索一下就可以发现,这个已经很接近了,ILSpy可能因为有什么困难不能反编译这个函数的每一条语句,但我们猜测在Connect()方法里肯定调用了RasDial这个函数(http://msdn.microsoft.com/en-us/library/ms897090.aspx):

    DWORDRasDial(

      LPRASDIALEXTENSIONS dialExtensions,

      LPTSTR phoneBookPath,

      LPRASDIALPARAMS rasDialParam,

      DWORD NotifierType,

      LPVOID notifier,

      LPHRASCONN pRasConn

    );

    这里跟我们相关的是这个rasDialParam参数,在Ras.RasManager类里面也有这样一个成员:

    private RasManager.RASDIALPARAMS YnfcdQt6s;

    继续看这个结构:

    [StructLayout(LayoutKind.Sequential, CharSet= CharSet.Auto, Pack = 4)]

    public struct RASDIALPARAMS {

        public int dwSize;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst= 257)]

        public string szEntryName;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst= 129)]

        public string szPhoneNumber;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst= 129)]

        public string szCallbackNumber;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst= 257)]

        public string szUserName;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst= 257)]

        public string szPassword;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst= 16)]

        public string szDomain;

        public int dwSubEntry;

        public int dwCallbackId;

        static RASDIALPARAMS()

        {

            gkmJgD0Q1OPjD7Vp9g.VdUL8hajk();

         }

     }

    用户名、密码、名称、域等一应俱全。为了验证这个想法,我们在Ras.RasManager.Connect()上打一个断点。(果然可以在这里断下,跳出后网络就连通了,这个过程不赘述。)

    但在这里看到一个有意思的事情:用户名还是原来的用户名,但是密码已经和原来不同了。把这个密码拿出来,使用这个密码直接拨号(命令行使用rasdial,用法可以/?查看),连网成功!这个是不小的发现。在我的Ubuntu下面也可以上网了,哈哈。

    经过一段时间的分析,锁定了SupperRadiusClient.FMain.Ovpt85v4r()这个函数。这个函数就是将输入的密码转换成最终在用MD5计算CHAP回应时使用的密码。这次范围比较小了,专门hack这个函数就可以了。最终对这几行汇编代码产生了兴趣:

    00000282  call       FAD475BC

    00000287  mov        esi,eax

    00000301  mov        eax,dword ptr [ebp-24h]

    00000304  mov        ecx,dword ptr [eax+000001F4h]

    0000030a  mov        edx,dword ptr [ebp-14h]

    0000030d  sar        edx,2

    00000310  call       dword ptr ds:[00317638h]

    00000316  mov        word ptr [esi+4],ax

    这里需要逐行解释一下这几行汇编代码。

    00000282  call       FAD475BC

    00000287  mov        esi,eax

    构造一个空的String,然后将构造的对象的地址放到ESI寄存器中。

    00000301  mov        eax,dword ptr [ebp-24h]

    00000304  mov        ecx,dword ptr [eax+000001F4h]

    将一个String的地址放到了ECX寄存器,我们监视一下这个字符串,内容是: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 这个很重要。

    0000030a  mov        edx,dword ptr [ebp-14h]

    把当前要加密的字符取了出来,放到EDX寄存器中。

    0000030d  sar        edx,2

    除以4.

    00000310  call       dword ptr ds:[00317638h]

    EDX寄存器中的值作为索引,取ECX寄存器中那个String的一个字符。例如,EDX的值为0,那么这个函数执行之后,就把字母A放到在AX寄存器中。(String是使用Unicode表示的,一个字符占2个字节,所以是AX寄存器,不是EAX,也不是AL)。

    00000316  mov        word ptr [esi+4],ax

    AX寄存器的值放到ESI+4的地址。ESI是刚才构造的一个新的String对象。这样一个字符就加密好了。剩下的过程类似,在这里我就不分析了。

    总结一下加密算法:

    1.      密码分成3个一组,例如原始的密码是abcdefg,那么分组后就是 abc def g,对每组中的字符分别加密,每组之间的过程是独立的;

    2.      一组的3位密码再次进行加密后,变成4位,每位是这样计算的:

    (1): [0]>> 2

    (2): (([0]&0x03) << 4) + (([1]&0xf0) >> 4)

    (3): (([1]&0x0f) << 2) + (([2]&0xc0) >> 6)

    (4): [2]&0x3f

    计算出的索引,然后再找那个长字符串(ABCD...789+/)对应的字符。

    如果不足3位,那么是这样的:1位的密码计算(1)(2),然后补两个“=”;2位的密码计算(1)(2)(3),然后补一个“=”。如“1”加密后就是“MQ==”,“ab”加密后就是“YWI=”。

     

    2012年12月11日补充:原来这个加密算法就是所谓的Base64.

  • 相关阅读:
    列出python中可变数据类型和不可变数据类型,并简述原理
    python 字典操作
    Python的is和==
    python2和python3区别
    下面的代码在Python2中的输出是什么?解释你的答案
    编程用sort进行排序,然后从最后一个元素开始判断,去重
    如何在一个function里面设置一个全局的变量?
    用Python匹配HTML tag的时候,<.>和<.?>有什么区别?
    请写出一段Python代码实现删除一个list里面的重复元素
    什么是lambda函数?它有什么好处?
  • 原文地址:https://www.cnblogs.com/zhangbaoqiang/p/2769400.html
Copyright © 2020-2023  润新知