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

gef fails to provide right context for aarch64 binary #458

Closed
3 tasks done
pedrib opened this issue Aug 13, 2019 · 27 comments · Fixed by #503
Closed
3 tasks done

gef fails to provide right context for aarch64 binary #458

pedrib opened this issue Aug 13, 2019 · 27 comments · Fixed by #503
Labels

Comments

@pedrib
Copy link

pedrib commented Aug 13, 2019

Your issue will be closed unless you confirm the following:

Step 1: Describe your environment

  • Operating System: Debian 10
  • Architecture: x86-64
  • GDB version (including the Python library version):
    GNU gdb (Debian 8.2.1-2) 8.2.1
    Python 3.7.3 (default, Apr 3 2019, 05:39:12)

Step 2: Describe your problem

When I load certain aarch64 binaries with gef, it complains about lack of .gnu_debugdata and then it says most features won't work. I would be OK with that if it actually displayed context correctly, but it doesn't. It then believes all code is x86, and proceeds to dereference x86 registers, which of course fails.

Steps to reproduce

  1. Load aarch64 binary without .gnu_debugdata
  2. Attempt to debug stepping as normal

Observed Results

I get the following warning when loading the binary

GEF for linux ready, type `gef' to start, `gef config' to configure
76 commands loaded for GDB 8.2.1 using Python engine 3.7
[*] 4 commands could not be loaded, run `gef missing` to know why.
[+] Configuration from '/home/user/.gef.rc' restored
Reading symbols from binary...Reading symbols from .gnu_debugdata for /home/user/binary...(no debugging symbols found)...done.
[!] '.gnu_debugdata for /home/user/binary' not found/readable
[!] Failed to get file debug information, most of gef features will not work
(no debugging symbols found)...done.
gef➤  

Which probably confuses gef, since it identifies the code as x86:32:

