5. 第三轮尝试
本轮的主要目的是在主程序中查找channel.serialData的位置. 根据我对cef的理解,应该是在程序启动时注册了一部分回调函数. 触发channel.serialData操作时,请遵循回调函数和代码.
根据DLL版本,我找到了相应的cef源代码版本. cef是注册回调时的整个结构. 必须找到相应的版本,以避免由于新版本的结构不同而导致的偏移位置不同.
在查看源代码的过程中,我在结构中发现了一个非常有趣的字段,即调试端口,我对其进行了调查,该端口非常有用,您可以远程使用DevTools,因此使用什么警报.
如果要在调用初始化(API Hook修改或静态文件修改)之前更改结构,如果文件被修改,则只能丢弃一些无用的代码来更改结构,我选择了一个不会影响的赋值语句,请为该地址分配9222.
根据源代码中的结构计算偏移值
修改cloudmusic.dll代码的原始方法增加了代码段修改方法
//将调试端口修改为9222 void SetDebugPort(BYTE * pbFileData){if(!memcmp(pbFileData + 0x14EED,bSettingAsm,sizeof(bSettingAsm))){bSettingAsm [2] = 0x94; //结构偏移bSettingAsm [6] = 0x06; // 0x2406是9222端口bSettingAsm [7] = 0x24; memcpy(pbFileData + 0x14EED,bSettingAsm,sizeof(bSettingAsm));}}
现在,我可以通过以下方式远程访问DevTools: 9222. 但是当我打开网页时,它是空白的. 目前,根据我对cef的粗略了解,程序目录中没有与devtools相关的资源. 实际上,只要资源文件被填满(官方网站上没有这样的旧资源)文件文件,这就是我在网上找到的devtools资源文件的3.1916版本)
这时,所有JS调试命令都可以更改为console.log,这更加方便.
回到主题,很难遵循注册后的代码. 一个是有许多注册的内容,一个在另一个之上,并且该程序使用C ++ warp的C语言库,与源代码略有不同. 这时候我想到了一个很好的方法,那就是创建一个无限循环.
6. 第四轮尝试

如上所述,我放弃了从注册逐步跟踪回调函数的麻烦解决方案,而是知道了JS中的死循环并一直调用channel.serialData函数. 当程序的单核被完全加载时,我只需要将调试器附加到程序上并暂停一下,基本上就是与该功能相关的业务流程的代码(JS到机器码的代码应该在堆上据我了解,加密的代码应该在程序代码段中,所以我要定位当您可以忽略很多JS代码时,找到真正相关的代码位置)
实际上,channel.serialData具有很多汇编代码,并且该过程分为许多部分. 这部分工作量无法减少,但是其中许多可能是为了防止对代码进行静态分析,并且某些功能字符串是运行时. 生成的,但是由于功能部件的这一部分是固定的,因此无需仔细考虑(但是,我花了一两天的时间看一堆汇编代码来计算功能部件,这很沮丧,并且我知道,如果我知道的话. 那很好,但是说实话,很难向后推,主要是因为您必须了解它.
转换过程的简要说明
1. 输入网址(请求部分)和数据(提交的json数据)
2客户端文件加密软件,拼写为“ nobody” + url +“ use” + data +“ md5forencrypt”字符串
3. 在字符串上计算MD5
4. 次要缝合网址+“ -36cd479b6b5-” +数据+“ -36cd479b6b5-” + md5
5,0×10对齐,缺少的部分将用缺少的数字填充
6. 私有转换方法(也许是我不知道的加密方法?)
附上分析图的一部分
要加密的数据,对齐0×10字节,每次处理0×10字节的数据
辅助加密数据(动态生成,但是固定的,我仍然很愚蠢地复制了生成过程)
开始转换0×10
一堆XOR和位移计算,在C代码中仍然很不错. 这会更长,并且不会全部粘贴.
Snprintf在循环转换后根据“ X”格式转换为字符串. 我对这种加密算法的原理不太了解,只是从字面上翻译了编码.
后来,我试图进行反向分析. 我一整个上午都没有看到它,并简要描述了为什么它很难逆转.
内存块内存

a1b1b1c1a1b1b1c1 a2b2b2c2a2b2b2c2………
eax = a1a2a3a4
ebx = b1b2b3b4
ecx = c1c2c3
edx = d1d2d3d4
eax = mem [a4 * 8] ^ mem [b3 * 8 + 3] ^ mem [c2 * 8 + 2] ^ mem [d1 * 8 + 1]
ebx = mem [a3 * 8] ^ mem [b2 * 8 + 3] ^ mem [c1 * 8 + 2] ^ mem [d4 * 8 + 1]
ecx = mem [a2 * 8] ^ mem [b1 * 8 + 3] ^ mem [ * 8 + 2] ^ mem [d3 * 8 + 1]
edx = mem [a1 * 8] ^ mem [b4 * 8 + 3] ^ mem [c3 * 8 + 2] ^ mem [d2 * 8 + 1]
了解到以下eax,ebx,ecx和edx使原始文档退后了,这似乎不太可能,但是mem不是随机的内存块,并且在数组索引期间进行了一些巧妙的补偿. 实际上,对于存储块有很多规则(例如,当a1是偶数时,b1是a1的一半,c1是a1 ^ b1),并且与索引的偏移量可以互补. 如果您发现该技巧可能是可以解决的,感兴趣的朋友也可以进行研究(Tan1993: 我还没有学过密码学,只有几个概念)
7. 摘要
实际上,在此步骤中,在通过远程devtools将其发送之前,我可以看到未加密的内容和结构. 同时,我还可以通过加密的加密方法加密并发送不同的业务数据. 我发现请求数据返回的一部分内容也已加密,但这可以控制客户端上e_r的值来控制是否需要返回加密的内容.
编写一个小示例,以模拟客户端下载歌曲. 可以通过服务器验证来下载最初发送和接收加密数据的下载接口. 尽管此过程中仍有很多内容,但分析现已结束. 如果有机会,研究会在未来继续进行.
8. 摘要
由于我没有找到任何参考资料,所以我也间歇性地学习了一个星期. 除了实现目标之外,还有很多收获,例如更有趣的加密算法,数字签名方法,cef库和一些相反的想法.
令人遗憾的是,尚未解析出解密算法. 同时,显然不是在客户端上控制返回数据的值以控制返回数据是否被加密的好方法. 官方只需要忽略此参数即可强制将加密数据返回到某些API. 客户端没有任何影响(它是否具有平台依赖性,因此我将此参数放在客户端上?).
(Tan1993: 考虑是否在github上提供源代码)
9. 复活节彩蛋
有趣的是,当我尝试在外部IP服务器上调用Web api接口时,发现它不适用,并且我无法获取数据. 然后我发现其逻辑与另一个JS代码不同. 我发现了一个有趣的功能字符串(您可能看不到,总是有调皮的程序员):
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/bofangqi/article-226512-2.html
万年牢不是合理经济的设计