Skip to content

Instantly share code, notes, and snippets.

@xrombar
Forked from namazso/SuperReturn.c
Created June 21, 2025 13:02
Show Gist options
  • Select an option

  • Save xrombar/d8b5df0de6dab5c5ea01be6946e19d0d to your computer and use it in GitHub Desktop.

Select an option

Save xrombar/d8b5df0de6dab5c5ea01be6946e19d0d to your computer and use it in GitHub Desktop.

Revisions

  1. @namazso namazso revised this gist Jun 21, 2025. 1 changed file with 4 additions and 25 deletions.
    29 changes: 4 additions & 25 deletions SuperReturn.c
    Original file line number Diff line number Diff line change
    @@ -15,35 +15,12 @@ DECLSPEC_NOINLINE void SuperReturn(

    CONTEXT LocalContext;
    if (!Context) {
    // I'm not sure why we don't need this on x86 and don't have the patience to figure it out
    #if !defined(_M_IX86)
    FramesToSkip += 1; // skip this frame
    #endif
    LocalContext.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
    RtlCaptureContext(&LocalContext);
    Context = &LocalContext;
    }

    #if defined(_M_IX86)

    // Force frame ptr at least in this function so that we can reliably skip ourselves even if /Gy- is not used
    volatile ULONG_PTR OpaqueSize = sizeof(ULONG_PTR);
    volatile ULONG_PTR* volatile UselessAlloc = (PULONG_PTR)_alloca(OpaqueSize);
    *UselessAlloc = (ULONG_PTR)UselessAlloc;

    PULONG_PTR CurrentEbp = (PULONG_PTR)Context->Ebp;

    for (ULONG i = 0; i < FramesToSkip; i++) {
    Context->Esp = (ULONG_PTR)(CurrentEbp + 2);
    Context->Eip = CurrentEbp[1];
    Context->Ebp = CurrentEbp[0];
    CurrentEbp = (PULONG_PTR)(Context->Ebp);
    }

    Context->Eax = RetVal;

    #else

    #if defined(_M_X64)
    #define CTX_IP(Ctx) (Ctx->Rip)
    #define CTX_SP(Ctx) (Ctx->Rsp)
    @@ -52,6 +29,10 @@ DECLSPEC_NOINLINE void SuperReturn(
    #define CTX_IP(Ctx) (Ctx->Pc)
    #define CTX_SP(Ctx) (Ctx->Sp)
    #define CTX_RV(Ctx) (Ctx->X0)
    #elif defined(_M_IX86)
    #error Can't possibly work on x86: no way to restore nonvolatile registers.
    #else
    #error Unsupported architecture!
    #endif

    ULONG64 ControlPc = CTX_IP(Context);
    @@ -88,7 +69,5 @@ DECLSPEC_NOINLINE void SuperReturn(
    #undef CTX_SP
    #undef CTX_RV

    #endif

    NtContinue(Context, FALSE);
    }
  2. @namazso namazso revised this gist Jun 21, 2025. 1 changed file with 25 additions and 0 deletions.
    25 changes: 25 additions & 0 deletions SuperReturn.c
    Original file line number Diff line number Diff line change
    @@ -15,12 +15,35 @@ DECLSPEC_NOINLINE void SuperReturn(

    CONTEXT LocalContext;
    if (!Context) {
    // I'm not sure why we don't need this on x86 and don't have the patience to figure it out
    #if !defined(_M_IX86)
    FramesToSkip += 1; // skip this frame
    #endif
    LocalContext.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
    RtlCaptureContext(&LocalContext);
    Context = &LocalContext;
    }

    #if defined(_M_IX86)

    // Force frame ptr at least in this function so that we can reliably skip ourselves even if /Gy- is not used
    volatile ULONG_PTR OpaqueSize = sizeof(ULONG_PTR);
    volatile ULONG_PTR* volatile UselessAlloc = (PULONG_PTR)_alloca(OpaqueSize);
    *UselessAlloc = (ULONG_PTR)UselessAlloc;

    PULONG_PTR CurrentEbp = (PULONG_PTR)Context->Ebp;

    for (ULONG i = 0; i < FramesToSkip; i++) {
    Context->Esp = (ULONG_PTR)(CurrentEbp + 2);
    Context->Eip = CurrentEbp[1];
    Context->Ebp = CurrentEbp[0];
    CurrentEbp = (PULONG_PTR)(Context->Ebp);
    }

    Context->Eax = RetVal;

    #else

    #if defined(_M_X64)
    #define CTX_IP(Ctx) (Ctx->Rip)
    #define CTX_SP(Ctx) (Ctx->Rsp)
    @@ -65,5 +88,7 @@ DECLSPEC_NOINLINE void SuperReturn(
    #undef CTX_SP
    #undef CTX_RV

    #endif

    NtContinue(Context, FALSE);
    }
  3. @namazso namazso revised this gist Jun 21, 2025. 1 changed file with 22 additions and 7 deletions.
    29 changes: 22 additions & 7 deletions SuperReturn.c
    Original file line number Diff line number Diff line change
    @@ -12,24 +12,35 @@ DECLSPEC_NOINLINE void SuperReturn(
    _In_opt_ ULONG_PTR RetVal,
    _In_opt_ PCONTEXT Context
    ) {

    CONTEXT LocalContext;
    if (!Context) {
    FramesToSkip += 1; // skip this frame
    LocalContext.ContextFlags = CONTEXT_INTEGER;
    LocalContext.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
    RtlCaptureContext(&LocalContext);
    Context = &LocalContext;
    }

    ULONG64 ControlPc = Context->Rip;
    #if defined(_M_X64)
    #define CTX_IP(Ctx) (Ctx->Rip)
    #define CTX_SP(Ctx) (Ctx->Rsp)
    #define CTX_RV(Ctx) (Ctx->Rax)
    #elif defined(_M_ARM64)
    #define CTX_IP(Ctx) (Ctx->Pc)
    #define CTX_SP(Ctx) (Ctx->Sp)
    #define CTX_RV(Ctx) (Ctx->X0)
    #endif

    ULONG64 ControlPc = CTX_IP(Context);

    for (ULONG i = 0; i < FramesToSkip; i++) {
    ULONG_PTR ImageBase = 0;
    PRUNTIME_FUNCTION FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL);

    if (!FunctionEntry) {
    // leaf
    Context->Rip = *(ULONG64*)Context->Rsp;
    Context->Rsp += sizeof(ULONG64);
    CTX_IP(Context) = *(ULONG64*)CTX_SP(Context);
    CTX_SP(Context) += sizeof(ULONG64);
    } else {
    PVOID HandlerData;
    ULONG64 EstablisherFrame;
    @@ -45,10 +56,14 @@ DECLSPEC_NOINLINE void SuperReturn(
    );
    }

    ControlPc = Context->Rip;
    ControlPc = CTX_IP(Context);
    }

    Context->Rax = RetVal;
    CTX_RV(Context) = RetVal;

    #undef CTX_IP
    #undef CTX_SP
    #undef CTX_RV

    NtContinue(Context, FALSE);
    }
    }
  4. @namazso namazso revised this gist Jun 21, 2025. 1 changed file with 7 additions and 7 deletions.
    14 changes: 7 additions & 7 deletions SuperReturn.c
    Original file line number Diff line number Diff line change
    @@ -9,8 +9,8 @@
    // @param Context Context to start from, in case you want to SuperReturn from somewhere deeper.
    DECLSPEC_NOINLINE void SuperReturn(
    _In_ ULONG FramesToSkip,
    _In_opt_ ULONG_PTR RetVal = 0,
    _In_opt_ PCONTEXT Context = nullptr
    _In_opt_ ULONG_PTR RetVal,
    _In_opt_ PCONTEXT Context
    ) {
    CONTEXT LocalContext;
    if (!Context) {
    @@ -20,11 +20,11 @@ DECLSPEC_NOINLINE void SuperReturn(
    Context = &LocalContext;
    }

    auto ControlPc = Context->Rip;
    ULONG64 ControlPc = Context->Rip;

    for (ULONG i = 0; i < FramesToSkip; i++) {
    ULONG_PTR ImageBase{};
    const auto FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, nullptr);
    ULONG_PTR ImageBase = 0;
    PRUNTIME_FUNCTION FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL);

    if (!FunctionEntry) {
    // leaf
    @@ -41,7 +41,7 @@ DECLSPEC_NOINLINE void SuperReturn(
    Context,
    &HandlerData,
    &EstablisherFrame,
    nullptr
    NULL
    );
    }

    @@ -51,4 +51,4 @@ DECLSPEC_NOINLINE void SuperReturn(
    Context->Rax = RetVal;

    NtContinue(Context, FALSE);
    }
    }
  5. @namazso namazso created this gist Jun 21, 2025.
    54 changes: 54 additions & 0 deletions SuperReturn.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,54 @@
    // Return, but across multiple frames.
    //
    // This function unwinds the given number of frames, then sets the return value provided, emulating as if this number
    // of functions returned, with the last one returning the value provided in RetVal. Can be used to hook a callee when
    // you don't have a convenient way to hook it directly and actually just want to stub it out with a return value.
    //
    // @param FramesToSkip The number of frames to skip, starting from the current frame.
    // @param RetVal The value to return from the last frame.
    // @param Context Context to start from, in case you want to SuperReturn from somewhere deeper.
    DECLSPEC_NOINLINE void SuperReturn(
    _In_ ULONG FramesToSkip,
    _In_opt_ ULONG_PTR RetVal = 0,
    _In_opt_ PCONTEXT Context = nullptr
    ) {
    CONTEXT LocalContext;
    if (!Context) {
    FramesToSkip += 1; // skip this frame
    LocalContext.ContextFlags = CONTEXT_INTEGER;
    RtlCaptureContext(&LocalContext);
    Context = &LocalContext;
    }

    auto ControlPc = Context->Rip;

    for (ULONG i = 0; i < FramesToSkip; i++) {
    ULONG_PTR ImageBase{};
    const auto FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, nullptr);

    if (!FunctionEntry) {
    // leaf
    Context->Rip = *(ULONG64*)Context->Rsp;
    Context->Rsp += sizeof(ULONG64);
    } else {
    PVOID HandlerData;
    ULONG64 EstablisherFrame;
    RtlVirtualUnwind(
    UNW_FLAG_NHANDLER,
    ImageBase,
    ControlPc,
    FunctionEntry,
    Context,
    &HandlerData,
    &EstablisherFrame,
    nullptr
    );
    }

    ControlPc = Context->Rip;
    }

    Context->Rax = RetVal;

    NtContinue(Context, FALSE);
    }