481 lines
21 KiB
TeX
481 lines
21 KiB
TeX
Linux is now one of the most widespread operating system for embedded devices due
|
|
to its openess as well as the wide variety of platforms it can run on. Many
|
|
manufacturer actually use it in firmware you can find on many devices: DVB-T
|
|
decoders, routers, print servers, DVD players ... Most of the time the stock
|
|
firmware is not really open to the consumer, even if it uses open source software.
|
|
|
|
You might be interested in running a Linux based firmware for your router for
|
|
various reasons: extending the use of a network protocol (such as IPv6), having
|
|
new features, new piece of software inside, or for security reasons. A fully
|
|
open-source firmware is de-facto needed for such applications, since you want to
|
|
be free to use this or that version of a particular reason, be able to correct a
|
|
particular bug. Few manufacturers do ship their routers with a Sample Development Kit,
|
|
that would allow you to create your own and custom firmware and most of the time,
|
|
when they do, you will most likely not be able to complete the firmware creation process.
|
|
|
|
This is one of the reasons why OpenWrt and other firmware exists: providing a
|
|
version independent, and tools independent firmware, that can be run on various
|
|
platforms, known to be running Linux originaly.
|
|
|
|
\subsection{Which Operating System does this device run?}
|
|
|
|
There is a lot of methods to ensure your device is running Linux. Some of them do
|
|
need your router to be unscrewed and open, some can be done by probing the device
|
|
using its external network interfaces.
|
|
|
|
\subsubsection{Operating System fingerprinting and port scanning}
|
|
|
|
A large bunch of tools over the Internet exists in order to let you do OS
|
|
fingerprinting, we will show here an example using \textbf{nmap}:
|
|
|
|
\begin{Verbatim}
|
|
nmap -P0 -O <IP address>
|
|
Starting Nmap 4.20 ( http://insecure.org ) at 2007-01-08 11:05 CET
|
|
Interesting ports on 192.168.2.1:
|
|
Not shown: 1693 closed ports
|
|
PORT STATE SERVICE
|
|
22/tcp open ssh
|
|
23/tcp open telnet
|
|
53/tcp open domain
|
|
80/tcp open http
|
|
MAC Address: 00:13:xx:xx:xx:xx (Cisco-Linksys)
|
|
Device type: broadband router
|
|
Running: Linksys embedded
|
|
OS details: Linksys WRT54GS v4 running OpenWrt w/Linux kernel 2.4.30
|
|
Network Distance: 1 hop
|
|
\end{Verbatim}
|
|
|
|
nmap is able to report whether your device uses a Linux TCP/IP stack, and if so,
|
|
will show you which Linux kernel version is probably runs. This report is quite
|
|
reliable and it can make the distinction between BSD and Linux TCP/IP stacks and others.
|
|
|
|
Using the same tool, you can also do port scanning and service version discovery.
|
|
For instance, the following command will report which IP-based services are running
|
|
on the device, and which version of the service is being used:
|
|
|
|
\begin{verbatim}
|
|
nmap -P0 -sV <IP address>
|
|
Starting Nmap 4.20 ( http://insecure.org ) at 2007-01-08 11:06 CET
|
|
Interesting ports on 192.168.2.1:
|
|
Not shown: 1693 closed ports
|
|
PORT STATE SERVICE VERSION
|
|
22/tcp open ssh Dropbear sshd 0.48 (protocol 2.0)
|
|
23/tcp open telnet Busybox telnetd
|
|
53/tcp open domain ISC Bind dnsmasq-2.35
|
|
80/tcp open http OpenWrt BusyBox httpd
|
|
MAC Address: 00:13:xx:xx:xx:xx (Cisco-Linksys)
|
|
Service Info: Device: WAP
|
|
\end{verbatim}
|
|
|
|
The web server version, if identified, can be determining in knowing the Operating
|
|
System. For instance, the \textbf{BOA} web server is typical from devices running
|
|
an open-source Unix or Unix-like.
|
|
|
|
\subsubsection{Wireless Communications Fingerprinting}
|
|
|
|
Although this method is not really known and widespread, using a wireless scanner
|
|
to discover which OS your router or Access Point run can be used. We do not have
|
|
a clear example of how this could be achieved, but you will have to monitor raw
|
|
802.11 frames and compare them to a very similar device running a Linux based firmware.
|
|
|
|
\subsubsection{Web server security exploits}
|
|
|
|
The Linksys WRT54G was originally hacked by using a "ping bug" discovered in the
|
|
web interface. This tip has not been fixed for months by Linksys, allowing people
|
|
to enable the "boot\_wait" helper process via the web interface. Many web servers
|
|
used in firmwares are open source web server, thus allowing the code to be audited
|
|
to find an exploit. Once you know the web server version that runs on your device,
|
|
by using \textbf{nmap -sV} or so, you might be interested in using exploits to reach
|
|
shell access on your device.
|
|
|
|
\subsubsection{Native Telnet/SSH access}
|
|
|
|
Some firmwares might have restricted or unrestricted Telnet/SSH access, if so,
|
|
try to log in with the web interface login/password and see if you can type in
|
|
some commands. This is actually the case for some Broadcom BCM963xx based firmwares
|
|
such as the one in Neuf/Cegetel ISP routers, Club-Internet ISP CI-Box and many
|
|
others. Some commands, like \textbf{cat} might be left here and be used to
|
|
determine the Linux kernel version.
|
|
|
|
\subsubsection{Analysing a binary firmware image}
|
|
|
|
You are very likely to find a firmware binary image on the manufacturer website,
|
|
even if your device runs a proprietary operating system. If so, you can download
|
|
it and use an hexadecimal editor to find printable words such as \textbf{vmlinux},
|
|
\textbf{linux}, \textbf{ramdisk}, \textbf{mtd} and others.
|
|
|
|
Some Unix tools like \textbf{hexdump} or \textbf{strings} can be used to analyse
|
|
the firmware. Below there is an example with a binary firmware found other the Internet:
|
|
|
|
\begin{verbatim}
|
|
hexdump -C <binary image.extension> | less (more)
|
|
00000000 46 49 52 45 32 2e 35 2e 30 00 00 00 00 00 00 00 |FIRE2.5.0.......|
|
|
00000010 00 00 00 00 31 2e 30 2e 30 00 00 00 00 00 00 00 |....1.0.0.......|
|
|
00000020 00 00 00 00 00 00 00 38 00 43 36 29 00 0a e6 dc |.......8.C6)..??|
|
|
00000030 54 49 44 45 92 89 54 66 1f 8b 08 08 f8 10 68 42 |TIDE..Tf....?.hB|
|
|
00000040 02 03 72 61 6d 64 69 73 6b 00 ec 7d 09 bc d5 d3 |..ramdisk.?}.???|
|
|
00000050 da ff f3 9b f7 39 7b ef 73 f6 19 3b 53 67 ea 44 |???.?9{?s?.;Sg?D|
|
|
\end{verbatim}
|
|
|
|
Scroll over the firmware to find printable words that can be significant.
|
|
|
|
\subsubsection{Amount of flash memory}
|
|
|
|
Linux can hardly fit in a 2MB flash device, once you have opened the device and
|
|
located the flash chip, try to find its characteristics on the Internet. If
|
|
your flash chip is a 2MB or less device, your device is most likely to run a
|
|
proprietary OS such as WindRiver VxWorks, or a custom manufacturer OS like Zyxel ZynOS.
|
|
|
|
OpenWrt does not currently run on devices which have 2MB or less of flash memory.
|
|
This limitation will probably not be worked around since those devices are most
|
|
of the time micro-routers, or Wireless Access Points, which are not the main
|
|
OpenWrt target.
|
|
|
|
\subsubsection{Pluging a serial port}
|
|
|
|
By using a serial port and a level shifter, you may reach the console that is being shown by the device
|
|
for debugging or flashing purposes. By analysing the output of this device, you can
|
|
easily notice if the device uses a Linux kenrel or something different.
|
|
|
|
\subsection{Finding and using the manufacturer SDK}
|
|
|
|
Once you are sure your device run a Linux based firmware, you will be able to start
|
|
hacking on it. If the manufacturer respected the GPL, it will have released a Sample
|
|
Development Kit with the device.
|
|
|
|
\subsubsection{GPL violations}
|
|
|
|
Some manufacturers do release a Linux based binary firmware, with no sources at all.
|
|
The first step before doing anything is to read the license coming with your device,
|
|
then write them about this lack of Open Source code. If the manufacturer answers
|
|
you they do not have to release a SDK containing Open Source software, then we
|
|
recommend you get in touch with the gpl-violations.org community.
|
|
|
|
You will find below a sample letter that can be sent to the manufacturer:
|
|
|
|
\begin{verse}
|
|
Miss, Mister,
|
|
|
|
I am using a <device name>, and I cannot find neither on your website nor on the
|
|
CD-ROM the open source software used to build or modify the firmware.
|
|
|
|
In conformance to the GPL license, you have to release the following sources:
|
|
|
|
\begin{itemize}
|
|
\item complete toolchain that made the kernel and applications be compiled (gcc, binutils, libc)
|
|
\item tools to build a custom firmware (mksquashfs, mkcramfs ...)
|
|
\item kernel sources with patches to make it run on this specific hardware, this does not include binary drivers
|
|
\end{itemize}
|
|
|
|
Thank you very much in advance for your answer.
|
|
|
|
Best regards, <your name>
|
|
\end{verse}
|
|
|
|
\subsubsection{Using the SDK}
|
|
|
|
Once the SDK is available, you are most likely not to be able to build a complete
|
|
or functional firmware using it, but parts of it, like only the kernel, or only
|
|
the root filesystem. Most manufacturers do not really care releasing a tool that
|
|
do work every time you uncompress and use it.
|
|
|
|
You should anyway be able to use the following components:
|
|
|
|
\begin{itemize}
|
|
\item kernel sources with more or less functional patches for your hardware
|
|
\item binary drivers linked or to be linked with the shipped kernel version
|
|
\item packages of the toolchain used to compile the whole firmware: gcc, binutils, libc or uClibc
|
|
\item binary tools to create a valid firmware image
|
|
\end{itemize}
|
|
|
|
Your work can be divided into the following tasks:
|
|
|
|
\begin{itemize}
|
|
\item create a clean patch of the hardware specific part of the linux kernel
|
|
\item spot potential kernel GPL violations especially on netfilter and USB stack stuff
|
|
\item make the binary drivers work, until there are open source drivers
|
|
\item use standard a GNU toolchain to make working executables
|
|
\item understand and write open source tools to generate a valid firmware image
|
|
\end{itemize}
|
|
|
|
\subsubsection{Creating a hardware specific kernel patch}
|
|
|
|
Most of the time, the kernel source that comes along with the SDK is not really
|
|
clean, and is not a standard Linux version, it also has architecture specific
|
|
fixes backported from the \textbf{CVS} or the \textbf{git} repository of the
|
|
kernel development trees. Anyway, some parts can be easily isolated and used as
|
|
a good start to make a vanilla kernel work your hardware.
|
|
|
|
Some directories are very likely to have local modifications needed to make your
|
|
hardware be recognized and used under Linux. First of all, you need to find out
|
|
the linux kernel version that is used by your hardware, this can be found by
|
|
editing the \textbf{linux/Makefile} file.
|
|
|
|
\begin{verbatim}
|
|
head -5 linux-2.x.x/Makefile
|
|
VERSION = 2
|
|
PATCHLEVEL = x
|
|
SUBLEVEL = y
|
|
EXTRAVERSION = z
|
|
NAME=A fancy name
|
|
\end{verbatim}
|
|
|
|
So now, you know that you have to download a standard kernel tarball at
|
|
\textbf{kernel.org} that matches the version being used by your hardware.
|
|
|
|
Then you can create a \textbf{diff} file between the two trees, especially for the
|
|
following directories:
|
|
|
|
\begin{verbatim}
|
|
diff -urN linux-2.x.x/arch/<sub architecture> linux-2.x.x-modified/arch/<sub architecture> > 01-architecture.patch
|
|
diff -urN linux-2.x.x/include/ linux-2.x.x-modified/include > 02-includes.patch
|
|
diff -urN linux-2.x.x/drivers/ linux-2.x.x-modified/drivers > 03-drivers.patch
|
|
\end{verbatim}
|
|
|
|
This will constitute a basic set of three patches that are very likely to contain
|
|
any needed modifications that has been made to the stock Linux kernel to run on
|
|
your specific device. Of course, the content produced by the \textbf{diff -urN}
|
|
may not always be relevant, so that you have to clean up those patches to only
|
|
let the "must have" code into them.
|
|
|
|
The fist patch will contain all the code that is needed by the board to be
|
|
initialized at startup, as well as processor detection and other boot time
|
|
specific fixes.
|
|
|
|
The second patch will contain all useful definitions for that board: adresses,
|
|
kernel granularity, redefinitions, processor family and features ...
|
|
|
|
The third patch may contain drivers for: serial console, ethernet NIC, wireless
|
|
NIC, USB NIC ... Most of the time this patch contains nothing else than "glue"
|
|
code that has been added to make the binary driver work with the Linux kernel.
|
|
This code might not be useful if you plan on writing drivers from scratch for
|
|
this hardware.
|
|
|
|
\subsubsection{Using the device bootloader}
|
|
|
|
The bootloader is the first program that is started right after your device has
|
|
been powered on. This program, can be more or less sophisticated, some do let you
|
|
do network booting, USB mass storage booting ... The bootloader is device and
|
|
architeture specific, some bootloaders were designed to be universal such as
|
|
RedBoot or U-Boot so that you can meet those loaders on totally different
|
|
platforms and expect them to behave the same way.
|
|
|
|
If your device runs a proprietary operating system, you are very likely to deal
|
|
with a proprietary boot loader as well. This may not always be a limitation,
|
|
some proprietary bootloaders can even have source code available (i.e : Broadcom CFE).
|
|
|
|
According to the bootloader features, hacking on the device will be more or less
|
|
easier. It is very probable that the bootloader, even exotic and rare, has a
|
|
documentation somewhere over the Internet. In order to know what will be possible
|
|
with your bootloader and the way you are going to hack the device, look over the
|
|
following features :
|
|
|
|
\begin{itemize}
|
|
\item does the bootloader allow net booting via bootp/DHCP/NFS or tftp
|
|
\item does the bootloader accept loading ELF binaries ?
|
|
\item does the bootloader have a kernel/firmware size limitation ?
|
|
\item does the bootloader expect a firmware format to be loaded with ?
|
|
\item are the loaded files executed from RAM or flash ?
|
|
\end{itemize}
|
|
|
|
Net booting is something very convenient, because you will only have to set up network
|
|
booting servers on your development station, and keep the original firmware on the device
|
|
till you are sure you can replace it. This also prevents your device from being flashed,
|
|
and potentially bricked every time you want to test a modification on the kernel/filesystem.
|
|
|
|
If your device needs to be flashed every time you load a firmware, the bootlader might
|
|
only accept a specific firmware format to be loaded, so that you will have to
|
|
understand the firmware format as well.
|
|
|
|
\subsubsection{Making binary drivers work}
|
|
|
|
As we have explained before, manufacturers do release binary drivers in their GPL
|
|
tarball. When those drivers are statically linked into the kernel, they become GPL
|
|
as well, fortunately or unfortunately, most of the drivers are not statically linked.
|
|
This anyway lets you a chance to dynamically link the driver with the current kernel
|
|
version, and try to make them work together.
|
|
|
|
This is one of the most tricky and grey part of the fully open source projects.
|
|
Some drivers require few modifications to be working with your custom kernel,
|
|
because they worked with an earlier kernel, and few modifications have been made
|
|
to the kernel in-between those versions. This is for instance the case with the
|
|
binary driver of the Broadcom BCM43xx Wireless Chipsets, where only few differences
|
|
were made to the network interface structures.
|
|
|
|
Some general principles can be applied no matter which kernel version is used in
|
|
order to make binary drivers work with your custom kernel:
|
|
|
|
\begin{itemize}
|
|
\item turn on kernel debugging features such as:
|
|
\begin{itemize}
|
|
\item CONFIG\_DEBUG\_KERNEL
|
|
\item CONFIG\_DETECT\_SOFTLOCKUP
|
|
\item CONFIG\_DEBUG\_KOBJECT
|
|
\item CONFIG\_KALLSYMS
|
|
\item CONFIG\_KALLSYMS\_ALL
|
|
\end{itemize}
|
|
\item link binary drivers when possible to the current kernel version
|
|
\item try to load those binary drivers
|
|
\item catch the lockups and understand them
|
|
\end{itemize}
|
|
|
|
Most of the time, loading binary drivers will fail, and generate a kernel oops.
|
|
You can know the last symbol the binary drivers attempted to use, and see in the
|
|
kernel headers file, if you do not have to move some structures field before or
|
|
after that symbol in order to keep compatibily with both the binary driver and
|
|
the stock kernel drivers.
|
|
|
|
\subsubsection{Understanding the firmware format}
|
|
|
|
You might want to understand the firmware format, even if you are not yet capable
|
|
of running a custom firmware on your device, because this is sometimes a blocking
|
|
part of the flashing process.
|
|
|
|
A firmare format is most of the time composed of the following fields:
|
|
|
|
\begin{itemize}
|
|
\item header, containing a firmare version and additional fields: Vendor, Hardware version ...
|
|
\item CRC32 checksum on either the whole file or just part of it
|
|
\item Binary and/or compressed kernel image
|
|
\item Binary and/or compressed root filesystem image
|
|
\item potential garbage
|
|
\end{itemize}
|
|
|
|
Once you have figured out how the firmware format is partitioned, you will have
|
|
to write your own tool that produces valid firmare binaries. One thing to be very
|
|
careful here is the endianness of either the machine that produces the binary
|
|
firmware and the device that will be flashed using this binary firmware.
|
|
|
|
\subsubsection{Writing a flash map driver}
|
|
|
|
The flash map driver has an important role in making your custom firmware work
|
|
because it is responsible of mapping the correct flash regions and associated
|
|
rights to specific parts of the system such as: bootloader, kernel, user filesystem.
|
|
|
|
Writing your own flash map driver is not really a hard task once you know how your
|
|
firmware image and flash is structured. You will find below a commented example
|
|
that covers the case of the device where the bootloader can pass to the kernel its partition plan.
|
|
|
|
First of all, you need to make your flash map driver be visible in the kernel
|
|
configuration options, this can be done by editing the file \
|
|
\textbf{linux/drivers/mtd/maps/Kconfig}:
|
|
|
|
\begin{verbatim}
|
|
config MTD_DEVICE_FLASH
|
|
tristate "Device Flash device"
|
|
depends on ARCHITECTURE && DEVICE
|
|
help
|
|
Flash memory access on DEVICE boards. Currently only works with
|
|
Bootloader Foo and Bootloader Bar.
|
|
\end{verbatim}
|
|
|
|
Then add your source file to the \textbf{linux/drivers/mtd/maps/Makefile}, so
|
|
that it will be compiled along with the kernel.
|
|
|
|
\begin{verbatim}
|
|
obj-\$(CONFIG_MTD_DEVICE_FLASH) += device-flash.o
|
|
\end{verbatim}
|
|
|
|
You can then write the kernel driver itself, by creating a
|
|
\textbf{linux/drivers/mtd/maps/device-flash.c} C source file.
|
|
|
|
\begin{verbatim}
|
|
// Includes that are required for the flash map driver to know of the prototypes:
|
|
#include <asm/io.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mtd/map.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <linux/mtd/partitions.h>
|
|
#include <linux/vmalloc.h>
|
|
|
|
// Put some flash map definitions here:
|
|
#define WINDOW_ADDR 0x1FC00000 /* Real address of the flash */
|
|
#define WINDOW_SIZE 0x400000 /* Size of flash */
|
|
#define BUSWIDTH 2 /* Buswidth */
|
|
|
|
static void __exit device_mtd_cleanup(void);
|
|
|
|
static struct mtd_info *device_mtd_info;
|
|
|
|
static struct map_info devicd_map = {
|
|
.name = "device",
|
|
.size = WINDOW_SIZE,
|
|
.bankwidth = BUSWIDTH,
|
|
.phys = WINDOW_ADDR,
|
|
};
|
|
|
|
static int __init device_mtd_init(void)
|
|
{
|
|
// Display that we found a flash map device
|
|
printk("device: 0x\%08x at 0x\%08x\n", WINDOW_SIZE, WINDOW_ADDR);
|
|
// Remap the device address to a kernel address
|
|
device_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
|
|
|
|
// If impossible to remap, exit with the EIO error
|
|
if (!device_map.virt) {
|
|
printk("device: Failed to ioremap\n");
|
|
return -EIO;
|
|
}
|
|
|
|
// Initlialise the device map
|
|
simple_map_init(&device_map);
|
|
|
|
/* MTD informations are closely linked to the flash map device
|
|
you might also use "jedec_probe" "amd_probe" or "intel_probe" */
|
|
device_mtd_info = do_map_probe("cfi_probe", &device_map);
|
|
|
|
if (device_mtd_info) {
|
|
device_mtd_info->owner = THIS_MODULE;
|
|
|
|
int parsed_nr_parts = 0;
|
|
|
|
// We try here to use the partition schema provided by the bootloader specific code
|
|
if (parsed_nr_parts == 0) {
|
|
int ret = parse_bootloader_partitions(device_mtd_info, &parsed_parts, 0);
|
|
if (ret > 0) {
|
|
part_type = "BootLoader";
|
|
parsed_nr_parts = ret;
|
|
}
|
|
}
|
|
|
|
add_mtd_partitions(devicd_mtd_info, parsed_parts, parsed_nr_parts);
|
|
|
|
return 0;
|
|
}
|
|
iounmap(device_map.virt);
|
|
|
|
return -ENXIO;
|
|
}
|
|
|
|
// This function will make the driver clean up the MTD device mapping
|
|
static void __exit device_mtd_cleanup(void)
|
|
{
|
|
// If we found a MTD device before
|
|
if (device_mtd_info) {
|
|
// Delete every partitions
|
|
del_mtd_partitions(device_mtd_info);
|
|
// Delete the associated map
|
|
map_destroy(device_mtd_info);
|
|
}
|
|
|
|
// If the virtual address is already in use
|
|
if (device_map.virt) {
|
|
// Unmap the physical address to a kernel space address
|
|
iounmap(device_map.virt);
|
|
// Reset the structure field
|
|
device_map.virt = 0;
|
|
}
|
|
}
|
|
|
|
|
|
// Macros that indicate which function is called on loading/unloading the module
|
|
module_init(device_mtd_init);
|
|
module_exit(device_mtd_cleanup);
|
|
|
|
|
|
// Macros defining licence and author, parameters can be defined here too.
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Me, myself and I <memyselfandi@domain.tld");
|
|
\end{verbatim}
|