WhatsApp私信协议实现记录

最近看了下WhatsApp (android及pc版本),实现了协议发送私信(模板信息也可以发), 记录下学习过程,技术交流用。

一、软硬件环境:

WhatsApp v2.22

IDA 7.5

Frida 14.2.2

Gda3.86

JEB

jadx-gui

unidbg

LineageOs 17.1 (android 10)

小米8

二、流水账     

        刚开始了解了下WhatsApp,说是消息端对端加密的,信息只能双方解密,服务器都不知道的,初始看了一脸懵,在网上找了下资料:

        

      【翻译】WhatsApp 加密概述(技术白皮书)

        【翻译】WhatsApp 加密概述(技术白皮书)-汗血宝马

        WhatsAPP通讯协议端对端加密人工智能

        https://blog.csdn.net/BMW33939/article/details/120322512

        Signal 协议 

        聊天-群聊(2)_yzpyzp的博客-CSDN博客

        不过看这些资料其实有点尴尬,刚开始不知道的时候,看这些也看不懂,各种密钥概念,加密过程直接绕晕了,等开始分析app,能看懂的时候,

        发现也搞完了,回过头来看,确实上面写的(特别是白皮书)都是对的,只是初始不了解的时候理解不了,毕竟上面文章不是实操流程。

        首先看下数据流,确定下网络传输方式,结合Wireshark,通过hook及下断点等方式确定了是TCP:

 

查了下IP:157.240.199.61香港 Facebook

知道发送点后,再结合JNI函数(根据名称就可以确定重要的模块libwhatsapp.so,libcurve25519.so),逐步确定调用线。

看到上面so的名称,查了下知识点:

Curve25519 是目前最高水平的 Diffie-Hellman 函数,适用于广泛的场景,由 Daniel J. Bernstein 教授设计。

在密码学中,Curve25519 是一个椭圆曲线提供 128 位安全性,

设计用于椭圆曲线 Diffie-Hellman(ECDH)密钥协商方案。它是最快的 ECC 曲线之一,并未被任何已知专利所涵盖。

libcurve25519.so boolean org.whispersystems.curve25519.NativeCurve25519Provider.smokeCheck(int) 0x9d782548 func: 0x78b8406288 0x0  iOffset: 4288

libcurve25519.so byte[] org.whispersystems.curve25519.NativeCurve25519Provider.generatePrivateKey(byte[]) 0x9d782638 func: 0x78b8405a2c 0x0  iOffset: 3a2c

libcurve25519.so byte[] org.whispersystems.curve25519.NativeCurve25519Provider.calculateAgreement(byte[], byte[]) 0x9d7825c0 func: 0x78b8405b68 0x0  iOffset: 3b68
 

生成密钥:

retval: [object Object]

java.lang.Exception

        at org.whispersystems.curve25519.NativeCurve25519Provider.generatePublicKey(Native Method)

        at org.whispersystems.curve25519.OpportunisticCurve25519Provider.generatePublicKey(:750206)

找到了发送信息的明文("11"):

[MI 10::com.whatsapp]-> byteArray,byte src : [10,2,49,49]        protobuf格式

byteArray,md5str:

11

java.lang.Exception

        at X.1FH.A02(Native Method)

        at com.whatsapp.jobqueue.job.SendE2EMessageJob.writeObject(:271863)

        at java.lang.reflect.Method.invoke(Native Method)

顺着流程,会发现很多加密相关类的调用:

java.security.MessageDigest

javax.crypto.Mac

javax.crypto.Cipher

可以直接hook了看数据流的变化,这个时候对加密模式就有了一定了解:

客户端跟服务器有一个加密方式AES-256-GCM,每个包的加密IV都不同,如果发送的数据包是私信内容的,

那里面的私信内容是第二层的加密(aes-256-cbc),这一层的数据因为key的生成用到了对方的公钥做DH得到,

所以只能接收方才能解密,每条私信内容加密的key也是不同的。

每次打开app都会重新发起TCP连接(已经是用验证码登录的情况,后续的打开app),

这个时候要初始化一对密钥,公钥会在连接建立后的第一个发送包中包含,发给服务器。

