Skip to content
/ RASD Public

An interesting way to detect return address spoofing on x64-windows.

License

Notifications You must be signed in to change notification settings

cryotb/RASD

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RASD

Parts of this project have been reverse engineered and reconstructed from R5AC, an in-house anticheat solution currently deployed in APEX LEGENDS. I have simplified some of their checks due to time constrains, but behavior should be identical. I have written a few rather small tests to confirm this, which execute a function monitored by a replicated R5AC stackwalk, which intentionally do following things:

  • call into monitored function from legitimate place (e.g from within the main executable)
  • call into monitored function from code residing in a manually allocated RWX page.
  • call into monitored function with 3 open-source return address spoofers. (gadgets used: jmp, add rsp; ret, int3)

Sample Output

Make sure to compile in x64 Release!

------( trying to call from process itself... )------
<<EXTRA>> [INFO] CALLED BY 'r5sw.exe'+0xae65
-----------------------------------------------------
------( trying to call from RWX page... )------
<<EXTRA>> [FLAG//////UNBACKED CODE EXECUTION(PRIMARY)] caller is originated within non-module memory.
<<EXTRA>> [INFO] CALLED BY 'UNK_be1d4ff540'
[FLAG//////UNBACKED CODE EXECUTION(SECONDARY)] suspicious record at 0 (0000022AB3B30017)
-----------------------------------------------------
------( trying to call with spoof (namazso): )------
<<EXTRA>> [INFO] CALLED BY 'r5sw.exe'+0x1000
[FLAG//////RETADDR SPOOFER] suspicious record at 0 (00007FF703471000)
-----------------------------------------------------
------( trying to call with spoof (beakers): )------
<<EXTRA>> [INFO] CALLED BY 'kernel32.dll'+0x18f24
[FLAG//////RETADDR SPOOFER] suspicious record at 0 (00007FFE230B8F24)
[FLAG//////RETADDR SPOOFER] suspicious record at 1 (00007FFE230D5B34)
[FLAG//////RETADDR SPOOFER] suspicious record at 2 (00007FFE230B5FB6)
[FLAG//////RETADDR SPOOFER] suspicious record at 3 (00007FFE230FEC99)
[FLAG//////RETADDR SPOOFER] suspicious record at 4 (00007FF70347D43B)
-----------------------------------------------------
------( trying to call with spoof (ReaP): )------
[!] spoofers::reap::VectoredHandler: Old return address: 7ff70347ba2f
[!] spoofers::reap::VectoredHandler: New return address: 7ffe2378248f
<<EXTRA>> [INFO] CALLED BY 'user32.dll'+0x7248f
[FLAG//////RETADDR SPOOFER] suspicious record at 0 (00007FFE2378248F)
[!] spoofers::reap::VectoredHandler: Returning back to 00007FF70347BA2F...
-----------------------------------------------------

How does it work?

Currently they use an API for generating a backtrace recording. It's located in kernel32.dll and named RtlCaptureStackBackTrace. These checks are riddled around the game's normal code and you will eventually call into them.

If your cheat generates a CALL instruction on anything that later on may land into a stackwalk check, your cheat module may be exposed because by generating aforementioned CALL, you are pushing a return address to the stack which will obviously point to your cheat if you want it to return normally. There are ways to overcome this, but this isn't a bypass repository.

You should always keep in mind that it's not hard for an anticheat to detect an anomaly here, if you add the fact that all of this comes from a module that isn't even in LDR nor signed/in a whitelist, then you definitely know something's up.

Which gadgets does it detect?

It pretty much detects certain variations of gadgets commonly used when doing anything with return address spoofing, i have included a couple of open-source projects to demonstrate the detection:

  1. https://www.unknowncheats.me/forum/anti-cheat-bypass/268039-x64-return-address-spoofing-source-explanation.html
  2. https://www.unknowncheats.me/forum/anti-cheat-bypass/512002-x64-return-address-spoofing.html
  3. https://github.com/Peribunt/Exception-Ret-Spoofing/

All credits for these go to the corresponding authors. They are just being shipped for demonstration purposes.

What's interesting about it?

Currently, they use following logic for what i assume, is for detecting gadgets commonly used for this purpose:

  if( !tools::retaddr_is_call_insn(retaddr) )
            {
                // IF THIS RETURN ADDRESS HAS NOT BEEN GENERATED BY A CALL INSTRUCTION,
                //  PERFORM ADDITIONAL ANALYSIS TO DETERMINE IF A RETURN ADDRESS SPOOFER WAS POTENTIALLY USED.
                DWORD_PTR v50 = BASE_OF(retaddr);
                DWORD_PTR v57 = 2i64;
                while (*(BYTE*)(v50 - v57) != 0xFF || (((*(BYTE*)(v50 - v57 + 1) & 0x38) - 16) & 0xF7) != 0) // <------------------------------- see here
                {
                    if (++v57 > 7)
                    {
                        printf("[FLAG//////RETADDR SPOOFER] suspicious record at %i (%p)\n", i, retaddr);
                        break;
                    }
                }
            }

They seem to use this generic algorithm for detecting a range of gadgets. Further analysis is to be done on it.

Practical example (usage in Apex Legends)

// this code can be inlined and also be in their own sub 
      if ( v14 == -1 )
        {
          pCurrentStackTraceRIP = pStackTrace;
          nStackTraceIndex = 0;
          do
          {
            retaddr = *(_QWORD *)pCurrentStackTraceRIP;
 
            if ( !*(_QWORD *)pCurrentStackTraceRIP || (index = 0, r5::ac::max_whitelisted_sections <= 0) )
            {
 
on_detected_violation2:
              r5::ac::push_violation((__int64)aCsCinputInputC, 1);
              goto LABEL_17;
            }
 
            whitelisted_sections = (unsigned __int64 *)r5::ac::whitelisted_sections;
 
            // v17 looks like a RETURN ADDRESS
            // - at index zero, pcurr[0] is start of text section and pcurr[1] is end of .text section
            while ( retaddr < *whitelisted_sections || retaddr > whitelisted_sections[1] )
            {
              ++index;
              whitelisted_sections += 2;
              if ( index >= r5::ac::max_whitelisted_sections )
                goto on_detected_violation2;
            }
            if ( *(_BYTE *)(retaddr - 5) != 0xE8 )
            {
              some_idx = 2i64;
              while ( *(_BYTE *)(retaddr - some_idx) != 0xFF
                   || (((*(_BYTE *)(retaddr - some_idx + 1) & 0x38) - 16) & 0xF7) != 0 )
              {
                if ( ++some_idx > 6 )
                  goto on_detected_violation2;
              }
            }
            ++nStackTraceIndex;
            pCurrentStackTraceRIP = (__int128 *)((char *)pCurrentStackTraceRIP + 8);
          }
          while ( nStackTraceIndex < numStackTraceRecords2 );
        }
      }
    }

About

An interesting way to detect return address spoofing on x64-windows.

Resources

License

Stars

Watchers

Forks

Packages

No packages published