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

x86 FPU emulation doesn't change FPU instruction pointer #7205

Open
bthiae2934 opened this issue Nov 18, 2024 · 2 comments
Open

x86 FPU emulation doesn't change FPU instruction pointer #7205

bthiae2934 opened this issue Nov 18, 2024 · 2 comments
Assignees

Comments

@bthiae2934
Copy link

bthiae2934 commented Nov 18, 2024

Describe the bug
I tried to use Ghidra to emulate shellcode encoded by shikata_ga_nai. Ghidra can't emulate following instruction sequence correctly:

        00000005 da  ce           FCMOVE     ST0 ,ST6
                                                      $U44a00 :1 =  BOOL_NEGATE  ZF
                                                      CBRANCH  *[ram ]0x7 :4, $U44a00 :1
                                                      ST0  =  COPY  ST6
        00000007 d9  74  24       FNSTENV    [ESP  + -0xc ]
                 f4
                                                      $U3780 :4 =  INT_ADD  0xfffffff4 :4, ESP
                                                      STORE  ram ($U3780 :4), FPUControlWord
                                                      $U4ae80 :4 =  INT_ADD  $U3780 :4, 4:4
                                                      STORE  ram ($U4ae80 :4), FPUStatusWord
                                                      $U4af00 :4 =  INT_ADD  $U3780 :4, 8:4
                                                      STORE  ram ($U4af00 :4), FPUTagWord
                                                      $U4af80 :4 =  INT_ADD  $U3780 :4, 20 :4
                                                      STORE  ram ($U4af80 :4), FPUDataPointer
                                                      $U4b000 :4 =  INT_ADD  $U3780 :4, 12 :4
                                                      STORE  ram ($U4b000 :4), FPUInstructionPointer
                                                      $U4b080 :4 =  INT_ADD  $U3780 :4, 18 :4
                                                      STORE  ram ($U4b080 :4), FPULastInstructionOpcode
        0000000b 5d              POP        EBP
                                                      $U2e680 :4 =  COPY  0:4
                                                      $U2e680 :4 =  LOAD  ram (ESP )
                                                      ESP  =  INT_ADD  ESP , 4:4
                                                      EBP  =  COPY  $U2e680 :4

These three instructions are a clever way to get EIP into EBP. The outcome of the first one is to set FPU instruction pointer to 0x5 and the second one is used to save FPU instruction pointer (and several other registers) on the stack.

Ghidra doesn't change FPU last instruction pointer so this register will remain at 0. Maybe it's necessary to change PCode of many floating-point instructions.

To Reproduce
Steps to reproduce the behavior:

  1. Create a file (shellcode) with following content:
    bf d3 7a d2 76 da ce d9 74 24 f4 5d 29 c9 b1 06 31 7d 12 83 c5 04 03 ae 74 30 83 61 40 43 8d 31 59 c4 61 42 36 14 16 8b a4 7d 88 5a cb b3 d4
  2. Import this binary, arch: x86 (i386).
  3. Run emulator to emulate this shellcode.
  4. Emulate instruction at offset 0x5.

Expected behavior
FPUInstructionPointer should change to 0x5.

Screenshots
image

Attachments
Nothing

Environment (please complete the following information):

  • OS: Windows 11
  • Java Version: JDK 21.0.4
  • Ghidra Version: 11.2.1 PUBLIC
  • Ghidra Origin: official GitHub distro

Additional context
From Intel Developer Manual:
image

@nsadeveloper789
Copy link
Contributor

I'm in agreement that p-code to set FPUInstructionPointer to 5 is missing. For this particular case, I think the following patch would fix it, but I'm not currently in a great position to test:

diff --git a/Ghidra/Processors/x86/data/languages/ia.sinc b/Ghidra/Processors/x86/data/languages/ia.sinc
index f7a29889a9..fa086de47f 100644
--- a/Ghidra/Processors/x86/data/languages/ia.sinc
+++ b/Ghidra/Processors/x86/data/languages/ia.sinc
@@ -4608,7 +4608,7 @@ define pcodeop to_bcd;
 :FNCLEX         is vexMode=0 & byte=0xDB; byte=0xE2                 { FPUStatusWord[0,8] = 0; FPUStatusWord[15,1] = 0; } 
 
 :FCMOVB ST0, freg   is vexMode=0 & byte=0xDA; frow=12 & fpage=0 & freg & ST0        { if ( !CF ) goto inst_next; ST0 = freg; }   
-:FCMOVE ST0, freg   is vexMode=0 & byte=0xDA; frow=12 & fpage=1 & freg & ST0        { if ( !ZF ) goto inst_next; ST0 = freg; }   
+:FCMOVE ST0, freg   is vexMode=0 & byte=0xDA; frow=12 & fpage=1 & freg & ST0        { FPUInstructionPointer = inst_start; if ( !ZF ) goto inst_next; ST0 = freg; }   
 :FCMOVBE ST0, freg  is vexMode=0 & byte=0xDA; frow=13 & fpage=0 & freg & ST0        { if ( !CF & !ZF ) goto inst_next; ST0 = freg; } 
 :FCMOVU  ST0, freg  is vexMode=0 & byte=0xDA; frow=13 & fpage=1 & freg & ST0        { if ( !PF ) goto inst_next; ST0 = freg; }   
 :FCMOVNB ST0, freg  is vexMode=0 & byte=0xDB; frow=12 & fpage=0 & freg & ST0        { if ( CF ) goto inst_next; ST0 = freg; }   

This is incomplete, however, as you've pointed out. Many of the FP instructions would need this change. If you can confirm the above change fixes your case, then we'll know how to get started. Thanks!

@nsadeveloper789 nsadeveloper789 added Status: Prioritize This is currently being prioritized and removed Status: Triage Information is being gathered labels Nov 21, 2024
@bthiae2934
Copy link
Author

@nsadeveloper789 I have tried your patch on master branch. Ghidra emulates this shellcode correctly after your patch applied. Thanks!
image
I changed base address and ESP to verify whether it works.

Shellcode after decode:
image
image

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

No branches or pull requests

4 participants