The file descriptors of a process can be manipulated in multiple ways to get information about the opened files of a program, in which permissions, duplicate them as new file descriptors, close, change the seek, open a file on an already opened filedescriptor or making a TCP connection for example.
All this stuff is system-dependant, so if it is not implemented in your favourite operating system(R) i'll be happy to receive feedback and patches.
It is handled by the '!fd' command:
[0x4A13C00E]> !fd? Usage: !fd[s|d] [-#] [file | host:port] !fd ; list filedescriptors !fdd 2 7 ; dup2(2, 7) !fds 3 0x840 ; seek filedescriptor !fdr 3 0x8048000 100 ; read 100 bytes from fd=3 at 0x80.. !fdw 3 0x8048000 100 ; write 100 bytes from fd=3 at 0x80.. !fdio [fdnum] ; enter fd-io mode on a fd, no args = back to dbg-io !fd -1 ; close stdout !fd /etc/motd ; open file at fd=3 !fd 127.0.0.1:9999 ; open socket at fd=5
Here's the filedescriptor list once a process is created with stdin, stdout and sterr
[0x4A13C00E]> !fd 0 0x00000013 rwC /dev/pts/0 1 0x00000000 rwC /dev/pts/0 2 0x00000000 rwC /dev/pts/0
Now we can close the stdout of the program or just redirect them to a file opening a file on the fd number '1':
[0x4A13C00E]> !fd /tmp/stdout.txt ; open new fd [0x4A13C00E]> !fd -1 ; close stdout [0x4A13C00E]> !fdd 3 1 ; stdout = new_fd [0x4A13C00E]> !fd -3 ; close file
There are three commands for reading and writing from/to filedescriptors of the child process from the debugger. !fdr and !fdw are the basic ones. As a simple example you can just use the child stdout filedescriptor for dumping the ELF signature to stdout.
[0x465D8810]> !fdw 1 0x8048001 3 ELF
Cool right? :)
Now you can do the same with !fdr. Both commands are used to inject a simple syscall instruction on the child process, so the addresses should be accessible from the target process.
Be aware if you are trying to read from stdin or from a socket, because this can break the program execution or trash your terminal.
The latest experimental but really interesting command is the
'!fdio' one. It is used to abstract the debugger IO layer to work transparently by using a simple syscall proxying using read and write over the child file descriptors and let the developer read and write on child filedescriptors.
[0x465D8810]> !fdio 3 FDIO enabled on 3 ... [0x465D8810]> !fdio FDIO disabled
Once FDIO is enabled, radare will read and write over the child, so you'll loss the view of the process memory layout to have access to the file opened by the process.
This is useful for debugging remote programs, because this way you can access to these files. In the same way you can open a new file and access it thru the remote debugger like if it was a local one.