router firmware emulating(qemu)

ripemo·2025년 2월 6일

커널이미지 다운

mips32를 에뮬레이팅

» qemu-system-mips -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0"

또는

» qemu-system-mips -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0"

mips64를 에뮬레이팅

» qemu-system-mips64 -M malta -kernel vmlinux-2.6.32-5-5kc-malta -hda debian_squeeze_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0"

또는

» qemu-system-mips64 -M malta -kernel vmlinux-3.2.0-4-5kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0"

ssh 연결

» qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0" -netdev user,id=mynet,hostfwd=tcp::2022-:22,hostfwd=tcp::2080-:80 -device e1000,netdev=mynet
  • 2022로 22포트, 2080으로 80포트 연결
  • ssh root@localhost -p 2022 로 접속

파일시스템

  • nextu 사의 fleta ax1500 router firmware를 홈페이지에서 다운로드
  • 버전은 1.0.2

binwalk로 파일시스템 추출

» binwalk -e AX1500_1.0.2.bin
  • 추출 과정에서 WARNING: Extractor.execute failed to run external extractor 'sasquatch -p 1 -le -d 'squashfs-root-0' '%e'': [Errno 2] No such file or directory: 'sasquatch', 'sasquatch -p 1 -le -d 'squashfs-root-0' '%e'' might not be installed correctly 과 같은 오류가 발생
  • sasquatch로 인한 오류로 판단하여 설치 후 진행하려 했는데 잘 되지 않음. 따라서 직접 추출

dd를 이용하여 직접 추출

» binwalk AX1500_1.0.2.bin                                                                 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
10264         0x2818          LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 5194424 bytes
1745954       0x1AA422        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 6065580 bytes, 790 inodes, blocksize: 131072 bytes, created: 2038-05-06 08:36:16
  • binwalk를 이용해 인덱스를 확인한 후 dd를 이용해 수동으로 추출
» dd if=AX1500_1.0.2.bin bs=1 skip=1745954 of=squashfs.img
  • if : 추출할 펌웨어

  • bs : block size. 한 번에 읽고 쓰는 데이터 블록의 크기

  • skip : 해당 주소부터 추출(10진수)

  • of : 추출 후 이름

  • 추출된 img 파일을 unsquashfs 를 이용해 파일시스템 추출

» sudo unsquashfs -d squashfs-root squashfs.img
  • 오류 발생할 수 있기 때문에 sudo로 진행
» ls                                                                                                                      
AX1500_1.0.2.bin  squashfs.img  squashfs-root

binwalk에서 보이는 LZMA 압축데이터를 분석해 본 결과 에뮬에 필요한 커널인 것이라고 판단했으나 추출에 실패해서 포기. 위의 커널이미지 사용

파일 시스템 전송

  • scp로 파일시스템을 전송.

chroot 환경 구성

  • 여기서는 두가지 환경으로 나누어 설명하겠다.
    첫 번째는 위에서 설명한 qemu-system 을 통해 전체 시스템을 가상화하는 방법.
    두 번째는 qemu-user 를 통해 개별 실행 파일을 에뮬레이션

qemu-system

  • 위에서 qemu-system을 이용해 환경을 세팅했다.
  • 현재 환경은 mipsel
root@debian-mipsel:~# uname -a
Linux debian-mipsel 2.6.32-5-4kc-malta #1 Tue Sep 24 01:20:35 UTC 2013 mips GNU/Linux
  • 따라서 chroot통해 추출한 파일시스템을 root 환경으로 구성해주면 바이너리가 잘 작동
root@debian-mipsel:~/squashfs-root# ls
bin  dev  etc  home  init  jffs2  lib  mnt  proc  qemu-mipsel-static  root  sys  tmp  usr  var	web

root@debian-mipsel:~/squashfs-root# chroot . busybox
BusyBox v1.24.1 (2023-09-26 16:15:21 KST) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2015.
Licensed under GPLv2. See source distribution for detailed
copyright notices.

