HexHive / retrowrite

RetroWrite -- Retrofitting compiler passes through binary rewriting
Other
664 stars 77 forks source link

Illegal Instruction when running rewritten, re-compiled ELF #18

Closed AdamVanScyoc closed 3 years ago

AdamVanScyoc commented 3 years ago

Hello, thanks for retrowrite - I enjoyed your 36c3 presentation.

I installed retrowrite in a fresh Ubuntu docker container:

root@aba6d7a9e538:/# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.1 LTS
Release:        20.04
Codename:       focal
root@aba6d7a9e538:/# python3 --version
Python 3.8.5

... and followed the indicated setup steps by running the startup script setup.sh and created a virtual environment. Presumably this installed the correct version of capstone, and there doesn't appear to be a different capstone version installed through APT.

I have an x86_64 ELF that was a CTF challenge ( https://github.com/CUCyber/cuctf-2020-challenges/blob/main/reverse-engineering/virtual/virtual.c ) that I'm attempting to rewrite, but when I execute the re-written and re-compiled binary, I get an Illegal Instruction signal.

I compiled virtual.c with gcc virtual.c -O0 -ggdb -Wall -Wpedantic -Wextra -fPIC -fPIE -pie -o virtual_pie (ignore the makefile that's in the linked github repo), confirmed that this executable runs without error, and re-wrote with: /retrowrite/retrowrite virtual_pie virtual_pie.s

Next, I re-compiled with: gcc virtual_pie.s -o virtual_pie_retro

Now when I try to run the executable, I get:

 (retro) root@aba6d7a9e538:/home# ./virtual_pie_retro
Illegal instruction

Any ideas? Thanks again

DBGilles commented 3 years ago

I encountered the same error when trying out RetroWrite on a very simple piece of code and have not yet been able to figure it out. My sample piece of code is this:

  #include <stdio.h>

  int main(){
       int y = 10;
       int x = 12 + y;
       printf("result is %i\n", x);
   }

I recompiled in exactly the same way and got the same error. Interestingly the error did not occur when I commented out the line containing printf.

I ran to the executable with GDB to check which instruction caused the exception i.e.

gdb a.out
run 
x/i $pc

This is the GDB output

Program received signal SIGILL, Illegal instruction.
0x0000555555555050 in _start ()
(gdb) x/i $pc
=> 0x555555555050 <_start+16>:  lock push %rax
(gdb) 

I have inspected the generated binary further and I found that the call to printf seems to point to the wrong address.

   0x1151 <main+33>:    lea    0xeb0(%rip),%rdi        # 0x2008
   0x1158 <main+40>:    mov    $0x0,%eax
   0x115d <main+45>:    callq  0x1050 <_start+16>
   0x1162 <main+50>:    mov    $0x0,%eax
   0x1167 <main+55>:    leaveq 
   0x1168 <main+56>:    retq  

The instruction callq jumps to <_start+16> which then triggers the illegal instruction error. I also inspected the assembly generated by retrowrite and it also contains the instruction.

Maybe this call is not position independent? Or does RetroWrite not support using stdlib functions?

DBGilles commented 3 years ago

@AdamVanScyoc I noticed that in the demo the devs provided they use clang instead of GCC, and compiling with clang instead of GCC seemed to fix the issue for me.

gannimo commented 3 years ago

For reference, call you post the corresponding code sections before/after RetroWrite symbolized them?

gannimo commented 3 years ago

FYI: I'm having a hard time reproducing your issue :/

gannimo@apidae:~/repos/RetroWrite/x{0}$ wget https://github.com/CUCyber/cuctf-2020-challenges/raw/main/reverse-engineering/virtual/virtual.c
--2021-01-10 21:15:06--  https://github.com/CUCyber/cuctf-2020-challenges/raw/main/reverse-engineering/virtual/virtual.c
Resolving github.com (github.com)... 140.82.121.4
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
HTTP request sent, awaiting response... 302 Found                          
Location: https://raw.githubusercontent.com/CUCyber/cuctf-2020-challenges/main/reverse-engineering/virtual/virtual.c [following]
--2021-01-10 21:15:06--  https://raw.githubusercontent.com/CUCyber/cuctf-2020-challenges/main/reverse-engineering/virtual/virtual.c
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.240.133
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.240.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6015 (5.9K) [text/plain]    
Saving to: ‘virtual.c’              

virtual.c                          100%[===============================================================>]   5.87K  --.-KB/s    in 0s      

2021-01-10 21:15:07 (32.1 MB/s) - ‘virtual.c’ saved [6015/6015]

gannimo@apidae:~/repos/RetroWrite/x{0}$ gcc virtual.c -O0 -ggdb -Wall -Wpedantic -Wextra -fPIC -fPIE -pie -o virtual_pie
virtual.c: In function ‘vm’:
virtual.c:50:5: warning: ISO C forbids nested functions [-Wpedantic]
     void push(int v)                                                  
     ^~~~                         
virtual.c:54:5: warning: ISO C forbids nested functions [-Wpedantic]
     int pop()               
     ^~~                                                                     
virtual.c:70:5: warning: ISO C forbids nested functions [-Wpedantic]
     int fetch()                                                 
     ^~~                                  
virtual.c:84:5: warning: ISO C forbids nested functions [-Wpedantic]
     void decode(int bc)                                                   
     ^~~~                                                 
virtual.c:94:5: warning: ISO C forbids nested functions [-Wpedantic]
     void eval()                          
     ^~~~                           
virtual.c:217:5: warning: ISO C forbids nested functions [-Wpedantic]
     void run()                     
     ^~~~                           
virtual.c:83:41: warning: variable ‘reg3’ set but not used [-Wunused-but-set-variable]
     int instr=0, imm=0, reg1=0, reg2=0, reg3=0, tmp1=0, tmp2=0;
                                         ^~~~         
virtual.c:83:33: warning: variable ‘reg2’ set but not used [-Wunused-but-set-variable]
     int instr=0, imm=0, reg1=0, reg2=0, reg3=0, tmp1=0, tmp2=0;
                                 ^~~~
virtual.c: In function ‘main’:
virtual.c:236:20: warning: variable ‘z’ set but not used [-Wunused-but-set-variable]                                                          
     void *y[128], *z[128];                           
                    ^                                                                                                                       
virtual.c:236:11: warning: variable ‘y’ set but not used [-Wunused-but-set-variable]                                    
     void *y[128], *z[128];                      
           ^                                                         
gannimo@apidae:~/repos/RetroWrite/x{0}$ ../retrowrite virtual_pie virtual.s
[*] Relocations for a section that's not loaded: .rela.dyn                                                                      
[*] Relocations for a section that's not loaded: .rela.plt                                                                         
[x] Could not replace value in .init_array                                        
[x] Couldn't find valid section 3df0                                                                  
[x] Couldn't find valid section 3fd8          
[x] Couldn't find valid section 3fe0
[x] Couldn't find valid section 3fe8
[x] Couldn't find valid section 3ff0
[x] Couldn't find valid section 3ff8                                                                                                      
gannimo@apidae:~/repos/RetroWrite/x{0}$ gcc virtual.s 
gannimo@apidae:~/repos/RetroWrite/x{0}$ ./a.out                
Enter Password:
asdf                                                                                                                    
Incorrect.                  
gannimo@apidae:~/repos/RetroWrite/x{0}$                             
gannimo@apidae:~/repos/RetroWrite/x{0}$ gcc --version
gcc (Debian 8.3.0-6) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
AdamVanScyoc commented 3 years ago

So with the most current Ubuntu docker image and gcc 8.4 installed through Apt, I am able to get to compile, but all the stdio.h functions don't seem to work. When modifying virtual.c in order to get it to compile with clang, I am able to get it to compile and run correctly.

(retro) root@916cb7ed4b8e:/src# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.1 LTS
Release:    20.04
Codename:   focal
(retro) root@916cb7ed4b8e:/src# gcc-8 --version
gcc-8 (Ubuntu 8.4.0-3ubuntu2) 8.4.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

(retro) root@916cb7ed4b8e:/src# python3 --version
Python 3.8.5

Here's running the working version (before retrowriting with retrowrite virtual_no_pie virtual_no_pie.s ) and the non-working version (after re-compiling/re-assembling with gcc 8.4 gcc-8 virtual_no_pie.s -o virtual_retro):

(retro) root@916cb7ed4b8e:/src# ./virtual_no_pie 
Enter Password:
asdasd
Incorrect.
(retro) root@916cb7ed4b8e:/src# ./virtual_retro
(retro) root@916cb7ed4b8e:/src# 

Note that none of ./virtual_retro's puts() or fgets() calls are working.

This is a bit of a non-issue since it works with clang, and worked for you with gcc 8.3. But it probably relates to #19 so I thought that maybe it'd be useful info. If not I'm OK with closing this issue, up to you

Thanks again

virtual_no_pie.s.txt

cyanpencil commented 3 years ago

After some investigation, it looks like retrowrite did not parse the .plt section generated by old gcc (such as 8.4) I recently pushed a fix, it should now work even there. Recent gcc compilers do not have this problem.

Closing this issue, feel free to reopen if it's still not working.