Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,22 @@ make install
```
The compiled binaries can be found in `qemu-build` directory.

On a clean **Archlinux** VM run

```
sudo pacman -Syu
sudo pacman -S qemu-emulators-full ninja base-devel git #qemu-full will install all the archs

# the above will setup all the configures for archs but just for fun here

git clone --depth 1 https://github.com/qemu/QEMU
cd qemu
mkdir build
cd build
./configure --prefix=$(realpath ../qemu-build) --static --target-list=arm-softmmu,mips-softmmu,mipsel-softmmu --disable-smartcard --disable-libusb --disable-usb-redir
make
sudo make install
```
Note: qemu-emulators-full package provides all the archs

Note: It should also be possible to compile qemu statically on an alpine system but this hasn't been tested. In general compiling on alpine is preferred to Ubuntu as the former comes with musl libc which is better at static linkage than glibc on Ubuntu.
72 changes: 63 additions & 9 deletions fat.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,143 +30,197 @@ def show_banner():


def get_next_unused_iid():
for i in range(1, 1000):
if not os.path.isdir(os.path.join(firmadyne_path, "scratch", str(i))):
return str(i)

for index in range(1, 1000):

if not os.path.isdir(os.path.join(firmadyne_path, "scratch", str(index))):

return str(index)

return ""


def run_extractor(firm_name):

print ("[+] Firmware:", os.path.basename(firm_name))

print ("[+] Extracting the firmware...")

extractor_cmd = os.path.join(firmadyne_path, "sources/extractor/extractor.py")
extractor_command = os.path.join(firmadyne_path, "sources/extractor/extractor.py")
extractor_args = [
"--",
extractor_cmd,
extractor_command,
"-np",
"-nk",
firm_name,
os.path.join(firmadyne_path, "images")
]

child = pexpect.spawn("sudo", extractor_args, timeout=None)

child.sendline(sudo_pass)

child.expect_exact("Tag: ")

tag = child.readline().strip().decode("utf8")

child.expect_exact(pexpect.EOF)

image_tgz = os.path.join(firmadyne_path, "images", tag + ".tar.gz")

if os.path.isfile(image_tgz):

iid = get_next_unused_iid()

if iid == "" or os.path.isfile(os.path.join(os.path.dirname(image_tgz), iid + ".tar.gz")):

print ("[!] Too many stale images")
print ("[!] Please run reset.py or manually delete the contents of the scratch/ and images/ directory")

return ""

os.rename(image_tgz, os.path.join(os.path.dirname(image_tgz), iid + ".tar.gz"))

print ("[+] Image ID:", iid)

return iid

return ""


def identify_arch(image_id):

print ("[+] Identifying architecture...")

identfy_arch_cmd = os.path.join(firmadyne_path, "scripts/getArch.sh")
identfy_arch_args = [
os.path.join(firmadyne_path, "images", image_id + ".tar.gz")
]

child = pexpect.spawn(identfy_arch_cmd, identfy_arch_args, cwd=firmadyne_path)

child.expect_exact(":")

arch = child.readline().strip().decode("utf8")

print ("[+] Architecture: " + arch)

try:
child.expect_exact(pexpect.EOF)

except Exception as e:
child.close(force=True)

return arch


def make_image(arch, image_id):

print ("[+] Building QEMU disk image...")

makeimage_cmd = os.path.join(firmadyne_path, "scripts/makeImage.sh")
makeimage_args = ["--", makeimage_cmd, image_id, arch]

child = pexpect.spawn("sudo", makeimage_args, cwd=firmadyne_path)
child.sendline(sudo_pass)
child.expect_exact(pexpect.EOF)


def infer_network(arch, image_id, qemu_dir):

print ("[+] Setting up the network connection, please standby...")

network_cmd = os.path.join(firmadyne_path, "scripts/inferNetwork.sh")
network_args = [image_id, arch]

if qemu_dir:

path = os.environ["PATH"]
newpath = qemu_dir + ":" + path
child = pexpect.spawn(network_cmd, network_args, cwd=firmadyne_path, env={"PATH":newpath})
else:

child = pexpect.spawn(network_cmd, network_args, cwd=firmadyne_path)

child.expect_exact("Interfaces:", timeout=None)
interfaces = child.readline().strip().decode("utf8")

print ("[+] Network interfaces:", interfaces)

child.expect_exact(pexpect.EOF)


def final_run(image_id, arch, qemu_dir):

runsh_path = os.path.join(firmadyne_path, "scratch", image_id, "run.sh")
if not os.path.isfile(runsh_path):

print ("[!] Cannot emulate firmware, run.sh not generated")

return

if qemu_dir:

if arch == "armel":

arch = "arm"
elif arch == "mipseb":

arch = "mips"
print ("[+] Using qemu-system-{0} from {1}".format(arch, qemu_dir))
cmd = 'sed -i "/QEMU=/c\QEMU={0}/qemu-system-{1}" "{2}"'.format(qemu_dir, arch, runsh_path)
pexpect.run(cmd)

command = 'sed -i "/QEMU=/c\QEMU={0}/qemu-system-{1}" "{2}"'.format(qemu_dir, arch, runsh_path)

pexpect.run(command)

print ("[+] All set! Press ENTER to run the firmware...")
input ("[+] When running, press Ctrl + A X to terminate qemu")

print ("[+] Command line:", runsh_path)
run_cmd = ["--", runsh_path]
child = pexpect.spawn("sudo", run_cmd, cwd=firmadyne_path)

run_command = ["--", runsh_path]

child = pexpect.spawn("sudo", run_command, cwd=firmadyne_path)
child.sendline(sudo_pass)
child.interact()


def main():

show_banner()

parser = argparse.ArgumentParser()
parser.add_argument("firm_path", help="The path to the firmware image", type=str)
parser.add_argument("-q", "--qemu", metavar="qemu_path", help="The qemu version to use (must exist within qemu-builds directory). If not specified, the qemu version installed system-wide will be used", type=str)

args = parser.parse_args()

qemu_ver = args.qemu
qemu_dir = None

if qemu_ver:

qemu_dir = os.path.abspath(os.path.join("qemu-builds", qemu_ver))
if not os.path.isdir(qemu_dir):

print ("[!] Directory {0} not found".format(qemu_dir))
print ("[+] Using system qemu")
qemu_dir = None

image_id = run_extractor(args.firm_path)

if image_id == "":

print ("[!] Image extraction failed")
else:

arch = identify_arch(image_id)

make_image(arch, image_id)
infer_network(arch, image_id, qemu_dir)
final_run(image_id, arch, qemu_dir)


if __name__ == "__main__":

main()
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pexpect
argparse
os_sys
configparse
6 changes: 5 additions & 1 deletion reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@

config = ConfigParser()
config.read("fat.config")

firmadyne_path = config["DEFAULT"].get("firmadyne_path", "")

sudo_pass = config["DEFAULT"].get("sudo_password", "")

print ("[+] Cleaning previous images and created files by firmadyne")

child = pexpect.spawn("/bin/sh" , ["-c", "sudo rm -rf " + os.path.join(firmadyne_path, "images/*.tar.gz")])
child.sendline(sudo_pass)
child.expect_exact(pexpect.EOF)

child = pexpect.spawn("/bin/sh", ["-c", "sudo rm -rf " + os.path.join(firmadyne_path, "scratch/*")])
child.sendline(sudo_pass)
child.expect_exact(pexpect.EOF)
print ("[+] All done. Go ahead and run fat.py to continue firmware analysis")

print ("[+] All done. Now you can go ahead and run fat.py to continue firmware analysis")