0

I am trying to reverse RtlInitUnicodeStringEx from ntdll.dll as an exercise to myself. Here is dissambled version from x64dbg.

enter image description here

I was able to produce almost exactly the same binary from my own code, except for the int3 instruction in line 0x00007FFDE53AA856.

I have analysed the module using both ghidra and x64dbg but I was not able to find any jumps to that line. I also considered it is used to align, but next instruction is not aligned to any power of two boundary.

My search online led to __debugbreak intrinsic. According to Microsoft docs it "Causes a breakpoint in your code, where the user will be prompted to run the debugger.". However, It doesn't make sense that extra int3 is caused by __debugbreak as it is in an unreachable position.

I assume existence of that instruction means something is going on that I am not aware of, because compiler wouldn't have put it there if it weren't for a reason. So, what is the reason compiler would int3 instruction at a unreachable position?

I am using cl.exe version 19.30.30715 and compiling with /c /O2s flags. I am viewing the result by using dumpbin /disasm on .obj file. Also tried to create a .dll from cl.exe /LD /O2s but it didn't make any difference.

2 Answers 2

0

This instruction is inserted after the functions ret, it is normally unreachable.

In case the ret instruction caused a memory access violation (or some other exception) - the exception handler will be called and the current state saved.

The exception handler can return execution to the original state, that is, to the instruction after the one that threw the exception, which is after the function's end.

In this case the int3 will run and either:

Crash (if there is no debugger attached or available) or break to the debugger.

The debugger, in turn, can "handle" (ignore) the int3 and continue, running the next instruction that effectively returns 0xC0000106

2
  • Interesting, it doesn't appear on my version though, any idea what compiler flags I might be missing?
    – yasar
    Commented Apr 30 at 19:17
  • int3 is added between functions code by compilers when compiling debug builds (as opposed to releasebuilds). This code in ntdll specifically returns STATUS_NAME_TOO_LONG error value, that makes sense in this context, not something generic that a compiler would add automatically.
    – Yotamz
    Commented Apr 30 at 19:35
0

If you look through other functions in NTDLL.DLL you will see the addition of int3s after ret instructions is common.

This would appear to be a mitigation for straight-line speculation vulnerabilities. Originally discovered on ARM, the x86 architecture was also found to be vulnerable.

For more information see here

The -mharden-sls=all compiler option can be used in gcc and clang to include these extra int3 instructions. I'm not sure what MSVC's equivalent option is.

1
  • Closest option I could find was /Qspectre-jmp that puts int3 after jmp instruction. There is no mention of any option that puts int3 after ret anywhere.
    – yasar
    Commented May 2 at 22:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.