# 1 Android 模拟器按进程捕包准备
## 1.1 adb
### 1.1.1 adb简介
Android 调试桥 (adb) 是一种功能多样的命令行工具，可与设备进行通信，所有 adb 客户端均使用端口 5037 与 adb 服务器通信。
## 1.1.2 adb安装
推荐使用模拟器相同的adb.exe进行连接，打开模拟器安装路径，找到adb.exe，并将其路径添加到系统path中。

或官网下载adb进行安装并将其添加到系统path中。（不推荐 可能因为adb版本问题导致连接异常）
https://dl.google.com/android/repository/platform-tools-latest-windows.zip
## 1.2 Android模拟器
常用模拟器及其端口
|  模拟器名称   | 端口  |
|  ----  | ----  |
| 网易MuMu安卓模拟器 | 7555 |
| 夜神安卓模拟器 | 62001 |
| 逍遥安卓模拟器 | 21503 |
| 雷电安卓模拟器 | 5555 |
| 天天安卓模拟器 | 5037 |
| 安卓模拟器大师 | 54001 |
## 1.2.1 获取模拟器adb连接地址
启动模拟器，终端运行
`adb devices`
获取该模拟器端口（适用于本机有多个模拟器或使用多开器时）
## 1.2.2 adb连接
连接设备
`adb connect [ip：prot]`
断开连接的设备
`adb disconnect`
查看连接列表
`adb devices -l`
## 1.2.3 adb常用操作
将文件从本地capy到Android内
`adb push [src_path] [dst_path]`
将文件从Android内capy到本地
`"adb -s [指定连接] pull [src_path] [dst_path] `
Android内使用shell命令
`adb shell `
指定adb连接并获取root权限
`adb -s [ip：prot] root`
## 1.3 获取目标App包名
获取全部app包名列表
`adb shell pm list packages`
获取第三方app包名列表
`adb shell pm list packages -3`		
## 1.4 获取目标App类名
获取当前运行的App类名
`adb shell dumpsys activity activities`
## 1.5 模拟器开启root权限及开发者模式
### 1.5.1 root设置
一般在模拟器设置选项中
![alt](./Images/模拟器root模式.jpg)
### 1.5.2 开发者模式
一般点击系统版本号5次进入开发者模式
![alt](./Images/模拟器开发者模式.jpg)

