Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an "--all" option to the got command #1101

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

gordonmessmer
Copy link
Contributor

@gordonmessmer gordonmessmer commented May 10, 2024

Description

This change adds a "got-all" command which expands on the existing "got" command by providing data about relocations in mapped shared object files in addition to the relocations specific to the main executable.

Particularly for auditing purposes, users may be interested in the state of all relocations, not only those for the primary executable file.

  • My code follows the code style of this project.
  • My change includes a change to the documentation, if required.
  • If my change adds new code, adequate tests have been added.
  • I have read and agree to the CONTRIBUTING document.

Copy link

🤖 Coverage update for 35ed738 🟢

Old New
Commit 18c1f7c 35ed738
Score 71.5629% 71.5629% (0)

@gordonmessmer
Copy link
Contributor Author

This draft is functional, but incomplete. It does not include documentation or tests. In its current condition, it exists only to ask whether the maintainers are interested in expanding the "got" command to provide information about relocations in mapped shared object files or not. And, if so, what the output from that command might look like. The current implementation merely prints relocations for all files, without indicating which file's relocations are being printed.

I'm also not sure whether such a command needs to accept a filter, allowing the user to print relocations for specific files, other than the primary executable file.

Please let me know what you think, and whether or not I should finish work on this command.

@Grazfather
Copy link
Collaborator

Can you please show its use? Couldn't you just add this as an argument to the existing got command?

@gordonmessmer
Copy link
Contributor Author

I can show its output, but it tends to be quite long. I'll try attaching it as a file.

The existing got command will show the state of relocatable symbols present in the primary binary executable. That's not all of the relocations, though. For example, the login binary is linked to libpam.so.0 on GNU/Linux systems, and libpam uses dlopen() to load libraries from /usr/lib64/security (or a similar path). The existing got command will not show any instance of dlopen, because there's a separate offset table for each ELF library in addition to the offset table for the main binary.

Arguments to the current got command reduce its output by filtering for matching symbols. The proposed got-all command makes the output much longer, by printing all of the offset tables for each mapped shared object in addition to the binary executable.

@gordonmessmer
Copy link
Contributor Author

Attaching got-all and got output for the login process

got-all.txt
got.txt

@Grazfather
Copy link
Collaborator

Yes I understand, but we could add an argument to have it run got 'deeper'. I see this as a recursive GOT command, but with depth one. We could add a --all argument, or even a --depth argument that limits how deep it looks into libraries' libraries.

@gordonmessmer
Copy link
Contributor Author

You could do that, but since the existing behavior is for all arguments to act as filters, the change would not be fully backward compatible, which is largely why I didn't pursue that path.

If that's your preference, I can continue developing in that direction.

@Grazfather
Copy link
Collaborator

I believe it would be, but maybe it would be breaking searching for symbols that contain -all. @hugsy care to weigh in?

@hugsy
Copy link
Owner

hugsy commented May 21, 2024

I see this as a recursive GOT command, but with depth one. We could add a --all argument, or even a --depth argument that limits how deep it looks into libraries' libraries.

I'm with @Grazfather on this, I don't feel like this doesn't deserve its own standalone command, but if you wish to make it so, then I'd suggest moving it to gef-extras instead.
In any case, yes tests and docs are lacking, to explain more usage to users.

Copy link

🤖 Coverage update for 424e876 🟢

Old New
Commit 18c1f7c 424e876
Score 71.4923% 71.4923% (0)

Copy link

🤖 Coverage update for 59b76cf 🟢

Old New
Commit 18c1f7c 59b76cf
Score 71.4923% 71.4923% (0)

@gordonmessmer
Copy link
Contributor Author

I've modified the implementation to use an optional flag to the got command.

The current implementation prints the GOT for each shared object without labeling them, which isn't very user-friendly. What would be idiomatic for GEF?

gef.py Outdated Show resolved Hide resolved
tests/commands/got.py Outdated Show resolved Hide resolved
@Grazfather
Copy link
Collaborator

you could do something like

title = Color.colorify(lib_name, "yellow bold") # idc about the colour much, but should be bold

or maybe

title = titlify(lib_name)

though this is used more for drawing the context window.

@gordonmessmer
Copy link
Contributor Author

Using titlify:

gef➤  got --all malloc
──────────────────────────────────────────── /usr/bin/sleep ────────────────────────────────────────────

GOT protection: Full RelRO | GOT functions: 45
 
[0x55555555bf00] malloc@GLIBC_2.2.5  →  0x7ffff7e64670
───────────────────────────────────────── /usr/lib64/libc.so.6 ─────────────────────────────────────────

GOT protection: Full RelRO | GOT functions: 18
 
[0x7ffff7fa0d68] malloc@@GLIBC_2.2.5  →  0x7ffff7e64670
─────────────────────────────────── /usr/lib64/ld-linux-x86-64.so.2 ───────────────────────────────────

GOT protection: Full RelRO | GOT functions: 0

Copy link

🤖 Coverage update for 377fa94 🟢

Old New
Commit 18c1f7c 377fa94
Score 71.4923% 71.4923% (0)

Copy link

🤖 Coverage update for f353980 🟢

Old New
Commit 18c1f7c f353980
Score 71.4923% 71.4923% (0)

@Grazfather
Copy link
Collaborator

That looks pretty good to me.

Copy link

🤖 Coverage update for 9d3c406 🟢

Old New
Commit 18c1f7c 9d3c406
Score 71.4923% 71.4923% (0)

Copy link

🤖 Coverage update for 3e55c02 🟢

Old New
Commit 18c1f7c 3e55c02
Score 71.4923% 71.4923% (0)

Copy link

🤖 Coverage update for 030fe44 🟢

