QQ"坦白说"简单分析

2018-04-04

本文首发于,作者initialize_Zero,转载请注明作者以及本文链接


近期腾讯在手Q上线了新功能--坦白说,可以匿名对好友打上标签,许多人玩的不亦乐乎,也引发了各个平台上的大讨论,趁着假期写篇文章

坦白说

既然是刚刚上线的新功能,必然有不完善的地方,大神们各显神通,只为了找到坦白说背后的发送者

经过整理大致有以下几种方法:

1.将对方所发图片收藏,然后进入我的收藏,即可看到

2.iOS版手Q可以通过搜索消息记录的方式搜索到发送方

3.API数据接口 https://ti.qq.com/cgi-node/honest-say/receive/mine ,在手Q中打开此链接,获取并解码相关json数据

截止4月3日,第1,2种方法已失效,下面我们重点分析第三种方法.

4月1日之前,对API接口请求可得到如下所示的json数据:

{
  "code":0,
  "data":{
    "list":[
      {
        "fromNick":"一个认识y年的男生",
        "fromEncodeUin":"*S1*oKoezon5",
        "fromFaceUrl":"man.png",
        "fromGender":0,
        "toUin":1088668866,
        "toNick":"",
        "topicId":666,
        "topicName":"别人对你的匿名评价",
        "timestamp":1534435200
      },
      {
        "fromNick":"一个x岁的女生",
        "fromEncodeUin":"*S1*oKoezon5",
        "fromFaceUrl":"woman.png",
        "fromGender":1,
        "toUin":1088668866,
        "toNick":"",
        "topicId":666,
        "topicName":"别人对你的匿名评价",
        "timestamp":1534435200
      }
    ]
  }
}

不过tx也很快地封杀了第三种方法,4月1日后直接对API请求会返回如下结果:

{
    "code":2333333,
    "msg":"皮这一下你很开心么ヽ( ̄▽ ̄)ノ"
}

皮一下可以,皮几万就不行了,这更加激发了我的好奇心,身为程序猿的我,怎能因为这样的一点小困难止步不前


下面祭出神器

Packet Capture (又名无root抓包)

Packet Capture

Packet Capture
Packet capture/Network traffic sniffer app with SSL decryption.
Not that feature rich yet, but it's a powerful debugging tool especially when developing an app.

Features:

  • Capture network packets and record them.
  • SSL decryption using man-in-the-middle technique.
  • No root required.
  • Easy to use.
  • Show packet in either hex or text.

简单来说此软件可以抓取并记录各种APP的网络请求数据包,安装自带的证书后还可以解密https内容.
下面我们介绍使用方法

1.安装

酷安APP中搜索并下载,安装完成后打开

  • 点击GetStarted
  • Continue
  • 这里一定要点击 Install Certificate , 安装配套证书,不然在稍后操作中无法看到https内容
  • 会弹出Android 系统证书添加界面,点击确定
  • 这样就进入到主界面,左上角两个绿色按钮分别是抓取单一APP网络请求与抓取全部请求

这样就完成了抓取前的准备工作

2.抓取请求

抓取前尽可能关掉除QQ与Packet Capture以外的所有软件,提高成功率,还可以避免抓到其他无关数据

下面是重点,请严格按照如图所示操作

1.确保QQ列表中有"坦白说"这一会话

2.切换到Packet Capture,点击右上角的绿色按钮,开始抓取
第一次使用会提示,点击"确定"即可

3.绿色按钮变为红色表示正在抓取中,同时出现提示信息

4.切换回QQ,点击坦白说会话进入

5.继续点击进入,如果有下图提示,一定点击继续访问,原因是我们抓取数据时用了Packet Capture自带证书替换了原证书.

6.右上角点击进入"收到的坦白说"

7.进入此列表,等待一会,一定保证列表加载完全

8.切换到Packet Capture,点击右上角的红色停止按钮,结束本次抓包,这样我们就有了一次记录,点击进入

9.重点寻找QQ发出/接收的数据包,包含坦白说列表数据的数据包大小在10KB以上,且为SSL类型
(抓到的数据包大约在30~50个左右,耐心寻找)

10.点击进入此记录,如果找到了具有如下类型请求的数据包,恭喜你,离成功又近了一步!
(GET /honest-say/main.html , Host: ti.qq.com)

11.这个数据包很大,使劲往下翻,如果类似于发现下图中的数据,就是我们所需分析的重点内容
(可以长按文本进行选择,复制等操作)


接下来重点分析这些数据

{
  "code":0,
  "data":{
    "list":[
      {
        "fromNick":"一个认识y年的男生",
        "fromEncodeUin":"*S1*oKoezon5",
        "fromFaceUrl":"man.png",
        "fromGender":0,
        "toUin":1088668866,
        "toNick":"",
        "topicId":666,
        "topicName":"别人对你的匿名评价",
        "timestamp":1534435200
      },
      {
        "fromNick":"一个x岁的女生",
        "fromEncodeUin":"*S1*oKoezon5",
        "fromFaceUrl":"woman.png",
        "fromGender":1,
        "toUin":1088668866,
        "toNick":"",
        "topicId":666,
        "topicName":"别人对你的匿名评价",
        "timestamp":1534435200
      }
    ]
  }
}
参数名 含义 备注
fromNick 对方所显示的匿名昵称 e.g."一个认识3年的男生"
"一个南京的女生"
fromEncodeUin 编码后的对方QQ 重点,从这里入手
fromFaceUrl 对方头像图片文件
fromGender 对方性别 0为男,1为女
toUin 你的QQ号
toNick 你的昵称 似乎都是空
topicId 话题ID 话题索引
topicName 话题名称 e.g. "我身边的最强大脑"
"将来能干一番大事"
"拥有迷人的长发"
也是对方给你发送的第一条消息
timestamp 时间戳 1970年1月1日(UTC/GMT的午夜)开始所经过的秒数