# 2 捕包类"CapturePacket"
## 2.1 类构造
`app_package_name`      ：App包名
`app_class_name`        ：App类名
`simulator_ip_port`     ：模拟器连接端口及地址
`pcap_file_ID：pacp`    ：文件编号
`pcap_src_file_path`    : pcap文件导出路径
```python
    def __init__(self, app_package_name, app_class_name, simulator_ip_port, capture_packet_mode):
        self._app_package_name = app_package_name
        self._app_class_name = app_class_name
        self._simulator_ip_port = simulator_ip_port
        self._capture_packet_mode = capture_packet_mode
        # 杀死本地adb进程。防止在循环多次执行过程中adb进程阻塞
        os.system('taskkill /f /im %s' % 'adb.exe')        
        # 建立连接
        time.sleep(1)
        command_str = "adb connect " + self._simulator_ip_port
        os.system(command_str)
        time.sleep(1)
        # 获取root权限 
        command_str = "adb -s " + self._simulator_ip_port + " root"
        os.system(command_str)
        time.sleep(1)
        # 自动获取安卓系统wlan0 IP地址
        command_str = "adb -s " + self._simulator_ip_port + " shell ip addr show wlan0"
        ip_str_src = re.sub(r"\s+", " ", os.popen(command_str).read().splitlines()[2])
        # print(ip_str_src)
        ip_str = ip_str_src.split(' ')[2].split('/')[0]
        self._ip = str(ip_str)
        print("System Wlan0 IP : ", self._ip)
        # 自动获取APP的userID
        command_str = "adb -s " + self._simulator_ip_port + " shell \"cat /data/system/packages.xml | grep " + self._app_package_name 
        userID = os.popen(command_str).read()
        for i in userID.split(' ') :
            if "userId" in i:
                userID = i.split('"')[1]
        self._userID = str(userID)
        print("APP User ID : ", self._userID)
        time.sleep(1)
        # 清空规则
        command_str = "adb -s " + self._simulator_ip_port + " shell iptables -F"
        os.popen(command_str)
        time.sleep(2)
        # 添加规则
        # 把OUTPUT数据包里面，用户ID是userID的流打上userID的流标签
        command_str = "adb -s " + self._simulator_ip_port + " shell iptables -A OUTPUT -m owner --uid-owner " + self._userID + " -j CONNMARK --set-mark "+ self._userID
        os.popen(command_str)
        time.sleep(2)
        # 把入站流中带有userID标签的数据包放到nflog消息池中，消息池的标号为userID
        command_str = "adb -s " + self._simulator_ip_port + " shell iptables -A INPUT -m connmark --mark " + self._userID + " -j NFLOG --nflog-group " + self._userID
        os.popen(command_str)
        time.sleep(2)
        # 把出站流中带有userID标签的数据包放到nflog消息池中，消息池的标号为userID
        command_str = "adb -s " + self._simulator_ip_port + " shell iptables -A OUTPUT -m connmark --mark "+ self._userID + " -j NFLOG --nflog-group " + self._userID
        os.popen(command_str)
        # 系统反应慢休息休息
        time.sleep(10)
```
1.建立adb连接
```python
command_str = "adb connect " + self._simulator_ip_port
os.system(command_str)
```
2.根据App包名自动获取userID获取
```python
command_str = "adb -s " + self._simulator_ip_port + " shell \"cat /data/system/packages.xml | grep " + self._app_package_name 
userID = os.popen(command_str).read()
for i in userID.split(' ') :
    if "userId" in i:
        userID = i.split('"')[1]
self._userID = str(userID)
```
3.进行iptables配置
```python
# 清空规则
command_str = "adb -s " + self._simulator_ip_port + " shell iptables -F"
os.popen(command_str)
# 添加规则
# 把OUTPUT数据包里面，用户ID是userID的流打上userID的流标签
command_str = "adb -s " + self._simulator_ip_port + " shell iptables -A OUTPUT -m owner --uid-owner " + self._userID + " -j CONNMARK --set-mark "+ self._userID
os.popen(command_str)
# 把入站流中带有userID标签的数据包放到nflog消息池中，消息池的标号为userID
command_str = "adb -s " + self._simulator_ip_port + " shell iptables -A INPUT -m connmark --mark " + self._userID + " -j NFLOG --nflog-group " + self._userID
os.popen(command_str)
# 把出站流中带有userID标签的数据包放到nflog消息池中，消息池的标号为userID
command_str = "adb -s " + self._simulator_ip_port + " shell iptables -A OUTPUT -m connmark --mark "+ self._userID + " -j NFLOG --nflog-group " + self._userID
os.popen(command_str)
```

## 2.2 析构
1.恢复iptables配置
```python
# 删除规则
command_str = "adb -s " + self._simulator_ip_port + " shell iptables -F"
os.popen(command_str)
```
2.断开adb连接
```python
# 关闭adb连接
os.system('adb disconnect')
# 杀死本地adb进程。防止在循环多次执行过程中adb进程阻塞
os.system('taskkill /f /im %s' % 'adb.exe')
```

