19

I want to run gdb with only a single variable MyVar in its environment.

However, the variable's contents are rather complex, containing non-printable ASCII, and so is best set using e.g. MyVar=$(python -c 'print("\xff...")').

I'm left with two options:

  1. Set the MyVar in bash before running gdb. Then inside gdb, remove all other environment variables individually using unset environment NAME (very tedious!).

  2. Clear all environment variables using unset environment. Then inside gdb, set MyVar using a shell command (as above) (how?)

Any ideas?

2

3 Answers 3

12

When starting gdb from shell command-line, you can specify which program to run, with which arguments (with --args), and even modify the environment of the program with the help of env!

I just did it successfully like this:

gdb --ex=run --args env LD_BIND_NOW=1 LD_DEBUG=libs \
apt-get install --yes $(cat pkgs-to-install-to-crash-apt)

--ex=run is to ask gdb to run it immediately.

In your case, you would do env -i.

It differs from the suggested env -i VAR=... gdb program in that only your examined program is under the special environment, but not gdb.

Sign up to request clarification or add additional context in comments.

3 Comments

Even though my answer was accepted, I like this one better since it does not affect gdb itself.
One thing to take into account with that: if you want to break at the beginning of your main entry point, then you have to use something else than the start command, otherwise, you stop in env's entry point, before it replaces the program with some exec*().
@JohanBoulé That is so if env has the main symbol. E.g. on Alpine Linux it doesn't by default. More details in my answer. A better way to use env is via the exec-wrapper setting. In this case it won't try to stop in env.
7

Option 2 is possible.

(gdb) unset environment
(gdb) python gdb.execute("set environment Myvar=\xff")
(gdb) show environment 
Myvar=ÿ

Option 1 can be done with env(1).

$ env -i MyVar=$(python -c 'print("xyz")') gdb
(gdb) show environment
MyVar=xyz
LINES=35
COLUMNS=80

Then you just have to clear LINES and COLUMNS.

8 Comments

Thank you. Do you know what LINES and COLUMNS mean? They are environment variables set by GDB I suppose.
Yes, they are the lines and columns of the terminal, and GDB sets them internally. See here and here.
it's wierd, I cannot get option 2 to work. After having executed the Python command, I check show environment and no environment variables there. The option 1 is also wierd. The stack is different from running env -i MyVar="..." prog with ASLR disabled - any ideas?
I edited my answer to show an example for option 2. I actually like it better than option 1 now! As for ASLR, that wasn't in your original question but if I disable it with echo 0 | sudo tee /proc/sys/kernel/randomize_va_space then I get the same output from this program every time: #include <stdio.h> void foo() { int x = 0; printf("&x = %p\n", &x); } int main() { foo(); return 0; }
If you need to run a program with a custom environment, it'd be better not run the whole gdb under env, but just the program, with gdb --args env ..., as demonstrated in stackoverflow.com/a/60571461/94687
|
2

tl;dr If you need only MyVar to be set:

$ gdb -ex 'set exec-wrapper env -i MyVar="`...`"' \
      ...
# or
$ gdb -ex 'set startup-with-shell off' \
      -ex 'with confirm off -- unset env' \
      -ex 'set env MyVar = '"`...`" \
      ...

You can do it this way:

$ readelf -s `which env` | grep '\bmain\b'

$ cat print-env.gdb
set $i = 0
while environ[$i]
    print environ[$i]
    set $i = $i + 1
end

$ gdb -ex 'break main' \
      -ex  run \
      -x print-env.gdb \
      --args env -i MyVar=`echo test` ruby
...
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
Starting program: /usr/bin/env -i MyVar=test ruby
warning: Error disabling address space randomization: Operation not permitted
process 32829 is executing new program: /usr/local/bin/ruby

Breakpoint 1, main (argc=1, argv=0x7ffd3cbca038) at ./main.c:55
55          ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffd3cbcafd9 "MyVar=test"
(gdb)

But in this case it starts env, which exec's ruby. Also gdb can't find the main symbol in the env binary, so it asks if you want to set the breakpoint anyway.

If you use start in place of break main; run, the result is the same (except that the breakpoint is temporary).