各参数说明如下:

参数名 含义 备注
fromNick 对方所显示的匿名昵称 e.g."一个认识3年的男生"
"一个南京的女生"
fromEncodeUin 编码后的对方QQ 重点,从这里入手
fromFaceUrl 对方头像图片文件
fromGender 对方性别 0为男,1为女
toUin 你的QQ号
toNick 你的昵称 似乎都是空
topicId 话题ID 话题索引
topicName 话题名称 e.g. "我身边的最强大脑"
"将来能干一番大事"
"拥有迷人的长发"
也是对方给你发送的第一条消息
timestamp 时间戳 1970年1月1日(UTC/GMT的午夜)开始所经过的秒数

接下来就是如何解码fromEncodeUin的问题,我们以*S1*oKoezon5为例,写出具体解码过程:

1.去除*S1*这四个字符

结果变为 oKoezon5

2.对照表格解码

下图是之前某大神总结出的部分规律(有点像AES加密中的S盒,不过对应关系简单了许多)

经过测试,发现部分对应关系存在问题(比如3同时对应了oi,o与i,很明显这里有问题,个人猜测原因是分析样本数据不足),重新测试十几条数据后,增添以下六组对应关系


为了解码方便,列出如下规则:

  • 规则1: 优先使用2字符对应1数字的关系,其次使用1字符对应1数字的关系
    e.g. oion6 解码为 301,而不是33301
    即'oi'对应3,'on'对应0,'6'对应1的方式
    不应按照'o'对应3,'i'对应3,'o'对应3,'n'对应0,'6'对应1的方式

  • 规则2: 遇到某一位无法解码,跳过该位从下一位进行解码

oKoezon5
'oK', 'oe', 'z', 'on', '5'
10001

这样就成功得到了对方QQ号,剩下怎么做就不需要我说了吧(滑稽)


对于懒于动手的同学,我提供了C++代码(附在最后),可复制到 VisualStudio / Dev Cpp / Code::blocks等IDE中运行
或者在以下网站在线编译运行
运行前将main()函数中的str数组内容修改为fromEncodeUin即可
http://www.dooccn.com/cpp/


严正声明:
本文中所提到的技术/方法仅能用于个人学习与交流,请勿用于商业以及其他目的;请勿进行非法操作,请勿损害他人/社会/国家利益,请遵守相关法律法规,否则由此带来的一切后果均由操作者本人承担,与本文作者无关.


C++ Code

#include 
#include 
static char* table[] = {
    "oe", "oK", "ow", "oi", "7e", "7K", "7w", "7i", "Ne", "NK",
     "n",  "6",  "-",  "o",  "v",  "4",  "C",  "S",  "c",  "E",
     "z",  "5",  "A",  "i",  "P",  "k",  "s",  "l",  "F",  "q"
};
int matchChar(char a, char b, char* mNum);
int decode(char* src, char * buf);
int main() {
    /*待解码fromEncodeUin放在这里,注意不要包含*S1*这四个字符*/
    char str[30] = { "oKoezon5" };
    char buf[30];
    memset(buf, 0, 30);
    decode(str, buf);
    std::cout << "解码结果:\n" << buf << std::endl;
    return 0;
}
int matchChar(char a, char b, char* mNum) {
    if (NULL == mNum)
        return -1;
    int i;
    if ('o' == a) {
        i = 0;
        while (i < 4) {
            if (table[i][1] == b) {
                *mNum = i % 10 + 48;
                return 2;
            }
            i++;
        }
        /*增加多条特殊情况,if语句太多了-_-*/
        if ('n' == b) {
            *mNum = 0 + 48;
            return 2;
        }
        if ('z' == b) {
            *mNum = 3 + 48;
            return 2;
        }
        *mNum = 3 + 48;
        return 1;
    }
    else if ('7' == a) {
        i = 4;
        while (i < 8) {
            if (table[i][1] == b) {
                *mNum = i % 10 + 48;
                return 2;
            }
            i++;
        }
        if ('v' == b) {
            *mNum = 5 + 48;
            return 2;
        }
        if ('z' == b) {
            *mNum = 7 + 48;
            return 2;
        }
        return -1;
    }
    else if ('N' == a) {
        i = 8;
        while (i < 10) {
            if (table[i][1] == b) {
                *mNum = i % 10 + 48;
                return 2;
            }
            i++;
        }
        if ('v' == b) {
            *mNum = 9 + 48;
            return 2;
        }
        if ('n' == b) {
            *mNum = 8 + 48;
            return 2;
        }
        return -1;
    }
    i = 10;
    while (i < 30) {
        if (table[i][0] == a) {
            *mNum = i % 10 + 48;
            return 1;
        }
        i++;
    }
    return -1;
}
int decode(char* src, char * buf) {
    if (NULL == src || NULL == buf) {
        return -1;
    }
    char* p = src;
    char* q = buf;
    int rc = 0;
    while ('\0' != *p && -1 != (rc = matchChar(*p, *(p + 1), q))) {
        p += rc;
        q++;
    }
    return 0;
}

你可能感兴趣的:(QQ"坦白说"简单分析)