commit 616238aaad290042d24539ce45503d7b8286452b Author: Ivan Holmes Date: Thu May 14 13:11:36 2020 +0100 First upload diff --git a/auto.py b/auto.py new file mode 100644 index 0000000..c5a7075 --- /dev/null +++ b/auto.py @@ -0,0 +1,37 @@ +from pathlib import Path + +from clishared import cli_connect_rom, cli_read_rom, cli_patch_rom, cli_write_rom, cli_choose_patches + +print("ThinkPad X230 Automatic Firmware Extract and Patch v1") + +project_name = input("What would you like to name your project? ") +project_name = project_name.replace("\\", "") # Remove backslashes so I can drag and drop folders +project_path = Path(project_name).resolve() + +if project_path.exists(): + if project_path.is_file(): + raise FileExistsError(f"Cannot create project folder {project_path}, a file already exists here.") + elif project_path.is_dir(): + response = input("Folder already exists at this location. Proceed and risk overwriting existing files? (y/N): ") + if not response or (response.strip()[0] not in ['y', 'Y']): + raise FileExistsError(f"Cannot create project folder {project_path}, a directory already exists here.") +else: + project_path.mkdir() + +clean_path = project_path / "clean" +if not clean_path.exists(): + clean_path.mkdir() + +patched_path = project_path / "patched" +if not patched_path.exists(): + patched_path.mkdir() + +patches = cli_choose_patches() + +cli_connect_rom("top") + +cli_read_rom("top", clean_path) +cli_patch_rom(patches, clean_path, patched_path) +cli_write_rom("top", patched_path) + +print("All done.") \ No newline at end of file diff --git a/clishared.py b/clishared.py new file mode 100644 index 0000000..a2b81b6 --- /dev/null +++ b/clishared.py @@ -0,0 +1,78 @@ +from pathlib import Path +import filecmp + +from romtools import * + +def cli_join_rom(clean_path): + print("Joining dumps in to full firmware ROM... ", end="") + with open(clean_path / "bottom.bin", "rb") as bottom, open(clean_path / "top.bin", "rb") as top, open(clean_path / "clean.rom", "wb+") as rom: + rom.write(bottom.read() + top.read()) + print("Done.") + +def cli_split_rom(patched_path): + print("Splitting full rom in to images for each chip... ", end="") + with open(patched_path / "bottom.bin", "wb+") as bottom, open(patched_path / "top.bin", "wb+") as top, open(patched_path / "patched.rom", "rb") as rom: + rom_array = rom.read() + bottom.write(rom_array[:0x800000]) + top.write(rom_array[0x800000:]) + print("Done.") + +def cli_connect_rom(chip_loc): + input(f"Please connect programming clip to {chip_loc} flash chip. Press Enter when done...") + cli_reconnect_rom(chip_loc) + +def cli_reconnect_rom(chip_loc): + print("Checking if ROM is connected... ", end="") + try: + check_rom(chip_loc) + print("ROM is reachable.") + except: + response = input("ROM is not reachable. Try again? (Y/q): ") + if response in ['q', 'Q']: + raise ROMError("Could not connect to ROM. User elected to quit.") + else: + cli_reconnect_rom(chip_loc) + +def cli_read_rom(chip_loc, clean_path, verify=True): + files = [chip_loc + ".bin", chip_loc + "_2.bin"] if verify else [chip_loc + ".bin"] + for count, file_name in enumerate(files): + print(f"Reading {chip_loc} ROM, pass {count+1} of {len(files)}... ", end="", flush=True) + read_rom(chip_loc, clean_path, file_name) + print("Success.") + + if verify: + print("Comparing ROM files... ", end="") + if filecmp.cmp(clean_path / files[0], clean_path / files[1]): + print("Files match.") + else: + raise ROMError("ROM files do not match. Read the log and try again.") + else: + print("ROM file will not be verified, proceed at your own risk!") + + print(f"{chip_loc.capitalize()} ROM read successfully.") + +def cli_write_rom(chip_loc, patched_path, verify=True): + file_name = chip_loc + '_patched.bin' + print(f"Writing {chip_loc} ROM (takes a while)... ", end="", flush=True) + write_rom(chip_loc, patched_path, file_name) + print("Success.") + + print(f"{chip_loc.capitalize()} ROM written successfully.") + +def cli_choose_patches(): + which_patches = input("Patch (W)LAN whitelist, (A)dvanced menu or (B)oth? ") + + if which_patches.strip()[0] in ['w', 'W']: + patches = ["wifi"] + elif which_patches.strip()[0] in ['a', 'A']: + patches = ["advanced"] + elif which_patches.strip()[0] in ['b', 'B']: + patches = ["wifi", "advanced"] + else: + raise ValueError("Selection invalid.") + + return patches + +def cli_patch_rom(patches, clean_path, patched_path): + patch_rom(patches, clean_path / "top.bin", patched_path / "top_patched.bin") + print("ROM patched successfully.") \ No newline at end of file diff --git a/patchbios.py b/patchbios.py new file mode 100644 index 0000000..f906eef --- /dev/null +++ b/patchbios.py @@ -0,0 +1,19 @@ +import os, subprocess +from pathlib import Path + +from clishared import cli_patch_rom, cli_choose_patches + +print("ThinkPad X230 Bios Patcher v1") + +project_name = input("Where is your project folder? ") +project_name = project_name.replace("\\", "") # Remove backslashes so I can drag and drop folders +project_path = Path(project_name).resolve() + +patched_path = project_path / "patched" +clean_path = project_path / "clean" + +if not patched_path.exists(): + patched_path.mkdir() + +patches = cli_choose_patches() +cli_patch_rom(patches, clean_path, patched_path) \ No newline at end of file diff --git a/patches.py b/patches.py new file mode 100644 index 0000000..851539d --- /dev/null +++ b/patches.py @@ -0,0 +1,8 @@ +PATCH_DATA = {"wifi": """# Remove WLAN whitelist +79E0EDD7-9D1D-4F41-AE1A-F896169E5216 10 O:C83:90E9 +79E0EDD7-9D1D-4F41-AE1A-F896169E5216 10 O:CD9:00 +79E0EDD7-9D1D-4F41-AE1A-F896169E5216 10 O:CEE:EB +""", +"advanced": """# Replace Date tab with Advanced tab +32442D09-1D11-4E27-8AAB-90FE6ACB0489 10 P:04320b483cc2e14abb16a73fadda475f:778b1d826d24964e8e103467d56ab1ba +"""} \ No newline at end of file diff --git a/readbios.py b/readbios.py new file mode 100644 index 0000000..0b56815 --- /dev/null +++ b/readbios.py @@ -0,0 +1,31 @@ +from pathlib import Path + +from clishared import cli_join_rom, cli_read_rom + +print("ThinkPad X230 Bios Dumper v1") + +project_name = input("What would you like to name your project? ") +project_path = Path(project_name).resolve() + +if project_path.exists(): + if project_path.is_file(): + raise FileExistsError(f"Cannot create project folder {project_path}, a file already exists here.") + elif project_path.is_dir(): + response = input("Folder already exists at this location. Proceed and risk overwriting existing files? (y/N): ") + if not response or (response.strip()[0] not in ['y', 'Y']): + raise FileExistsError(f"Cannot create project folder {project_path}, a directory already exists here.") +else: + project_path.mkdir() + +clean_path = project_path / "clean" +if not clean_path.exists(): + clean_path.mkdir() + +cli_read_rom("bottom", clean_path) +# Bell to remind user to move clip +print("\a", end="") +cli_read_rom("top", clean_path) + +cli_join_rom(clean_path) + +print("All done.") \ No newline at end of file diff --git a/romtools.py b/romtools.py new file mode 100644 index 0000000..ed5ca93 --- /dev/null +++ b/romtools.py @@ -0,0 +1,63 @@ +import subprocess +from pathlib import Path + +from patches import PATCH_DATA + +TOP_ROM_TYPE = "MX25L3206E/MX25L3208E" +BOTTOM_ROM_TYPE = "MX25L6406E/MX25L6408E" +PROGRAMMER = "ch341a_spi" + +UEFIPATCH_LOCATION = "./UEFIPatch_0.28.0_mac/UEFIPatch" + +LOG_FILE_NAME = "x230patch.log" + +class ROMError(Exception): + pass + +def log(text): + with open(LOG_FILE_NAME, 'a') as f: + f.write(text) + +def check_rom(chip_loc): + if chip_loc == "top": + chip = TOP_ROM_TYPE + elif chip_loc == "bottom": + chip = BOTTOM_ROM_TYPE + + fr = subprocess.run(["flashrom", "-p", PROGRAMMER, "-c", chip], capture_output=True) + log(fr.stdout.decode('ascii')) + if fr.returncode != 0: + raise ROMError("ROM not found.") + +def read_rom(chip_loc, save_dir, file_name): + if chip_loc == "top": + chip = TOP_ROM_TYPE + elif chip_loc == "bottom": + chip = BOTTOM_ROM_TYPE + + fr = subprocess.run(["flashrom", "-p", PROGRAMMER, "-c", chip, "-r", file_name], cwd=save_dir, capture_output=True) + log(fr.stdout.decode('ascii')) + if fr.returncode != 0: + raise ROMError + +def write_rom(chip_loc, open_dir, file_name): + if chip_loc == "top": + chip = TOP_ROM_TYPE + elif chip_loc == "bottom": + chip = BOTTOM_ROM_TYPE + + fr = subprocess.run(["flashrom", "-p", PROGRAMMER, "-c", chip, "-w", file_name], cwd=open_dir, capture_output=True) + log(fr.stdout.decode('ascii')) + if fr.returncode != 0: + raise ROMError + +def patch_rom(patches, open_path, save_path): + patches_path = save_path.parent / "patches.txt" + with open(patches_path, 'w') as patches_file: + for patch in patches: + patches_file.write(PATCH_DATA[patch]) + + up = subprocess.run([UEFIPATCH_LOCATION, open_path, patches_path, "-o", save_path], capture_output=True) + log(up.stdout.decode('ascii')) + if up.returncode != 0: + raise ROMError("Patching failed.") \ No newline at end of file diff --git a/writebios.py b/writebios.py new file mode 100644 index 0000000..a06e863 --- /dev/null +++ b/writebios.py @@ -0,0 +1,21 @@ +import os, subprocess +from pathlib import Path + +from clishared import cli_split_rom, cli_write_rom + +print("ThinkPad X230 Bios Writer v1") + +project_name = input("Where is your project folder? ") +project_path = Path(project_name).resolve() + +patched_path = project_path / "patched" +clean_path = project_path / "clean" + +cli_split_rom(patched_path) + +cli_write_rom("bottom", patched_path) +# Bell to remind user to move clip +print("\a", end="") +cli_write_rom("top", patched_path) + +print("All done.") \ No newline at end of file