Skip to content

Zeroed data on ref struct parameter after patching #126

@d3xMachina

Description

@d3xMachina

Unity Version: 2022.3.28f1 (IL2CPP)
BepInEx Version: Both 6.0.0-be.733 and 6.0.0-pre.2
Game : Suikoden I&II HD Remaster

Using the simple following code :

[HarmonyPatch(typeof(EVENTCON), nameof(EVENTCON.EventAtariCheckFunc1))]
[HarmonyPostfix]
static void CheckAtari(ref RECT rec)
{
    Plugin.Log.LogWarning($"x={rec.x} y={rec.y} w={rec.w} h={rec.h}");
}

Or even with the simpler code :

[HarmonyPatch(typeof(EVENTCON), nameof(EVENTCON.EventAtariCheckFunc1))]
[HarmonyPrefix]
static void CheckAtari()
{
}

To patch this function :
private void EventAtariCheckFunc1(int pno, int dir, ref RECT rec)

It results in the "ref RECT rec" always returning a rec with zeroed data. I had to check with a debugger (x96dbg).

In the console I get this info with the patch :
x=512 y=768 w=0 h=0

The assembly code at the call site for EventAtariCheckFunc1 :

Image

Result without the patch :

Image

Image

As you can see the data for the rec which is on the stack, is not zeroed at the address pointed by the R9 register. 0x300 (=768) corresponds to the y value of rec and 0x200 (=512) corresponds to the x value of the rec.

Result with the patch :

Image

Image

Now the data for rec is zeroed !!! The hook displayed the data of rec properly, so its data is lost at some point after the Postfix.

Decompiled RECT:

[StructLayout(LayoutKind.Explicit)]
public struct RECT
{
    private static readonly System.IntPtr NativeFieldInfoPtr_x;

    private static readonly System.IntPtr NativeFieldInfoPtr_y;

    private static readonly System.IntPtr NativeFieldInfoPtr_w;

    private static readonly System.IntPtr NativeFieldInfoPtr_h;

    private static readonly System.IntPtr NativeMethodInfoPtr__ctor_Public_Void_Int32_Int32_Int32_Int32_0;

    [FieldOffset(0)]
    public int x;

    [FieldOffset(4)]
    public int y;

    [FieldOffset(8)]
    public int w;

    [FieldOffset(12)]
    public int h;

    [CallerCount(28)]
    [CachedScanResults(RefRangeStart = 739593, RefRangeEnd = 739621, XrefRangeStart = 739593, XrefRangeEnd = 739593, MetadataInitTokenRva = 0L, MetadataInitFlagRva = 0L)]
    public unsafe RECT([DefaultParameterValue(null)] int _x, [DefaultParameterValue(null)] int _y, [DefaultParameterValue(null)] int _w, [DefaultParameterValue(null)] int _h)
    {
        System.IntPtr* ptr = stackalloc System.IntPtr[4];
        *ptr = (nint)(&_x);
        *(int**)((byte*)ptr + checked((nuint)1u * unchecked((nuint)sizeof(System.IntPtr)))) = &_y;
        *(int**)((byte*)ptr + checked((nuint)2u * unchecked((nuint)sizeof(System.IntPtr)))) = &_w;
        *(int**)((byte*)ptr + checked((nuint)3u * unchecked((nuint)sizeof(System.IntPtr)))) = &_h;
        System.Runtime.CompilerServices.Unsafe.SkipInit(out System.IntPtr exc);
        System.IntPtr intPtr = IL2CPP.il2cpp_runtime_invoke(NativeMethodInfoPtr__ctor_Public_Void_Int32_Int32_Int32_Int32_0, (nint)System.Runtime.CompilerServices.Unsafe.AsPointer(ref this), (void**)ptr, ref exc);
        Il2CppException.RaiseExceptionIfNecessary(exc);
    }

    static RECT()
    {
        Il2CppClassPointerStore<RECT>.NativeClassPtr = IL2CPP.GetIl2CppClass("GSDShare.dll", "", "RECT");
        IL2CPP.il2cpp_runtime_class_init(Il2CppClassPointerStore<RECT>.NativeClassPtr);
        NativeFieldInfoPtr_x = IL2CPP.GetIl2CppField(Il2CppClassPointerStore<RECT>.NativeClassPtr, "x");
        NativeFieldInfoPtr_y = IL2CPP.GetIl2CppField(Il2CppClassPointerStore<RECT>.NativeClassPtr, "y");
        NativeFieldInfoPtr_w = IL2CPP.GetIl2CppField(Il2CppClassPointerStore<RECT>.NativeClassPtr, "w");
        NativeFieldInfoPtr_h = IL2CPP.GetIl2CppField(Il2CppClassPointerStore<RECT>.NativeClassPtr, "h");
        NativeMethodInfoPtr__ctor_Public_Void_Int32_Int32_Int32_Int32_0 = IL2CPP.GetIl2CppMethodByToken(Il2CppClassPointerStore<RECT>.NativeClassPtr, 100663932);
    }

    public unsafe Il2CppSystem.Object BoxIl2CppObject()
    {
        return new Il2CppSystem.Object(IL2CPP.il2cpp_value_box(Il2CppClassPointerStore<RECT>.NativeClassPtr, (nint)System.Runtime.CompilerServices.Unsafe.AsPointer(ref this)));
    }
} 

Decompiled EVENTCON :

[CallerCount(17)]
[CachedScanResults(RefRangeStart = 105008, RefRangeEnd = 105025, XrefRangeStart = 104988, XrefRangeEnd = 105008, MetadataInitTokenRva = 0L, MetadataInitFlagRva = 0L)]
public unsafe void EventAtariCheckFunc1([DefaultParameterValue(null)] int pno, [DefaultParameterValue(null)] int dir, [DefaultParameterValue(null)] ref RECT rec)
{
    IL2CPP.Il2CppObjectBaseToPtrNotNull(this);
    System.IntPtr* ptr = stackalloc System.IntPtr[3];
    *ptr = (nint)(&pno);
    *(int**)((byte*)ptr + checked((nuint)1u * unchecked((nuint)sizeof(System.IntPtr)))) = &dir;
    *(void**)((byte*)ptr + checked((nuint)2u * unchecked((nuint)sizeof(System.IntPtr)))) = System.Runtime.CompilerServices.Unsafe.AsPointer(ref rec);
    System.Runtime.CompilerServices.Unsafe.SkipInit(out System.IntPtr exc);
    System.IntPtr intPtr = IL2CPP.il2cpp_runtime_invoke(NativeMethodInfoPtr_EventAtariCheckFunc1_Private_Void_Int32_Int32_byref_RECT_0, IL2CPP.Il2CppObjectBaseToPtrNotNull(this), (void**)ptr, ref exc);
    Il2CppInterop.Runtime.Il2CppException.RaiseExceptionIfNecessary(exc);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions