
1. 前言
Internet上有很多用于Web界面分析的方法,但是基本上没有信息可以在客户端界面分析中找到. 本文主要分析网易云音乐PC客户端的API接互方法.
通过内部代理设置,使用fiddler作为代理工具,您可以查看交互过程:
您可以查看交互模式. 通过HTTPS POST交互,POST一系列参数内容,对该内容进行加密,并返回JSON内容. 我想做的关键是解析参数的生成方法以模拟这种交互.
(Tan1993: 这是后续内容,许多屏幕快照是补充的,因此看来不同的调试工具,不同的环境,不同的时间等都不会影响阅读. 此外,我的工作主要是linux像这样的网络方向只是我的一点爱好,很少扭转. 如果还有更多业余操作或想法,我希望指出)
2. 初步了解
下载NetEase Cloud安装的PC版本的最新版本(当前版本为2.3.0.196231),并分析程序所在目录中的文件.
动态链接库和可执行文件:
最吸引我注意的第一件事是libcurl. 该网络库可用于HTTP协议交互. 如果通过此库与服务器交互,请将断点设置为curl_easy_perform并将其推回以确定转换算法的位置,但是事实比我想象的要复杂得多,该库仅用于程序执行时的一些无关的网络交互正在运行(Tan1993: 我不记得了,它似乎是与版本或客户端信息相关的请求).
第二个是libcef,这是一个基于C / C ++的Web浏览器控件,可以简单地理解为浏览器外壳(Tan: 为什么关键API不使用libcurl库,因为除了初始的cef框架在初始化之前,网络交互仅使用该库,一个小的cef环境出现了,所有的交互都是通过JS ajax进行的.
除了cef依赖的dll之外,两个主要程序和cloudmusic.dll都值得关注.
资源文件:
除了该软件包下的软件包之外,还有依赖于cef库的资源文件.
所有均为未知格式. 我通常会看到格式未知的文件. 我将尝试用7z打开它们,看看它们是否是某种存档格式的文件. zip格式不了解这一点.
除了可以通过后缀看到的一些外观文件外,还有两个可疑文件. 翻转较大的orpheus.ntpk文件. 您可以看到它们都是与Web相关的资源文件. 该core.js让我想起了Web版本的API提取中使用的core.js文件. 我打算更换它,然后动态分析转换过程. 事实并不令人满意. 压缩文件已加密.
好的,研究阶段已经结束. 在进行反向分析之前,您可以在这里停止.
3. 第一轮尝试

实际上,我最初将注意力集中在libcurl上. 当断点达到curl库的功能时,我发现在程序刚运行时它仅被触发了几次. 以后所有的网络交互都没有使用该库. 头孢cef的重点是内部JS文件,这是提取此文件的关键.
0×2712是CURLOPT_URL宏,并且url的字符串指针存储在eax中,而eax基本上是无关的url.
第一个任务来了,正在反向查找功能字符串,即密码. 此处,断点位于系统文件操作API上,并已分解为CreateFileW. 吃完F9餐后,您可以看到它已加载到default.skin文件中(图是native.ntpk,是相同类型的加密ZIP文件),然后将逐步对其进行调试.
然后您会看到一个特殊的内存块. 当您查看PNG格式的文件头时,可以判断此步骤已将资源解压缩到内存中.
按几个步骤,设置断点,缩小范围,然后继续,查看在何处进行压力操作,然后逐步执行该功能. (Tan1993: 可能更业余,但是我只能稍微缩小范围,稍微看一下过程. 我可以根据经验判断我会做什么,并将其缩短到相对较短的范围,否则会比较麻烦. 汇编代码实在是难以忍受,谢谢世界上所有的程序员都接近它.
知道密码后,您可以提取core.js文件(Tan1993: 此处仅提供想法,不提供便捷服务)
又是一堆令人窒息的混乱. 嘉怀疑自己的生活. 首先解压缩,然后观看.
解压缩后,搜索一些关键字,例如params,eapi,batch和HTTP交互的其他主要功能
关键代码,例如令人困惑的JS代码,如果未通过调试器进行跟踪,将很难理解. 目前可以看出,只有channel.serialData应该是最关键的转换函数,但是在搜索了整个JS文件之后,我发现直到了解函数定义之前,我都不知道它是否是一个令人困惑的地方
尽管cef随DevTools一起提供,但它已被阻止并且无法在程序中调用,因此我想向JS文件添加警报调试关键参数. 然后,我修改了core.js文件,并根据原始密码将其压缩回去. 但是该程序根本不起作用. 为什么?查看原始的.ntpk文件,很明显,有一些奇怪的东西与zip文件结合在一起以合成该ntpk文件格式. 从经验来看,这很可能类似于数字签名(Tan1993: 之前,我还将在某些文件的末尾添加盐到某些文件中,这些文件可能会被篡改以生成用于验证的哈希值,但随后遵循网易云的数字签名. 这是我学到了很多东西.
4. 第二轮尝试
为了方便调试,我需要替换资源文件中的core.js文件,但是资源文件不仅经过加密和压缩,而且还存在其他一些内容,因此这次用的代码是为了了解除了zip文件本身的其他部分的作用.
它仍然对CreateFileW函数无效. 实际上,当我在第一轮中遵循代码时,我已经找到了一些调用系统加密服务提供程序(CSP)库的函数.
一步一步,我发现使用了SHA1数字签名算法(Tan1993: 我对CSP库了解不多,但这是为Windows系列操作系统开发的底层加密接口. 与我了解的SHA相同(程序中的部分称为公钥,使用文件头中的验证数据进行验证).
NTPK文件头,文件长度为0x0D5C5B,检查字符串长度为0×100
仅短0×110. 除了用于验证0×100的数据外,还有一个0×10标头.
因为我无法在不知道私钥的情况下再次签名文件,所以我只能替换程序中用于验证的公钥并生成相应的测试数据,以通过系统验证,或直接修改验证部分的代码跳转逻辑(Tan1993: 实际上,更改分支流程的修改可能更容易,但是我最初选择替换公钥来重新生成验证数据).

int GenKey(HCRYPTPROV hProv){HCRYPTKEY hKey;处理hFile = NULL,hOutFile = NULL; DWORD dwSize = 0,dwRead = 0,dwWrite = 0,dwBlobLen = sizeof(bRsaKey); BYTE * pbFileData = NULL; int ret = -1; //首先读取原始dll并将其加载到内存中hFile = CreateFileW(L“ cloudmusic_src.dll”,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); dwSize = GetFileSize(hFile,NULL); pbFileData =新的BYTE [dwSize]; ReadFile(hFile,pbFileData,dwSize和dwRead,NULL); CloseHandle(hFile); if(!memcmp(pbFileData + 0x7C3438,bRsaKey,sizeof(bRsaKey))){//为CryptGenKey重新生成密钥(hProv,AT_SIGNATURE,CRYPT_EXPORTABLE和hKey); memset(bRsaKey,0,sizeof(bRsaKey)); CryptExportKey(hKey,NULL,PUBLICKEYBLOB,0,bRsaKey和dwBlobLen); //覆盖由dll生成的原始公共密钥memcpy中的公共密钥(pbFileData + 0x7C3438,bRsaKey,sizeof(bRsaKey)); //打开调试端口(稍后说明)SetDebugPort(pbFileData); hOutFile = CreateFileW(L“ cloudmusic.
dll“,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); //写回到DLL WriteFile(hOutFile,pbFileData,dwSize和dwWrite,NULL); CloseHandle(hOutFile); ret = 0;删除[] pbFileData; CryptDestroyKey(hKey);返回ret;} int EncFile(HCRYPTPROV hProv,LPCWCHAR wstrInFile,LPCWCHAR wstrOutFile){HCRYPTHASH hHash; DWORD dwSize = 0,dwRead = 0,dwWrite = 0,dwOutSign;; BYTE * pbFileData = NULL,* pbSignData = NULL; //使用密码hFile = CreateFileW打开压缩文件(wstrInFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); dwSize = GetFileSize(hFile,NULL)); pb BYTE [dwSize]; ReadFile(hFile,pbFileData,dwSize和dwRead,NULL); CloseHandle(hFile); //打开输出文件hOutFile = CreateFileW(wstrOutFile,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYTE,FILE_ATTRIBU); //写入文件头WriteFile(hOutFile,bHead,sizeof(bHead)和dwWrite,空值); //写入原始压缩文件的长度为WriteFile(hOutFile客户端文件加密软件,&dwSize,sizeo f(int),&dwWrite,NULL); //创建并计算哈希值CryptCreateHash(hProv,CALG_SHA,0、0和hHash); CryptHashData(hHash,pbFileData,dwSize,0); CryptSignHash(hHash,AT_SIGNATURE,NULL,0,NULL和dwOutSignSize); pbSignData =新的BYTE [dwOutSignSize]; CryptSignHash(hHash,AT_SIGNATURE,NULL,0,pbSignData和dwOutSignSize); //写入哈希值大小WriteFile(hOutFile&dwOutSignSize,sizeof(int)&dwWrite,); //写入哈希值(检查数据)WriteFile(hOutFile,pbSignData,dwOutSignSize和dwWrite,NULL); //写入原始压缩文件WriteFile(hOutFile,pbFileData,dwSize和dwWrite,NULL); CloseHandle(hOutFile);删除[] pbSignData;删除[] pbFileData; CryptDestroyHash(hHash);返回0;}
剪切一部分代码以修改cloudmusic.dll中的二进制数据. 偏移量是根据内存加载地址和基址来计算的,可以直接固定偏移量.
在这一点上,尽管警报项目符号框不那么容易使用,我实际上可以替换core.js文件并发出警报以弹出一个对话框以显示一些JS运行时数据.
通过警报,我可以看到加密之前的内容,即发送了什么数据以及加密之后的样子. 不幸的是,当我尝试警报(channel.serialData)时,我发现它是[native code],根据我个人的理解,应该显示它的系统二进制函数(对JS不太熟悉),怀疑是库函数,但是查询没有效果,后来我考虑了JS是否调用了C ++代码(通过对cef的粗略了解),我尝试进行检查,并且还可以,那么很有可能加密转换的这部分代码仍在主程序中,这非常令人头疼,只是从主程序逆转到了JS的自由世界中,我们必须回到汇编程序编码的环境.
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/bofangqi/article-226512-1.html
连1900也没有
因为下—次来的胡朋狗友二流子比这一次多的多
滚动复利