Old New
Commit 18c1f7c 030fe44
Score 71.4923% 71.4923% (0)

Copy link

🤖 Coverage update for 3f91ce0 🟢

Old New
Commit 18c1f7c 3f91ce0
Score 71.4923% 71.4923% (0)

Copy link

🤖 Coverage update for f6038e4 🟢

Old New
Commit 18c1f7c f6038e4
Score 71.4923% 71.4923% (0)

@gordonmessmer
Copy link
Contributor Author

I'm happier with this test setup.

There's still the matter of the do_invoke_for function name, if that's important to you. Otherwise, if you are happy with this, I can squash it before merging...

docs/commands/got.md Outdated Show resolved Hide resolved
docs/commands/got.md Outdated Show resolved Hide resolved
docs/commands/got.md Show resolved Hide resolved
gef.py Outdated Show resolved Hide resolved
tests/commands/got.py Outdated Show resolved Hide resolved
tests/commands/got.py Outdated Show resolved Hide resolved
tests/commands/got.py Outdated Show resolved Hide resolved
tests/commands/got.py Outdated Show resolved Hide resolved
tests/commands/got.py Show resolved Hide resolved
Copy link

🤖 Coverage update for 94ab477 🟢

Old New
Commit 18c1f7c 94ab477
Score 71.4923% 71.4923% (0)

@Grazfather
Copy link
Collaborator

Other than line length nitpicking looks good to me.

@Grazfather Grazfather marked this pull request as ready for review May 25, 2024 21:06
@Grazfather Grazfather requested a review from hugsy May 25, 2024 21:06
Copy link

🤖 Coverage update for 4a750cf 🟢

Old New
Commit 2c26e33 4a750cf
Score 71.4923% 71.4923% (0)

@gordonmessmer
Copy link
Contributor Author

Thank you for your review. It sounds like this is just about ready, so I've squashed the commits.

docs/commands/got.md Outdated Show resolved Hide resolved
gef.py Outdated Show resolved Hide resolved
gef.py Outdated
pathlib.Path(mapfile.path).is_file() and
mapfile.permission & Permission.EXECUTE)
for mapfile in mapfiles:
self.print_got_for(mapfile, args.symbols)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think this will work in this way when remoting, because readelf is checked locally whereas the mapped files will not point to local files. Either exclude the command from running when not local, or make sure the remote files are correctly tested (against the ones in the temporary namespace).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take another look at this one, tomorrow.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take another look at this one, tomorrow.

No problem, thanks!
Also quick note, I've flagged your PR to be part of the next release.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for remote does this normally work by downloading the binary offer, e.g. the file object points to the local copy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... the list comprehension is looping over Section objects, and Section objects have a .realpath property that looks like it's intended to handle paths in the context of a remote session, so using that value seems like maybe it should be sufficient for this context.

But: the got command doesn't seem to work with remote sessions in the main branch. I've checked out 29fb74e and run a gef-remote session to examine a process on a remote host. It looks like all of the files have been copied to the local host, in /tmp/tmp8alv77xz/, but the got command fails:

(remote) gef➤  got
[!] Command 'got' failed to execute properly, reason: '/usr/libexec/postfix/master' not found/readable, most gef features will not work

I'm new to python extensions for gdb, so I'm actually not sure how to print the full exception/backtrace when something in gef fails, which makes it difficult for me to diagnose this further.

If everything else looks good, maybe this would be a good point to merge this PR, and then start a new PR to work on fixing remote use of the got command. (Unless the failure of the pre-existing got command is the result of something I'm doing wrong.)

gef.py Outdated Show resolved Hide resolved
Copy link

🤖 Coverage update for e18c40d 🟢

Old New
Commit 2c26e33 e18c40d
Score 71.4923% 71.4923% (0)

@gordonmessmer
Copy link
Contributor Author

(Squashed again.)

Copy link

🤖 Coverage update for 0d9507d 🟢

Old New
Commit bdf8219 0d9507d
Score 71.4923% 71.4923% (0)

@gordonmessmer gordonmessmer changed the title Add a "got-all" command Add an "--all" option to the got command May 26, 2024
Copy link

🤖 Coverage update for 5c68a97 🟢

Old New
Commit bdf8219 5c68a97
Score 71.4923% 71.4923% (0)

@gordonmessmer
Copy link
Contributor Author

gordonmessmer commented May 27, 2024

I've attached a commit that partially fixes the use of the got command with remote debugging. However, the realpath() property still doesn't work some of the time, because self.path reflects the path the appears in /proc//maps, but the file is being downloaded to what looks like the path that appears in the output of ldd (and that, I would argue, is a bug). The path that appears in maps is the real path; the path that appears in the output of ldd might include a symlink, and isn't canonicalized with realpath()

So, for example, /tmp/tmpttzy8t_f/lib64/libc.so.6 is present locally, but maps refers to /usr/lib64/libc.so.6

Personally, I think this is a problem that should be solved in a separate PR. The patch is a little messy, and fixing the got command when remote debugging isn't strictly related to the --all option. If you agree, I'm happy to back that commit out and open a new PR.

@gordonmessmer
Copy link
Contributor Author

I think that resolving remote use of got will require fixing bug https://sourceware.org/bugzilla/show_bug.cgi?id=23764

The current suggestion is that python code should use realpath instead using .filename, but that isn't possible for remote debugging.

I've requested an account for sourceware's bug system, but those require human approval, so it'll take a while to set that up. And since this looks like a gdb bug, I don't think it's going to be possible to fix remote use of got --all in the immediate future.

The patch that I've provided for remote debugging is probably still good, and appropriate, but as I said earlier: messy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants