---
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` 中
但這邊發現

似乎 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

以下看看在這一階段時, 各目錄底下是什麼東東
```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();
```