此次分析并不涉及到具体的算法是如何实现的,只是浅析位于模块cryptsp.dll中几个自认为比较重要的加密与解密函数在飞秋中的使用,另外以下内容都是我自身的理解,如果有错还请各位大佬指点一二,小菜鸟不胜感激!函数名如下:
BLOB:分为头BLOBHEADER和身体,后面我就称呼他为秘钥块啦,身体其实就是秘钥本体,那BLOBHEADER其实是一个结构体也可以说是这个秘钥的身份证,如下所示
CEODIDA32使用的飞秋版本2013正式版(可以直接在网上下载)
本次分析的整体思路就是先分析消息的接收方如何对消息进行解密的,通过定位消息内容在内存中的位置,进而找到对应的代码位置,再通过栈回溯找到解密函数。入手点我借鉴了鬼手56的一篇微信分析方法,使用CE对发送内容在内存的位置进行查找。虽然此处有两个位置存在消息,我通过依次对两个位置进行下内存写入断点,结果发现如果在0x119F64位置下断,由于我在接收方进行调试导致消息接收延迟,在没有调试到正确位置的时候,发送方会出现发送不成功的通知,最后接收不到消息,导致调试失败,当在第二个位置下断的时候成功。删除内存断点,在出现明文附近的函数下断,通过栈回溯,找到将密文转换为明文的CALL,方法也很简单,在每个函数下断,函数肯定会有一个字符串指针参数,观察字符串的变化。可以通过WireShark对其进行抓包来验证参数是真的直接发送过来的密文下面要进行的就是单独对这个解密函数中的各个CALL进行分析
参数一:密文字符串指针参数二:0x3A(分析发现是一个分割标志,按照“:”进行分割)参数三:out输出参数 返回值:EAX指向字符串1001,输出参数指向了密文1001后面的字符串 函数总结:按照0x3A将密文进行了分割。
函数总结:此函数是一个C语言函数,可以将字符串转换为长整型正数,将字符串1001转换为整形0x1001。
参数一:0参数二:0x3A参数三:out函数总结如下图:
参数一:0参数二:0参数三:out函数总结如下图:
参数一:待解密的BLOB的身体部分,也就是上个函数EAX指向的位置开始到字符串结尾参数二:获取解密完成的BLOB算是一个输出参数参数三:固定值0x3F4参数四:输出值,用来存放解密完成之后BLOB身体部分的字节数函数总结:通过特定的算法解密消息中的BLOB部分函数sub_768870参数一:加密的BLOB中的字节码
参数一:CSP句柄参数二:秘钥块指针参数三:秘钥块大小参数四:用于解密秘钥块中的秘钥参数五:标志位参数六:out返回导入秘钥的句柄下面我会逐个解释我所理解的参数含义,以及这些参数从何而来参数二:秘钥块的指针在执行函数0x007688C0获取BLOB身体之前执行了如下代码,其实就是在给BLOB头进行赋值操作下图就是一个完整的秘钥块,这个秘钥块其实是被加密过的秘钥块,需要参数四来进行解密,大小是0x4C让我们简单看看这个秘钥头
ALG_ID这里使用了两种算法,我的理解是RC2算法是针对通信内容的,RSA公钥交换算法是针对会话密钥而言,因为会话密钥也是被加密了的,他需要公钥去进行解密。至于为什么是公钥交换,只是我在全部弄完之后所理解的,接收方和发送方在添加好友(飞秋会自动添加局域网内的人)的时候就进行了PY交易,互相交换了公钥,所以在发送消息的时候,发送方会通过接收方当初给他的公钥对会话密钥进行加密,接收方也就可以通过相同的公钥对会话密钥进行解密。如果有大佬觉得不对的话,还希望能指点一二,感谢!参数三:秘钥块的大小函数0x007688C0的参数四在执行完成之后就是秘钥块身体的大小,只要在加上秘钥头0XC个字节就行了。参数五:标志位此时为固定值零,这里不做分析参数一:CSP句柄通过CryptAcquireContext函数获得的CSP的句柄。需要注意的是句柄在飞秋进行初始化的时候就已经获得了。先简单补充下CryptAcquireContext函数的用法继续运行,初始化完成之后向其发送一条消息,断在正在分析的位置,发现两个CSP句柄是现同的,可以证明句柄就是在初始化的时候获取的。参数四:用于解密会话密钥的密钥句柄此参数起始也是来自于初始化中,调用函数CryptGetUserKey获得
关于这个函数的返回值我还是有些疑问的,希望有了解的大佬能够指点一二,嘿嘿,因为这个函数的返回值在函数CryptImportKey和函数CryptExportKey函数中都有使用,但是却不尽相同,如下,在函数CryptImportKey中如果会话密钥的加密方式是公钥/私钥对,那这个参数就是公钥/私钥对的句柄,还举了个SIMPLEBLOB的例子,根据刚才分析BLOBHEADER的时候,我认为SIMPLEBLOB就是修饰整个密钥快的是一个会话密钥,难道还能用来修饰会话密钥使用的什么方式加密了?在函数CryptExportKey中的使用就更有意思了,比如其第二个参数要想使用这个参数,得先使用CryptGetUserKey获得句柄之后,在使用CryptExportKey按照公钥的方式将其导出,在使用CryptImportKey将这个密钥块导入到CSP并获取其句柄才能使用。最后我会附上一张表来说明。
下图实在初始化图中调用的函数CryptGetUserKey同理,初始化完成后再次向其发送一条消息,让其断在正在分析位置可以看到句柄是相同的 可以验证此参数就是在初始化的时候调用CryptGetUserKey获得的。
函数总结:对发送过来的密文的最后一段进行解密,但是仍然不是明文,需要进一步调用函数CryptDecrypt
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)