这篇文章上次修改于 285 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

前言

本文将带领读者学习如何使用stackplz快速定位网络请求位置

正文

对某软件抓包,得见一些wns开头的请求,显然是有自定义加密在里面的

如何用stackplz快速定位请求呢?答案很简单,直接追踪网络请求有关的syscall即可~

stackplz中对许多有关联性的syscall做了划分,可以通过-s %net这样快速追踪一些列有关网络的syscall

注意%net不包含发送接收数据,如果需要追踪请-s %net,%send,%recv这样写,不过注意发送接收数据通常调用很频繁,建议加上-q

额外补充:read和write系列中的函数也是能通过网络请求的fd读取和写入数据的,所以有时候也有必要追踪它们,那么关闭网络连接用close自然也是合理的

不过这里没有把close这个放进任何一个组,这是因为不管什么情况下,close调用都很频繁,对于需要输出堆栈的情况下,会很慢

  • %net

    • socket,socketpair
    • bind,listen,accept,accept4,connect
    • getsockname,getpeername,setsockopt,getsockopt
    • shutdown
  • %send

    • sendto,sendmsg,sendmmsg
  • %recv

    • recvfrom,recvmsg,recvmmsg
  • %read

    • read,readv,pread64,preadv,pread2
  • %write

    • write,writev,pwrite64,pwritev,pwrite2

于是通过下面的命令来追踪有关网络请求的系统调用,并打印调用栈:

./stackplz -n com.tencent.qqmusic -s %net -o tmp.log --stack

然后在日志翻到这么一条日志:

[18994|19205|cgi-ui-1] socket(domain=2, type=0x80801(SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK), protocol=0) LR:0x7a7a866bdc PC:0x7a8594f038 SP:0x76ea4a0ed0, Backtrace:
  #00 pc 000000000009e038  /apex/com.android.runtime/lib64/bionic/libc.so (__socket+8)
  #01 pc 0000000000004bd8  /system/lib64/libnetd_client.so ((anonymous namespace)::netdClientSocket(int, int, int) (.cfi)+100)
  #02 pc 000000000004f908  /apex/com.android.runtime/lib64/bionic/libc.so (socket+44)
  #03 pc 00000000001ccaa8  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so
  #04 pc 00000000001d658c  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so (uv_tcp_init_ex+136)
  #05 pc 000000000010d0c8  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #06 pc 00000000000e1710  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #07 pc 00000000000df320  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #08 pc 00000000000df100  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #09 pc 00000000000bd860  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #10 pc 00000000001c92b0  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #11 pc 000000000010c480  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #12 pc 00000000001cc404  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so
  #13 pc 00000000001dad28  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so

[18994|19205|cgi-ui-1] socket(domain=2, type=0x80801(SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK), protocol=0, ret=219)

我们知道发起请求肯定得去connect,然后顺着这个syscall的返回值219往下翻到这样一条日志:

端口和截图的不一样?多测试就会发现它会在网络不好的时候换好几个端口尝试

[18994|19205|cgi-ui-1] connect(sockfd=219, addr=0x76ea4a0fe8(family=AF_INET, port=20480, addr=14.116.235.38), addrlen=16) LR:0x7a7a8668e8 PC:0x7a8594f098 SP:0x76ea4a0ef0, Backtrace:
  #00 pc 000000000009e098  /apex/com.android.runtime/lib64/bionic/libc.so (__connect+8)
  #01 pc 00000000000048e4  /system/lib64/libnetd_client.so ((anonymous namespace)::netdClientConnect(int, sockaddr const*, unsigned int) (.cfi)+280)
  #02 pc 00000000001d6818  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so
  #03 pc 000000000010d274  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #04 pc 00000000000e1710  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #05 pc 00000000000df320  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #06 pc 00000000000df100  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #07 pc 00000000000bd860  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #08 pc 00000000001c92b0  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #09 pc 000000000010c480  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #10 pc 00000000001cc404  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so
  #11 pc 00000000001dad28  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so

[18994|19205|cgi-ui-1] connect(sockfd=219, addr=0x76ea4a0fe8, addrlen=16, ret=-115)

再往后就没有什么发现了,要看发包情况,就要考虑追踪%send%write系列函数了,命令如下:

先确定是哪个系统调用,这里直接grep后重定向日志看看

./stackplz -n com.tencent.qqmusic -s %send,%write | grep wns > tmp.log

