|
2 | 2 | # UFADE - Universal Forensic Apple Device Extractor (c) C.Peter 2024
|
3 | 3 | # Licensed under GPLv3 License
|
4 | 4 | import customtkinter as ctk
|
5 |
| -from PIL import ImageTk |
| 5 | +from PIL import ImageTk, Image |
6 | 6 | from tkinter import StringVar
|
7 | 7 | from pymobiledevice3 import usbmux, exceptions, lockdown
|
8 | 8 | from pymobiledevice3.services.mobile_image_mounter import DeveloperDiskImageMounter, MobileImageMounterService, PersonalizedImageMounter
|
|
33 | 33 | from iOSbackup import iOSbackup
|
34 | 34 | from pyiosbackup import Backup
|
35 | 35 | from playsound import playsound
|
| 36 | +from io import BytesIO |
36 | 37 | import crossfiledialog
|
37 | 38 | import hashlib
|
38 | 39 | import plistlib
|
@@ -180,6 +181,14 @@ def switch_menu(self, menu_name):
|
180 | 181 | self.show_crash_report()
|
181 | 182 | elif menu_name == "Media":
|
182 | 183 | self.show_media()
|
| 184 | + elif menu_name == "FileLS": |
| 185 | + dvt = DvtSecureSocketProxyService(lockdown) |
| 186 | + dvt.__enter__() |
| 187 | + self.show_fileloop(dvt) |
| 188 | + elif menu_name == "Shot": |
| 189 | + dvt = DvtSecureSocketProxyService(lockdown) |
| 190 | + dvt.__enter__() |
| 191 | + self.screen_device(dvt) |
183 | 192 | elif menu_name == "NoDevice":
|
184 | 193 | self.show_nodevice()
|
185 | 194 | elif menu_name == "NotPaired":
|
@@ -249,14 +258,14 @@ def show_dev_menu(self):
|
249 | 258 | self.skip = ctk.CTkLabel(self.dynamic_frame, text="UFADE by Christian Peter", text_color="#3f3f3f", height=40, padx=20, font=self.stfont)
|
250 | 259 | self.skip.grid(row=0, column=1, sticky="w")
|
251 | 260 | self.menu_buttons = [
|
252 |
| - ctk.CTkButton(self.dynamic_frame, text="Take screenshots", command=lambda: self.switch_menu("Menu1"), width=200, height=70, font=self.stfont), |
| 261 | + ctk.CTkButton(self.dynamic_frame, text="Take screenshots", command=lambda: self.switch_menu("Shot"), width=200, height=70, font=self.stfont), |
253 | 262 | ctk.CTkButton(self.dynamic_frame, text="Chat capture", command=lambda: self.switch_menu("Menu2"), width=200, height=70, font=self.stfont),
|
254 |
| - ctk.CTkButton(self.dynamic_frame, text="Capture filesystem\nto text", command=lambda: self.switch_menu("Menu3"), width=200, height=70, font=self.stfont), |
| 263 | + ctk.CTkButton(self.dynamic_frame, text="Capture filesystem\nto text", command=lambda: self.switch_menu("FileLS"), width=200, height=70, font=self.stfont), |
255 | 264 | ctk.CTkButton(self.dynamic_frame, text="Unmount\nDeveloperDiskImage", command=lambda: self.switch_menu("Menu3"), width=200, height=70, font=self.stfont),
|
256 | 265 | ]
|
257 | 266 | self.menu_text = ["Take screenshots from device screen.\nScreenshots will be saved under \"screenshots\" as PNG.",
|
258 | 267 | "Loop through a chat taking screenshots.\nOne screenshot is taken per message.",
|
259 |
| - "Write a filesystem list to a textfile.\nStarting from /var Folder. This may take some time.", |
| 268 | + "Write a filesystem list to a textfile. (iOS < 16)\nStarting from /var Folder. This may take some time.", |
260 | 269 | "Try to unmount the image. Reboot the device if this fails"]
|
261 | 270 | self.menu_textbox = []
|
262 | 271 | for btn in self.menu_buttons:
|
@@ -1252,6 +1261,7 @@ def show_media(self):
|
1252 | 1261 |
|
1253 | 1262 | ### check start
|
1254 | 1263 |
|
| 1264 | +# Try to mount a suitable developerdiskimage |
1255 | 1265 | def mount_developer(self, change, text):
|
1256 | 1266 | global developer
|
1257 | 1267 | global lockdown
|
@@ -1282,21 +1292,22 @@ def mount_developer(self, change, text):
|
1282 | 1292 | self.nob.pack_forget()
|
1283 | 1293 | try:
|
1284 | 1294 | AmfiService(lockdown).enable_developer_mode(enable_post_restart=True)
|
| 1295 | + self.choose.set(False) |
1285 | 1296 | text.configure(text="Wait for the device to reboot.\nUnlock it and confirm the activation of the developer mode.\nAfter this, press \"OK\".")
|
| 1297 | + self.okbutton = ctk.CTkButton(self.dynamic_frame, text="OK", font=self.stfont, command=lambda: self.choose.set(True)) |
| 1298 | + self.wait_variable(self.choose) |
| 1299 | + self.okbutton.pack_forget() |
1286 | 1300 | except:
|
1287 | 1301 | text.configure(text="Uh-Oh, an error was raised. Please remove the PIN/PW and try again")
|
1288 | 1302 | developer = False
|
1289 | 1303 | change.set(1)
|
1290 |
| - raise SystemExit |
| 1304 | + return("nope") |
1291 | 1305 | else:
|
1292 | 1306 | self.yesb.pack_forget()
|
1293 | 1307 | self.nob.pack_forget()
|
1294 | 1308 | developer = False
|
1295 | 1309 | change.set(1)
|
1296 |
| - raise SystemExit |
1297 |
| - except SystemExit: |
1298 |
| - change.set(1) |
1299 |
| - raise SystemExit |
| 1310 | + return("nope") |
1300 | 1311 | except:
|
1301 | 1312 | pass
|
1302 | 1313 | if int(version.split(".")[0]) < 17:
|
@@ -1440,12 +1451,125 @@ def developer_options(self):
|
1440 | 1451 | self.start_developer.start()
|
1441 | 1452 | self.wait_variable(self.change)
|
1442 | 1453 | if developer == True:
|
| 1454 | + #lockdown = create_using_usbmux() |
| 1455 | + #dvt = DvtSecureSocketProxyService(lockdown) |
| 1456 | + #dvt.__enter__() |
1443 | 1457 | self.switch_menu("DevMenu")
|
1444 | 1458 | else:
|
1445 | 1459 | self.after(100, lambda: ctk.CTkButton(self.dynamic_frame, text="OK", font=self.stfont, command=lambda: self.switch_menu("MainMenu")).pack(pady=40))
|
1446 | 1460 |
|
| 1461 | +# Device screenshot |
| 1462 | + def screen_device(self, dvt): |
| 1463 | + #ls = os.listdir(path="screenshots") |
| 1464 | + #lss = "\n".join(str(element) for element in ls) |
| 1465 | + ctk.CTkLabel(self.dynamic_frame, text="UFADE by Christian Peter", text_color="#3f3f3f", height=40, padx=40, font=self.stfont).pack(anchor="center") |
| 1466 | + ctk.CTkLabel(self.dynamic_frame, text="Take Screenshots", height=40, width=585, font=("standard",24), justify="left").pack(pady=10) |
| 1467 | + self.shotframe = ctk.CTkFrame(self.dynamic_frame, width=400, corner_radius=0, fg_color="transparent") |
| 1468 | + self.textframe = ctk.CTkFrame(self.dynamic_frame, width=200, corner_radius=0, fg_color="transparent") |
| 1469 | + self.shotframe.pack(side="left", pady=20, padx=40, fill="y", expand=True) |
| 1470 | + self.textframe.pack(side="left", pady=20, fill="both", expand=True) |
| 1471 | + self.placeholder_image = ctk.CTkImage(dark_image=Image.open(os.path.join(os.path.dirname(__file__), "assets" , "screen_ufade.png")), size=(240, 426)) |
| 1472 | + self.imglabel = ctk.CTkLabel(self.shotframe, image=self.placeholder_image, text=" ", width=240, height=426, fg_color="transparent", font=self.stfont, anchor="w", justify="left") |
| 1473 | + self.imglabel.pack() |
| 1474 | + try: os.mkdir("screenshots") |
| 1475 | + except: pass |
| 1476 | + self.abortbutton = ctk.CTkButton(self.textframe, text="Screenshot", font=self.stfont, command=lambda: self.shot(dvt, self.imglabel, self.namefield)) |
| 1477 | + self.abortbutton.pack(pady=30, ipadx=0, anchor="w") |
| 1478 | + self.abortbutton = ctk.CTkButton(self.textframe, text="Back", font=self.stfont, command=lambda: self.switch_menu("DevMenu")) |
| 1479 | + self.abortbutton.pack(pady=5, ipadx=0, anchor="w") |
| 1480 | + self.namefield = ctk.CTkLabel(self.textframe, text=" ", width=300, height=100, fg_color="transparent", font=self.stfont, anchor="w", justify="left") |
| 1481 | + self.namefield.pack(anchor="w", pady=10) |
| 1482 | + |
| 1483 | + def shot(self, dvt, imglabel, namefield, chat=None): |
| 1484 | + hsize = 426 |
| 1485 | + try: |
| 1486 | + png = Screenshot(dvt).get_screenshot() |
| 1487 | + except: |
| 1488 | + png = ScreenshotService(lockdown).take_screenshot() |
| 1489 | + png_bytes = BytesIO() |
| 1490 | + png_bytes.write(png) |
| 1491 | + shot = Image.open(png_bytes) |
| 1492 | + hperc = (hsize/float(shot.size[1])) |
| 1493 | + wsize = int((float(shot.size[0])*float(hperc))) |
| 1494 | + if wsize > 300: |
| 1495 | + wsize = 300 |
| 1496 | + wperc = (wsize/float(shot.size[0])) |
| 1497 | + hsize = int((float(shot.size[1])*float(wperc))) |
| 1498 | + screensh = ctk.CTkImage(dark_image=shot, size=(wsize, hsize)) |
| 1499 | + imglabel.configure(image=screensh) |
| 1500 | + if chat == None: |
| 1501 | + filename = hardware + "_" + str(datetime.now().strftime("%m_%d_%Y_%H_%M_%S")) + ".png" |
| 1502 | + with open(os.path.join("screenshots", filename), "wb") as file: |
| 1503 | + file.write(png) |
| 1504 | + namefield.configure(text=f"Screenshot saved as:\n{filename}") |
| 1505 | + else: |
| 1506 | + pass |
| 1507 | + |
| 1508 | + |
| 1509 | +# Fileloop window |
| 1510 | + def show_fileloop(self, dvt): |
| 1511 | + ctk.CTkLabel(self.dynamic_frame, text="UFADE by Christian Peter", text_color="#3f3f3f", height=40, padx=40, font=self.stfont).pack(anchor="center") |
| 1512 | + ctk.CTkLabel(self.dynamic_frame, text="Filesystem content", height=80, width=585, font=("standard",24), justify="left").pack(pady=20) |
| 1513 | + self.text = ctk.CTkLabel(self.dynamic_frame, text="Creating a filesystem-list. This will take a while.", width=585, height=60, fg_color="transparent", font=self.stfont, anchor="w", justify="left") |
| 1514 | + self.text.pack(anchor="center", pady=25) |
| 1515 | + self.prog_text = ctk.CTkLabel(self.dynamic_frame, text="0%", width=585, height=20, font=self.stfont, anchor="w", justify="left") |
| 1516 | + self.prog_text.pack() |
| 1517 | + self.progress = ctk.CTkProgressBar(self.dynamic_frame, width=585, height=30, corner_radius=0) |
| 1518 | + self.progress.set(0) |
| 1519 | + self.progress.pack() |
| 1520 | + self.folder_text = ctk.CTkLabel(self.dynamic_frame, text="Folder: ", width=585, height=40, fg_color="transparent", font=self.stfont, anchor="w", justify="left") |
| 1521 | + self.folder_text.pack() |
| 1522 | + self.waitls = ctk.IntVar(self, 0) |
| 1523 | + self.dev_ls = threading.Thread(target=lambda: self.call_fileloop(dvt, self.waitls, self.prog_text, self.progress, self.folder_text)) |
| 1524 | + self.dev_ls.start() |
| 1525 | + self.wait_variable(self.waitls) |
| 1526 | + self.prog_text.pack_forget() |
| 1527 | + self.progress.pack_forget() |
| 1528 | + self.folder_text.pack_forget() |
| 1529 | + self.text.configure(text="Creation of filesystem-list complete.") |
| 1530 | + self.after(100, lambda: ctk.CTkButton(self.dynamic_frame, text="OK", font=self.stfont, command=lambda: self.switch_menu("DevMenu")).pack(pady=40)) |
| 1531 | + |
| 1532 | +# Call the fileloop and write the output to a file |
| 1533 | + def call_fileloop(self, dvt, waitls, prog_text, progress, folder_text): |
| 1534 | + folders = [] |
| 1535 | + for line in DeviceInfo(dvt).ls("/"): |
| 1536 | + folders.append(line) |
| 1537 | + fcount = len(folders) |
| 1538 | + cnt = 0 |
| 1539 | + pathlist = [] |
| 1540 | + pathlist = fileloop(dvt, "/var", pathlist, fcount, cnt, folder_text, progress, prog_text) |
| 1541 | + with open(udid + "_var_filesystem.txt", "w") as files: |
| 1542 | + for line in pathlist: |
| 1543 | + files.write("\n" + line) |
| 1544 | + prog_text.configure(text="100%") |
| 1545 | + progress.set(1) |
| 1546 | + waitls.set(1) |
| 1547 | + |
1447 | 1548 | ### check stop
|
1448 | 1549 |
|
| 1550 | +# Developer Mode filesystem-"ls"-loop |
| 1551 | +def fileloop(dvt, start, lista, fcount, cnt, folder_text, progress, prog_text): |
| 1552 | + pathlist = lista |
| 1553 | + try: |
| 1554 | + next = DeviceInfo(dvt).ls(start) |
| 1555 | + for line in next: |
| 1556 | + next_path = (start + "/" + line) |
| 1557 | + if len(next_path.split("/")) == 3: |
| 1558 | + cnt += 1 |
| 1559 | + fpro = int(44*(cnt/fcount))%100 |
| 1560 | + prog_text.configure(text=f"{fpro}%") |
| 1561 | + progress.set(fpro/100) |
| 1562 | + folder_text.configure(text="Folder: " + next_path) |
| 1563 | + if next_path in pathlist: |
| 1564 | + break |
| 1565 | + else: |
| 1566 | + pathlist.append(next_path) |
| 1567 | + fileloop(dvt, next_path, pathlist, fcount, cnt, folder_text, progress, prog_text) |
| 1568 | + except: |
| 1569 | + pass |
| 1570 | + finally: |
| 1571 | + return(pathlist) |
| 1572 | + |
1449 | 1573 | # Pull Media-files
|
1450 | 1574 | def media_export(l_type, dest="Media", archive=None, text=None, prog_text=None, progress=None, change=None):
|
1451 | 1575 | media_list = []
|
|
0 commit comments