使用Python實(shí)現(xiàn)監(jiān)測(cè)APP的使用流量
本文介紹了一種用于監(jiān)測(cè)單個(gè)APK包上下行及總流量的方法,同時(shí)實(shí)現(xiàn)了定時(shí)備份與郵件提醒功能。通過(guò)Python腳本,可以獲取設(shè)備UID,計(jì)算設(shè)備流量,并將數(shù)據(jù)保存至Excel表格中。定時(shí)備份功能確保數(shù)據(jù)安全,而郵件提醒則方便遠(yuǎn)程監(jiān)控。
實(shí)現(xiàn)代碼如下:
#!/usr/bin/env python
# _*_ coding: utf-8 _*_
'''
# @Time : 2018/1/17 21:54
# @Author : Soner
# @version : V1.0
# @license : Copyright(C), Your Company
# @實(shí)現(xiàn)的功能:
1.單APK包的上下行及總流量監(jiān)測(cè)
2.定時(shí)備份
3.定時(shí)發(fā)送郵件提醒
'''
import time
import subprocess
import xlwt
import os
import shutil
import datetime
# ----------------- 獲取設(shè)備UID -----------------
def getUid(package_name):#獲取UID
cmd = 'adb shell dumpsys package ' + package_name + ' | findstr userId'
p1 = subprocess.Popen(cmd, shell = True,
stdout=subprocess.PIPE, stderr = subprocess.PIPE)#用adb獲取信息
uidLongString = str(p1.stdout.read().strip(), encoding="utf-8")
uidLongList = uidLongString.split("=")
uid = uidLongList[1]
return uid[0:5]
# ----------------- 計(jì)算設(shè)備流量 -----------------
def getFlowFromUid(packagename, uid=None):
'''
# 通過(guò)應(yīng)用uid,獲取應(yīng)用當(dāng)前消耗的流量
# 獲取PID adb shell ps | findstr 包名 如果是LINUx 系統(tǒng) findstr 換成 grep
# 獲取網(wǎng)絡(luò)標(biāo)識(shí)及流量 “adb shell cat /proc/應(yīng)用PID碼/net/dev”
# return (rcv,snd)
'''
if uid is None:
uid = getUid(packagename)
cmd = 'adb shell cat /proc/net/xt_qtaguid/stats | findstr %s' % uid
std = os.popen(cmd)
net_rcv_bck = []
net_rcv_front = []
net_snd_bck = []
net_snd_front = []
lo_rcv_bck = []
lo_rcv_front = []
lo_snd_bck = []
lo_snd_front = []
for line in std: # if 判斷里的 “ccmni0”根據(jù)使用的SIM卡變換,具體的需要先去查詢(xún)改掉這里,不然會(huì)出錯(cuò)“adb shell cat /proc/應(yīng)用PID碼/net/dev”
data = line.split()
if 'ccmni0' in line:
background_flow = int(data[4]) == 0
if background_flow:
net_rcv_bck.append(int(data[5]))
net_snd_bck.append(int(data[7]))
else:
net_rcv_front.append(int(data[5]))
net_snd_front.append(int(data[7]))
elif 'lo' in line:
background_flow = int(data[4]) == 0
if background_flow:
lo_rcv_bck.append(int(data[5]))
lo_snd_bck.append(int(data[7]))
else:
lo_rcv_front.append(int(data[5]))
lo_snd_front.append(int(data[7]))
return sum(net_rcv_bck), sum(net_rcv_front), sum(net_snd_bck), sum(net_snd_front), \
sum(lo_rcv_bck), sum(lo_rcv_front), sum(lo_snd_bck), sum(lo_snd_front)
# ----------------- 發(fā)送郵件 -----------------
'''''
函數(shù)說(shuō)明:Send_email_text() 函數(shù)實(shí)現(xiàn)發(fā)送帶有附件的郵件,可以群發(fā),附件格式包括:xlsx,pdf,txt,jpg,mp3等
參數(shù)說(shuō)明:
1. subject:郵件主題
2. content:郵件正文
3. filepath:附件的地址, 輸入格式為["","",...]
4. receive_email:收件人地址, 輸入格式為["","",...]
'''
def Send_email_text(subject,content,filepath,receive_email):
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
sender = "Youn Email" # 發(fā)送人的賬號(hào), 已配置為 163 郵箱服務(wù),如果使用其它的郵箱,可在方法下面修改
passwd = "PASSWORD" # 發(fā)送人的密碼
receivers = receive_email #收件人郵箱
msgRoot = MIMEMultipart()
msgRoot['Subject'] = subject
msgRoot['From'] = sender
if len(receivers)>1:
msgRoot['To'] = ','.join(receivers) #群發(fā)郵件
else:
msgRoot['To'] = receivers[0]
part = MIMEText(content)
msgRoot.attach(part)
##添加附件部分--可以自己追加類(lèi)型,或者全部打開(kāi)自動(dòng)判斷
for path in filepath:
# if ".jpg" in path:
# #jpg類(lèi)型附件
# jpg_name = path.split("\\")[-1]
# part = MIMEApplication(open(path,'rb').read())
# part.add_header('Content-Disposition', 'attachment', filename=jpg_name)
# msgRoot.attach(part)
#
# if ".pdf" in path:
# #pdf類(lèi)型附件
# pdf_name = path.split("\\")[-1]
# part = MIMEApplication(open(path,'rb').read())
# part.add_header('Content-Disposition', 'attachment', filename=pdf_name)
# msgRoot.attach(part)
if ".xls" in path:
#xlsx類(lèi)型附件
xlsx_name = path.split("\\")[-1]
part = MIMEApplication(open(path,'rb').read())
part.add_header('Content-Disposition', 'attachment', filename=xlsx_name)
msgRoot.attach(part)
# if ".txt" in path:
# #txt類(lèi)型附件
# txt_name = path.split("\\")[-1]
# part = MIMEApplication(open(path,'rb').read())
# part.add_header('Content-Disposition', 'attachment', filename=txt_name)
# msgRoot.attach(part)
#
# if ".mp3" in path:
# #mp3類(lèi)型附件
# mp3_name = path.split("\\")[-1]
# part = MIMEApplication(open(path,'rb').read())
# part.add_header('Content-Disposition', 'attachment', filename=mp3_name)
# msgRoot.attach(part)
try:
global m
m = smtplib.SMTP()
m.connect("smtp.163.com") #這里我使用的是163郵箱,也可以使用其它郵箱
m.login(sender, passwd)
m.sendmail(sender, receivers, msgRoot.as_string())
print("郵件發(fā)送成功")
except smtplib.SMTPException as e:
print("Error, 發(fā)送失敗")
finally:
m.quit()
# ----------------- 創(chuàng)建EXCEL表格 -----------------
col =0
row =0
book_Mirror = xlwt.Workbook(encoding='utf-8', style_compression=0) # 創(chuàng)建新的工作簿Mirror
sheet_load_Mirror = book_Mirror.add_sheet('流量', cell_overwrite_ok=True) # 創(chuàng)建新的sheet,并命名為流量
sheet_load_Mirror.write(row, col, "時(shí)間")
sheet_load_Mirror.write(row, col + 1, "網(wǎng)絡(luò)下行(KB)")
sheet_load_Mirror.write(row, col + 2, "網(wǎng)絡(luò)上行(KB)")
sheet_load_Mirror.write(row, col + 3, "網(wǎng)絡(luò)總流量(KB)")
sheet_load_Mirror.write(row, col + 4, "本地下行(KB)")
sheet_load_Mirror.write(row, col + 5, "本地上行(KB)")
sheet_load_Mirror.write(row, col + 6, "本地總流量(KB)")
# ----------------- 需要監(jiān)測(cè)的包名 -----------------
time_end =0 # 監(jiān)測(cè)初始時(shí)間,單位秒
package_name_Mirror= "You 的APK名稱(chēng)" # 改成自己需要測(cè)試的APk包名
uid = (getUid(package_name_Mirror))[0:5]
try:
uid_sdk = getUid(package_name_Mirror)
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) +' uid = '+str(uid_sdk))
except:
print('獲取Mirror-uid失敗')
# ----------------- 定位文件目錄 -----------------
file_dir = "D:\\" # 可改成自己的目錄,要與下邊對(duì)稱(chēng)
os.chdir(file_dir)
row =1
col =0
s = 1
net_bck_start_rx, net_front_start_rx, net_bck_start_tx, net_front_start_tx, \
lo_bck_start_rx, lo_front_start_rx, lo_bck_start_tx, lo_front_start_tx = getFlowFromUid(package_name_Mirror, uid)
net_start_rx = net_bck_start_rx + net_front_start_rx
net_start_tx = net_bck_start_tx + net_front_start_tx
lo_start_rx = lo_bck_start_rx + lo_front_start_rx
lo_start_tx = lo_bck_start_tx + lo_front_start_tx
i = 1
time_k,time_s = 0,0
while time_end <= 259200:
net_bck_end_rx, net_front_end_rx, net_bck_end_tx, net_front_end_tx, \
lo_bck_end_rx, lo_front_end_rx, lo_bck_end_tx, lo_front_end_tx = getFlowFromUid(package_name_Mirror, uid)
net_end_rx = net_bck_end_rx + net_front_end_rx
net_end_tx = net_bck_end_tx + net_front_end_tx
lo_end_rx = lo_bck_end_rx + lo_front_end_rx
lo_end_tx = lo_bck_end_tx + lo_front_end_tx
net_flow_rx, net_flow_tx = net_end_rx - net_start_rx, net_end_tx - net_start_tx
lo_flow_rx, lo_flow_tx = lo_end_rx - lo_start_rx, lo_end_tx - lo_start_tx
net_rx_kb, net_tx_kb = round(net_flow_rx / 1024, 3), round(net_flow_tx / 1024, 3)
lo_rx_kb, lo_tx_kb = round(lo_flow_rx / 1024, 3), round(lo_flow_tx / 1024, 3)
timeNow = time.strftime('%Y-%m-%d %H-%M-%S', time.localtime(time.time())) # 獲取當(dāng)前時(shí)間用于輸出
timenew = time.strftime('%Y-%m-%d %H-%M', time.localtime(time.time())) # 獲取當(dāng)前時(shí)間用戶(hù)備份時(shí)的命名
sheet_load_Mirror.write(row, col, timeNow) # 寫(xiě)入時(shí)間
sheet_load_Mirror.write(row, col + 1, net_rx_kb) # 寫(xiě)入網(wǎng)絡(luò)下行(KB)
sheet_load_Mirror.write(row, col + 2, net_tx_kb) # 寫(xiě)入網(wǎng)絡(luò)上行(KB)
sheet_load_Mirror.write(row, col + 3, round(net_rx_kb + net_tx_kb, 3)) # 寫(xiě)入網(wǎng)絡(luò)總流量(KB)
sheet_load_Mirror.write(row, col + 4, lo_rx_kb) # 寫(xiě)入本地上行(KB)
sheet_load_Mirror.write(row, col + 5, lo_tx_kb) # 寫(xiě)入本地下行(KB)
sheet_load_Mirror.write(row, col + 6, round(lo_rx_kb + lo_tx_kb, 3)) # 寫(xiě)入本地總流量(KB)
book_Mirror.save("d:\Mirror_Folw.xls") # 保存EXCEL表
print(" %s ---------- %s %s ----------" % (row, package_name_Mirror, timeNow))
print(
'網(wǎng)絡(luò)下行:', net_rx_kb, 'KB\t',
'網(wǎng)絡(luò)上行:', net_tx_kb, 'KB\t',
'網(wǎng)絡(luò)總流量', round(net_rx_kb + net_tx_kb, 3), 'KB\t\t',
'本地下行:', lo_rx_kb, 'KB\t',
'本地上行:', lo_tx_kb, 'KB\t',
'本地總流量', round(lo_rx_kb + lo_tx_kb, 3), 'KB\t\n'
)
row = row + 1 # EXCEL表格追加下一行寫(xiě)入
time.sleep(10) # 控制監(jiān)測(cè)頻率
time_end += 10 # 監(jiān)測(cè)計(jì)時(shí)器
time_s += 10 # 備份計(jì)時(shí)器
time_k += 10 # 郵件計(jì)時(shí)器
# 定時(shí)備份
if time_s == 300:
shutil.copy("Mirror_Folw.xls", "d:\\test\\Mirror_Folw_%s.xls" % timenew) # 改成自己需要復(fù)制的文件(此處的路徑為上面定位目錄),和復(fù)制后的文件路徑以及名稱(chēng)
time_s = 0
print('備份成功~!')
try:
# 定時(shí)發(fā)送郵件
if time_end == (1800 * i):
subject = "郵件標(biāo)題"
content = "郵件正文"
Mirror_path = "d:\\test\\Mirror_Folw_%s.xls" % timenew # 附件地址
file_path = [Mirror_path] # 可添加多個(gè)附件到郵箱
receive_email = ["接收人的郵箱"]
Send_email_text(subject,content,file_path,receive_email)
i += 1
except:
continue
print("---------- END 統(tǒng)計(jì)時(shí)長(zhǎng):%s----------" % str(time_k))知識(shí)擴(kuò)展:
下面我們就來(lái)看看如何使用 Python 獲取 Android App 的網(wǎng)絡(luò)數(shù)據(jù),核心思路主要有兩種:
1) 直接模擬 App 請(qǐng)求,這通常是繞過(guò) App 復(fù)雜邏輯進(jìn)行高效數(shù)據(jù)采集的最終目的;
2) 進(jìn)行實(shí)時(shí)的網(wǎng)絡(luò)流量捕獲與分析。最終采用哪種路徑,取決于你的技術(shù)棧和目標(biāo) App 的防護(hù)強(qiáng)度。
核心方法一:App 請(qǐng)求模擬(直接數(shù)據(jù)采集)
這是指通過(guò)分析 App 的網(wǎng)絡(luò)請(qǐng)求,找到規(guī)律后,直接用 Python 腳本(如 requests 庫(kù))構(gòu)造請(qǐng)求來(lái)模擬 App 的行為,直接獲取數(shù)據(jù)。它的目標(biāo)是實(shí)現(xiàn)高效、長(zhǎng)期穩(wěn)定的數(shù)據(jù)采集。
實(shí)踐流程:使用抓包工具分析 → 定位關(guān)鍵接口 → 分析參數(shù)規(guī)律 → 使用 requests 庫(kù)等編寫(xiě) Python 腳本模擬請(qǐng)求。
核心方法二:網(wǎng)絡(luò)流量捕獲與解析
通過(guò)設(shè)置代理或在設(shè)備上抓包,實(shí)時(shí)攔截 App 產(chǎn)生的網(wǎng)絡(luò)數(shù)據(jù)包,再進(jìn)行解析。
mitmproxy:一款強(qiáng)大的可編程中間人代理,原生支持 Python 腳本,可以實(shí)現(xiàn)請(qǐng)求和響應(yīng)的實(shí)時(shí)攔截、修改、自動(dòng)化處理。它非常適合需要實(shí)時(shí)介入或高度自動(dòng)化處理的場(chǎng)景,且跨平臺(tái)。tcpdump:一個(gè)經(jīng)典的網(wǎng)絡(luò)抓包工具,通過(guò)adb在 Android 設(shè)備上運(yùn)行,將網(wǎng)絡(luò)數(shù)據(jù)包保存為.pcap文件。r0capture:一款基于 Frida 的“通殺型”抓包工具,能夠在應(yīng)用層直接捕獲解密后的明文數(shù)據(jù)。它尤其適合用于對(duì)抗 SSL Pinning 和強(qiáng)加密的 App,是目前處理高防護(hù) App 的最強(qiáng)方案之一。
核心方法三:深度分析輔助
Frida:一款強(qiáng)大的動(dòng)態(tài)插樁框架,允許你在運(yùn)行時(shí)注入 JavaScript 代碼來(lái) Hook App 的函數(shù)。它主要用于突破客戶(hù)端安全防護(hù),如繞過(guò) SSL Pinning 證書(shū)校驗(yàn)或代理檢測(cè),是使用r0capture的基礎(chǔ),也為分析加密參數(shù)提供強(qiáng)大支持。Scapy:一款強(qiáng)大的 Python 庫(kù),可以用來(lái)處理.pcap文件,從原始數(shù)據(jù)包中過(guò)濾和解碼 TCP、UDP 等協(xié)議數(shù)據(jù)。
實(shí)戰(zhàn)指南
以下是三種主流方案的詳細(xì)操作指南,請(qǐng)根據(jù)你的技術(shù)背景和目標(biāo) App 的防護(hù)強(qiáng)度選擇。
方案一:mitmproxy 實(shí)時(shí)代理(適合實(shí)時(shí)介入與自動(dòng)化)
該方案的核心原理是在電腦上啟動(dòng) mitmproxy 服務(wù),并將手機(jī)的網(wǎng)絡(luò)代理指向電腦,從而讓所有流量都經(jīng)過(guò)代理,實(shí)現(xiàn)攔截、修改或自動(dòng)化處理。該方案無(wú)法繞過(guò)證書(shū)綁定(SSL Pinning)。
安裝與啟動(dòng):通過(guò) pip install mitmproxy 安裝。之后,可使用 mitmweb(Web界面)或 mitmdump -s your_script.py(加載腳本)來(lái)啟動(dòng)。
配置手機(jī):確保手機(jī)與電腦在同一 Wi-Fi,在手機(jī) Wi-Fi 設(shè)置中開(kāi)啟代理,填入電腦的局域網(wǎng) IP 和 mitmproxy 端口(默認(rèn) 8080)。首次使用需訪(fǎng)問(wèn) mitm.it 下載并安裝證書(shū)。
編寫(xiě) Python 腳本:新建 add_header.py 文件,編寫(xiě)如下示例腳本,用于在請(qǐng)求頭中添加自定義字段:
# add_header.py
def request(flow):
flow.request.headers["X-Custom-Header"] = "Value"
print(f"Modified headers for {flow.request.url}")運(yùn)行并查看:執(zhí)行 mitmdump -s add_header.py,手機(jī)上的流量便會(huì)實(shí)時(shí)流經(jīng)代理。你添加的請(qǐng)求頭會(huì)出現(xiàn)在每一條請(qǐng)求中,其效果等同于 curl -H。
方案二:r0capture + Frida 應(yīng)用層抓包(最強(qiáng)方案,可繞過(guò) SSL Pinning)
該方案利用 Frida 將 JavaScript 代碼注入到目標(biāo) App 進(jìn)程,直接從其內(nèi)存中 Hook 加密函數(shù),在數(shù)據(jù)加密前捕獲明文內(nèi)容。它能繞過(guò) SSL Pinning,是對(duì)付高防護(hù) App 的“大殺器”。
環(huán)境準(zhǔn)備:電腦端安裝 frida-tools (pip install frida-tools)。準(zhǔn)備一臺(tái)已 Root 的安卓手機(jī)或模擬器,下載對(duì)應(yīng)架構(gòu)的 frida-server并推送到手機(jī)運(yùn)行。
克隆與運(yùn)行:git clone https://github.com/r0ysue/r0capture.git。通過(guò) adb devices 確認(rèn)設(shè)備已連接。最后,使用以下命令啟動(dòng)抓包(-f 后跟目標(biāo)應(yīng)用包名,-p 指定輸出文件):
python3 r0capture.py -U -f com.example.app -v -p captured.pcap
分析數(shù)據(jù):該命令會(huì)生成 captured.pcap 文件。你可以用 Wireshark 打開(kāi)它進(jìn)行深度分析,也可以配合下文提到的 Scapy 進(jìn)行自動(dòng)化解析。
方案三:Scapy 離線(xiàn)解析 PCAP 文件(適合離線(xiàn)深度分析)
當(dāng)完成抓包并得到 .pcap 文件后,Scapy 是解析它并提取業(yè)務(wù)數(shù)據(jù)的利器。
安裝 Scapy:pip install scapy。
解析代碼示例:編寫(xiě)以下 Python 腳本,從 .pcap 文件中提取 HTTP 請(qǐng)求信息。
from scapy.all import rdpcap, TCP, IP, Raw
packets = rdpcap('captured.pcap') # 加載 PCAP 文件
for pkt in packets:
if IP in pkt and TCP in pkt and Raw in pkt:
payload = pkt[Raw].load
try:
# 簡(jiǎn)單解碼 HTTP 請(qǐng)求行
http_text = payload.decode('utf-8', errors='ignore')
if http_text.startswith('GET') or http_text.startswith('POST'):
print(f"發(fā)現(xiàn) HTTP 請(qǐng)求: {http_text.splitlines()[0]}")
except:
pass到此這篇關(guān)于使用Python實(shí)現(xiàn)監(jiān)測(cè)APP的使用流量的文章就介紹到這了,更多相關(guān)Python監(jiān)測(cè)APP流量?jī)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 基于Python實(shí)現(xiàn)網(wǎng)絡(luò)流量監(jiān)控的方法詳解
- 基于Python開(kāi)發(fā)實(shí)現(xiàn)網(wǎng)絡(luò)流量分析工具詳解
- 使用Python統(tǒng)計(jì)網(wǎng)站訪(fǎng)問(wèn)流量
- Python實(shí)現(xiàn)端口流量轉(zhuǎn)發(fā)的示例代碼
- python調(diào)用百度AI接口實(shí)現(xiàn)人流量統(tǒng)計(jì)
- python如何查看系統(tǒng)網(wǎng)絡(luò)流量的信息
- Python腳本實(shí)現(xiàn)網(wǎng)卡流量監(jiān)控
相關(guān)文章
Python爬取科目四考試題庫(kù)的方法實(shí)現(xiàn)
這篇文章主要介紹了Python爬取科目四考試題庫(kù)的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
簡(jiǎn)介二分查找算法與相關(guān)的Python實(shí)現(xiàn)示例
這篇文章主要介紹了二分查找算法與相關(guān)的Python實(shí)現(xiàn)示例,Binary Search同時(shí)也是算法學(xué)習(xí)當(dāng)中最基礎(chǔ)的知識(shí),需要的朋友可以參考下2015-08-08
Python從Manim中提取表格/坐標(biāo)系并轉(zhuǎn)GIF的四種高效方案
在數(shù)據(jù)可視化和數(shù)學(xué)動(dòng)畫(huà)創(chuàng)作中,我們經(jīng)常需要將 Manim 動(dòng)畫(huà)中的表格、坐標(biāo)系等核心元素單獨(dú)導(dǎo)出為 GIF,本文整理了四種高效方案,每種方案僅提供核心代碼,聚焦關(guān)鍵實(shí)現(xiàn)邏輯,需要的朋友可以參考下2025-08-08
Python代碼實(shí)現(xiàn)C++代碼依賴(lài)提取工具
這篇文章主要為大家詳細(xì)介紹了如何使用Python代碼實(shí)現(xiàn)一個(gè)C++代碼依賴(lài)提取工具,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2026-03-03
關(guān)于Pytorch的MLP模塊實(shí)現(xiàn)方式
今天小編就為大家分享一篇關(guān)于Pytorch的MLP模塊實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01
Python 3.x 判斷 dict 是否包含某鍵值的實(shí)例講解
今天小編就為大家分享一篇Python 3.x 判斷 dict 是否包含某鍵值的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07

