元和网络的密码加密过程

搬到南窑头以后,上网需要运行一个“元和网络”的拨号器,大是不爽;并且我的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.

原文地址:https://www.cnblogs.com/zhangbaoqiang/p/2769400.html