openwrt路由器启动流程 前言 只做了大致流程的分析
总结 通过/etc/rc.d下的链接执行**/etc/init.d**下的初始化服务,不过有的固件并没有init.d目录,因此还是得特殊情况特殊看待
流程 /sbin/init->第一次/sbin/procd->/sbin/preinit->第二次/sbin/procd
分析 /sbin/init 首先第一个启动的程序便是/sbin/init初始化程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int __cdecl main (int argc, const char **argv, const char **envp) { int v3; int v4; int v5; int v6; int v7; sigaction (15 , &asc_1244C, 0 ); sigaction (10 , &asc_1244C, 0 ); v3 = sigaction (12 , &asc_1244C, 0 ); v4 = sub_9110 (v3); sub_903C (v4); v5 = sub_9C04 (1 ); v6 = uloop_init (v5); v7 = sub_94D0 (v6); uloop_run (v7); return 0 ; }
sub_9110 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 int sub_9110 () { int result; int v1; struct stat v2; result = getpid (); if ( result == 1 ) { mount ("proc" , "/proc" , "proc" , 0x400 u, 0 ); mount ("sysfs" , "/sys" , "sysfs" , 0x400 u, 0 ); mount ("tmpfs" , "/tmp" , "tmpfs" , 0x406 u, 0 ); mkdir ("/tmp/run" , 0x1FF u); mkdir ("/tmp/lock" , 0x1FF u); mkdir ("/tmp/state" , 0x1FF u); symlink ("/tmp" , "/var" ); mount ("tmpfs" , "/dev" , "tmpfs" , 0x400 u, "mode=0755,size=512K" ); mkdir ("/dev/shm" , 0x1ED u); mkdir ("/dev/pts" , 0x1ED u); mount ("devpts" , "/dev/pts" , "devpts" , 0x400 u, "mode=600" ); sub_98F0 ("*" , 384 ); mknod ("/dev/null" , 0x1B6 u, 0x103 uLL); setenv ("PATH" , "/bin:/sbin:/usr/bin:/usr/sbin" , 1 ); if ( stat ("/dev/console" , &v2) ) { syslog (0 , "Failed to stat %s\n" , "/dev/console" ); fprintf ((FILE *)stderr, "procd: Failed to stat %s\n" , "/dev/console" ); } else { v1 = open ("/dev/console" , 2 ); if ( v1 < 0 ) v1 = open ("/dev/null" , 2 ); dup2 (v1, 0 ); dup2 (v1, 1 ); dup2 (v1, 2 ); if ( (unsigned int )v1 > 2 ) close (v1); } syslog (0 , "Console is alive\n" ); return fputs ("procd: Console is alive\n" , (FILE *)stderr); } return result; }
可以看到要求当前程序的pid为1,在仿真时,或许可以直接把其patch掉,挂载多个文件系统,创建目录和符号链接,设置模式,大小和权限,之后调用sub_98F0函数
sub_98F0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int __fastcall sub_98F0 (const char *a1, int a2, int a3) { size_t v6; _BYTE *v7; _DWORD v8[7 ]; v8[0 ] = a2; v8[1 ] = a3; if ( chdir ("/dev" ) ) return 1 ; v6 = strlen (a1); v7 = malloc (v6 + 2 ); *v7 = '*' ; strcpy (v7 + 1 , a1); v8[0 ] = v7; addrxx = (int )v8; dword_144A4 = 1 ; dword_12460 = a2; sub_9688 (1 ); sub_9688 (0 ); chdir ("/" ); return 0 ; }
其中的sub_9688函数,是为了遍历指定目录,读取符号链接,匹配特定模式,并创建相应的设备节点。
参数为 0:
路径为 /sys/dev/char
。
创建字符设备节点(v14 = 0x2000
)。
参数为 1:
路径为 "/sys/dev/block"
。
创建块设备节点(v14 = 24576
,即 0x6000
)。
sub_903C 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void sub_903C () { int v0; int v1; char buf[1016 ]; regex_t v3; regmatch_t v4; int v5; int v6; v0 = open ("/proc/cmdline" , 0 ); v1 = v0; if ( v0 >= 0 ) { buf[read (v0, buf, 0x3FF u)] = 0 ; close (v1); regcomp (&v3, "init_debug=([0-9]+)" , 1 ); if ( !regexec (&v3, buf, 2u , &v4, 0 ) ) { buf[v6] = 0 ; dword_12474 = atoi (&buf[v5]); } regfree (&v3); } }
此函数的作用是解析内核命令行参数,提取 init_debug
的值,并将其存储在全局变量 dword_12474
中
sub_9C04 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 const char *__fastcall sub_9C04 (int a1) { const char *result; const char *v3; int v4; int v5; int v6; FILE *v7; int v8; result = getenv ("WDTFD" ); v3 = result; if ( dword_12464 < 0 ) { dword_144B4 = (int )sub_9980; if ( result ) { if ( (unsigned int )dword_12474 > 1 ) { syslog (0 , "Watchdog handover: fd=%s\n" , result); fprintf ((FILE *)stderr, "procd: %s(%d): Watchdog handover: fd=%s\n" , "watchdog_init" , 103 , v3); } dword_12464 = atoi (v3); result = (const char *)unsetenv ("WDTFD" ); } else { dword_12464 = -1 ; } v4 = dword_12464; if ( dword_12464 >= 0 ) { if ( !a1 ) { v5 = fcntl (dword_12464, 1 ); fcntl (v4, 2 , v5 | 1 ); } syslog (0 , "- watchdog -\n" ); fputs ("procd: - watchdog -\n" , (FILE *)stderr); sub_9A84 (30 ); result = (const char *)sub_9980 (&unk_144A8); if ( (unsigned int )dword_12474 > 3 ) { v6 = sub_9A84 (0 ); syslog (0 , "Opened watchdog with timeout %ds\n" , v6); v7 = (FILE *)stderr; v8 = sub_9A84 (0 ); return (const char *)fprintf (v7, "procd: %s(%d): Opened watchdog with timeout %ds\n" , "watchdog_init" , 120 , v8); } } } return result; }
初始化看门狗计时器
sub_94D0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 int sub_94D0 () { __pid_t v0; const char *v1; const char *v2; __pid_t v3; int result; char *file[4 ]; char *v6[11 ]; v6[0 ] = "/bin/sh" ; v6[1 ] = "/etc/preinit" ; v6[2 ] = 0 ; file[0 ] = "/sbin/procd" ; file[1 ] = "-h" ; file[2 ] = "/etc/hotplug-preinit.json" ; file[3 ] = 0 ; syslog (0 , "- preinit -\n" ); fputs ("procd: - preinit -\n" , (FILE *)stderr); dword_12484 = (int )sub_9344; v0 = fork(); dword_12488 = v0; if ( !v0 ) { execvp (file[0 ], file); syslog (0 , "Failed to start plugd\n" ); v1 = &byte_9F8F; goto LABEL_8; } if ( v0 <= 0 ) { syslog (0 , "Failed to start new plugd instance\n" ); v2 = byte_9FAD; return fputs (v2, (FILE *)stderr); } uloop_process_add (&unk_12478); setenv ("PREINIT" , "1" , 1 ); dword_12498 = (int )sub_9350; v3 = fork(); dword_1249C = v3; if ( !v3 ) { execvp (v6[0 ], v6); syslog (0 , "Failed to start preinit\n" ); v1 = "procd: Failed to start preinit\n" ; LABEL_8: fputs (v1, (FILE *)stderr); exit (-1 ); } if ( v3 <= 0 ) { syslog (0 , "Failed to start new preinit instance\n" ); v2 = "procd: Failed to start new preinit instance\n" ; return fputs (v2, (FILE *)stderr); } result = uloop_process_add (&unk_1248C); if ( (unsigned int )dword_12474 > 3 ) { syslog (0 , "Launched preinit instance, pid=%d\n" , dword_1249C); return fprintf ((FILE *)stderr, "procd: %s(%d): Launched preinit instance, pid=%d\n" , "preinit" , 120 , dword_1249C); } return result; }
首先fork运行/sbin/procd,以-h /etc/hotplug-preinit.json作为参数,之后uloop_process_add(&unk_12478);, setenv(“PREINIT”, “1”, 1);设置preinit为1作为flag,之后fork运行/bin/sh,以/etc/preinit作为参数
execvp
会加载新的程序到当前进程的地址空间,覆盖原有的代码、数据和堆栈,因此紧跟execvp后面的便是错误的输出
uloop_process 是 Libubox 提供的一个进程管理工具,它并不会帮你创建进程,它主要用来等待子进程工作的结束,然后调用自定义的回调函数,所以一般需要配合 fork
一起使用。
这里注意观察两个地方
uloop_process_add(&dword_12478);和result = uloop_process_add(&dword_1248C);,其实这个add的是结构体
1 2 3 4 5 6 7 struct uloop_process { struct list_head list; bool pending; uloop_process_handler cb; pid_t pid; };
实际调用的函数是cb,而list_head一般包括pre和next两个指针占用8个字节,pending由于对齐占用4个字节
因此之后main函数运行uloop_run(v1);这个时,调用的分别是sub_9344和sub_9350函数,但是首先来看之后运行的/sbin/procd和/etc/preinit
/sbin/procd 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int __cdecl main (int argc, const char **argv, const char **envp) { char *v5; const char *v7; int v8; int v9; int v10; int v11; v5 = getenv ("DBGLVL" ); if ( v5 ) { dword_1CEE4 = atoi (v5); unsetenv ("DBGLVL" ); } while ( 1 ) { v8 = getopt (argc, (char *const *)argv, "d:s:h:" ); if ( v8 == -1 ) break ; switch ( v8 ) { case 'h' : return sub_10E20 (optarg); case 's' : dword_1AD68 = optarg; break ; case 'd' : ....... }
因为是-h参数,因此看前面即可,DBGLVL估计是测试debug用的
sub_10E20 1 2 3 4 5 6 7 8 9 int __fastcall sub_10E20 (int a1) { int v2; uloop_init (); v2 = sub_10C5C ((const char *)a1); uloop_run (v2); return 0 ; }
sub_10C5C函数功能如下
设置热插拔套接字: (类似usb等)
错误处理:
如果套接字创建或绑定失败,记录错误信息并退出程序。
配置套接字选项:
初始化 JSON 脚本:
添加到事件循环:
/etc/preinit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 # !/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications [ -z "$PREINIT" ] && exec /sbin/init export PATH=/bin:/sbin:/usr/bin:/usr/sbin pi_ifname= pi_ip=192.168.1.1 pi_broadcast=192.168.1.255 pi_netmask=255.255.255.0 fs_failsafe_ifname= fs_failsafe_ip=192.168.1.1 fs_failsafe_broadcast=192.168.1.255 fs_failsafe_netmask=255.255.255.0 fs_failsafe_wait_timeout=2 pi_suppress_stderr="y" pi_init_suppress_stderr="y" pi_init_path="/bin:/sbin:/usr/bin:/usr/sbin" pi_init_cmd="/sbin/init" . /lib/functions.sh . /lib/functions/preinit.sh . /lib/functions/system.sh . /lib/functions/userconfig.sh boot_hook_init preinit_essential boot_hook_init preinit_main boot_hook_init failsafe boot_hook_init initramfs boot_hook_init preinit_mount_root for pi_source_file in /lib/preinit/*; do . $pi_source_file done boot_run_hook preinit_essential pi_mount_skip_next=false pi_jffs2_mount_success=false pi_failsafe_net_message=false boot_run_hook preinit_main
1 2 3 4 . /lib/functions.sh . /lib/functions/preinit.sh . /lib/functions/system.sh . /lib/functions/userconfig.sh
function.sh定义了一系列配置函数,
/lib/functions/preinit.sh中定义了boot_hook_init函数
1 2 3 4 5 boot_hook_init() { local hook="${1}_hook" export -n "PI_STACK_LIST=${PI_STACK_LIST:+$PI_STACK_LIST }$hook" export -n "$hook=" }
类似于boot_hook_init preinit_essential,便有preinit_essential_hook= 之类的环境变量
之后初始化一系列的hook,然后调用/lib/preinit下的函数
调用了如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 # !/bin/sh define_default_set_state() { . /etc/diag.sh } boot_hook_add preinit_main define_default_set_state =============================================================================================== # !/bin/sh # # do_ipq806x () { . /lib/ipq806x.sh ipq806x_board_detect } boot_hook_add preinit_main do_ipq806x =============================================================================================== # !/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications # commands for emitting messages to network in failsafe mode indicate_failsafe_led () { set_state failsafe } indicate_failsafe() { echo "- failsafe -" preinit_net_echo "Entering Failsafe!\n" indicate_failsafe_led } boot_hook_add failsafe indicate_failsafe =============================================================================================== # !/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications preinit_ip() { # if the preinit interface isn't specified and ifname is set in # preinit.arch use that interface if [ -z "$pi_ifname" ]; then pi_ifname=$ifname fi [ -n "$pi_ifname" ] && grep -q "$pi_ifname" /proc/net/dev && { ifconfig $pi_ifname $pi_ip netmask $pi_netmask broadcast $pi_broadcast up } } preinit_ip_deconfig() { [ -n "$pi_ifname" ] && grep -q "$pi_ifname" /proc/net/dev && { ifconfig $pi_ifname 0.0.0.0 down } } preinit_net_echo() { [ -n "$pi_ifname" ] && grep -q "$pi_ifname" /proc/net/dev && { { [ "$pi_preinit_net_messages" = "y" ] || { [ "$pi_failsafe_net_message" = "true" ] && [ "$pi_preinit_no_failsafe_netmsg" != "y" ] } } && netmsg $pi_broadcast "$1" } } preinit_echo() { preinit_net_echo $1 echo $1 } pi_indicate_led() { set_state preinit } pi_indicate_preinit() { preinit_net_echo "Doing OpenWRT Preinit\n" pi_indicate_led } boot_hook_add preinit_main preinit_ip boot_hook_add preinit_main pi_indicate_preinit =============================================================================================== # !/bin/sh # Copyright (C) 2006-2010 OpenWrt.org # Copyright (C) 2010 Vertical Communications fs_wait_for_key () { local timeout=$3 local timer local do_keypress local keypress_true="$(mktemp)" local keypress_wait="$(mktemp)" local keypress_sec="$(mktemp)" if [ -z "$keypress_wait" ]; then keypress_wait=/tmp/.keypress_wait touch $keypress_wait fi if [ -z "$keypress_true" ]; then keypress_true=/tmp/.keypress_true touch $keypress_true fi if [ -z "$keypress_sec" ]; then keypress_sec=/tmp/.keypress_sec touch $keypress_sec fi trap "echo 'true' >$keypress_true; lock -u $keypress_wait ; rm -f $keypress_wait" INT trap "echo 'true' >$keypress_true; lock -u $keypress_wait ; rm -f $keypress_wait" USR1 [ -n "$timeout" ] || timeout=1 [ $timeout -ge 1 ] || timeout=1 timer=$timeout lock $keypress_wait { while [ $timer -gt 0 ]; do echo "$timer" >$keypress_sec timer=$(($timer - 1)) sleep 1 done lock -u $keypress_wait rm -f $keypress_wait } & echo "Press the [$1] key and hit [enter] $2" echo "Press the [1], [2], [3] or [4] key and hit [enter] to select the debug level" # if we' re on the console we wait for input { while [ -r $keypress_wait ]; do timer="$(cat $keypress_sec)" [ -n "$timer" ] || timer=1 timer="${timer%%\ *}" [ $timer -ge 1 ] || timer=1 do_keypress="" { read -t "$timer" do_keypress case "$do_keypress" in $1) echo "true" >$keypress_true ;; 1 | 2 | 3 | 4) echo "$do_keypress" >/tmp/debug_level ;; *) continue; ;; esac lock -u $keypress_wait rm -f $keypress_wait } done } lock -w $keypress_wait keypressed=1 [ "$(cat $keypress_true)" = "true" ] && keypressed=0 rm -f $keypress_true rm -f $keypress_wait rm -f $keypress_sec return $keypressed } failsafe_wait() { local is_failsafe=$(uci get system.@system[0].failsafe) echo "is_failsafe is $is_failsafe" > /dev/console if [ $is_failsafe = "true" ]; then FAILSAFE= grep -q 'failsafe=' /proc/cmdline && FAILSAFE=true && export FAILSAFE if [ "$FAILSAFE" != "true" ]; then pi_failsafe_net_message=true preinit_net_echo "Please press button now to enter failsafe" pi_failsafe_net_message=false fs_wait_for_key f 'to enter failsafe mode' $fs_failsafe_wait_timeout && FAILSAFE=true [ -f "/tmp/failsafe_button" ] && FAILSAFE=true && echo "- failsafe button "`cat /tmp/failsafe_button`" was pressed -" [ "$FAILSAFE" = "true" ] && export FAILSAFE && touch /tmp/failsafe fi fi } boot_hook_add preinit_main failsafe_wait =============================================================================================== # !/bin/sh # Copyright (C) 2006-2010 OpenWrt.org # Copyright (C) 2010 Vertical Communications run_failsafe_hook() { if [ "$FAILSAFE" = "true" ]; then boot_run_hook failsafe lock -w /tmp/.failsafe fi } boot_hook_add preinit_main run_failsafe_hook =============================================================================================== # !/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications indicate_regular_preinit() { preinit_net_echo "Continuing with Regular Preinit\n" pi_indicate_led } boot_hook_add preinit_main indicate_regular_preinit =============================================================================================== # !/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications initramfs_test() { if [ -n "$INITRAMFS" ]; then boot_run_hook initramfs preinit_ip_deconfig break fi } boot_hook_add preinit_main initramfs_test =============================================================================================== # !/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications do_mount_root() { ramoverlay find_mount_jffs2 rootfs_init_etc_ramfs boot_run_hook preinit_mount_root defFlag=`grep -o Default /tmp/userconfig/userconfig_default.tmp` if [ -n "$defFlag" ] && [ $defFlag = 'Default' ]; then # set dftmac from tddp flash /usr/bin/tddpd dftmac /lib/cfg_save/config.sh save else echo "no" fi } [ "$INITRAMFS" = "1" ] || boot_hook_add preinit_main do_mount_root =============================================================================================== # !/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications failsafe_netlogin () { telnetd -l /bin/login.sh <> /dev/null 2>&1 } failsafe_shell() { lock /tmp/.failsafe ash --login echo "Please reboot system when done with failsafe network logins" } boot_hook_add failsafe failsafe_netlogin boot_hook_add failsafe failsafe_shell =============================================================================================== # !/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications run_init() { preinit_ip_deconfig } boot_hook_add preinit_main run_init
执行preinit_essential,而preinit_essential中啥也没有,因此什么都不执行??
之后执行preinit_main,preinit_main中有define_default_set_state ,do_ipq806x ,preinit_ip ,pi_indicate_preinit ,failsafe_wait ,run_failsafe_hook ,indicate_regular_preinit ,initramfs_test ,do_mount_root ,run_init 函数,分别执行如下
1 2 3 define_default_set_state () { . /etc/diag.sh }
执行/etc/diag.sh,set_state() { :; }
default set
1 2 3 4 5 do_ipq806x() { . /lib/ipq806x.sh ipq806x_board_detect }
执行ipq806x.sh脚本中的**ipq806x_board_detect
**函数,作用:读取板子的信息并记录
preinit_ip
函数
接口选择 :
如果 pi_ifname
未设置,则使用 ifname
。
配置接口 :
检查接口是否存在于 /proc/net/dev
。
使用 ifconfig
命令配置接口的 IP 地址、子网掩码和广播地址,并激活接口。
**pi_indicate_preinit
**:输出并亮灯预示初始化的状态,想来启动路由器的时候的亮灯由此而来
failsafe_wait
函数:检测系统上是否应该进入故障安全模式(在给定的超时时间内等待特定按键输入 ,以触发故障安全模式或设置调试级别)
**run_failsafe_hook
**函数:检查系统是否进入故障安全模式,如果进入则执行failsafe,并创建一个锁lock -w /tmp/.failsafe
**indicate_regular_preinit
**函数:输出”Continuing with Regular Preinit\n”,并再次亮灯
**initramfs_test
**函数:检查系统是否在 initramfs 模式下运行,并执行相关操作
Initramfs 模式是一种用于引导 Linux 系统的初始文件系统模式。它通常用于在内核启动时提供一个临时的根文件系统,以便加载必要的驱动程序和初始化脚本,直到真正的根文件系统可以挂载为止。(或许解加密脚本便是从这里开始的??)
**do_mount_root
**函数:负责挂载根文件系统并进行相关初始化。
**run_init
**函数:运行preinit_ip_deconfig,负责卸载特定网络接口的 IP 配置。(对于重置网络环境十分重要)
至此preinit便运行完成
之后便回到/sbin/init中执行uloop_run(v1);
sub_9344 1 2 3 4 5 int __fastcall sub_9344 (int result) { *(_DWORD *)(result + 16 ) = 0 ; return result; }
或许是对下一个的赋值??要求下一个函数必须在子进程运行完毕才可运行?(感觉有道理
sub_9350 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 int sub_9350 () { const char *v0; FILE *v1; FILE *v2; struct stat v4; char *argv[2 ]; int v6; char s[12 ]; argv[0 ] = "/sbin/procd" ; argv[1 ] = 0 ; v0 = (const char *)sub_9BC4 (); if ( dword_12488 > 0 ) kill (dword_12488, 9 ); if ( !stat ("/tmp/sysupgrade" , &v4) ) { while ( 1 ) sleep (1u ); } unsetenv ("INITRAMFS" ); unsetenv ("PREINIT" ); if ( (unsigned int )dword_12474 > 1 ) { syslog (0 , "Exec to real procd now\n" ); fprintf ((FILE *)stderr, "procd: %s(%d): Exec to real procd now\n" , "spawn_procd" , 66 ); } if ( v0 ) setenv ("WDTFD" , v0, 1 ); v1 = fopen ("/tmp/debug_level" , "r" ); v6 = 0 ; v2 = v1; if ( v1 ) { fscanf (v1, "%d" , &v6); fclose (v2); unlink ("/tmp/debug_level" ); if ( (unsigned int )(v6 - 1 ) <= 3 ) dword_12474 = v6; } if ( dword_12474 ) { snprintf (s, 2u , "%d" , dword_12474); setenv ("DBGLVL" , s, 1 ); } return execvp (argv[0 ], argv); }
该函数用于清理环境、设置调试级别,并最终再次执行 /sbin/procd
/sbin/procd 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 int __cdecl main (int argc, const char **argv, const char **envp) { char *v5; const char *v7; int v8; int v9; int v10; int v11; v5 = getenv ("DBGLVL" ); if ( v5 ) { debug_level = atoi (v5); unsetenv ("DBGLVL" ); } while ( 1 ) { v8 = getopt (argc, (char *const *)argv, "d:s:h:" ); if ( v8 == -1 ) break ; ...... } v9 = uloop_init (); v10 = sub_A798 (v9); sub_F594 (v10); if ( getpid () == 1 ) v11 = sub_AE4C (); else v11 = sub_BD60 (); uloop_run (v11); return 0 ; }
由于没有参数,便直接break了
sub_A798 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int sub_A798 () { int result; signal (13 , (__sighandler_t )1 ); result = getpid (); if ( result == 1 ) { sigaction (15 , &act, 0 ); sigaction (10 , &act, 0 ); sigaction (12 , &act, 0 ); sigaction (11 , &off_1AA20, 0 ); sigaction (7 , &off_1AA20, 0 ); sigaction (1 , &off_1AA34, 0 ); sigaction (9 , &off_1AA34, 0 ); return sigaction (19 , &off_1AA34, 0 ); } return result; }
设置系统调用的处理方式,act是shutdown,1AA20是reboot,1AA34是无效调用
由于是execvp替换进程而pid是不会改变的,因此pid还是1,调用sub_AE4C函数
sub_AE4C 1 2 3 4 5 6 7 8 9 10 int __fastcall sub_AE4C (int a1) { if ( (unsigned int )debug_level > 3 ) { syslog (0 , "Change state %d -> %d\n" , choi, choi + 1 ); a1 = fprintf ((FILE *)stderr, "procd: %s(%d): Change state %d -> %d\n" , "procd_state_next" , 90 , choi, choi + 1 ); } ++choi; return sub_AC7C (a1); }
sub_AC7C 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 void sub_AC7C () { int v0; int v1; int v2; char v3[28 ]; strcpy (v3, "/sbin/ubusd" ); switch ( choi ) { case 1 : syslog (0 , "- early -\n" ); fputs ("procd: - early -\n" , (FILE *)stderr); sub_AACC (0 ); v0 = sub_10C5C ("/etc/hotplug.json" ); sub_FAF8 (v0); break ; case 2 : sub_AACC (0 ); syslog (0 , "- ubus -\n" ); v1 = fputs ("procd: - ubus -\n" , (FILE *)stderr); sub_BD60 (v1); syslog (0 , "- init -\n" ); v2 = fputs ("procd: - init -\n" , (FILE *)stderr); sub_D628 (v2); sub_D4CC ("ubus" , v3); sub_B458 (); sub_B3F4 ("respawn" ); sub_B3F4 ("askconsole" ); sub_B3F4 ("askfirst" ); sub_B3F4 ("sysinit" ); break ; case 3 : syslog (0 , "- init complete -\n" ); fputs ("procd: - init complete -\n" , (FILE *)stderr); break ; case 4 : syslog (0 , "- shutdown -\n" ); fputs ("procd: - shutdown -\n" , (FILE *)stderr); sub_B3F4 ("shutdown" ); sync (); break ; case 5 : syslog (0 , "- reboot -\n" ); fputs ("procd: - reboot -\n" , (FILE *)stderr); reboot (dword_1ACCC); break ; default : syslog (0 , "Unhandled state %d\n" , choi); fprintf ((FILE *)stderr, "procd: Unhandled state %d\n" , choi); break ; } }
状态处理
case 1
- Early :
记录日志 “- early -“
调用 sub_AACC(0)
执行早期初始化。
调用 sub_10C5C("/etc/hotplug.json")
处理配置文件。
调用 sub_FAF8
可能用于进一步初始化。
case 2
- Ubus Initialization :
调用 sub_AACC(0)
。
记录日志 “- ubus -“
调用 sub_BD60
进行 Ubus 初始化。
记录日志 “- init -“
调用 sub_D628
进行进一步初始化。
调用 sub_D4CC("ubus", v3)
启动 /sbin/ubusd
。
调用 sub_B458()
执行其他初始化步骤。
调用 sub_B3F4
处理一系列状态(”respawn”, “askconsole”, “askfirst”, “sysinit”)。
case 3
- Init Complete :
case 4
- Shutdown :
记录日志 “- shutdown -“
调用 sub_B3F4("shutdown")
处理关闭操作。
调用 sync()
确保数据完整性。
case 5
- Reboot :
记录日志 “- reboot -“
调用 reboot(dword_1ACCC)
执行重启。
default
- Unhandled State :
case1 sub_AACC函数确保在系统启动时正确初始化看门狗,以便在系统无响应时采取适当措施。
sub_10C5C函数用于设置和配置一个处理热插拔事件的套接字,并将其集成到事件循环中(或许第一次是初始化,这次才是配置?)
sub_FAF8 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 void *sub_FAF8 () { __pid_t v0; void *data; char *file[6 ]; file[1 ] = 0 ; file[0 ] = "udevtrigger" ; umount2 ("/dev/pts" , 2 ); umount2 ("/dev/" , 2 ); mount ("tmpfs" , "/dev" , "tmpfs" , 0 , "mode=0755,size=512K" ); mkdir ("/dev/shm" , 0x1ED u); mkdir ("/dev/pts" , 0x1ED u); mount ("devpts" , "/dev/pts" , "devpts" , 0 , 0 ); dword_1AE8C = (int )sub_FA40; v0 = fork(); dword_1AE90 = v0; if ( !v0 ) { execvp (file[0 ], file); syslog (0 , "Failed to start coldplug\n" ); fputs ("procd: Failed to start coldplug\n" , (FILE *)stderr); exit (-1 ); } if ( v0 <= 0 ) { syslog (0 , "Failed to start new coldplug instance\n" ); fputs ("procd: Failed to start new coldplug instance\n" , (FILE *)stderr); } else { uloop_process_add (&unk_1AE80); if ( (unsigned int )debug_level > 3 ) { syslog (0 , "Launched coldplug instance, pid=%d\n" , dword_1AE90); fprintf ((FILE *)stderr, "procd: %s(%d): Launched coldplug instance, pid=%d\n" , "procd_coldplug" , 65 , dword_1AE90); } } return data; }
整个函数的主要作用便是在系统启动时检测和初始化所有已连接的硬件设备,并add一个process sub_FA40
sub_FA40 1 2 3 4 5 6 7 8 9 int sub_FA40 () { if ( (unsigned int )debug_level > 3 ) { syslog (0 , "Finished udevtrigger\n" ); fprintf ((FILE *)stderr, "procd: %s(%d): Finished udevtrigger\n" , "udevtrigger_complete" , 36 ); } return sub_10C38 (sub_FA9C); }
大致的还是一个log的记录,主要还是
1 2 3 4 5 6 7 8 9 10 11 12 int sub_FA9C () { int v0; if ( (unsigned int )debug_level > 3 ) { syslog (0 , "Coldplug complete\n" ); fprintf ((FILE *)stderr, "procd: %s(%d): Coldplug complete\n" , "coldplug_complete" , 29 ); } v0 = sub_10C38 (0 ); return sub_AE4C (v0); }
又运行一次sub_AE4C函数
此时就是case2了
case2 sub_8D60 1 2 3 4 5 int sub_BD60 () { dword_1AD5C = (int )sub_BC18; return uloop_timeout_set (&unk_1AD50, 1000 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 int sub_BC18 () { int v0; v0 = ubus_connect (dword_1AD68); dword_1AD6C = v0; if ( v0 ) { *(_DWORD *)(v0 + 92 ) = sub_BBFC; sub_D610 (); sub_C79C (dword_1AD6C); sub_F99C (dword_1AD6C); if ( (unsigned int )debug_level > 1 ) { syslog (0 , "Connected to ubus, id=%08x\n" , *(_DWORD *)(dword_1AD6C + 80 )); fprintf ( (FILE *)stderr, "procd: %s(%d): Connected to ubus, id=%08x\n" , "ubus_connect_cb" , 58 , *(_DWORD *)(dword_1AD6C + 80 )); } return uloop_fd_add (dword_1AD6C + 44 , 9 ); } else { if ( (unsigned int )debug_level > 3 ) { syslog (0 , "Connection to ubus failed\n" ); fprintf ((FILE *)stderr, "procd: %s(%d): Connection to ubus failed\n" , "ubus_connect_cb" , 48 ); } return uloop_timeout_set (&unk_1AD50, 1000 ); } }
尝试连接到 ubus
,并根据连接结果执行相应的初始化和事件处理设置。成功时,配置回调并将连接添加到事件循环;失败时,设置一个定时器以便稍后重试。
sub_D4CC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int __fastcall sub_D4CC (char *a1, char *a2) { int v4; int v5; int v6; char *i; char *v8; int v9; blob_buf_init (&dword_1AD8C, 0 ); sub_CE04 ((int )"name" , a1); v4 = blobmsg_open_nested (&dword_1AD8C, "instances" , 0 ); v5 = blobmsg_open_nested (&dword_1AD8C, "instance1" , 0 ); v6 = blobmsg_open_nested (&dword_1AD8C, "command" , 1 ); for ( i = a2; ; i = 0 ) { v8 = strtok (i, " " ); if ( !v8 ) break ; sub_CE04 (0 , v8); } blob_nest_end (&dword_1AD8C, v6); v9 = blobmsg_open_nested (&dword_1AD8C, "respawn" , 1 ); sub_CE04 (0 , "3600" ); sub_CE04 (0 , "1" ); sub_CE04 (0 , (char *)"0" ); blob_nest_end (&dword_1AD8C, v9); blob_nest_end (&dword_1AD8C, v5); blob_nest_end (&dword_1AD8C, v4); return sub_D304 (0 , 0 , 0 , (char *)"add" , dword_1AD8C); }
通过构建 blobmsg
消息来组织和传递数据。它创建一个包含实例和命令的结构 ,适用于需要复杂数据传递的场景。最终调用 sub_D304
处理该消息。
函数 sub_D304
处理服务请求,根据输入参数更新现有服务或创建新服务。
sub_B458函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 void sub_B458 () { char *v0; _DWORD *v1; size_t v2; char *v3; int v4; int v5; regoff_t *p_rm_eo; regoff_t v7; regoff_t v8; _DWORD *v9; int v10; char *i; int v12; int v13; const char *v14; int v15; int v16; int v17; _DWORD *v18; FILE *stream; regmatch_t v20[5 ]; regex_t preg; int v22[14 ]; stream = fopen ("/etc/inittab" , "r" ); if ( stream ) { regcomp (&preg, "([a-zA-Z0-9]*):([a-zA-Z0-9]*):([a-zA-Z0-9]*):(.*)" , 1 ); v0 = (char *)malloc (0x80 u); v1 = malloc (0x64 u); memset (v1, 0 , 0x64 u); LABEL_26: while ( fgets (v0, 128 , stream) ) { v2 = strlen (v0); v3 = &v0[v2]; while ( 1 ) { v4 = (unsigned __int8)*--v3; if ( (*(_WORD *)(_ctype_b + 2 * v4) & 0x20 ) == 0 ) break ; --v2; } v0[v2] = 0 ; if ( *v0 != '#' && !regexec (&preg, v0, 5u , v20, 0 ) ) { if ( (unsigned int )debug_level > 3 ) { syslog (0 , "Parsing inittab - %s" , v0); fprintf ((FILE *)stderr, "procd: %s(%d): Parsing inittab - %s" , "procd_inittab" , 274 , v0); } v5 = 0 ; p_rm_eo = &v20[0 ].rm_eo; do { v7 = *p_rm_eo; v8 = p_rm_eo[1 ]; p_rm_eo += 2 ; v0[v7] = 0 ; v22[v5++] = (int )&v0[v8]; } while ( v5 != 4 ); v9 = v1; v10 = 0 ; for ( i = strtok ((char *)v22[3 ], " " ); ; i = strtok (0 , " " ) ) { v12 = (int )i; ++v9; if ( i ) v12 = 1 ; if ( v10 > 6 ) v12 = 0 ; if ( !v12 ) break ; v9[2 ] = i; ++v10; } v13 = v22[0 ]; v14 = (const char *)v22[2 ]; v15 = 0 ; v1[v10 + 3 ] = 0 ; v16 = 0 ; v1[2 ] = v13; v1[11 ] = v0; while ( v15 != 5 ) { v17 = strcmp ((&off_1AA58)[v16], v14); v16 += 3 ; if ( !v17 ) { *v1 = &off_1AA50; v18 = off_1AA54; off_1AA54 = v1; v1[1 ] = v18; v1[12 ] = &(&off_1AA58)[3 * v15]; *v18 = v1; v0 = (char *)malloc (0x80 u); v1 = malloc (0x64 u); memset (v1, 0 , 0x64 u); goto LABEL_26; } ++v15; } syslog (0 , "Unknown init handler %s\n" , v14); fprintf ((FILE *)stderr, "procd: Unknown init handler %s\n" , v14); } } fclose (stream); free (v0); free (v1); regfree (&preg); } else { syslog (0 , "Failed to open %s\n" , "/etc/inittab" ); fprintf ((FILE *)stderr, "procd: Failed to open %s\n" , "/etc/inittab" ); } }
读取/etc/inittab文件,匹配格式为 identifier:runlevel:action:process
的行,以处理服务初始化数据
/etc/inittab 1 2 3 4 # Copyright (c) 2013 The Linux Foundation. All rights reserved. ::sysinit:/etc/init.d/rcS S boot ::shutdown:/etc/init.d/rcS K shutdown ttyHSL1::askfirst:/bin/login
1 2 3 4 5 6 7 8 9 10 *v1 = &off_1AA50; v18 = off_1AA54; off_1AA54 = v1; v1[1 ] = v18; v1[12 ] = &(&off_1AA58)[3 * v15]; *v18 = v1; v0 = (char *)malloc (0x80 u); v1 = malloc (0x64 u); memset (v1, 0 , 0x64 u);goto LABEL_26;
可以看到这部分1AA50已然被修改了
之后执行
1 2 3 4 sub_B3F4 ("respawn" );sub_B3F4 ("askconsole" );sub_B3F4 ("askfirst" );sub_B3F4 ("sysinit" );
想来算是只会执行最后一个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const char *__fastcall sub_B3F4 (const char *s2) { const char *v1; void **i; const char **v3; const char *v4; v1 = s2; for ( i = (void **)off_1AA50; i != &off_1AA50; i = (void **)*i ) { v3 = (const char **)i[12 ]; s2 = (const char *)strcmp (*v3, v1); if ( !s2 ) { v4 = v3[1 ]; if ( !v3[2 ] ) return (const char *)((int (__fastcall *)(void **))v4)(i); s2 = (const char *)((int (__fastcall *)(void **))v4)(i); } } return s2; }
执行i[12]即sub_B398(sysinit对应的函数)
1 2 3 4 5 6 7 8 9 10 11 12 13 int __fastcall sub_B398 (int a1, int a2, int a3, int a4) { int v4; if ( *(_DWORD *)(a1 + 16 ) ) { v4 = *(_DWORD *)(a1 + 20 ); if ( v4 ) return sub_BB80 (*(_DWORD *)(a1 + 16 ), v4, sub_B394, a4); } syslog (0 , "valid format is rcS <S|K> <param>\n" , a3); return fputs ((const char *)&word_1162A, (FILE *)stderr); }
1 2 3 4 5 6 7 int __fastcall sub_BB80 (int a1, int a2, int a3) { runqueue_init ((int )&unk_1ACD0); dword_1AD04 = 1 ; dword_1AD0C = a3; return sub_B730 ((int )&unk_1ACD0, "/etc/rc.d" , a1, (int )"*" , a2); }
大致的作用差不多是添加/etc/rc.d/*下的脚本作为task并执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 ~/fr/t/_TL-WAR2/squashfs-root/etc ------------------------------------------------------------------------------------------------------------------ > ls -all ./rc.d 总用量 8 drwxr-xr-x 2 s1nec-1 o s1nec-1 o 4096 6 月 26 2019 . drwxr-xr-x 39 s1nec-1 o s1nec-1 o 4096 12 月 25 15 :44 .. lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 19 6 月 26 2019 K20miniupnpd -> ../init.d/miniupnpd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 15 6 月 26 2019 K26pppox -> ../init.d/pppox lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 18 6 月 26 2019 K50dropbear -> ../init.d/dropbear lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 13 6 月 26 2019 K80uac -> ../init.d/uac lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 20 6 月 26 2019 K81apdb_check -> ../init.d/apdb_check lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 18 6 月 26 2019 K90wireless -> ../init.d/wireless lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 14 6 月 26 2019 K98boot -> ../init.d/boot lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 K99luci_monitor -> ../init.d/luci_monitor lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 K99sys_ha -> ../init.d/sys_ha lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 K99umount -> ../init.d/umount lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 20 6 月 26 2019 S00sysfixtime -> ../init.d/sysfixtime lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 19 6 月 26 2019 S01led_early -> ../init.d/led_early lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 14 6 月 26 2019 S10boot -> ../init.d/boot lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S10system -> ../init.d/system lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 15 6 月 26 2019 S14tddpd -> ../init.d/tddpd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S15loggerd -> ../init.d/loggerd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 19 6 月 26 2019 S15rsa_check -> ../init.d/rsa_check lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S20network -> ../init.d/network lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S21switch -> ../init.d/switch lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S25sysctl -> ../init.d/sysctl lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 S26time_setting -> ../init.d/time_setting lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S31tmngtd -> ../init.d/tmngtd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 15 6 月 26 2019 S40fstab -> ../init.d/fstab lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 18 6 月 26 2019 S40watchdog -> ../init.d/watchdog lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S42ipgroup -> ../init.d/ipgroup lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S42ippool -> ../init.d/ippool lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 18 6 月 26 2019 S45firewall -> ../init.d/firewall lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 13 6 月 26 2019 S46nat -> ../init.d/nat lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 20 6 月 26 2019 S47access_ctl -> ../init.d/access_ctl lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 24 6 月 26 2019 S47administration -> ../init.d/administration lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 21 6 月 26 2019 S47dos_defense -> ../init.d/dos_defense lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 23 6 月 26 2019 S47flood_defense -> ../init.d/flood_defense lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 13 6 月 26 2019 S47imb -> ../init.d/imb lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 20 6 月 26 2019 S47mac_filter -> ../init.d/mac_filter lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 14 6 月 26 2019 S50cron -> ../init.d/cron lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 18 6 月 26 2019 S50dropbear -> ../init.d/dropbear lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 15 6 月 26 2019 S50pppox -> ../init.d/pppox lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 19 6 月 26 2019 S50pure-ftpd -> ../init.d/pure-ftpd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 20 6 月 26 2019 S50queueventd -> ../init.d/queueventd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S50telnet -> ../init.d/telnet lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 18 6 月 26 2019 S50wireless -> ../init.d/wireless lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 20 6 月 26 2019 S52qos-tplink -> ../init.d/qos-tplink lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 18 6 月 26 2019 S60collectd -> ../init.d/collectd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S60dnsmasq -> ../init.d/dnsmasq lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 15 6 月 26 2019 S60pptpd -> ../init.d/pptpd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 20 6 月 26 2019 S60url_filter -> ../init.d/url_filter lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S60xl2tpd -> ../init.d/xl2tpd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S62vpn_wan -> ../init.d/vpn_wan lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S65wifidog -> ../init.d/wifidog lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S69ipstat -> ../init.d/ipstat lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S69online -> ../init.d/online lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 S70freeStrategy -> ../init.d/freeStrategy lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 20 6 月 26 2019 S70init_iface -> ../init.d/init_iface lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 21 6 月 26 2019 S70qca-nss-drv -> ../init.d/qca-nss-drv lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 21 6 月 26 2019 S70qca-nss-ecm -> ../init.d/qca-nss-ecm lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 21 6 月 26 2019 S72shortcut-fe -> ../init.d/shortcut-fe lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 24 6 月 26 2019 S76session_limits -> ../init.d/session_limits lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S80websort -> ../init.d/websort lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 S83web_security -> ../init.d/web_security lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 19 6 月 26 2019 S85webfilter -> ../init.d/webfilter lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 21 6 月 26 2019 S89remote_mngt -> ../init.d/remote_mngt lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 18 6 月 26 2019 S90dnsproxy -> ../init.d/dnsproxy lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S93appdist -> ../init.d/appdist lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 19 6 月 26 2019 S93bwlist_qq -> ../init.d/bwlist_qq lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 14 6 月 26 2019 S93done -> ../init.d/done lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 14 6 月 26 2019 S93l2tp -> ../init.d/l2tp lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 15 6 月 26 2019 S93mwan3 -> ../init.d/mwan3 lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 25 6 月 26 2019 S94default_balance -> ../init.d/default_balance lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 19 6 月 26 2019 S94isp_route -> ../init.d/isp_route lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 S94load_balance -> ../init.d/load_balance lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 19 6 月 26 2019 S94miniupnpd -> ../init.d/miniupnpd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 S94policy_route -> ../init.d/policy_route lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 S94static_route -> ../init.d/static_route lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S94sysntpd -> ../init.d/sysntpd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 15 6 月 26 2019 S95ipsec -> ../init.d/ipsec lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 19 6 月 26 2019 S96cloud_sdk -> ../init.d/cloud_sdk lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 S97cloud_client -> ../init.d/cloud_client lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S97cmxddns -> ../init.d/cmxddns lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 S97dev_discover -> ../init.d/dev_discover lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 21 6 月 26 2019 S97dyn3322ddns -> ../init.d/dyn3322ddns lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 22 6 月 26 2019 S97luci_monitor -> ../init.d/luci_monitor lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S97phddns -> ../init.d/phddns lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 23 6 月 26 2019 S97storage_share -> ../init.d/storage_share lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S97sys_ha -> ../init.d/sys_ha lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 21 6 月 26 2019 S97sys_monitor -> ../init.d/sys_monitor lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 23 6 月 26 2019 S97system_params -> ../init.d/system_params lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S97tpddnsd -> ../init.d/tpddnsd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 16 6 月 26 2019 S97uhttpd -> ../init.d/uhttpd lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 20 6 月 26 2019 S98apdb_check -> ../init.d/apdb_check lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 13 6 月 26 2019 S98led -> ../init.d/led lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 13 6 月 26 2019 S98uac -> ../init.d/uac lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 24 6 月 26 2019 S98zero_boot_done -> ../init.d/zero_boot_done lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 27 6 月 26 2019 S99commit_sysupgrade -> ../init.d/commit_sysupgrade lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S99devmngr -> ../init.d/devmngr lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 18 6 月 26 2019 S99powerctl -> ../init.d/powerctl lrwxrwxrwx 1 s1nec-1 o s1nec-1 o 17 6 月 26 2019 S99thermal -> ../init.d/thermal
即执行/etc/init.d/下的所有服务
至此启动流程分析完毕