注册账号赠送5端口,免费24小时

注册 登录
当前位置:主页 > WhatsApp协议 > WhatsApp私信协议实现记录

WhatsApp私信协议实现记录

时间:2024-01-06 16:34 作者:admin 点击:0
whatsapp数据采集

  版权声明:本文为CSDN博主「wei_java144」的原创文章

  原文链接:https://blog.csdn.net/wei_java144/article/details/131506066

  ————————————————

  一、软硬件环境:

  WhatsApp v2.22

  IDA 7.5

  Frida 14.2.2

  Gda3.86

  JEB

  jadx-gui

  unidbg

  LineageOs 17.1 (android 10)

  小米8

  二、流水账

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

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

  http://www.caotama.com/1993224.html

  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),

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

WhatsApp私信协议实现记录

  这里还会用到其它几种密钥(自己的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模式,数据加密也会用到密钥对的生成。

  学习总结:

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

  熟悉了Curve25519应用。


  感兴趣的小伙伴,可以点击关注闪电WhatsApp云控TG客户群/点击联系客服进行咨询哦

whatsapp采集

热门软件栏目

  • WhatsApp筛号
  • WhatsApp云筛号
  • WhatsApp群发

您可能感兴趣