--- title: Back to analysis rc - 3 description: 分析 R6400 中的 rc tags: firmadyne, ARM lang: zh_tw --- # Back to analysis rc - 3 [TOC] # Intro 在這次分析前已經有過了兩篇筆記 - [Back to analysis rc - 1](https://hackmd.io/PvEXTp5TTJmpgE5xwBA6Ig) - [Back to analysis rc - 2](https://hackmd.io/lUw41ugKRSS4wpmo8ccicg) 但這兩次動靜態交叉分析是在以下前提進行的 - 缺少 source code - 缺少 decompiler 而這次分析則是兩者有利條件都有的情況下進行的, 會從以下前提開始進行 - 已知 source code (查 Netgear GPL 就能拿到) - kernel 使用基於 [firmadyne/kernel-v4.1](https://github.com/firmadyne/kernel-v4.1) 自己多改一行, 也就是在 `init/main.c` 做以下更改 ```c if (!try_to_run_init_process("/sbin/preinit") || !try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0; ``` 且是在 ubuntu 14.04 底下 compile kernel - 在 ubuntu 16.04 使用 firmadyne 框架進行分析 ## main() 以下是 `/sbin/preinit` 執行的片段: ```c if (strstr(base, "preinit")) { mount("devfs", "/dev", "tmpfs", MS_MGC_VAL, NULL); /* Michael added */ // mknod("/dev/nvram", S_IRWXU|S_IFCHR, makedev(252, 0)); /* mknod("/dev/mtdblock16", S_IRWXU|S_IFBLK, makedev(31, 16)); mknod("/dev/mtdblock17", S_IRWXU|S_IFBLK, makedev(31, 17)); mknod("/dev/mtd16", S_IRWXU|S_IFCHR, makedev(90, 32)); mknod("/dev/mtd16ro", S_IRWXU|S_IFCHR, makedev(90, 33)); mknod("/dev/mtd17", S_IRWXU|S_IFCHR, makedev(90, 34)); mknod("/dev/mtd17ro", S_IRWXU|S_IFCHR, makedev(90, 35));*/ /* Michael ended */ mknod("/dev/console", S_IRWXU|S_IFCHR, makedev(5, 1)); mknod("/dev/aglog", S_IRWXU|S_IFCHR, makedev(AGLOG_MAJOR_NUM, 0)); mknod("/dev/wps_led", S_IRWXU|S_IFCHR, makedev(WPS_LED_MAJOR_NUM, 0)); #ifdef __CONFIG_UTELNETD__ mkdir("/dev/pts", 0777); mknod("/dev/pts/ptmx", S_IRWXU|S_IFCHR, makedev(5, 2)); mknod("/dev/pts/0", S_IRWXU|S_IFCHR, makedev(136, 0)); mknod("/dev/pts/1", S_IRWXU|S_IFCHR, makedev(136, 1)); #endif /* __CONFIG_UTELNETD__ */ /* Foxconn added start pling 12/26/2011, for WNDR4000AC */ #if (defined GPIO_EXT_CTRL) mknod("/dev/ext_led", S_IRWXU|S_IFCHR, makedev(EXT_LED_MAJOR_NUM, 0)); #endif /* Foxconn added end pling 12/26/2011 */ #else /* LINUX26 */ if (strstr(base, "init")) { #endif /* LINUX26 */ main_loop(); return 0; } ``` 雖然在開放出來的 cpio filesystem 中的 `/dev` `/sys` 底下沒有任何東西, 但實際動態分析發現 到 `main_loop` 之前的所有 `mount` `mknod` 都是成功執行的 ## main_loop() 直接進 sysinit() ### sysinit() part1 ```c uname(&unamebuf); lx_rel = unamebuf.release; if (memcmp(lx_rel, "2.6", 3) == 0) { int fd; if ((fd = open("/dev/console", O_RDWR)) < 0) { if (memcmp(lx_rel, "2.6.36", 6) == 0) { mount("devfs", "/dev", "devtmpfs", MS_MGC_VAL, NULL); } else { mount("devfs", "/dev", "tmpfs", MS_MGC_VAL, NULL); mknod("/dev/console", S_IRWXU|S_IFCHR, makedev(5, 1)); } } else { close(fd); } } /* /proc */ mount("proc", "/proc", "proc", MS_MGC_VAL, NULL); #ifdef LINUX26 mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL); #endif /* LINUX26 */ /* /tmp */ mount("ramfs", "/tmp", "ramfs", MS_MGC_VAL, NULL); /* /var */ mkdir("/tmp/var", 0777); mkdir("/var/lock", 0777); mkdir("/var/log", 0777); mkdir("/var/run", 0777); mkdir("/var/tmp", 0777); mkdir("/tmp/media", 0777); /* Foxconn added start by Kathy, 10/14/2013 @ Facebook WiFi */ mkdir("/tmp/fbwifi", 0777); /* Foxconn added end by Kathy, 10/14/2013 @ Facebook WiFi */ #ifdef __CONFIG_UTELNETD__ /* If kernel enable unix908 pty then we have to make following things. */ mkdir("/dev/pts", 0777); if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, NULL) == 0) { /* pty master */ mknod("/dev/ptmx", S_IRWXU|S_IFCHR, makedev(5, 2)); } else { rmdir("/dev/pts"); } #endif /* LINUX2636 && __CONFIG_UTELNETD__ */ #ifdef BCMQOS mkdir("/tmp/qos", 0777); #endif ``` 再度的, `mount` `mknod` 都成功執行了 ```c /* Setup console */ if (console_init()) noconsole = 1; ``` 進入 `console_init()` ### console_init() ```c /* Clean up */ ioctl(0, TIOCNOTTY, 0); ``` `ioctl` return 了 -1 不確定會不會造成錯誤 根據 `man 4 tty` > This ioctl(2) call only works on file descriptors connected to /dev/tty. It is used by daemon processes when they are invoked by a user at a terminal. The process attempts to open /dev/tty. If the open succeeds, it detaches itself from the terminal by using TIOCNOTTY, while if the open fails, it is obviously not attached to a terminal and does not need to detach itself. > 好像 fail 也沒差, 頂多表示原本就沒有 controlling terminal 罷了 ```c close(0); close(1); close(2); setsid(); ``` `close` 都執行成功 `setsid` 則回傳 1, 表示執行成功, new session ID 為 1 此 process 的 pid 為 1, 且成為 session leader 以及 process group leader ```c if ((fd = open(_PATH_CONSOLE, O_RDWR)) < 0) { perror(_PATH_CONSOLE); return errno; } dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); ``` `open` 回傳 0, 表示成功執行 `dup2` 也都執行成功 ```c ioctl(0, TIOCSCTTY, 1); tcsetpgrp(0, getpgrp()); set_term(0); return 0; ``` `ioctl` return 0, 成功執行 `getpgrp` return 1, 其回傳 process group ID, 也就是 1, 是成功執行的 剩下的也都成功執行 ### sysinit() part2 ```c /* Setup console */ if (console_init()) noconsole = 1; ``` `console_init()` 回傳 0, `noconsole` 仍舊為 0 ```c #ifdef LINUX26 mkdir("/dev/shm", 0777); eval("/sbin/hotplug2", "--coldplug"); #endif /* LINUX26 */ ``` `eval` 這邊順利執行過去了, 但實際上做了什麼這邊先不細追 ```c if ((loglevel = nvram_get("console_loglevel"))) klogctl(8, NULL, atoi(loglevel)); else klogctl(8, NULL, 1); ``` 但就在這邊 `nvram_get` 沒有順利執行, 而是亂跳到了其他 code 段上 (0xC4B0) (0xC4B0: ) (`if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, NULL) == 0) {`) 而這是整體來說第一次 call 到 `nvram_get` 的時機點, 他就這樣壞掉了 而 `nvram_get` 的位址是 0x9CDC 在原本 rc 這支 binary 的 0x9CDC 位址, 的確也標出這裡是 `nvram_get`, 是需要外部引入的 firmadyne 的架構則是在 kernel `init/main.c` 加上了 ```c const char *envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", "LD_PRELOAD=/firmadyne/libnvram.so", NULL, }; ``` 而此 `libnvram.so` 則是 firmadyne 自己寫的, 用意是來自行 fake 出 nvram 的行為 這邊會需要先執行一段 loading 的程序後, 方能跳到 `firmadyne/libnvram.so` 的 `nvram_get` 中 但這邊發現 ![](https://i.imgur.com/W00j5Qo.png) 似乎 loading 的過程把原本的 rc code 段蓋爛, 導致 bug 但就在某次 debug 時他沒爛掉, 得以繼續執行下去 ```c /* Modules */ uname(&name); snprintf(buf, sizeof(buf), "/lib/modules/%s", name.release); if (stat("/proc/modules", &tmp_stat) == 0 && stat(buf, &tmp_stat) == 0) { ... } /*Foxconn add start by Hank for enable WAN LED amber 12/07/2012*/ /*Foxconn add start by Hank for disable WAN LED blinking 12/07/2012*/ #if defined(R7000) system("/usr/sbin/et robowr 0x0 0x10 0x3000"); system("/usr/sbin/et robowr 0x0 0x12 0x78"); system("/usr/sbin/et robowr 0x0 0x14 0x01"); /* force port 0 to use LED function 1 */ #else /*Foxconn add end by Hank for disable WAN LED blinking 11/08/2012*/ /*Foxconn add end by Hank for enable WAN LED amber 12/07/2012*/ if (memcmp(lx_rel, "2.6.36", 6) == 0) { ... } system("echo 20480 > /proc/sys/vm/min_free_kbytes"); /*Bob added on 09/05/2013, Set min free memory to 20Mbytes in case allocate memory failed */ /* Set a sane date */ stime(&tm); dprintf("done\n"); } ``` 這邊順順執行完了 ## main_loop() part2 ```c /* Foxconn added start pling 03/20/2014 */ /* Router Spec Rev 12: disable/enable ethernet interface when dhcp server start */ eval("landown"); /* Foxconn added end pling 03/20/2014 */ /* Add loopback */ config_loopback(); /* Restore defaults if necessary */ //restore_defaults(); /* foxconn removed, zacker, 08/06/2010, move to sysinit() */ /* Convert deprecated variables */ convert_deprecated(); ``` 順利執行 ```c /* Upgrade NVRAM variables to MBSS mode */ upgrade_defaults(); ``` `upgrade_defaults()` 內部主要是一個 if 包住 ```c /* Check whether upgrade is required or not * If lan1_ifnames is not found in NVRAM , upgrade is required. */ if (!nvram_get("lan1_ifnames") && !RESTORE_DEFAULTS()) { ``` 但動態執行的結果是沒有進 if 後面就一直執行下去, 然後發現實際的 binary 中沒有以下這一段 code ```c /* Foxconn added end, Wins, 04/20/2011, @RU_IPTV */ /* Foxconn add start, Edward zhang, 09/14/2012, @add ARP PROTECTION support for RU SKU*/ if ((!is_russia_specific_support()) && (!is_china_specific_support())) { nvram_set(NVRAM_ARP_ENABLED, "disable"); } ``` 而有以下這一段 code ```c /* Foxconn add end, Edward zhang, 09/14/2012, @add ARP PROTECTION support for RU SKU*/ /* Foxconn add start, Max Ding, 02/26/2010 */ #ifdef RESTART_ALL_PROCESSES nvram_unset("restart_all_processes"); #endif ``` ```c /* Setup signal handlers */ signal_init(); signal(SIGHUP, rc_signal); signal(SIGUSR2, rc_signal); signal(SIGINT, rc_signal); signal(SIGALRM, rc_signal); signal(SIGUSR1, rc_signal); signal(SIGQUIT, rc_signal); /* Foxconn added by EricHuang, 11/24/2006 */ signal(SIGILL, rc_signal); //ppp restart sigemptyset(&sigset); ``` 這一段順利執行 ```c /* Give user a chance to run a shell before bringing up the rest of the system */ if (!noconsole) run_shell(1, 0); ``` 這邊有進 if ### run_shell(1, 0) ```c #if !defined(__CONFIG_BUSYBOX__) || !defined(CONFIG_FEATURE_SH_IS_NONE) pid_t run_shell(int timeout, int nowait) { pid_t pid; char tz[1000]; char *envp[] = { "TERM=vt100", "HOME=/", "PATH=/usr/bin:/bin:/usr/sbin:/sbin", "SHELL=" SHELL, "USER=root", tz, NULL }; int sig; /* Wait for user input */ cprintf("Hit enter to continue..."); if (waitfor(STDIN_FILENO, timeout) <= 0) return 0; switch ((pid = fork())) { case -1: perror("fork"); return 0; case 0: /* Reset signal handlers set for parent process */ for (sig = 0; sig < (_NSIG-1); sig++) signal(sig, SIG_DFL); /* Reopen console */ console_init(); /* Pass on TZ */ snprintf(tz, sizeof(tz), "TZ=%s", getenv("TZ")); /* Now run it. The new program will take over this PID, * so nothing further in init.c should be run. */ execve(SHELL, (char *[]) { "/bin/sh", NULL }, envp); /* We're still here? Some error happened. */ perror(SHELL); exit(errno); default: if (nowait) return pid; else { waitpid(pid, NULL, 0); return 0; } } } #else /* Busybox configured w/o a shell */ ``` 這邊會等 1 秒鐘, 有按 Enter 就讓你進 shell ![](https://i.imgur.com/Onc81lf.jpg) 以下看看在這一階段時, 各目錄底下是什麼東東 ```shell # ls /dev acos_nat_cli ptyp0 tty34 aglog ptyp1 tty35 brcmboard ptyp2 tty36 console ptyp3 tty37 cpu_dma_latency ptyp4 tty38 dsl_cpe_api ptyp5 tty39 full ptyp6 tty4 hw_random ptyp7 tty40 kmem ptyp8 tty41 kmsg ptyp9 tty42 loop-control ptypa tty43 loop0 ptypb tty44 loop1 ptypc tty45 loop2 ptypd tty46 loop3 ptype tty47 loop4 ptypf tty48 loop5 ram0 tty49 loop6 ram1 tty5 loop7 ram10 tty50 mem ram11 tty51 memory_bandwidth ram12 tty52 mtd0 ram13 tty53 mtd0ro ram14 tty54 mtd1 ram15 tty55 mtd10 ram2 tty56 mtd10ro ram3 tty57 mtd11 ram4 tty58 mtd11ro ram5 tty59 mtd1ro ram6 tty6 mtd2 ram7 tty60 mtd2ro ram8 tty61 mtd3 ram9 tty62 mtd3ro random tty63 mtd4 rtc0 tty7 mtd4ro sc_led tty8 mtd5 shm tty9 mtd5ro tca0 ttyS0 mtd6 tty ttyp0 mtd6ro tty0 ttyp1 mtd7 tty1 ttyp2 mtd7ro tty10 ttyp3 mtd8 tty11 ttyp4 mtd8ro tty12 ttyp5 mtd9 tty13 ttyp6 mtd9ro tty14 ttyp7 mtdblock0 tty15 ttyp8 mtdblock1 tty16 ttyp9 mtdblock10 tty17 ttypa mtdblock11 tty18 ttypb mtdblock2 tty19 ttypc mtdblock3 tty2 ttypd mtdblock4 tty20 ttype mtdblock5 tty21 ttypf mtdblock6 tty22 tun mtdblock7 tty23 urandom mtdblock8 tty24 vcs mtdblock9 tty25 vcs1 network_latency tty26 vcsa network_throughput tty27 vcsa1 null tty28 vda nvram tty29 vda1 pib tty3 vga_arbiter port tty30 wps_led ppp tty31 zero ptmx tty32 zybtnio pts tty33 ``` ```shell # ls -al drwxrwxr-x 17 1000 1000 4096 Oct 24 2019 . drwxrwxr-x 17 1000 1000 4096 Oct 24 2019 .. drwxrwxr-x 2 1000 1000 4096 May 6 2015 bin drwxrwxrwt 4 0 root 3980 Mar 23 2020 dev drwxr-xr-x 9 1000 1000 4096 Oct 24 2019 etc drwxr-xr-x 4 0 root 4096 Oct 24 2019 firmadyne drwxr-xr-x 4 1000 1000 4096 May 6 2015 lib drwx------ 2 0 root 16384 Oct 24 2019 lost+found lrwxrwxrwx 1 1000 1000 9 Oct 24 2019 media -> tmp/media drwxrwxr-x 2 1000 1000 4096 May 6 2015 mnt drwxrwxr-x 7 1000 1000 4096 May 6 2015 opt dr-xr-xr-x 39 0 root 0 Jan 1 00:00 proc drwxr-xr-x 2 1000 1000 4096 May 6 2015 sbin drwxr-xr-x 3 1000 1000 4096 May 6 2015 share dr-xr-xr-x 12 0 root 0 Mar 23 2020 sys drwxr-xr-x 6 0 root 0 Mar 23 2020 tmp drwxrwxr-x 11 1000 1000 4096 May 6 2015 usr lrwxrwxrwx 1 1000 1000 7 Oct 24 2019 var -> tmp/var drwxr-xr-x 7 1000 1000 24576 May 6 2015 www ``` ```shell # ls -al /tmp/ drwxr-xr-x 6 0 root 0 Mar 23 2020 . drwxrwxr-x 17 1000 1000 4096 Oct 24 2019 .. drwxr-xr-x 2 0 root 0 Mar 23 2020 fbwifi drwxr-xr-x 2 0 root 0 Mar 23 2020 media drwxr-xr-x 2 0 root 0 Mar 23 2020 qos drwxr-xr-x 6 0 root 0 Mar 23 2020 var ``` ``` # ls -al /tmp/var/ drwxr-xr-x 6 0 root 0 Mar 23 2020 . drwxr-xr-x 6 0 root 0 Mar 23 2020 .. drwxr-xr-x 2 0 root 0 Mar 23 2020 lock drwxr-xr-x 2 0 root 0 Mar 23 2020 log drwxr-xr-x 2 0 root 0 Mar 23 2020 run drwxr-xr-x 2 0 root 0 Mar 23 2020 tmp ``` ```shell # ls proc/ 1 8 interrupts rtk_vlan_support 10 9 iomem sched_debug 11 BtnMode ioports scsi 12 RstBtnCnt irq self 13 blankstatus kallsyms simple_config 14 br_igmpProxy kmsg slabinfo 15 btnCnt kpagecount softirqs 16 buddyinfo kpageflags stat 17 bus led sw_nat 18 cmdline loadavg swaps 19 consoles locks sys 2 cpu meminfo sysrq-trigger 3 cpuinfo misc sysvipc 37 crypto modules thread-self 4 device-tree mounts timer_list 425 devices mtd tty 439 diskstats net uptime 5 driver pagetypeinfo version 51 execdomains partitions vmallocinfo 52 fb push_button vmstat 53 filesystems quantum zoneinfo 6 fs rt3052 7 gpio rtk_promiscuous ``` ```shell # ifconfig lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MULTICAST MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) ``` ```shell # netstat -ale Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State Active UNIX domain sockets (servers and established) Proto RefCnt Flags Type State I-Node Path ``` ```shell # echo $$ 425 ``` ```shell # cd /proc/425/ # ls -al dr-xr-xr-x 7 0 root 0 Jan 1 00:05 . dr-xr-xr-x 39 0 root 0 Jan 1 00:00 .. -r-------- 1 0 root 0 Jan 1 00:11 auxv --w------- 1 0 root 0 Jan 1 00:11 clear_refs -r--r--r-- 1 0 root 0 Jan 1 00:11 cmdline -rw-r--r-- 1 0 root 0 Jan 1 00:11 comm -rw-r--r-- 1 0 root 0 Jan 1 00:11 coredump_filter lrwxrwxrwx 1 0 root 0 Jan 1 00:11 cwd -> /proc/425 -r-------- 1 0 root 0 Jan 1 00:11 environ lrwxrwxrwx 1 0 root 0 Jan 1 00:11 exe -> /bin/busybox dr-x------ 2 0 root 0 Jan 1 00:11 fd dr-x------ 2 0 root 0 Jan 1 00:11 fdinfo -r--r--r-- 1 0 root 0 Jan 1 00:11 limits -r--r--r-- 1 0 root 0 Jan 1 00:11 maps -rw------- 1 0 root 0 Jan 1 00:11 mem -r--r--r-- 1 0 root 0 Jan 1 00:11 mountinfo -r--r--r-- 1 0 root 0 Jan 1 00:11 mounts -r-------- 1 0 root 0 Jan 1 00:11 mountstats dr-xr-xr-x 6 0 root 0 Jan 1 00:11 net dr-x--x--x 2 0 root 0 Jan 1 00:11 ns -rw-r--r-- 1 0 root 0 Jan 1 00:11 oom_adj -r--r--r-- 1 0 root 0 Jan 1 00:11 oom_score -rw-r--r-- 1 0 root 0 Jan 1 00:11 oom_score_adj -r-------- 1 0 root 0 Jan 1 00:11 pagemap -r-------- 1 0 root 0 Jan 1 00:11 personality lrwxrwxrwx 1 0 root 0 Jan 1 00:11 root -> / -rw-r--r-- 1 0 root 0 Jan 1 00:11 sched -r--r--r-- 1 0 root 0 Jan 1 00:11 smaps -r--r--r-- 1 0 root 0 Jan 1 00:11 stat -r--r--r-- 1 0 root 0 Jan 1 00:11 statm -r--r--r-- 1 0 root 0 Jan 1 00:11 status -r-------- 1 0 root 0 Jan 1 00:11 syscall dr-xr-xr-x 3 0 root 0 Jan 1 00:11 task -r--r--r-- 1 0 root 0 Jan 1 00:11 wchan ``` ## main_loop() part3 ```c /* Get boardflags to see if VLAN is supported */ #ifdef __CONFIG_VLAN__ boardflags = strtoul(nvram_safe_get("boardflags"), NULL, 0); #endif /* __CONFIG_VLAN__ */ ... /* Foxconn added start pling 07/13/2009 */ /* create the USB semaphores */ #ifdef SAMBA_ENABLE usb_sem_init(); //[MJ] for 5G crash #endif ``` 這邊順利執行完畢 接下來是無限 for loop, 裡面包著 switch case 動態執行發現是先進第 2 個 case, 以下附上 code ```c case START: dprintf("START\n"); pmon_init(); ```