Hacking a router 03: The serial interface
Booting
When we connect to the serial interface and then boot up the router we’re greeted with a huge amount of information. Let’s split it up into pieces:
Booting...
init_ram
00000102 W init ddr ok
DRAM Type: DDR2
DRAM frequency: 533MHz
DRAM Size: 32MB
JEDEC id C84015, EXT id 0xc840
found gd25q16
flash vendor: GigaDevice
gd25q16, size=2MB, erasesize=4KB, max_speed_hz=41000000Hz
auto_mode=0 addr_width=3 erase_opcode=0x00000020
=>CPU Wake-up interrupt happen! GISR=89000004
This block is the detection of DRAM and flash memory.
---Realtek RTL8197F boot code at 2018.03.02-14:45+0800 v3.4.11B.9 (600MHz)
Mac addr:b4-0f-3b-24-9b-90
wait for upgrage
port link 0x000030e0 0x000030e0 0x000030e0 0x000030e0
irq:0x00008480
Jump to image start=0x80500000...
decompressing kernel:
Uncompressing... done, booting the kernel.
done decompressing kernel.
start address: 0xa0000600
This block seems to establish an connection over the wire and wait for an upgrade (most likely over TFTP). After it’s done waiting it starts decompressing and booting the kernel.
From this we know the initial image is loaded at 0x80500000
and is compressed. And the decrompressed kernel starts at 0xa0000600
, which is an uncached mirror of the DRAM region at 0x80000600
. The base address of the DRAM region is at 0x80000000/0xa0000000
and the end is 32MB after 0x8200000/0xa2000000
(To find out about what all these addresses mean consult the processor manual).
With this we know where to find the firmware image in ram, which will come in handy very soon.
[cyg_net_init] Init: mbinit(0x00000000)
[cyg_net_init] Init: cyg_net_init_devs(0x00000000)
Init device 'rltk819x_eth0'
Init device 'rltk819x_eth1'
Init device 'rltk819x_eth7'
Init device 'rltk819x_peth0'
[peth0] added, mapping to [eth1]...
Init device 'rltk819x_eth4'
Init device 'rltk819x_eth3'
Init device 'rltk819x_eth2'
Init device 'rltk819x_wlan1'
RFE TYPE =0
[pwlan0] added...
Init device 'rltk819x_wlan1-vxd'
RFE TYPE =0
Init device 'rltk819x_wlan1-va3'
RFE TYPE =0
Init device 'rltk819x_wlan1-va2'
RFE TYPE =0
Init device 'rltk819x_wlan1-va1'
RFE TYPE =0
Init device 'rltk819x_wlan1-va0'
RFE TYPE =0
Init device 'rltk819x_wlan0'
[pwlan0] added...
Init device 'rltk819x_wlan0-vxd'
Init device 'rltk819x_wlan0-va3'
Init device 'rltk819x_wlan0-va2'
Init device 'rltk819x_wlan0-va1'
Init device 'rltk819x_wlan0-va0'
Init device 'rltk819x_pwlan0'
[cyg_net_init] Init: loopattach(0x00000000)
[cyg_net_init] Init: ifinit(0x00000000)
[cyg_net_init] Init: domaininit(0x00000000)
[cyg_net_init] Init: cyg_net_add_domain(0x80403298)
New domain internet at 0x00000000
IP packet filtering initialized, divert enabled, rule-based forwarding enabled, default to deny, logging disabled
Realtek FastPath:v1.03
[cyg_net_init] Init: cyg_net_add_domain(0x80403148)
New domain route at 0x00000000
[cyg_net_init] Init: call_route_init(0x00000000)
[cyg_net_init] Init: sysctl_register_all(0x00000000)
[cyg_net_init] Done
Creating Init Thread...
cpuload_calibration_value=6070449
Envram is initing! base_addr=0x1c000 size=0x3000
@@@[envram] crc=0x57 org=0x57
Nvram is initing! base_addr=0x1f8000 size=0x7fff
@@@[nvram] crc=0xa8 org=0xa8
System start
Realtek GPIO init
this thread can be checked ! if thread stack overflow or soiled
This looks like the boot code for the whole system, which initialises all the peripheral devices.
------------main
tenda_ifconfig error
: Invalid argument
[MAIN->tapf_vlan_config->262]:vlan config ok!
[MAIN->br_init->878]:br(bridge0) init ok!
[MAIN->br_init->878]:br(bridge1) init ok!
[MAIN->init_before_module_init_services->76]:init_before_module_init_services ok!
[DEBUG] [MSG->msg_init->37]:rc_msg init ok!
eth0: promiscuous mode enabled
kMAIrNe-8>6t5axp_fi_ovcltaln(_)c:o4n8f0i0g,- >s2e6t2 ]p:ovrlta n3 c0oMn fFiUgL Lo
!
[[WARN] Interface not initialized yet!
r[WARN] Interface not initialized yet!
->rc_imnsigt_W2e_btsl:v -l>a1n0i8p]=:[k1e9y2 .=1 6o8p.,1v7a8l.u7e7 ]=
3
0[-M-S-G--->-m-s-g-_-siennidt_Wmesbgs_-2-_-m-b-o-x--->-1-2-0-]2:7c0e-n-t-e-r-:-0-0-0-0-0-1
6
[MSG->msg_send_msg_2_mbox->136]:send msg [op=3] success!
[TPI->tpi_lan_start->146]:start success!
Interface doesn't accept private ioctl...
clear_acl_table (8B9E): Unknown error
Interface doesn't accept private ioctl...
clear_acl_table (8B9E): Unknown error
wlan0 fflush_acl_list:0
wlan1 fflush_acl_list:0
func:ipnat_init i = 0
[TPI->tpi_firewall_start->239]:start success!
_TPI->tpi_http_start->128]:htt#p# #sftuanrct= asuutcoc_ecsosn!n
!eCxLtIe>n d[_TsPtIa-t>utsp_ii_nuiptn,p _ssetta ratu-t>o1-0c7o]n:ns trasrsti stuoc c-e3s5s
osl_open_udp_socket: bind failed: Can't assign requested address[TPI->tpi_dnsmasq_start->75]:start success!
[TPI->tpi_sntp_start->80]:start success!
[TPI->tpi_dhcp_server_start->323]:start success!
[TPI->tpi_wan_surf_check_start->599]:start success!
pTPI->tpi_wan_mode_check_update_info->75]:mode_need_switch is not yes[!t
ri[_TiPgIm-p>_tipnii_ta]u[t1o2_6c]o[nlnu_msienraviesr]_ sntoatrhti-n>g1 7t3o] :dsot.a
t success!
[TPI->tpi_reboot_check_start->463]:start success!
[ERROR] [TPI->tpi_wifi_switch_sched_update_info->67]:schedule count[ 0 ] wrong
[ERROR] [TPI->tpi_ddns_start->105]:the mib is off, connot start!
.....ucloud.............do......option....
[TPI->tpi_ucloud_start->101]:start success!
[TPI->tpi_led_sta[WARN] Interface not initialized yet!
r[WARN] Interface not initialized yet!
t->447]:start success!
[TPI->tpi_switch_led_start->118]:start success!
[MAIN->init_after_module_init_services->87]:init_after_module_init_services ok!
[rc->rc_module_msg_handle->157]:start handle module[6,rc_firewall]'s msg,op=3.debug_id=0.wlan_ifname=.string_info=
[TPI->tpi_firewall_action->137]:op=3
ioctl(SIOCIPFFL): Invalid argument
[TPI->tpi_firewall_stop->263]:stop success!
Interface doesn't accept private ioctl...
clear_acl_table (8B9E): Unknown error
Interface doesn't accept private ioctl...
clear_acl_table (8B9E): Unknown error
wlan0 fflush_acl_list:0
wlan1 fflush_acl_list:0
func:ipnat_init i = 0
[TPI->tpi_firewall_start->239]:start success!
And this one starts the main loop in the kernel.
Now we see a CLI>
on the screen, which looks like a shell. When we try typing help
we get a bunch of output:
Realtek's command:
Commands Descriptions Usage
-------- ------------ -----
db db <Address> <Len>
dw dw <Address> <Len>
eth eth
wlan0 wlan0
wlan0-va0 wlan0-va0
wlan0-va1 wlan0-va1
wlan0-va2 wlan0-va2
wlan0-va3 wlan0-va3
wlan0-vxd wlan0-vxd
wlan1 wlan1
wlan1-va0 wlan1-va0
wlan1-va1 wlan1-va1
wlan0-va2 wlan0-va2
wlan1-va3 wlan1-va3
wlan1-vxd wlan1-vxd
eb eb <Address> <Value1> <Value2>...
ew ew <Address> <Value1> <Value2>...
alg alg
brconfig brconfig
cat
cpuload cpuload
date show system local time
excep cause exception
fastpath fastpath
getmib getmib
help Displays a list of commands
? Displays a list of commands
hexcat
dump Shows a memory dump
hw_nat hw_nat
ib ib
idd idd
ifconfig ifconfig
ipfw ipfw
irf irf
iw iw
iwpriv iwpriv
ll
mac mac <ifname> [mac addr]
mroute mroute
ob ob
od od
orf orf
ow ow
pdump dump a thread
ping ping
port_fwd port_fwd
version Shows build version
ps Shows a list of threads
reboot Reset the system
reinit reinit
reinit_test reinit_test
reset Reset the system
route set static route
rssi rssi
alias show alias table
rtl_vlan rtl_vlan
sp Sets a threads priority [thread ID]
setmib setmib
show show
tcpstats tcpstats
test_skb test_skb
kill Kills a running thread [thread ID]
release Break a thread out of any wait [thread ID]
trigger_port trigger_port
uptime Shows system uptime
watchdog on/off/res/reboot
Tenda's command:
msg ce_power app_debug build stat_link link_status
iwpriv realtek splx thread mbuf debug
route et wlconf fw ping ifconfig
syslog time tenda_arp arp envram nvram
restart reboot
I think Realtek's command
are commands that are provided by the firmware from Realtek and Tenda's command
are the commands this company added. Let’s have a look at the interesting ones:
eb eb <Address> <Value1> <Value2>...
ew ew <Address> <Value1> <Value2>...
eb/ew writes a sequence of bytes/words to the specified address. That means we can write anywhere from this serial console.
dump Shows a memory dump
And with this we have an arbitrary read, that’s basically all we need for now, since our goal is to get the firmware off of it for further reverse engineering.
The flash memory is 2048KB large and the kernel image is compressed. I doubt they’re using all of it so we’re just going to dump a little over 2MB.
With the addresses that we got from the bootlog we can get the kernel by typing dump 0x80000600 2424832
and logging the output to a file with something like tee
. With this we have a file that looks like a hexdump made with xxd -g 1
and we can convert it to a binary with this script:
#!/usr/bin/env python3
#Takes hexdumps (eg. xxd -g 1 [file]) and makes binaries out of them.
import struct
import sys
EXPECTEDADDR = 0
binaryData = b''
if len(sys.argv) > 3:
BASEADDR = int(sys.argv[3], 16) #Where is it loaded in RAM?
with open(sys.argv[1]) as k:
data = k.read()
data = data.split('\n')
for i, line in enumerate( data):
try:
address = line[:8]
fileAddr = int(address, 16)-BASEADDR
if(fileAddr != EXPECTEDADDR):
raise ValueError
hexBytes = line[10:57]
hexBytes = hexBytes.split(' ')
for byte in hexBytes:
binaryData += struct.pack("B", int(byte, 16))
EXPECTEDADDR += 16
print(i)
except ValueError: #read address is not the one expected
print("Error in line: ", i)
exit(1)
binaryData = bytearray(binaryData)
with open(sys.argv[2], "wb") as out:
out.write(binaryData)
print("File has been written to", sys.argv[2])
else:
print("Usage:", sys.argv[0], "[inputfile] [outputfile] [baseaddress]")
And if we’re at it let’s also dump the bootloader with dump 0xB0000000 74880
and parse it with my script.
Now that we have the binaries we can start reversing them!
But we will leave that for the next part of this series.