by Saumil Shah @therealsaumil
The sockout utilities are set of minimal ARM ELF binaries to facilitate large file transfer into and out of an ARM IoT Linux target, where you only have console I/O (e.g. telnet, minicom, reverse shell, etc).
sockbind
: listens on TCP port 4444 and dumps the contents to standard output.
sockconnect
: reads standard input and dumps it to an IP address on port 4444.
sockbind
behaves exactly like nc -l -p 4444
sockconnect
behaves exactly like nc <ip_address> 4444
sockbind
and sockconnect
are meant to be recreated on the target using printf
shell commands. (Refer sockbind.cmds
and sockconnect.cmds
)
Here you are! Copy and paste these lines in your shell and you will have an executable sockbind
and sockconnect
binaries, ready to run.
rm -f sockbind
printf "%b" "\x7f\x45\x4c\x46\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00" >> sockbind
printf "%b" "\x02\x00\x28\x00\x20\x00\x20\x00\x20\x00\x20\x00\x04\x00\x00\x00" >> sockbind
printf "%b" "\x09\x10\x8f\xe2\x11\xff\x2f\xe1\x34\x00\x20\x00\x01\x00\x00\x00" >> sockbind
printf "%b" "\x02\x20\x01\x21\x52\x40\xc8\x27\x51\x37\x00\xdf\x04\x1c\x11\xa1" >> sockbind
printf "%b" "\x10\x22\x01\x37\x00\xdf\x20\x1c\x49\x40\x02\x37\x00\xdf\x20\x1c" >> sockbind
printf "%b" "\x52\x40\x01\x37\x00\xdf\x04\x1c\x01\x23\x9b\x02\x69\x46\xc9\x1a" >> sockbind
printf "%b" "\x8d\x46\x20\x1c\x1a\x1c\x03\x27\x00\xdf\x02\x1c\x01\x20\x04\x27" >> sockbind
printf "%b" "\x00\xdf\x00\x2a\xf5\xdc\x20\x1c\x06\x27\x00\xdf\x40\x40\x01\x27" >> sockbind
printf "%b" "\x00\xdf\xc0\x46\x02\x00\x11\x5c\x00\x00\x00\x00" >> sockbind
chmod +x sockbind
rm -f sockconnect
printf "%b" "\x7f\x45\x4c\x46\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00" >> sockconnect
printf "%b" "\x02\x00\x28\x00\x20\x00\x20\x00\x20\x00\x20\x00\x04\x00\x00\x00" >> sockconnect
printf "%b" "\x09\x10\x8f\xe2\x11\xff\x2f\xe1\x34\x00\x20\x00\x01\x00\x00\x00" >> sockconnect
printf "%b" "\x02\x20\x01\x21\x52\x40\xc8\x27\x51\x37\x00\xdf\x04\x1c\x0c\xa1" >> sockconnect
printf "%b" "\x10\x22\x02\x37\x00\xdf\x01\x23\x9b\x02\x69\x46\xc9\x1a\x8d\x46" >> sockconnect
printf "%b" "\x00\x20\x1a\x1c\x03\x27\x00\xdf\x02\x1c\x20\x1c\x04\x27\x00\xdf" >> sockconnect
printf "%b" "\x00\x2a\xf5\xdc\x20\x1c\x06\x27\x00\xdf\x40\x40\x01\x27\x00\xdf" >> sockconnect
printf "%b" "\x02\x00\x11\x5c\x0a\x14\x03\x28" >> sockconnect
chmod +x sockconnect
sockbind.s
- ARM assembly source forsockbind
sockconnect.s
- ARM assembly source forsockconnect
sockbind_nothumb.s
- Pure ARM assembly source forsockbind
(no Thumb code)sockconnect_nothumb.s
- Pure ARM assembly source forsockconnect
(no Thumb code)
convert_to_printf.sh
- Generates.cmds
files for re-creating the binaries on a target ARM Linux IoT device, using only the command linepatch_ip
- Patches an IPv4 IP address intosockconnect
/sockconnect_nothumb
patch_port
- Patches a port number into thesockbind
/sockconnect
binaries and their_nothumb
variants
sockbind.cmds
- Cut and paste the contents to recreatesockbind
from CLIsockconnect.cmds
- Cut and paste the contents to recreatesockconnect
from CLIsockbind_nothumb.cmds
- Cut and paste the contents to recreatesockbind_nothumb
from CLIsockconnect_nothumb.cmds
- Cut and paste the contents to recreatesockconnect_nothumb
from CLI
Transfer a binary gdbserver
on an ARM Linux system using sockbind
. We assume the IP address of the ARM Linux system is 10.20.50.15.
First, we create the sockbind
binary on the target system using the sockbind.cmds
commands listed above.
Next, transfer gdbserver
to the target as follows:
On the target, run ./sockbind > gdbserver; chmod +x gdbserver
On the source system, run
nc 10.20.50.15 4444 < gdbserver
Sometimes you have to terminate the source nc
using Ctrl+C
.
We want to copy the contents of /dev/mtdblock5
from an ARM Linux system using sockconnect
. We assume the IP address of the target computer is 10.20.3.40, and it is listening on port 5555.
First, we patch the sockconnect
binary using the patch_ip
utility, and convert the resultant binary to printable commands:
./patch_ip 10.20.3.40 5555 sockconnect
./convert_to_printf sockconnect > sockconnect.cmds
Next, we create the sockconnect
binary on the target system using the sockconnect.cmds
commands listed above. Connect to your device's console and paste the commands from sockconnect.cmds
and lastly run chmod +x sockconnect
.
Run a netcat listener on the target computer on port 4444:
nc -nvv -l -p 4444 > mtdblock5.bin
Lastly, transfer the contents of /dev/mtdblock5
to the target computer 10.20.3.40 using:
dd if=/dev/mtdblock5 | ./sockconnect
Sometimes you have to terminate the source nc
using Ctrl+C
.
A few clever tricks have been employed to create a minimal sockbind
and sockconnect
binaries.
- Use THUMB instructions
strip
the binary after linking- Remove ELF sections that do not cripple the binary (e.g.
.ARM.attributes
) - Remove the
.shstrtab
section by truncating the ELF binary
Here is the output of make
for sockbind
as sockbind.s -o sockbind.o
ld sockbind.o -o sockbind
strip sockbind
objcopy --remove-section .ARM.attributes sockbind
./truncate_elf.sh sockbind
185+0 records in
185+0 records out
185 bytes copied, 0.012916 s, 14.3 kB/s
The resultant binary is 185 bytes.
This technique is inspired by mydzor/tinyelf-arm. Instead of linking to an ELF binary using ld
, we shall insert a minimal ELF header directly into the assembly code, and overload certain ELF headers that do not seem to impact the process of loading. The downside with this technique is that gdb
cannot work with the resultant binary anymore. The advantage is that it produces binaries 45 bytes smaller than my technique.
The assembly code is as follows:
.macro bump addr
.word \addr+0x200000
.endm
ehdr: /* Elf32_Ehdr */
.byte 0x7F /* e_ident */
.ascii "ELF"
.word 1 /* |p_type */
.word 0 /* |p_offset */
.word 0x200000 /* |p_vaddr */
.word 0x280002 /* e_type & e_machine |p_paddr */
bump main /* e_version |p_filesz */
bump main /* e_entry |p_memsz */
.word 4 /* e_phoff |p_flags */
main:
.code 32
adr r1, THUMB+1 /* e_shoff |p_allign */
bx r1 /* e_flags */
.word 0x00200034 /* e_ehsize & e_phentsize */
/* eoreq r0, r0, r4, lsr r0 */
.word 0x00000001 /* e_phnum & e_shentsize */
/* andeq r0, r0, r1 */
THUMB:
.code 16
// socket(2, 1, 0)
mov r0, #2
mov r1, #1
...
To build the binary, we use the following commands:
as program.s -o program.o
objcopy program.o -O binary program
chmod +x program
objcopy
is used to extract the raw assembly code from an object file. Our raw code already contains an ELF header, which results into a smaller ELF binary. The source code in this repository uses this minimal ELF technique. The older technique is mentioned for documentation.
Certain older kernels do not support syscalls in THUMB mode. For such cases, we have ARM-only versions of sockbind
and sockconnect
. Please refer to sockbind_nothumb.s
, sockconnect_nothumb.s
and the respect sockbind_nothumb.cmds
and sockconnect_nothumb.cmds
files.
I routinely use several statically compiled/linked ARM binaries when analysing IoT systems. A growing set of static ARM binaries can be found at https://github.com/therealsaumil/static-arm-bins
I have been actively developing the ARM-X Firmware Emulation Framework, for emulating ARM IoT devices. These utilities have been created during my research when I was faced with certain difficult situations.
Saumil Shah @therealsaumil