Usage: busybox [function [arguments]...]
   or: busybox --list
   or: function [arguments]...

	BusyBox is a multi-call binary that combines many common Unix
	utilities into a single executable.  Most people will create a
	link to busybox for each function they wish to use and BusyBox
	will act like whatever it was invoked as.

Currently defined functions:
	ash, awk, bunzip2, bzcat, cat, chmod, cp, cut, date, depmod,
	dnsdomainname, echo, expr, false, free, grep, halt, head, hostname,
	ifconfig, init, insmod, ip, kill, killall, klogd, ln, login, ls, lsmod,
	lzop, lzopcat, mkdir, modprobe, mount, ping, poweroff, ps, readahead,
	reboot, renice, rm, rmmod, route, sed, sh, sleep, syslogd, tail,
	telnetd, tftp, tftpd, top, true, umount, unlzop, unxz, uptime, vconfig,
	vi, wc, xz, xzcat
  • /bin/sh로 chroot 환경에서의 셸
root@debian-mipsel:~/squashfs-root# chroot . ./bin/sh

# ls
bin                 init                proc                tmp
dev                 jffs2               qemu-mipsel-static  usr
etc                 lib                 root                var
home                mnt                 sys                 web

qemu-user

  • 해당 방법은 우분투 x86_64에서 진행하였다.
  • qemu-user 환경에서는 호스트 커널을 사용하기 때문에 공유기 펌웨어 자체만으로 동작할 수 없으므로 호스트의 dev,proc,sys를 mount해주어야 함
» sudo mount --bind /proc ./proc
» sudo mount --bind /dev ./dev                           
» sudo mount --bind /sys ./sys
  • 아키텍쳐가 다르므로 알맞는 qemu 파일을 chroot 할 루트 디렉토리로 옮겨야 한다. 로컬 환경의 /usr/bin 을 참조하면 chroot 에서 접근할 수 없으므로 옮겨줘야 함
» sudo cp /usr/bin/qemu-mipsel-static ./
  • 그 후 qemu 파일을 이용하여 busybox 를 실행하면 잘 실행되는 것을 확인 할 수 있음
» sudo chroot . ./qemu-mipsel-static ./bin/busybox                                                    

BusyBox v1.24.1 (2023-09-26 16:15:21 KST) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2015.
Licensed under GPLv2. See source distribution for detailed
copyright notices.

Usage: busybox [function [arguments]...]
   or: busybox --list
   or: function [arguments]...

	BusyBox is a multi-call binary that combines many common Unix
	utilities into a single executable.  Most people will create a
	link to busybox for each function they wish to use and BusyBox
	will act like whatever it was invoked as.

Currently defined functions:
	ash, awk, bunzip2, bzcat, cat, chmod, cp, cut, date, depmod,
	dnsdomainname, echo, expr, false, free, grep, halt, head, hostname,
	ifconfig, init, insmod, ip, kill, killall, klogd, ln, login, ls, lsmod,
	lzop, lzopcat, mkdir, modprobe, mount, ping, poweroff, ps, readahead,
	reboot, renice, rm, rmmod, route, sed, sh, sleep, syslogd, tail,
	telnetd, tftp, tftpd, top, true, umount, unlzop, unxz, uptime, vconfig,
	vi, wc, xz, xzcat
  • 동일하게 /bin/sh를 이용해 chroot 환경에서의 셸
» sudo chroot . ./qemu-mipsel-static ./bin/sh                                                             

# ls
bin                 init                proc                tmp
dev                 jffs2               qemu-mipsel-static  usr
etc                 lib                 root                var
home                mnt                 sys                 web
#

run web binary

  • 일반적으로 공유기 관리자웹페이지에 접근을 해야하므로 web binary를 실행해야 함
  • 해당 기기에는 /etc/init.d 밑에 rcS와 rcS_GW가 있었다.
  • rcS sh 파일에서 boa라는 web 서버를 실행시키는 것을 확인. boa 바이너리를 실행해보았다.