结果关键词很多,不过很明显能看出来是write系统调用在写数据到网络请求fd

这个时候再去追踪write调用,看看堆栈

虽然syscall也提供了直接的过滤功能,但是本身已经通过配置文件预设好了参数类型,比如write的第二个参数是buffer,无法被当作字符串过滤

如果想直接通过syscall去过滤,那么得先通过配置文件修改参数类型,这样还是很麻烦的

stackplz提供了一种uprobe到syscall命令行的转换,即在常规的uprobe写法后面加一个s即可转为对应的syscall进行hook

于是通过下面的命令即可将write的buf参数当作字符串读,过滤wns开头的字符串(后续为buf类型扩展几个字节的比较,应该更灵活

./stackplz -n com.tencent.qqmusic -w write[int,str.f0]s -f w:wns --stack

然后就能得到这样的调用栈,不会有其他额外的write系统调用刷屏

[12746|12965|cgi-ui-1] write(arg_0=180, arg_1=0xb4000078ab080e10(wns)) LR:0x771b9dae9c PC:0x7a8594e358 SP:0x771a6f21a0, Backtrace:
  #00 pc 000000000009d358  /apex/com.android.runtime/lib64/bionic/libc.so (write+8)
  #01 pc 00000000001d5e98  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so
  #02 pc 00000000001d5dc8  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so (uv_write2+384)
  #03 pc 00000000000c4a84  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #04 pc 00000000001c9abc  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #05 pc 000000000010c480  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #06 pc 00000000001cc404  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so
  #07 pc 00000000001dad28  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so

不过顺着libWnsLiteNetwork.so + 0xc4a84这个位置翻了下,看不出什么名堂,只知道这里在发送,不过本文主题是数据包的加解密,所以还得想想怎么找到加解密的位置

这个so中函数看起来都很正规,调用比较实诚,那么猜测发送的数据应该会经历复制操作,不然顺着发送的调用栈怎么也能看出一点端倪

于是尝试hook一把memcpy/memmove函数,命令如下,这里还是将数据内容当作字符串读取

./stackplz -n com.tencent.qqmusic -w memmove[ptr,str.f0,int] -f w:wns --stack

然后就能得到下面这调用栈了,稍作回溯就能发现加密操作了

[22227|22445|Thread-8] memmove(arg_0=0xb4000078ab111cd0, arg_1=0xb4000078ab111e90(wns), arg_2=428) LR:0x7711d31250 PC:0x7a858fcc20 SP:0x76ece16d20, Backtrace:
  #00 pc 000000000004bc20  /apex/com.android.runtime/lib64/bionic/libc.so (memmove)
  #01 pc 000000000010a24c  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #02 pc 0000000000082b90  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #03 pc 0000000000198174  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #04 pc 00000000001b0178  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #05 pc 00000000001af998  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #06 pc 00000000001cd15c  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #07 pc 00000000001a9364  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #08 pc 00000000001a4fb4  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #09 pc 00000000001a4b1c  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #10 pc 00000000001b7458  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #11 pc 00000000001b7a44  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #12 pc 00000000000edb98  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #13 pc 00000000000ed264  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #14 pc 000000000010c480  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libWnsLiteNetwork.so
  #15 pc 00000000001cc404  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so
  #16 pc 00000000001dad28  /data/app/~~5ZUCrylK9S2pGeFpzR2OjQ==/com.tencent.qqmusic-_c0E3UlPZokNS6pya4N3Vg==/lib/arm64/libnetbase.so

libWnsLiteNetwork.so + 0x82b90这里往前翻一翻,就会发现zstd compresswns_pack,显然已经八九不离十了

在这个函数进行分析,便能发现还涉及一个关键函数0x71034

函数中的一个数字引起了注意,搜一下就会发现这是TEA加密算法,或者是改动过的,这类算法腾讯的软件经常用,很是合理

交叉引用一下0x71034,发现有一个Java的native函数,一看就算成对的,这下解密函数的位置也找到了(当然需要hook确定一下

算法还原略

总结

  1. stackplz追踪connect系统调用,配合抓包,对照ip可以快速确定到网络请求发起位置
  2. stackplz追踪%send%write系列系统调用,配合grep过滤可以快速确定具体是什么系统调用在操作网络数据
  3. 通过数据会被复制的思路,追踪libc.so的memmove/memcpy函数,配合过滤,快速定位到数据加密位置
  4. 在上一步基础上,交叉引用定位到数据解密位置(推测)