────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
[!] Command 'dereference' failed to execute properly, reason: Unknown register.
────────────────────────────────────────────────────────────────────────────────────────────── registers ────
──────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
   0x6fc195e880 <Object::Init()+168> b      0x6fc195e868 <_ZN10Object4InitEv+144>
   0x6fc195e884 <Object::Start()+0> ldr    x0,  [x0,  #16]
   0x6fc195e888 <Object::Start()+4> cbz    x0,  0x6fc195e898 <_ZN10Object5StartEv+20>
 → 0x6fc195e88c <Object::Start()+8> ldr    x8,  [x0]
   0x6fc195e890 <Object::Start()+12> ldr    x1,  [x8,  #24]
   0x6fc195e894 <Object::Start()+16> br     x1

But gdb detects the architecture correctly:
gef➤  show arch
The target architecture is set automatically (currently aarch64)

Expected results

As per the warning, I'm not expecting most of gef features to work (although to be honest, I haven't researched why), but I expect the context and registers to be displayed correctly.

@hugsy
Copy link
Owner

hugsy commented Aug 13, 2019

Can you provide a small binary we can use to repro your issue?

@hugsy hugsy added the triage label Aug 13, 2019
@pedrib
Copy link
Author

pedrib commented Aug 14, 2019

Sure, here you go. This is from a Samsung S7 phone. Most binaries I have pulled from this phone seem to be like this. I'm using gdb-multiarch BTW.
vndservice.zip

@Grazfather
Copy link
Collaborator

I don't currently have a debug env for this, but GEF for me does correctly identify the arch

gef➤  pi current_arch
<__main__.AARCH64 object at 0x7f58211569b0>

Can you run pi current_arch? This has to do with how it's detecting the filetype (which is by parsing the ELF header itself, not using gdb).

@pedrib
Copy link
Author

pedrib commented Nov 9, 2019

@Grazfather Not for me apparently...
gef> pi current_arch
<main.X86 object at 0x7610dc63db90>

This happens on gdb-multiarch 8.2.1 from Debian, and gdb-aarch64 8.2.1 that I built myself from buildroot.

Maybe something to do with the fact that I'm using a gdb for aarch64 on a x86 platform?

@hugsy
Copy link
Owner

hugsy commented Nov 9, 2019

What if you try to force it ?

gefpi set_arch("ARM64")

@pedrib
Copy link
Author

pedrib commented Nov 10, 2019

it accepts it:
gef➤ pi set_arch("ARM64") <__main__.AARCH64 object at 0x73cce3a64e10>

but still doesn't set the registers right:
──────────────────────────────────────────────────────────────────────────────────────────────── stack ──── [!] Command 'dereference' failed to execute properly, reason: Unknown register. ──────────────────────────────────────────────────────────────────────────────────────────── registers ──── ────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────

Both with gdb-multiarch and gdb-aarch64

@hugsy
Copy link
Owner

hugsy commented Nov 10, 2019

Just like @Grazfather I really cannot reproduce your issue, gef correctly identifies the binary, see below (using stadnard gdb-multiarch from ubuntu's repo:

hugsy@ubuntu-dev $ gdb-multiarch -q ./vndservice
GEF for linux ready, type `gef' to start, `gef config' to configure
80 commands loaded for GDB 8.1.0.20180409-git using Python engine 3.6
[...]
gef➤  pi current_arch
<__main__.AARCH64 object at 0x7fb26efd2048>

Your issue definitely comes from the fact that current_arch isn't of the proper type (is X86 when it should be AARCH64).

Can you try the following:

pi reset_all_caches()
pi set_arch("ARM64")
pi print(current_arch)

@pedrib
Copy link
Author

pedrib commented Nov 12, 2019

That seems to work! What do you think is going wrong? And how can I automate it?

@hugsy
Copy link
Owner

hugsy commented Nov 12, 2019

That's pretty weird but cool 👍

To automate, simply add those 3 lines after souce gef.py in your ~/.gdbinit

@pedrib
Copy link
Author

pedrib commented Nov 12, 2019

But won't that force aarch64 for all binaries that I load? What if I am loading a x86 or a MIPS binary?

@hugsy
Copy link
Owner

hugsy commented Nov 12, 2019

It will, my point was to have this in your .gdbinit.
You can always do something like in that file:

define set-arm64
  pi reset_all_caches()
  pi set_arch("ARM64")
  pi print(current_arch)
end

Then type in set-arm64 in the prompt whenever gef fails to recognize properly the binary.

@pedrib
Copy link
Author

pedrib commented Nov 13, 2019

Ok will do, thanks! But aren't you curious to get to the bottom of this? Any idea where I can investigate?

@pedrib
Copy link
Author

pedrib commented Nov 13, 2019

Also, it doesn't seem a real fix - if I'm debugging remotely, disconnect to the target, and connect again, it will think the binary is x86 again...

@hugsy
Copy link
Owner

hugsy commented Nov 13, 2019

Ok will do, thanks! But aren't you curious to get to the bottom of this? Any idea where I can investigate?

I am but if I (or any other gef devs) cannot reproduce it (it all works fine on my ARCH64 VM), I can't really do much.

@hugsy
Copy link
Owner

hugsy commented Nov 13, 2019

If you keep digging into this and come up with a proper solution and/or a good reproduction setup, then we can go further.

@pedrib
Copy link
Author

pedrib commented Nov 13, 2019

Ok will do, thanks! But aren't you curious to get to the bottom of this? Any idea where I can investigate?

I am but if I (or any other gef devs) cannot reproduce it (it all works fine on my ARCH64 VM), I can't really do much.

I think this might be the problem - have you tried running gdb-multiarch or a cross compiled gdb for aarch64 in an x86-64 computer? Because that's my setup, and that's where it probably gets confused!

If you keep digging into this and come up with a proper solution and/or a good reproduction setup, then we can go further.

I'm happy to do it, just looking for some tips or places to dig!

@hugsy
Copy link
Owner

hugsy commented Nov 13, 2019

I am always using gdb-multiarch, and it was recognizing the architecture immediately.

@pedrib
Copy link
Author

pedrib commented Nov 13, 2019

@hugsy sure, but did you use it on a x86 machine? Maybe I misunderstood, but I'm asking because you said this:

(it all works fine on my ARCH64 VM)

My use case is - I'm debugging a aarch64 phone, and doing it remotely on my x86 machine, running Debian x86, with gdb-multiarch.

@hugsy
Copy link
Owner

hugsy commented Nov 13, 2019

@hugsy sure, but did you use it on a x86 machine?

Yes.

@pedrib
Copy link
Author

pedrib commented Nov 14, 2019

Ok, understood. I will try to dig further and see what is wrong with my setup... any ideas where I can look?

@Grazfather
Copy link
Collaborator

First I would try to reproduce hugsy's setup... probably using qemu. @hugsy do you have a link to your qemus?

Then with your phone and qemu you can compare what's different -- GDB's idea of the arch, GEF's, etc.

@k3vinlusec
Copy link
Contributor

I also encountered this issue, I built GDB 8.3 from google android source code using the following command.
../configure --prefix=/Users/tool/gdb/gdb-8.3/build --target=aarch64-linux-android --disable-werror --with-python=/usr/local/bin/python3

This gdb is built with LZMA support, it could try to read symbols from .gnu_debugdata.

Reading symbols from target:/system/bin/app_process64...
Reading symbols from .gnu_debugdata for target:/system/bin/app_process64...
(No debugging symbols found in .gnu_debugdata for target:/system/bin/app_process64)
[c0lv3r_pwn]: get_filepath .gnu_debugdata for target:/system/bin/app_process64

In the regular gdb from homebrew on macOS, it isn't built with LZMA. When you debug the app, it won't show up "Reading symbols from .gnu_debugdata for target:/system/bin/app_process64...".

I added some debug code in GEF, last line is the output from debug code I added. I found in the function get_filepath(), the return value of gdb.current_progspace().filename is ".gnu_debugdata for target:/system/bin/app_process64" when the function get_filepath is invoked at first time. It will be "target:/system/bin/app_process64" when the function get_filepath is invoked at second time.

def get_filepath():
"""Return the local absolute path of the file currently debugged."""
filename = gdb.current_progspace().filename
...

Then in the function get_elf_headers(), it will pass the string ".gnu_debugdata for target:/system/bin/app_process64" to ELF().

@lru_cache()
def get_elf_headers(filename=None):
"""Return an Elf object with info from filename. If not provided, will return
the currently debugged file."""
if filename is None:
filename = get_filepath()

if filename.startswith("target:"):
    warn("Your file is remote, you should try using `gef-remote` instead")
    return

return Elf(filename)

Finally it could cause an error like "...not found/readable ...". In ELF, the default arch is X86. So it caused to display context info incorrectly.

We need to add some code to handle this case in the function get_filepath() or get_elf_headers().

Additionally, I'm not sure if it's a bug in gdb.current_progspace().filename, it returns ".gnu_debugdata for target:/system/bin/app_process64".

Hope this investigation can help you! @pedrib @hugsy

@k3vinlusec
Copy link
Contributor

k3vinlusec commented Feb 17, 2020

I patched the function get_filepath() like the following.

def get_filepath():
"""Return the local absolute path of the file currently debugged."""
filename = gdb.current_progspace().filename

if is_remote_debug():
    # if no filename specified, try downloading target from /proc
    if filename is None:
        pid = get_pid()
        if pid > 0:
            return download_file("/proc/{:d}/exe".format(pid), use_cache=True)
        return None

    # if target is remote file, download
    elif filename.startswith("target:"):
        fname = filename[len("target:"):]
        return download_file(fname, use_cache=True, local_name=fname)
    #cl0v3r_pwn patched
    elif filename.startswith(".gnu_debugdata for target:"):
        fname = filename[len(".gnu_debugdata for target:"):]
        return download_file(fname, use_cache=True, local_name=fname)
    #end cl0v3r_pwn patched

    elif __gef_remote__ is not None:
        return "/tmp/gef/{:d}/{:s}".format(__gef_remote__, get_path_from_info_proc())
    return filename
else:
    if filename is not None:
        return filename
    # inferior probably did not have name, extract cmdline from info proc
    return get_path_from_info_proc()

Please review it. @hugsy

@hugsy
Copy link
Owner

hugsy commented Feb 18, 2020

Hi @k3vinlusec ,

Please send a proper Pull Request with a reference to this issue for it to be merged.

Thanks,

@k3vinlusec
Copy link
Contributor

Hi, @hugsy

I have sent a pull request for this case, please review it! Thanks!

@hugsy hugsy linked a pull request Feb 18, 2020 that will close this issue
@hugsy
Copy link
Owner

hugsy commented Feb 18, 2020

I'll take a look. Thanks for the PR

@pedrib
Copy link
Author

pedrib commented Mar 22, 2020

Seems to have fixed it for me too, thanks!

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

Successfully merging a pull request may close this issue.

4 participants