## 2.3 类成员函数
```python
    def StartCapturePacket(self, pcap_file_ID, pcap_src_file_path, network_cord_name):   
        # 安进程抓包
        if self._capture_packet_mode & 0b0001:
            print("模拟器安进程抓包")
            command_str = "adb -s " + self._simulator_ip_port + " shell tcpdump -i nflog:" + self._userID +" -w  /sdcard/Pictures/simulator_uid_" + self._userID + "_" + str(pcap_file_ID) +".pcap &"
            os.popen(command_str)

        # 全系统抓包
        if self._capture_packet_mode & 0b0010:
            print("模拟器全系统抓包")  
            command_str = "adb -s " + self._simulator_ip_port + " shell tcpdump -w  /sdcard/Pictures/simulator_all_" + str(pcap_file_ID) +".pcap &"
            os.popen(command_str)

        if self._capture_packet_mode & 0b0100:
            print("Host机按模拟器IP抓包")  
            command = tshark_file_path + ' -i \"' + network_cord_name + '\"  -w ' + pcap_src_file_path + 'host_' + self._ip + '_' + str(pcap_file_ID)  + '.pcap -T text host ' + self._ip + ' > NUL'
            print(command)
            subprocess.Popen(command, shell=True)
        
        if self._capture_packet_mode & 0b1000:
            print("Host机全系统抓包")  
            command = tshark_file_path + ' -i \"' + network_cord_name + '\"  -w ' + pcap_src_file_path + 'host_all_' + str(pcap_file_ID)  + '.pcap' + ' -T text > NUL'
            print(command)
            subprocess.Popen(command, shell=True)
        return 
    
    def StopCapturePacket(self, pcap_file_ID, pcap_src_file_path):
        if self._capture_packet_mode & 0b0001:
            print("停止模拟器安进程抓包")
            command_str = "adb -s " + self._simulator_ip_port + " shell killall tcpdump"
            os.popen(command_str)
            # 查看保存的pcap包
            command_str = "adb -s " + self._simulator_ip_port + " shell ls -l /sdcard/Pictures/simulator_uid_" + self._userID + "_" + str(pcap_file_ID) +".pcap"
            return_str = os.popen(command_str).read()
            print(return_str)
            # pull pcap包 到本地
            command_str = "adb -s " + self._simulator_ip_port + " pull /sdcard/Pictures/simulator_uid_" + self._userID + "_" + str(pcap_file_ID) + ".pcap " + pcap_src_file_path
            return_str = os.popen(command_str).read()
            # 等待pcap包拷贝
            time.sleep(10)
            print(return_str)
            # 删除Android内保存的pcap包
            command_str = "adb -s " + self._simulator_ip_port + " shell rm /sdcard/Pictures/simulator_uid_"  + self._userID + "_" + str(pcap_file_ID) +".pcap"
            os.popen(command_str)          

        if self._capture_packet_mode & 0b0010:
            print("停止模拟器全系统抓包")  
            command_str = "adb -s " + self._simulator_ip_port + " shell killall tcpdump"
            os.popen(command_str)
            # 查看保存的pcap包
            command_str = "adb -s " + self._simulator_ip_port + " shell ls -l /sdcard/Pictures/simulator_all_" + str(pcap_file_ID) +".pcap &"
            return_str = os.popen(command_str).read()
            print(return_str)
            # pull pcap包 到本地
            command_str = "adb -s " + self._simulator_ip_port + " pull /sdcard/Pictures/simulator_all_" + str(pcap_file_ID) +".pcap " + pcap_src_file_path
            return_str = os.popen(command_str).read()
            # 等待pcap包拷贝
            time.sleep(10)
            print(return_str)
            # 删除Android内保存的pcap包
            command_str = "adb -s " + self._simulator_ip_port + " shell rm /sdcard/Pictures/simulator_all_" + str(pcap_file_ID) +".pcap"
            os.popen(command_str)  

        if self._capture_packet_mode & 0b0100:
            print("停止Host机按模拟器IP抓包")  
            process_name = 'tshark.exe'
            pid = os.popen('tasklist /fi "imagename eq %s"' % process_name).readlines()[3].split()[1]
            os.system('taskkill /F /PID %s' % pid)

        if self._capture_packet_mode & 0b1000:
            print("停止Host系统系统抓包")  
            process_name = 'tshark.exe'
            pid = os.popen('tasklist /fi "imagename eq %s"' % process_name).readlines()[3].split()[1]
            os.system('taskkill /F /PID %s' % pid)
        return

    def StartAPP(self):
        command_str = "adb -s " + self._simulator_ip_port + " shell am start -n " + self._app_package_name + self._app_class_name
        ret = os.popen(command_str).read()
        if not 'Starting' in ret:
            return -1
        return 1
    
    def StopAPP(self):
        command_str = "adb -s " + self._simulator_ip_port + " shell am force-stop " + self._app_package_name 
        print(os.popen(command_str).read())
        return
    
    def WgetFile(self, file_url):
        command_str = "adb -s " + self._simulator_ip_port + " shell wget " + file_url + " -O /sdcard/Pictures/wgetfile" 
        print(os.popen(command_str).read())
        return
    
    def CurlFile(self, file_url):
        command_str = "adb -s " + self._simulator_ip_port + " shell curl -k " + file_url
        os.popen(command_str).read()
        return 
    
    def GetPublicIP(self):
        command_str = "adb -s " + self._simulator_ip_port + " shell curl ifconfig.me/ip "
        string = os.popen(command_str).read()
        public_ip = string[string.rfind('\n') + 1:]
        return public_ip

    def GetLocation(self, ip):
        try:
            response = requests.get(f'https://ipinfo.io/{ip}/json')
            location_data = response.json()
            return location_data
        except Exception as e:
            print(f"Error: {e}")
            return None
        
    def SendDataByUdp(self, ip, port, file):
        # 拷贝文件到安卓模拟器
        for i in range(100):
            command_str = "adb -s " + self._simulator_ip_port + " shell \"echo 'Hello Word !!!' | busybox nc " + ip + " " + port + "\""
            string = os.popen(command_str).read()
        return string
```