# ./bin/boa
Read hw setting header failed!
Invalid hw setting signature [sig=]!
Initialize AP MIB failed!asp_init:1008
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault (core dumped)
  • 해당 오류를 해결하기 위해 boa 바이너리 분석

    • analyzing boa

    • boa 바이너리에서 string을 검색하여 위 오류를 검색했지만 찾지 못했다. 그래서 시스템 단에서 발생하는 문제인 것이라 생각하고 qemu-system과 qemu-user를 변경해보고 커널을 변경하는 등 많은 시도를 해보았지만 여전히 오류 발생.
    • 실제로 문제는 boa 바이너리에서 발생하는게 맞았고 "Initialize AP MIB failed!asp_init:1008" 해당 오류는 boa 바이너리에 있었음. 다른 문자열은 라이브러리에서 링킹되기 때문에 찾지 못했다.
    • boa - sub_40d710

      • apmib_init 이라는 함수의 return 값이 1이어야 함

      • apmib_init 함수에 들어가면

        int apmib_init(void){
        	return off_45A068();
        }

        이렇게만 나와있기 때문에 해당하는 라이브러리에서 apmib_init 내용을 찾아줘야 함

      • 파일시스템의 lib 폴더의 라이브러리에서 apmib_init 검색

        » grep -r "apmib_init" ./lib/
        
        grep: ./lib/libapmib.so: binary file matches
      • apmib_init 함수 안의 apmib_hwconf 함수에서 나머지 오류 메시지 확인

    • libapmib.so - apmib_init

    int apmib_init()
      {
        int v0; // $v0
        int v2; // $v0
        int v3; // $a0
        int v4; // $a1
        int v5; // $v0
        int v6; // $v0
    
        apmib_sem_lock();
        if ( !pMib )
        {
          v0 = apmib_hwconf();                        
          if ( !v0 )
          {
      LABEL_3:
            apmib_sem_unlock();
            return 0;
          }
          fbss = v0;
          v2 = apmib_dsconf();
          if ( !v2 )
          {
            v3 = fbss;
            v4 = 0;
      LABEL_6:
            apmib_shm_free(v3, v4);
            goto LABEL_3;
          }
          pMibDef = v2;
          v5 = apmib_csconf();
          if ( !v5 )
          {
            apmib_shm_free(fbss, 0);
            v4 = 1;
            v3 = pMibDef;
            goto LABEL_6;
          }
          pMib = v5;
          v6 = apmib_customerHwconf();
          if ( !v6 )
          {
            apmib_shm_free(fbss, 0);
            apmib_shm_free(pMibDef, 1);
            v4 = 2;
            v3 = pMib;
            goto LABEL_6;
          }
          pCustomerHwSetting = v6;
        }
        apmib_sem_unlock();
        return 1;
      }
      • apmib_init의 return 값은 1이어야 한다고 했다.
        하지만 지금 apmib_hwconf 함수에서 0을 return 하면서 내부 블록에서 return 0으로 종료된다.
    • libapmib.so - apmib_init 의 오류 발생 부분

      int apmib_hwconf()
      {
      .
      .
      .
      if ( read_mtdblock0((int)v20, 0x20000, 6) )
        compress_hw_setting = memcmp(v20, &off_A9AC, 2) && memcmp(v20, &off_A9B0, 2) && memcmp(v20, &off_A9B4, 2);
      else
        puts("Read hw setting header failed!");
      if ( !compress_hw_setting )
      {
        if ( !read_mtdblock0((int)&hsHeader, 0x20000, 6) )
          return 0;
      .
      .
      .
      }
    • read_mtdblock0의 내용은 mtdblock0에서 특정 인덱스만큼 값을 읽어오는 것이다.
      mtdblock0 에 대해 찾아본 결과 커널과 관련이 있는 부분이라고 판단하여 qemu를 실행할 때 펌웨어에서 커널을 추출하여 에뮬레이팅하려고 계획.
      하지만 bin 파일을 binwalk 했을 때 커널을 제대로 추출할 수 없었음
      따라서 FAT를 이용해 에뮬레이팅하기로 결정
profile
hackyFrog

0개의 댓글