这里还会用到其它几种密钥(自己的identity_key,标记登录会话类似抖音session token的key,服务器的公钥),这些key相互组合通过calculateAgreement

及HKDF扩展得到中间数据和密钥,包括用来加密下面的数据,

第一个发送包中包含有手机环境信息:

 

这个数据校验通过后,就是连接正常建立了,后面就可以发送私信了。

前面提到,私信内容的加密其实又是一种加密模式,这个数据是发送者和接收者交互用的,服务器也解密不了的,它只是转发加密后的数据。

私信内容加密是aes-256-cbc,这里会用到消息密钥(Message Key), 80 个字节的值,用于加密消息内容。

32 个字节用于 AES-256 密钥,32 个字节用于 HMAC-SHA256 密钥,16 个字节用于 IV。

HMAC-SHA256密钥用于计算私信内容aes加密的结果的消息认证码,只取结果的前8字节。

这个消息密钥(Message Key)的计算涉及到一个棘轮变换,每加密一条消息后,就要通过sha256的组合算法计算下一条消息加密用的Message Key了。

私信消息的发送,有个会话的概念,双方建立端对端通信需要建立一个会话,就是构造约定好密钥,建立会话后,双方就可以保存这个相关环境参数,直接按

消息序号就可以根据当前的密钥计算出要加解密的Message Key,用于加解密消息了。就算之后重新打开app,私信内容的加解密也可以继续按之前的会话继续,不用重新建立会话。

所以刚开始分析的时候,为了简单,就是在app会话建立的基础上分析的,这样只用hook拿到当前的消息的Message Key,就可以计算出指定消息序号的Message Key,直接加密信息发送就可以了。

然后就开始实现建立会话,这个过程可以参考白皮书:

会话发起人为接收人申请身份公钥(public Identity Key)、已签名的预共享公钥(public Signed Pre Key)和一个一次性预共享密钥(One-Time Pre Key)。

服务器返回所请求的公钥。一次性预共享密钥(One-Time Pre Key)仅使用一次,因此请求完成后将从服务器删除。如果一次性预共享密钥(One-Time Pre Key)被用完且尚未补充,则返回空。

发起人将接收人的身份密钥(Identity Key)存为 Irecipient,将已签名的预共享密钥(Signed Pre Key)存为 Srecipient,将一次性预共享密钥(One-Time Pre Key)存为 Orecipient。

发起者生成一个临时的 Curve25519 密钥对  Einitiator

发起者加载自己的身份密钥(Identity Key)作为 Iinitiator

发起者计算主密钥 master_secret = ECDH ( Iinitiator, Srecipient ) || ECDH ( Einitiator, Irecipient ) || ECDH ( Einitiator, Srecipient )  || ECDH ( Einitiator, Orecipient ) 。如果没有一次性预共享密钥(One-Time Pre Key),最终 ECDH 将被忽略。

发起者使用 HKDF 算法从 master_secret 创建一个根密钥(Root Key)和链密钥(Chain Keys)。

过程是这样,但是这个要实际跟一遍,才能比较了解,涉及到的算法本身不复杂,主要是这些key的变换流程。

对建立会话,初始还要创建2组密钥对(OurBaseKey和ourRatchetKey):

 

按流程实现后测试,发送信息后对方能收到,但是看不到内容:

 

查了下,网上有说是发送方换设备了就可能这样,那我这个肯定不属于这个情况。

当时一直没搞定,开始认为是加密算法还原有问题,反复核对了几遍,按照hook的数据及参数加密结果跟实际的完全一致。

反复的核对这种数据,搞得没脾气了,后来就静下心来想了下,要实现这个端对端加密应该怎么做,最后发现一个不确定的点,就是oneTimePreKey,

对方没法知道用的是哪个。

这个请求的时候,服务器直接返回了一个对方的一次性预共享密钥,这个接收方是提供了一批存到服务器的,用一个就删除一个,如果会话最后没建立成功,服务器应该

也不会同步给接收方的,并且实际发送建立会话的数据中也没看到有回发这个过去,那接收方要能正确解密,肯定要能知道当前会话用的是哪个一次性预共享密钥,