If env has the main symbol, gdb first stops in env, then in ruby (after continue):

$ readelf -s ../busybox/env | grep '\bmain\b'
  5553: 00000000000110b9   122 FUNC    GLOBAL DEFAULT   12 main

$ gdb -ex 'b main' \
      -ex  r \
      -ex  continue \
      -x print-env.gdb \
      --args ../busybox/env -i MyVar=`echo test` ruby
...
Breakpoint 1 at 0x110b9: file libbb/appletlib.c, line 1034.
Starting program: /busybox/env -i MyVar=test ruby
warning: Error disabling address space randomization: Operation not permitted

Breakpoint 1, main (argc=32723, argv=0x7fff2421cf10) at libbb/appletlib.c:1034
1034    {
Continuing.
process 32937 is executing new program: /usr/local/bin/ruby

Breakpoint 1, main (argc=1, argv=0x7ffd9c5d5568) at ./main.c:55
55          ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffd9c5d6fd9 "MyVar=test"
(gdb)

A better way is to use the exec-wrapper option:

$ gdb -ex 'set exec-wrapper ../busybox/env -i MyVar='`echo test` \
      -ex start \
      -x print-env.gdb \
      --args ruby
...
Temporary breakpoint 1 at 0x363ed: file ./main.c, line 55.
Starting program: /ruby/ruby
warning: Error disabling address space randomization: Operation not permitted

Temporary breakpoint 1, main (argc=1, argv=0x7ffd0ebeea28) at ./main.c:55
55          ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffd0ebeefe2 "MyVar=test"
(gdb)

In this case gdb won't try to stop in env.

If you're okay with the process receiving some extra variables:

$ gdb -ex 'with confirm off -- unset env' \
      -ex 'set env MyVar = '`echo test` \
      -ex  start \
      -x print-env.gdb \
      --args ruby
...
Temporary breakpoint 1 at 0x363ed: file ./main.c, line 55.
Starting program: /ruby/ruby
warning: Error disabling address space randomization: Operation not permitted

Temporary breakpoint 1, main (argc=1, argv=0x7ffcbee70748) at ./main.c:55
55          ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffcbee70fd0 "SHLVL=1"
$2 = 0x7ffcbee70fd8 "MyVar=test"
$3 = 0x7ffcbee70fe3 "PWD=/ruby"
(gdb)

The extra variables are set by the shell that gdb uses to execute your program:

If a shell is available on your target, the shell is used to pass the arguments, so that you may use normal conventions (such as wildcard expansion or variable substitution) in describing the arguments. In Unix systems, you can control which shell is used with the SHELL environment variable. If you do not define SHELL, GDB uses the default shell (/bin/sh). You can disable use of any shell with the set startup-with-shell command (see below for details).

https://sourceware.org/gdb/current/onlinedocs/gdb.html/Starting.html#Starting

So a way to get rid of them is:

$ gdb -ex 'set startup-with-shell off' \
      -ex 'with confirm off -- unset env' \
      -ex 'set env MyVar = '`echo test` \
      -ex  start \
      -x print-env.gdb \
      --args ruby
...
Temporary breakpoint 1 at 0x363ed: file ./main.c, line 55.
Starting program: /ruby/ruby
warning: Error disabling address space randomization: Operation not permitted

Temporary breakpoint 1, main (argc=1, argv=0x7ffce046e2f8) at ./main.c:55
55          ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffce046ffe2 "MyVar=test"
(gdb)

In cases more complex then echo test if you rely on the env utility you can use ${var@Q}:

$ MyVar=`...`
$ gdb -ex "set exec-wrapper env -i MyVar=${MyVar@Q}" ...

printf %q:

$ printf -v MyVar %q "`...`"
$ gdb -ex "set exec-wrapper env -i MyVar=$MyVar" ...

or just put the command inside exec-wrapper:

$ gdb -ex 'set exec-wrapper env -i MyVar="`...`"' ...

If you choose set env, then just enclose the command in double quotes:

$ gdb -ex 'set env MyVar = '"`...`" ...

gdb will skip the leading whitespaces, and take the rest of the line for the value of the MyVar variable.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.