I had the sudden realization that I didn’t really know how environment variables work. I think We all take them for granted, regardless of OS…
Some high level details about some such variables and their purpose can be found in the FreeBSD Handbook section 3.9. Shells.
You can view such variables in your shell using the env command:
root@freebsd:~ # env
PAGER=less
LANG=C.UTF-8
MAIL=/var/mail/root
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin
ENV=/root/.shrc
OLDPWD=/
PWD=/root
TERM=xterm-256color
USER=root
HOME=/root
SHELL=/bin/sh
MM_CHARSET=UTF-8
BLOCKSIZE=K
root@freebsd:~ #
…but what are all the low level specifics that nobody bothers to ask?
- Where are they stored?
- How are they accessed?
- How can I find them in LLDB?
The first stop on our quest is the FreeBSD Github repo. When in doubt, read the source code.
Now that we found the global environ1 variable, debug any binary with lldb and inspect the memory:
(lldb) target variable environ
(char **) environ = 0x00003c821c148ed0
(lldb) memory read -format x -size 8 -c 10 0x00003c821c148ed0
0x3c821c148ed0: 0x00003c821c14919d 0x00003c821c1491ad
0x3c821c148ee0: 0x00003c821c1491c1 0x00003c821c1491cd
0x3c821c148ef0: 0x00003c821c1491db 0x00003c821c149227
0x3c821c148f00: 0x00003c821c149232 0x00003c821c149246
0x3c821c148f10: 0x00003c821c149256 0x00003c821c149261
What we find is an array of pointers.
Reading from each address to the address of the next pointer address - 1
we can see each NAME=value pair in memory.
(lldb) memory read 0x00003c821c14919d 0x00003c821c1491ac
0x3c821c14919d: 4f 4c 44 50 57 44 3d 2f 75 73 72 2f 73 72 63 OLDPWD=/usr/src
(lldb) memory read 0x00003c821c1491ad 0x00003c821c1491c0
0x3c821c1491ad: 54 45 52 4d 3d 78 74 65 72 6d 2d 32 35 36 63 6f TERM=xterm-256co
0x3c821c1491bd: 6c 6f 72 lor
(lldb) memory read 0x00003c821c1491c1 0x00003c821c1491ce
0x3c821c1491c1: 42 4c 4f 43 4b 53 49 5a 45 3d 4b 00 53 BLOCKSIZE=K.S
(lldb) memory read 0x00003c821c1491cd 0x00003c821c1491da
0x3c821c1491cd: 53 48 45 4c 4c 3d 2f 62 69 6e 2f 73 68 SHELL=/bin/sh
From execve(2):
↩︎The argument envp is also a pointer to a null-terminated array of character pointers to null-terminated strings. A pointer to this array is normally stored in the global variable environ. These strings pass information to the new process that is not directly an argument to the command (see environ(7))