后来想到应该是有个ID来标识的,当时也确实发现发送建立会话的数据中有几个字段值不确定,但是从返回的接收方key数据和发送的私信数据中没找到这个共同的值

(后来发现其实是数据格式不同)。

一直没解决,就新开了条战线,分析PC版本,希望能找出这个差异点,解决这个问题。

PC上的WhatsApp 7个进程,也是首先找收发数据点,最后确定下面的进程是处理网络数据的,根据命令行参数也可以看出来:

 跟踪发现PC上发送是用的websocket,直接下载ssl源码,参考标出函数:

找到了数据收发点:

 

跟踪的时候发现,接收的数据没有进一步的逻辑处理,有时候就请求清空内存了,就猜测可能是发到其它进程去了,看到有MojoMessages相关的。

查了下:

Mojom是chromium最新的跨平台进程通信框架

关注点不在这,没细看。

最后发现,这个进程只是处理网络层的,具体的私信逻辑在另一个进程:

 

 

 后面就是顺着调用跟踪加密流程了,弄清楚后,可以直接上frida hook PC数据(注入dll也行):

最后核对数据,确定了是有个ID来标记共享密钥:

 

完善这个后,再测试发送,对方就能看到内容了:

模板信息也是可以的: 

 

 

顺便提一下,第一次拿码登录流程是走的http模式,数据加密也会用到密钥对的生成。

学习总结:

  1. 学习了端对端通信实现,对这种加密模式有了一定了解。

  2. 熟悉了Curve25519应用。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1. 在前端增加右下角的询盘表单弹窗 可以使用以下步骤实现: 1. 在WordPress的主题文件中打开 functions.php 文件,添加以下代码: ``` function add_popup_form() { ?> <div id="popup-form-container"> <form id="popup-form" method="post" action=""> <label for="name">Name:</label> <input type="text" id="name" name="name" required> <label for="email">Email:</label> <input type="email" id="email" name="email" required> <label for="message">Message:</label> <textarea id="message" name="message"></textarea> <input type="submit" value="Submit"> </form> </div> <?php } add_action('wp_footer', 'add_popup_form'); ``` 2. 将上面代码中的表单内容进行自定义修改,比如修改表单项的名称,添加更多的表单项等。 3. 在 WordPress 的主题样式文件(style.css)中添加以下 CSS 代码,以控制弹窗的显示和样式: ``` #popup-form-container { position: fixed; bottom: 0; right: 0; width: 300px; height: 400px; background-color: #fff; border: 1px solid #ccc; z-index: 9999; display: none; } #popup-form-container form { padding: 20px; } #popup-form-container label { display: block; margin-bottom: 10px; } #popup-form-container input, #popup-form-container textarea { display: block; width: 100%; margin-bottom: 20px; } #popup-form-container input[type="submit"] { background-color: #333; color: #fff; border: none; padding: 10px 20px; cursor: pointer; } ``` 4. 最后,使用 JavaScript 代码监听点击事件,显示和隐藏弹窗: ``` jQuery(document).ready(function($) { $('#popup-form-container').fadeIn(); }); $('#close-button').click(function(e) { e.preventDefault(); $('#popup-form-container').fadeOut(); }); ``` 2. 在前端的右侧增加在线悬浮窗口 可以使用以下步骤实现: 1. 在 WordPress 的主题文件中打开 functions.php 文件,添加以下代码: ``` function add_whatsapp_button() { ?> <div id="whatsapp-button"> <a href="https://wa.me/8518588629881" target="_blank"><img src="path/to/whatsapp-icon.png" alt="WhatsApp"></a> </div> <?php } add_action('wp_footer', 'add_whatsapp_button'); ``` 2. 修改代码中的 WhatsApp 号码,以及 WhatsApp 图标的路径。 3. 在 WordPress 的主题样式文件(style.css)中添加以下 CSS 代码,以控制按钮的显示和样式: ``` #whatsapp-button { position: fixed; right: 20px; bottom: 20px; z-index: 9999; } #whatsapp-button img { width: 50px; height: 50px; } ``` 3. 实现后台的排版 可以使用以下步

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值