Android 7以上版本抓包https

背景:

  相信很多测试小伙伴发现,app安装在Android7以下的设备上使用fiddler或charles工具可以正常抓包https,但安装在Android7以上的设备上就无法抓包成功,造成测试bug定位困难。

前言:

  当项目开发人员完成开发需求后,会直接给我们测试人员一个测试apk,为了不麻烦开发人员,我们需要自己利用相关工具重新编译这个apk,才能在android7以上的设备上进行抓包https。

一、工具准备:

1.下载和安装Java开发工具包(JDK)。安装之后,配置环境变量,APKTool、zipalign、apksigner 和 keytool 需要预先安装,并在系统路径中可用

liunx系统安装:
wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
unzip commandlinetools-linux-11076708_latest.zip -d cmdline-tools
mkdir --parents "$ANDROID_HOME/cmdline-tools/latest"
sudo mv cmdline-tools/* "$ANDROID_HOME/cmdline-tools/latest/"
export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$PATH 

windows安装暂不作说明,自行在网上找教程安装。

二、生成签名文件

如果能直接找开发人员拿到签名文件,就可以忽略该步骤
cmd doc窗口执行:
keytool -genkey -v -keystore my-release-key.keystore -alias dm -keyalg RSA -keysize 2048 -validity 10000

输入2次相同密码,再按六次回车,最后一次输入:y

 

三、在电脑中新建一个空的文件夹,文件夹命名不要有中文

 1 把步骤1生成的签名文件my-release-key.keystore放在该文件夹下

 2 把下列python脚本放在该文件夹中,用编译器打开,import导入代码中需要的相关工具包

 3 把代码中

/home/dm/work/apk/my-release-key.keystore 改为自己存放my-release-key.keystore的路径
/home/dm/work/apk  改为开发人员给的apk存放的目录路径
  1 import os
  2 import subprocess
  3 import logging
  4 import tkinter as tk
  5 from tkinter import filedialog, messagebox
  6 from tkinter.ttk import Progressbar
  7 import shutil
  8 
  9 # 配置日志
 10 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 11 
 12 
 13 class APKToolGUI:
 14     def __init__(self, root):
 15         self.root = root
 16         self.root.title("APK 处理工具")
 17         self.root.geometry("400x300")
 18 
 19         self.file_paths = []
 20         self.keystore_path = "/home/dm/work/apk/my-release-key.keystore"
 21         self.default_apk_path = "/home/dm/work/apk"
 22 
 23         # 文件选择按钮
 24         self.select_button = tk.Button(root, text="选择 APK 文件", command=self.select_files)
 25         self.select_button.pack(pady=10)
 26 
 27         # 显示选中文件
 28         self.file_label = tk.Label(root, text="")
 29         self.file_label.pack(pady=10)
 30 
 31         # 选择密钥库按钮
 32         self.keystore_button = tk.Button(root, text="选择密钥库", command=self.select_keystore)
 33         self.keystore_button.pack(pady=10)
 34 
 35         # 显示密钥库路径
 36         self.keystore_label = tk.Label(root, text=f"已选择密钥库: {self.keystore_path}")
 37         self.keystore_label.pack(pady=10)
 38 
 39         # 确定按钮
 40         self.confirm_button = tk.Button(root, text="确定", command=self.start_process, state=tk.DISABLED)
 41         self.confirm_button.pack(pady=10)
 42 
 43         # 进度条
 44         self.progress = Progressbar(root, orient=tk.HORIZONTAL, length=300, mode='determinate')
 45         self.progress.pack(pady=10)
 46 
 47         # 绑定关闭窗口事件
 48         self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
 49 
 50         # 默认选中第一个 APK 文件
 51         self.load_default_apk()
 52 
 53     def load_default_apk(self):
 54         default_path = self.default_apk_path
 55         # 清空当前文件路径
 56         self.file_paths = []
 57 
 58         # 获取目录下的所有 APK 文件
 59         apk_files = [f for f in os.listdir(default_path) if f.endswith('.apk')]
 60         if apk_files:
 61             # 选择第一个 APK 文件
 62             self.file_paths = [os.path.join(default_path, apk_files[0])]
 63             self.file_label.config(text=f"已选择文件: {self.file_paths[0]}")
 64             self.confirm_button.config(state=tk.NORMAL)
 65         else:
 66             self.file_label.config(text="")
 67             self.confirm_button.config(state=tk.DISABLED)
 68 
 69         # 强制更新界面
 70         self.root.update_idletasks()
 71 
 72     def select_files(self):
 73         default_path = self.default_apk_path
 74         self.root.update_idletasks()  # 强制更新界面
 75         selected_files = filedialog.askopenfilenames(initialdir=default_path, filetypes=[("APK Files", "*.apk")])
 76         if selected_files:
 77             self.file_paths = list(selected_files)
 78             self.file_label.config(text=f"已选择文件: {', '.join(self.file_paths)}")
 79             self.confirm_button.config(state=tk.NORMAL)
 80 
 81     def select_keystore(self):
 82         self.keystore_path = filedialog.askopenfilename(initialdir="/home/dm/work/apk",
 83                                                         filetypes=[("Keystore Files", "*.keystore")])
 84         if self.keystore_path:
 85             self.keystore_label.config(text=f"已选择密钥库: {self.keystore_path}")
 86 
 87     def start_process(self):
 88         if not self.keystore_path:
 89             messagebox.showerror("错误", "请先选择密钥库文件!")
 90             return
 91 
 92         self.confirm_button.config(state=tk.DISABLED)
 93 
 94         for apk_file in self.file_paths:
 95             self.process_apk(apk_file)
 96 
 97         self.confirm_button.config(state=tk.NORMAL)
 98         self.load_default_apk()  # 处理完所有文件后刷新默认选项
 99 
100     def process_apk(self, apk_file):
101         output_dir = os.path.splitext(os.path.basename(apk_file))[0]
102         unsigned_apk = f"{output_dir}_unsigned.apk"
103         signed_apk = f"{output_dir}_signed.apk"
104 
105         try:
106             self.update_progress(10, "解压 APK 文件")
107             self.decompile_apk(apk_file, output_dir)
108             self.update_progress(30, "修改 APK 文件")
109             self.modify_apk(output_dir)
110             self.update_progress(50, "重新编译 APK 文件")
111             self.recompile_apk(output_dir, unsigned_apk, signed_apk)
112             self.update_progress(70, "优化 APK 文件")
113             self.optimize_apk(unsigned_apk, signed_apk)
114             self.update_progress(90, "签名 APK 文件")
115             self.sign_apk(signed_apk)
116             self.update_progress(100, "处理完成")
117             messagebox.showinfo("成功", f"APK 文件处理完成:{apk_file}")
118 
119             # 删除 unsigned_apk 文件和手动选择的 apk 文件
120             self.delete_file(unsigned_apk, "未签名的 APK 文件")
121             self.delete_file(apk_file, "原始选择的 APK 文件")
122 
123             # 删除编译出来的文件夹中的所有内容
124             self.delete_folder_contents(output_dir, "编译出来的文件夹")
125         except Exception as e:
126             messagebox.showerror("错误", f"处理过程中发生错误: {str(e)}")
127             logging.error(f"处理过程中发生错误: {str(e)}")
128 
129     def update_progress(self, value, message=""):
130         self.progress['value'] = value
131         self.root.update_idletasks()
132         logging.info(message)
133 
134     def decompile_apk(self, apk_file, output_dir):
135         # subprocess.run(["apktool", "d", "-f", apk_file, "-o", output_dir], check=True)
136         subprocess.run(["apktool", "d", "-f", apk_file, "-o", output_dir],
137                        check=True, stdout=subprocess.DEVNULL)
138 
139         logging.info("APK 文件解压完成")
140 
141     def modify_apk(self, output_dir):
142         manifest_path = os.path.join(output_dir, "AndroidManifest.xml")
143         with open(manifest_path, "r") as file:
144             manifest_content = file.read()
145 
146         manifest_content = manifest_content.replace(
147             'android:extractNativeLibs="false"', 'android:extractNativeLibs="true"')
148         manifest_content = manifest_content.replace(
149             '<application', '<application android:networkSecurityConfig="@xml/network_security_config"')
150 
151         with open(manifest_path, "w") as file:
152             file.write(manifest_content)
153 
154         xml_dir = os.path.join(output_dir, "res/xml")
155         os.makedirs(xml_dir, exist_ok=True)
156         config_content = """<?xml version="1.0" encoding="utf-8"?>
157 <network-security-config>
158     <base-config cleartextTrafficPermitted="true">
159         <trust-anchors>
160             <certificates src="user"/>
161             <certificates src="system"/>
162         </trust-anchors>
163     </base-config>
164 </network-security-config>"""
165 
166         with open(os.path.join(xml_dir, "network_security_config.xml"), "w") as file:
167             file.write(config_content)
168         logging.info("APK 文件修改完成")
169 
170     def recompile_apk(self, output_dir, unsigned_apk, signed_apk):
171         if os.path.exists(signed_apk):
172             os.remove(signed_apk)
173             logging.info(f"已删除旧的已签名 APK 文件: {signed_apk}")
174         subprocess.run(["apktool", "b", "-f", output_dir, "-o", unsigned_apk], check=True, stdout=subprocess.DEVNULL)
175         logging.info("APK 文件重新编译完成")
176 
177     def optimize_apk(self, unsigned_apk, signed_apk):
178         subprocess.run(["zipalign", "-v", "4", unsigned_apk, signed_apk], check=True, stdout=subprocess.DEVNULL)
179         logging.info("APK 文件优化完成")
180 
181     def sign_apk(self, signed_apk):
182         key_password = "123456"
183         try:
184             subprocess.run([
185                 "apksigner", "sign",
186                 "--v2-signing-enabled", "false",
187                 "--ks", self.keystore_path,
188                 "--ks-pass", f"pass:{key_password}",
189                 signed_apk
190             ], check=True, stdout=subprocess.DEVNULL)
191             logging.info("APK 文件签名完成")
192         except subprocess.CalledProcessError as e:
193             raise RuntimeError(f"APK 签名失败: {e}")
194 
195     def delete_file(self, file_path, file_description):
196         if os.path.exists(file_path):
197             try:
198                 os.remove(file_path)
199                 logging.info(f"{file_description} 已删除: {file_path}")
200             except Exception as e:
201                 messagebox.showerror("错误", f"无法删除 {file_description}: {str(e)}")
202                 logging.error(f"无法删除 {file_description}: {str(e)}")
203         else:
204             logging.warning(f"{file_description} 不存在: {file_path}")
205 
206     def delete_folder_contents(self, folder_path, folder_description):
207         if os.path.exists(folder_path):
208             try:
209                 shutil.rmtree(folder_path)
210                 logging.info(f"{folder_description} 中的所有内容已删除: {folder_path}")
211             except Exception as e:
212                 messagebox.showerror("错误", f"无法删除 {folder_description} 中的内容: {str(e)}")
213                 logging.error(f"无法删除 {folder_description} 中的内容: {str(e)}")
214         else:
215             logging.warning(f"{folder_description} 不存在: {folder_path}")
216 
217     def on_closing(self):
218         pid = os.getpid()
219         os.kill(pid, 9)
220         self.root.destroy()
221 
222 
223 if __name__ == "__main__":
224     root = tk.Tk()
225     app = APKToolGUI(root)
226     root.mainloop()

四、执行代码,会弹出一个GUI界面,直接点击确定即可运行生成一个新的APK,新的APK和代码同一个目录下,该apk安装在手机上可以用fiddler或charles工具抓包。

 
posted @ 2025-02-18 13:30  重庆_伦儿  阅读(96)  评论(0)    收藏  举报