2018-04-04
本文首發(fā)于簡書,作者initialize_Zero,轉(zhuǎn)載請注明作者以及本文鏈接
近期騰訊在手Q上線了新功能--坦白說,可以匿名對好友打上標(biāo)簽,許多人玩的不亦樂乎,也引發(fā)了各個平臺上的大討論,趁著假期寫篇文章
既然是剛剛上線的新功能,必然有不完善的地方,大神們各顯神通,只為了找到坦白說背后的發(fā)送者
經(jīng)過整理大致有以下幾種方法:
1.將對方所發(fā)圖片收藏,然后進入我的收藏,即可看到
2.iOS版手Q可以通過搜索消息記錄的方式搜索到發(fā)送方
3.API數(shù)據(jù)接口 https://ti.qq.com/cgi-node/honest-say/receive/mine ,在手Q中打開此鏈接,獲取并解碼相關(guān)json數(shù)據(jù)
截止4月3日,第1,2種方法已失效,下面我們重點分析第三種方法.
4月1日之前,對API接口請求可得到如下所示的json數(shù)據(jù):
{
"code":0,
"data":{
"list":[
{
"fromNick":"一個認(rèn)識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請求會返回如下結(jié)果:
{
"code":2333333,
"msg":"皮這一下你很開心么ヽ( ̄▽ ̄)?"
}
皮一下可以,皮幾萬就不行了,這更加激發(fā)了我的好奇心,身為程序猿的我,怎能因為這樣的一點小困難止步不前
下面祭出神器
Packet Capture (又名無root抓包)
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的網(wǎng)絡(luò)請求數(shù)據(jù)包,安裝自帶的證書后還可以解密https內(nèi)容.
下面我們介紹使用方法
1.安裝
酷安APP中搜索并下載,安裝完成后打開
- 點擊GetStarted
- Continue
- 這里一定要點擊 Install Certificate , 安裝配套證書,不然在稍后操作中無法看到https內(nèi)容
- 會彈出Android 系統(tǒng)證書添加界面,點擊確定
- 這樣就進入到主界面,左上角兩個綠色按鈕分別是抓取單一APP網(wǎng)絡(luò)請求與抓取全部請求
這樣就完成了抓取前的準(zhǔn)備工作
2.抓取請求
抓取前盡可能關(guān)掉除QQ與Packet Capture以外的所有軟件,提高成功率,還可以避免抓到其他無關(guān)數(shù)據(jù)
下面是重點,請嚴(yán)格按照如圖所示操作
1.確保QQ列表中有"坦白說"這一會話
2.切換到Packet Capture,點擊右上角的綠色按鈕,開始抓取
第一次使用會提示,點擊"確定"即可
3.綠色按鈕變?yōu)榧t色表示正在抓取中,同時出現(xiàn)提示信息
4.切換回QQ,點擊坦白說會話進入
5.繼續(xù)點擊進入,如果有下圖提示,一定點擊繼續(xù)訪問,原因是我們抓取數(shù)據(jù)時用了Packet Capture自帶證書替換了原證書.
6.右上角點擊進入"收到的坦白說"
7.進入此列表,等待一會,一定保證列表加載完全
8.切換到Packet Capture,點擊右上角的紅色停止按鈕,結(jié)束本次抓包,這樣我們就有了一次記錄,點擊進入
9.重點尋找QQ發(fā)出/接收的數(shù)據(jù)包,包含坦白說列表數(shù)據(jù)的數(shù)據(jù)包大小在10KB以上,且為SSL類型
(抓到的數(shù)據(jù)包大約在30~50個左右,耐心尋找)
10.點擊進入此記錄,如果找到了具有如下類型請求的數(shù)據(jù)包,恭喜你,離成功又近了一步!
(GET /honest-say/main.html , Host: ti.qq.com)
11.這個數(shù)據(jù)包很大,使勁往下翻,如果類似于發(fā)現(xiàn)下圖中的數(shù)據(jù),就是我們所需分析的重點內(nèi)容
(可以長按文本進行選擇,復(fù)制等操作)
接下來重點分析這些數(shù)據(jù)
{
"code":0,
"data":{
"list":[
{
"fromNick":"一個認(rèn)識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
}
]
}
}
參數(shù)名 | 含義 | 備注 |
---|---|---|
fromNick | 對方所顯示的匿名昵稱 | e.g."一個認(rèn)識3年的男生" "一個南京的女生" |
fromEncodeUin | 編碼后的對方QQ | 重點,從這里入手 |
fromFaceUrl | 對方頭像圖片文件 | |
fromGender | 對方性別 | 0為男,1為女 |
toUin | 你的QQ號 | |
toNick | 你的昵稱 | 似乎都是空 |
topicId | 話題ID | 話題索引 |
topicName | 話題名稱 | e.g. "我身邊的最強大腦" "將來能干一番大事" "擁有迷人的長發(fā)" 也是對方給你發(fā)送的第一條消息 |
timestamp | 時間戳 | 1970年1月1日(UTC/GMT的午夜)開始所經(jīng)過的秒數(shù) |
各參數(shù)說明如下:
參數(shù)名 | 含義 | 備注 |
---|---|---|
fromNick | 對方所顯示的匿名昵稱 | e.g."一個認(rèn)識3年的男生" "一個南京的女生" |
fromEncodeUin | 編碼后的對方QQ | 重點,從這里入手 |
fromFaceUrl | 對方頭像圖片文件 | |
fromGender | 對方性別 | 0為男,1為女 |
toUin | 你的QQ號 | |
toNick | 你的昵稱 | 似乎都是空 |
topicId | 話題ID | 話題索引 |
topicName | 話題名稱 | e.g. "我身邊的最強大腦" "將來能干一番大事" "擁有迷人的長發(fā)" 也是對方給你發(fā)送的第一條消息 |
timestamp | 時間戳 | 1970年1月1日(UTC/GMT的午夜)開始所經(jīng)過的秒數(shù) |
接下來就是如何解碼fromEncodeUin的問題,我們以*S1*oKoezon5為例,寫出具體解碼過程:
1.去除*S1*這四個字符
結(jié)果變?yōu)?oKoezon5
2.對照表格解碼
下圖是之前某大神總結(jié)出的部分規(guī)律(有點像AES加密中的S盒,不過對應(yīng)關(guān)系簡單了許多)
經(jīng)過測試,發(fā)現(xiàn)部分對應(yīng)關(guān)系存在問題(比如3同時對應(yīng)了oi,o與i,很明顯這里有問題,個人猜測原因是分析樣本數(shù)據(jù)不足),重新測試十幾條數(shù)據(jù)后,增添以下六組對應(yīng)關(guān)系
為了解碼方便,列出如下規(guī)則:
規(guī)則1: 優(yōu)先使用2字符對應(yīng)1數(shù)字的關(guān)系,其次使用1字符對應(yīng)1數(shù)字的關(guān)系
e.g. oion6 解碼為 301,而不是33301
即'oi'對應(yīng)3,'on'對應(yīng)0,'6'對應(yīng)1的方式
不應(yīng)按照'o'對應(yīng)3,'i'對應(yīng)3,'o'對應(yīng)3,'n'對應(yīng)0,'6'對應(yīng)1的方式規(guī)則2: 遇到某一位無法解碼,跳過該位從下一位進行解碼
oKoezon5
'oK', 'oe', 'z', 'on', '5'
10001
這樣就成功得到了對方QQ號,剩下怎么做就不需要我說了吧(滑稽)
對于懶于動手的同學(xué),我提供了C++代碼(附在最后),可復(fù)制到 VisualStudio / Dev Cpp / Code::blocks等IDE中運行
或者在以下網(wǎng)站在線編譯運行
運行前將main()函數(shù)中的str數(shù)組內(nèi)容修改為fromEncodeUin即可
http://www.dooccn.com/cpp/
嚴(yán)正聲明:
本文中所提到的技術(shù)/方法僅能用于個人學(xué)習(xí)與交流,請勿用于商業(yè)以及其他目的;請勿進行非法操作,請勿損害他人/社會/國家利益,請遵守相關(guān)法律法規(guī),否則由此帶來的一切后果均由操作者本人承擔(dān),與本文作者無關(guān).
C++ Code
#include <memory.h>
#include <iostream>
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 << "解碼結(jié)果:\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;
}