c# StateMachine, ezVM, Texture Render, ComputeShader kernel

工具们

.NET 编译产物基本构成#

编译一个项目通常得到一个可执行文件和一个 assembly

.cs 源码编译成 IL 装进一个程序集(assembly),常见的形态有

  • .dll
  • .exe(Windows 上的“托管 exe”)

跨平台的主体通常是 dll,运行时由 dotnet 去加载它,JIT 成本机指令执行。元数据也会放到里面。

编译得到的可执行文件通常是一个 launcher,负责:

  • 找到合适的 .NET runtime, *.runtimeconfig.json, *.deps.json
  • 启动并加载 assembly

.NET 逆向#

IL+元数据#

.NET 程序通常编译成中间语言 IL,还有丰富的元数据(类型、方法签名、属性、泛型信息),分析元数据常用 dnspy、ilspy

C# 状态机#

async/await 状态机本质是把一个函数切成多段,用 state 组织起来,并用回调把下一段安排在未来执行

async Task<int> Foo()
{
    var a = 1;
    await SomeIO(); // 伪阻塞
    var b = 2;
    return a + b;
}

编译后形成这样的逻辑

class FooStateMachine {
	int state; // 记录状态,保证阻塞结束的运行
	int a; // 需要的局部变量被提升成字段保存起来。阻塞后出现的 b 则不需要
	TaskAwaiter awaiter
	TskCompletionSource<int> tcs; // 用来完成返回的 Task
	
	void MoveNext() {
		try {
			if (state == 0) {
                a = 1;
                awaiter = SomeIO().GetAwaiter();
                if (!awaiter.IsCompleted) {
                    state = 1; // 若操作未完成,async 方法被挂起,调用 OnCompleted 来注册一个“续体”
                    awaiter.OnCompleted(MoveNext); // 注册回调,语义为等这个 awaiter 完成时再调用一次 MoveNext()
                    return;
                }
            }
            if (state == 1) {
                awaiter.GetResult();
                int b = 2;
                tcs.SetResult(a + b);
            }
		} catch (Exception e) {
            tcs.SetException(e);
        }
	}
}

更完全的逻辑

async Task<int> Foo()
{
    A();
    await Bar();
    B();
    return 42;
}
private struct Foo_d__0 : IAsyncStateMachine
{
    public int _state;
    public AsyncTaskMethodBuilder<int> _builder;
    private TaskAwaiter _awaiter;
    private int _result;
    
    public void MoveNext()
    {
        int result;
        try
        {
            if (_state == 0) goto After Await0;
            
            A();
            
            // await Bar();
            var awaiter = Bar().GetAwaiter();
            if (!awaiter.IsCompleted)
            {
                _state = 0;
                _awaiter = awaiter;
                _builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
                return;
            }
            
        AfterAwait0:
            if (_state == 0)
            {
                awaiter = _awaiter;
                _awaiter = default;
                _state = -1;
            }
            awaiter.GetResult();
            
            B();
            result = 42;
        }
        catch (Exception ex)
        {
            _state = -2;
            _builder.SetException(ex);
            return;
        }
        
        _state = -2;
        _builder.SetResult(result);
    }
    
    public void SetStateMachine(IAsyncStateMachine sm)
        => _builder.SetStateMachine(sm);
}

原方法 Foo() 被改写成

Task<int> Foo()
{
    var sm = new Foo_d__0();
    sm._builder = AsyncTaskMethodBuilder<int>.Create();
    sm._state = -1;
    sm._builder.Start(ref sm);
    return sm._builder.Task;
}

普通 await 可能挂起 async(awaiter.IsCompleted 返回 false),也可能同步直通(awaiter.IsCompleted 返回 true,一般是缓存命中、完成态Task等),await Task.Yield() 强制在 await 处停下等待回调,制造一个必定异步的切分点

反编译#

反编译器做语法糖重建,可能出现 async

命名风格#

00000000 struct Controller_o // sizeof=0x90
00000000 {
00000000     Controller_c *klass;
00000008     void *monitor;
00000010     Controller_Fields fields;
00000090 };

00000000 struct Controller_c // sizeof=0x178
00000000 {
00000000     Il2CppClass_1 _1;
000000B8     void *static_fields;
000000C0     Il2CppRGCTXData *rgctx_data;
000000C8     Il2CppClass_2 _2;
00000138     Controller_VTable vtable;
00000178 };

00000000 struct __cppobj Controller_Fields : UnityEngine_MonoBehaviour_Fields // sizeof=0x80
00000000 {                                       // XREF: Controller_o/r
00000010     struct TMPro_TMP_InputField_o *InputField0;
00000018     struct UnityEngine_UI_Button_o *Button0;
00000020     struct UnityEngine_ComputeShader_o *Shader0;
00000028     struct UnityEngine_Texture2D_o *Tex0;
00000030     struct UnityEngine_Texture2D_o *Tex1;
00000038     struct UnityEngine_Vector2_o tileScale;
00000040     float scrollSpeed;
00000044     float rotationDegrees;
00000048     struct UnityEngine_RenderTexture_o *TexF;
00000050     struct UnityEngine_Texture2D_o *TexC;
00000058     struct UnityEngine_Texture2D_o *TexV;
00000060     struct UnityEngine_ComputeBuffer_o *Buffer0;
00000068     struct UnityEngine_RenderTexture_o *TexR;
00000070     int32_t K0;
00000074     int32_t K1;
00000078     int32_t K2;
0000007C     // padding byte
0000007D     // padding byte
0000007E     // padding byte
0000007F     // padding byte
00000080 };
  • *_o 对象实例(Object)

  • *_c 类(运行时用来描述类型本身的元数据结构)

  • *_Fields 实例字段区

00000000 struct Controller__Check_d__18_Fields // sizeof=0x58
00000000 {                                       // XREF: Controller__Check_d__18_o/r
00000000     int32_t __1__state;
00000004     // padding byte
00000005     // padding byte
00000006     // padding byte
00000007     // padding byte
00000008     struct System_Runtime_CompilerServices_AsyncVoidMethodBuilder_o __t__builder;
00000028     struct Controller_o *__4__this;
00000030     struct System_Func_byte____Task__o *_uploadFlagAsync_5__2;
00000038     struct System_Func_Task__o *_runEncryptAsync_5__3;
00000040     struct System_Func_Task__o *_runCheckAsync_5__4;
00000048     struct System_Runtime_CompilerServices_TaskAwaiter_byte____o __u__1;
00000050     struct System_Runtime_CompilerServices_TaskAwaiter_o __u__2;
00000058 };
  • Check_d__18,即 <Check>d__18,表示方法 Check 的 StateMachine type,18 为元数据序号
00000000 struct Controller___Check_b__18_3_d_Fields // sizeof=0x30
00000000 {                                       // XREF: Controller___Check_b__18_3_d_o/r
00000000     int32_t __1__state;
00000004     // padding byte
00000005     // padding byte
00000006     // padding byte
00000007     // padding byte
00000008     struct System_Runtime_CompilerServices_AsyncTaskMethodBuilder_o __t__builder;
00000020     struct Controller_o *__4__this;
00000028     struct System_Runtime_CompilerServices_YieldAwaitable_YieldAwaiter_o __u__1;
00000029     // padding byte
0000002A     // padding byte
0000002B     // padding byte
0000002C     // padding byte
0000002D     // padding byte
0000002E     // padding byte
0000002F     // padding byte
00000030 };

*b_* 即 <Check>b__18_3,表示某个 lambda 被编译器提升后的一个生成方法,3 表示这是 Check 中第四个 lambda

Delegate#

很像是一个虚调用

il2cpp:00007FFB93134A27 test    r15, r15
il2cpp:00007FFB93134A2A jz      short loc_7FFB93134A7F
il2cpp:00007FFB93134A2C mov     rax, [r15+18h]
il2cpp:00007FFB93134A30 mov     r8, [r15+28h]
il2cpp:00007FFB93134A34 mov     rdx, r13
il2cpp:00007FFB93134A37 mov     rcx, [r15+40h]
il2cpp:00007FFB93134A3B call    raxil2cpp:00007FFB93134A27 test    r15, r15
il2cpp:00007FFB93134A2A jz      short loc_7FFB93134A7F
il2cpp:00007FFB93134A2C mov     rax, [r15+18h]
il2cpp:00007FFB93134A30 mov     r8, [r15+28h]
il2cpp:00007FFB93134A34 mov     rdx, r13
il2cpp:00007FFB93134A37 mov     rcx, [r15+40h]
il2cpp:00007FFB93134A3B call    rax

c# 中,delegate 是一种类似函数指针的类型,它指向一个静态或实例方法,当指向实例方法时还能带上一个目标对象(实例方法的 this),还能多播(multicast,+= 叠加多个回调)。il2cpp 中,delegate 被写成一个堆对象(Il2CppDelegate),核心字段有:

  • 要调用的函数指针(通常不指向函数本体,而是一个wrapper,详见后文)
  • target:实例方法需要的 this (静态委托该字段为 null)
  • method / MethodInfo:方法元信息(反射/泛型/调试/某些调用适配需要)
  • (多播时)invocation list:一串子 delegate
test    r15, r15	; r15 是 Il2CppDelegate*,为空就不调。相当于 if 
jz      short loc_7FFB93134A7F
mov     rax, [r15+18h]	; 从对象里取一个可执行地址
mov     r8, [r15+28h]
mov     rdx, r13
mov     rcx, [r15+40h]
call    raxil2cpp:00007FFB93134A27 test    r15, r15
jz      short loc_7FFB93134A7F
mov     rax, [r15+18h]
mov     r8, [r15+28h]
mov     rdx, r13
mov     rcx, [r15+40h]
call    rax

alictf 2026 pixelflow#

Il2CppDumper dump 下来 Assembly-CSharp.dll 用 ilspy 看一眼

image-20260223175905044

Controller 瞩目。看一眼,是 UnityEngine.MonoBehaviour 的子类。

UnityEngine.MonoBehaviour#

挂在 GameObject 上的行为脚本的基类。有以下特点

  • 必须附着在 GameObject 上
  • 不能用 new 创建(如果 new 的话不会运行生命周期函数
  • 必须通过 AddComponent<T>() 添加

不是普通的 C# 类,而是一个由引擎托管的对象

生命周期函数#

MonoBehaviour 定义了一套由引擎自动调用的回调函数,常见的声明周期顺序是:

  • Awake() 对象被加载时调用,用于初始化
  • Start() 第一次启动时调用
  • Update() 每帧调用一次
  • LateUpdate() 在 Update 之后执行,常用于相机跟随等收尾工作
  • FixedUpdate() 固定时间间隔调用,用于物理计算
  • OnDestroy() 对象被销毁时调用

继承后#

继承 MonoBehaviour 后,可以:

  • 监听触发器(OnTriggerEnter)

  • 监听碰撞(OnCollisionEnter)

  • 使用 Unity 的系统事件

因此按钮绑定的逻辑和它强相关

Start()#

void Controller__Start(Controller_o *this, const MethodInfo *method)
{
  Controller_o *v2; // rdi
  struct UnityEngine_UI_Button_o *Button0; // rax
  UnityEngine_Events_UnityEvent_o *m_OnClick; // rbx
  UnityEngine_AI_NavMesh_OnNavMeshPreUpdate_o *v5; // rsi
  int32_t Kernel; // eax
  int32_t v7; // eax
  int32_t v8; // eax
  __int64 v9; // rdx
  UnityEngine_ComputeBuffer_o *v10; // rbx
  UnityEngine_ComputeBuffer_o *Buffer0; // rbx
  __int64 v12; // rax
  Il2CppObject *object; // rax
  Il2CppObject *v14; // rax
  __int64 v15; // rdx
  UnityEngine_RenderTexture_o *v16; // rbx

  v2 = this;
  if ( !byte_180F4C9A0 )
  {
    sub_180157800(&UnityEngine_ComputeBuffer_TypeInfo);
    sub_180157800(&Method_Controller_Check__);
    sub_180157800(&int___TypeInfo);
    sub_180157800(&UnityEngine_RenderTexture_TypeInfo);
    sub_180157800(&UnityEngine_Events_UnityAction_TypeInfo);
    sub_180157800(&StringLiteral_4359);
    sub_180157800(&StringLiteral_3332);
    sub_180157800(&StringLiteral_3956);
    sub_180157800(&StringLiteral_3328);
    sub_180157800(&StringLiteral_3330);
    sub_180157800(&StringLiteral_1742);
    sub_180157800(&StringLiteral_312);
    sub_180157800(&StringLiteral_1800);
    sub_180157800(&StringLiteral_320);
    sub_180157800(&StringLiteral_1730);
    byte_180F4C9A0 = 1;
  } // il2cpp 常见的 lazy init,把后面用到的类型信息、方法指针、字符串常量加载进来
   
  // 绑定回调
  Button0 = v2->fields.Button0;
  if ( !Button0 )
    goto LABEL_27;
  m_OnClick = Button0->fields.m_OnClick;
  v5 = sub_1800F5060(UnityEngine_Events_UnityAction_TypeInfo, method); // new 一个 UnityAction 实例
  UnityEngine_AI_NavMesh_OnNavMeshPreUpdate___ctor(v5, v2, Method_Controller_Check__, 0); // 构造 UnityAction:target = this, method = Controller.Check。反编译成 navmesh 应该是类型判断错位
  if ( !m_OnClick )
    goto LABEL_27;
  UnityEngine_Events_UnityEvent__AddListener(m_OnClick, v5, 0);
  this = v2->fields.Shader0; // 把 UnityAction 加到按钮 onClick
  if ( !this )
    goto LABEL_27;
    
  // ComputeShader: 查三个 kernel (K0, K1, K2),分别绑定 TexR/Tex0/Tex1 三张纹理
  Kernel = UnityEngine_ComputeShader__FindKernel(this, StringLiteral_3328, 0); // -> K0
  this = v2->fields.Shader0;
  v2->fields.K0 = Kernel;
  if ( !this )
    goto LABEL_27;
  v7 = UnityEngine_ComputeShader__FindKernel(this, StringLiteral_3330, 0); // -> K1
  this = v2->fields.Shader0;
  v2->fields.K1 = v7;
  if ( !this )
    goto LABEL_27;
  v8 = UnityEngine_ComputeShader__FindKernel(this, StringLiteral_3332, 0); // -> K2
  this = v2->fields.Shader0;
  v2->fields.K2 = v8;
  if ( !this )
    goto LABEL_27;
  UnityEngine_ComputeShader__SetTexture_6451781712(this, v8, StringLiteral_3956, v2->fields.TexR, 0); // SetTextuer(Shader0, K2, "OutputTex", TexR); StringLiteral_3956 = OutputTex
  this = v2->fields.Shader0;
  if ( !this )
    goto LABEL_27;
  UnityEngine_ComputeShader__SetTexture_6451781712(this, v2->fields.K2, StringLiteral_1800, v2->fields.Tex0, 0); // SetTexture(Shader0, K2, "CorrectTex", Tex0); StringLiteral_1800 = CorrectTex
  this = v2->fields.Shader0;
  if ( !this )
    goto LABEL_27;
  UnityEngine_ComputeShader__SetTexture_6451781712(this, v2->fields.K2, StringLiteral_320, v2->fields.Tex1, 0); // SetTexture(Shader0, K2, "WrongTex", Tex1); StringLiteral_320 = WrongTex
    
  v10 = sub_1800F5060(UnityEngine_ComputeBuffer_TypeInfo, v9);
    
  // 创建 ComputeBuffer:1 个 4 字节的 Buffer,初始值 -1,绑定到 K1/K2
  UnityEngine_ComputeBuffer___ctor(v10, 1, 4, 0);
  v2->fields.Buffer0 = v10;
  sub_180156C70(&v2->fields.Buffer0, v10);
  Buffer0 = v2->fields.Buffer0;
  v12 = sub_180156CF0(int___TypeInfo, 1);
  if ( !v12 )
    goto LABEL_27;
  if ( !*(v12 + 24) )
    sub_1801579E0();
  *(v12 + 32) = -1;
  if ( !Buffer0 )
    goto LABEL_27;
    
  UnityEngine_ComputeBuffer__SetData(Buffer0, v12, 0);
  this = v2->fields.Shader0;
  if ( !this )
    goto LABEL_27;
  UnityEngine_ComputeShader__SetBuffer_6451781152(this, v2->fields.K1, StringLiteral_4359, v2->fields.Buffer0, 0); // StringLiteral_4359 = SharedState
  this = v2->fields.Shader0;
  if ( !this )
    goto LABEL_27;
  UnityEngine_ComputeShader__SetBuffer_6451781152(this, v2->fields.K2, StringLiteral_4359, v2->fields.Buffer0, 0);
    
  if ( !byte_180F4C9A3 )
  {
    sub_180157800(&Method_UnityEngine_Resources_Load_Texture2D___);
    sub_180157800(&StringLiteral_1408);
    sub_180157800(&StringLiteral_1380);
    byte_180F4C9A3 = 1;
  }
    
  // 从 Resources 加载 Texture2D,
  // object = Resources.Load<Texture2D>(ciTex); v2->fields.TexC = object;
  // ciTex 是路径字符串
  object = UnityEngine_Resources__Load_object_(StringLiteral_1380, Method_UnityEngine_Resources_Load_Texture2D___); // StringLiteral = ciTex
  v2->fields.TexC = object;
  sub_180156C70(&v2->fields.TexC, object);
  v14 = UnityEngine_Resources__Load_object_(StringLiteral_1408, Method_UnityEngine_Resources_Load_Texture2D___); // StringLiteral = coTex
  v2->fields.TexV = v14;
  sub_180156C70(&v2->fields.TexV, v14);
    
  this = v2->fields.Shader0;
  if ( !this )
    goto LABEL_27;
  UnityEngine_ComputeShader__SetTexture_6451781712(this, v2->fields.K0, StringLiteral_1742, v2->fields.TexV, 0); // Shader0.SetTexture(K0, CoTex, TexV); StringLiteral = CoTex
  this = v2->fields.Shader0;
  if ( !this )
    goto LABEL_27;
  UnityEngine_ComputeShader__SetTexture_6451781712(this, v2->fields.K1, StringLiteral_1730, v2->fields.TexC, 0); // Shader0.SetTexture(K1, CiTex); StringLiteral = CiTex
    
  // Construct RenderTexture
  // v16 = new RenderTexture(16, 1, 0, 16, 1)
  // v2.fields.TexF = v16
  v16 = sub_1800F5060(UnityEngine_RenderTexture_TypeInfo, v15);
  UnityEngine_RenderTexture___ctor_6451745168(v16, 16, 1, 0, 16, 1, 0);
  v2->fields.TexF = v16;
  sub_180156C70(&v2->fields.TexF, v16);
  this = v2->fields.TexF;
  if ( !this )
    goto LABEL_27;
  UnityEngine_RenderTexture__set_enableRandomWrite(this, 1, 0);
  this = v2->fields.TexF;
  if ( !this )
    goto LABEL_27;
  UnityEngine_Texture__set_filterMode(this, 0, 0);
  this = v2->fields.TexF;
  if ( !this
    || (UnityEngine_Texture__set_wrapMode(this, 1, 0), (this = v2->fields.TexF) == 0)
    || (UnityEngine_RenderTexture__Create(this, 0), (this = v2->fields.Shader0) == 0)
    || (UnityEngine_ComputeShader__SetTexture_6451781712(this, v2->fields.K0, StringLiteral_312, v2->fields.TexF, 0), // StringLiteral = WorkTex
        (this = v2->fields.Shader0) == 0) )
  {
LABEL_27:
    sub_1801579F0(this, method);
  }
  UnityEngine_ComputeShader__SetTexture_6451781712(this, v2->fields.K1, StringLiteral_312, v2->fields.TexF, 0);
}

Unity 渲染链路#

图片资源导入引擎得到 Texture2D (由 ComputeShader 读取,计算完成后输出到一个 RenderTexture)

ComputeBuffer (结构化数据,optional)

把 Texture / Buffer 绑定到某个 kernel 的参数槽

ComputeShader(kernel) 计算 Texture2D(模糊处理等)

Shader 将 RenderTexture 渲染出来

// *.compute
#pragma kernel BlurH
#pragma kernel BlurV

Texture2D<float4> _Src;
RWTexture2D<float4> _Dst;
SamplerState samplerLinear;

[numthreads(8,8,1)]
void BlurH(uint3 id : SV_DispatchThreadID) { ... }

[numthreads(8,8,1)]
void BlurV(uint3 id : SV_DispatchThreadID) { ... }

.compute 文件里长这样,C# 与其交互:

  • FindKernel(“BlurH”) 得到一个 kernelIndex
  • SetTexture(kernelIndex, “_Src”, tex) / SetBuffer(kernelIndex, “_Particles”, buf) 绑定资源到 kernel
  • Dispatch(kernelIndex, groupX, groupY, groupZ) 让 GPU 跑起来

ComputeShader#

kernel 即核函数,就是一段可以被 GPU 并行执行的函数的入口

int kernel = computeShader.FindKernel("CSMain");
computeShader.Dispatch(kernel, 16, 16, 1); // Dispatch 的三个参数表示线程组数量,线程总数 = numthreads * Dispatch 数量

Texture 不只是贴图,也是一个二/三维的数据容器,常见类型有

  • Texture2D // 只读
  • RWTexture2D // 可读写
RWTexture2D Result;
[numthreads(8, 8, 1)]
void CSMain (uint3 id: SV_DispatchThreadID)
{
    float2 uv = id.xy / 512.0;
    Result[id.xy] = float4(uv, 9, 1);
}

Controller.Check()#

void Controller__Check(Controller_o *this, const MethodInfo *method)
{
  System_Runtime_CompilerServices_AsyncVoidMethodBuilder_o v3; // [rsp+20h] [rbp-88h] BYREF
  Controller__Check_d__18_o v4; // [rsp+40h] [rbp-68h] BYREF

  if ( !byte_180F4C9A2 )
  {
    sub_180157800(&Method_System_Runtime_CompilerServices_AsyncVoidMethodBuilder_Start_Controller__Check_d__18___);
    byte_180F4C9A2 = 1;
  }
  memset(&v4, 0, sizeof(v4)); // 状态机结构体清零
  v4.fields.__t__builder = *System_Runtime_CompilerServices_AsyncVoidMethodBuilder__Create(&v3, 0); // 创建 async void 的 builder(负责异常传播、继续执行、调度等)
  sub_180156C70(&v4.fields.__t__builder, 0);
  v4.fields.__4__this = this; // 把外层 Controller 实例捕获进去(等价于 C# 中的 this 被 state machine 持有)
  sub_180156C70(&v4.fields.__4__this, this);
  v4.fields.__1__state = -1; // 初始化 state,-1 表示尚未进入任何 await 分支
  System_Runtime_CompilerServices_AsyncVoidMethodBuilder__Start_Controller__Check_d__18_(&v4.fields.__t__builder, &v4);// 启动状态机,内部调用 MoveNext()
}

这个函数是一个 async void 方法的启动器(真正的逻辑在 Controller__Check_d_18__),有以下特征:

  • 用了 System.Runtime.CompilerServices.AsyncVoidMethodBuilder
  • 调用了 AsyncVoidMethodBuilder.Create()
  • __1__state = -1
  • AsyncVoidMethodBuilder.Start<TStateMachine>(ref builder, ref stateMachine)

AsyncVoidMethodBuilder.Start() 里面可以找到对 MoveNext() 的调用,这就是核心逻辑

MoveNext()#

监听 Button 绑定到 Check(),做一些 kernel 的初始化和绑定

void Controller__Check_d__18__MoveNext(Controller__Check_d__18_o *this, const MethodInfo *method)
{
  Il2CppObject *_4__this; // rdi
  __int64 v4; // rsi
  __int64 v5; // rdx
  UnityEngine_UIElements_UIElementsRuntimeUtility_CreateRuntimePanelDelegate_o *v6; // r15
  __int64 v7; // rdx
  UnityEngine_UIElements_GetViewDataDictionary_o *v8; // r15
  __int64 v9; // rdx
  UnityEngine_UIElements_GetViewDataDictionary_o *v10; // r15
  __int64 v11; // rdx
  __int64 v12; // rcx
  System_Threading_Tasks_Task_TResult__o *v13; // rax
  __int64 v14; // rdx
  __int64 v15; // rcx
  Il2CppObject *Result; // rax
  __int64 v17; // rdx
  __int64 v18; // rcx
  struct System_Func_byte____Task__o *uploadFlagAsync_5__2; // rcx
  System_Threading_Tasks_Task_o *v20; // rax
  __int64 v21; // rdx
  __int64 v22; // rcx
  __int64 v23; // rdx
  struct System_Func_Task__o *runEncryptAsync_5__3; // rcx
  System_Threading_Tasks_Task_o *v25; // rax
  __int64 v26; // rdx
  __int64 v27; // rcx
  __int64 v28; // rdx
  struct System_Func_Task__o *v29; // rcx
  System_Threading_Tasks_Task_o *v30; // rax
  __int64 v31; // rdx
  __int64 v32; // rcx
  __int64 v33; // rdx
  struct System_Func_Task__o *v34; // rcx
  System_Threading_Tasks_Task_o *v35; // rax
  __int64 v36; // rdx
  __int64 v37; // rcx
  __int64 v38; // rdx
  struct System_Func_Task__o *runCheckAsync_5__4; // rcx
  System_Threading_Tasks_Task_o *v40; // rax
  __int64 v41; // rdx
  __int64 v42; // rcx
  UnityEngine_ComputeBuffer_o *klass; // rdi
  __int64 v44; // rax
  __int64 v45; // rdx
  __int64 v46; // rcx
  System_Runtime_CompilerServices_ConfiguredTaskAwaitable_ConfiguredTaskAwaiter_TResult__o v47; // [rsp+80h] [rbp+18h] BYREF

  if ( !byte_180F4C9B1 )
  {
    sub_180157800(&Method_System_Runtime_CompilerServices_AsyncVoidMethodBuilder_AwaitUnsafeOnCompleted_TaskAwaiter_byte_____Controller__Check_d__18___);
    sub_180157800(&Method_System_Runtime_CompilerServices_AsyncVoidMethodBuilder_AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18___);
    sub_180157800(&Method_Controller__Check_b__18_0__);
    sub_180157800(&Method_Controller__Check_b__18_1__);
    sub_180157800(&Method_Controller__Check_b__18_2__);
    sub_180157800(&Method_Controller__Check_b__18_3__);
    sub_180157800(&System_Func_Task__TypeInfo);
    sub_180157800(&System_Func_Task_byte_____TypeInfo);
    sub_180157800(&System_Func_byte____Task__TypeInfo);
    sub_180157800(&int___TypeInfo);
    sub_180157800(&Method_System_Runtime_CompilerServices_TaskAwaiter_byte____GetResult__);
    sub_180157800(&Method_System_Runtime_CompilerServices_TaskAwaiter_byte____get_IsCompleted__);
    sub_180157800(&Method_System_Threading_Tasks_Task_byte____GetAwaiter__);
    byte_180F4C9B1 = 1;
  }
  *&v47.fields.m_continueOnCapturedContext = 0;
  v47.fields.m_task = 0;
  _4__this = this->fields.__4__this;
  switch ( this->fields.__1__state )	// 第一次进入 default 分支,之后恢复对应的 await,共 6 次
  {
    case 0:
      *&v47.fields.m_continueOnCapturedContext = this->fields.__u__1.fields.m_task;
      this->fields.__u__1.fields.m_task = 0;
      this->fields.__1__state = -1;
      goto LABEL_13;
    case 1:
      v47.fields.m_task = this->fields.__u__2.fields.m_task;
      this->fields.__u__2.fields.m_task = 0;
      this->fields.__1__state = -1;
      goto LABEL_17;
    case 2:
      v47.fields.m_task = this->fields.__u__2.fields.m_task;
      this->fields.__u__2.fields.m_task = 0;
      this->fields.__1__state = -1;
      goto LABEL_20;
    case 3:
      v47.fields.m_task = this->fields.__u__2.fields.m_task;
      this->fields.__u__2.fields.m_task = 0;
      this->fields.__1__state = -1;
      goto LABEL_23;
    case 4:
      v47.fields.m_task = this->fields.__u__2.fields.m_task;
      this->fields.__u__2.fields.m_task = 0;
      this->fields.__1__state = -1;
      goto LABEL_26;
    case 5:
      v47.fields.m_task = this->fields.__u__2.fields.m_task;
      this->fields.__u__2.fields.m_task = 0;
      this->fields.__1__state = -1;
      goto LABEL_29;
    default:
      // 创建 4 个委托(v4, v6, v8, v10)
      // v4 = new Func<Task<byte[]>>(_4__this, Check_b__18_0);
      v4 = sub_1800F5060(System_Func_Task_byte_____TypeInfo, method);
      UnityEngine_UIElements_GetViewDataDictionary___ctor(v4, _4__this, Method_Controller__Check_b__18_0__, 0); // 编译成 UIElements 依旧是类型判断错位
      // v6 = new Func<byte[], Task>(_4__this, Check_b__18_1);
      v6 = sub_1800F5060(System_Func_byte____Task__TypeInfo, v5);
      UnityEngine_UIElements_UIElementsRuntimeUtility_CreateRuntimePanelDelegate___ctor(
        v6,
        _4__this,
        Method_Controller__Check_b__18_1__,
        0);
      this->fields._uploadFlagAsync_5__2 = v6;
      sub_180156C70(&this->fields._uploadFlagAsync_5__2, v6);
      // v8 = new Func<Task>(_4__this, Check_b__18_2);
      v8 = sub_1800F5060(System_Func_Task__TypeInfo, v7);
      UnityEngine_UIElements_GetViewDataDictionary___ctor(v8, _4__this, Method_Controller__Check_b__18_2__, 0);
      this->fields._runEncryptAsync_5__3 = v8;
      sub_180156C70(&this->fields._runEncryptAsync_5__3, v8);
      // v10 = new Func<Task>(_4__this, Check_b__18_3);
      v10 = sub_1800F5060(System_Func_Task__TypeInfo, v9);
      UnityEngine_UIElements_GetViewDataDictionary___ctor(v10, _4__this, Method_Controller__Check_b__18_3__, 0);
      this->fields._runCheckAsync_5__4 = v10;
      sub_180156C70(&this->fields._runCheckAsync_5__4, v10);
      // 这段 new 了一堆 Func,相当于写了一堆 lambda:
      	/*
        var get = new Func<Task<byte[]>>(() => ... );
        Func<byte[], Task> uploadFlagAsync = (bytes) => ...;
        Func<Task> runEncryptAsync = () => ...;
        Func<Task> runCheckAsync   = () => ...;
        */
          
      if ( !v4 ) // 判断有没有 get 到 byte[] Result
        sub_1801579F0(v12, v11); // 没 get 到抛出 nullException
      v13 = (*(v4 + 24))(*(v4 + 64), *(v4 + 40)); // Task<byte[]> t = v4.Invoke();
      if ( !v13 )
        sub_1801579F0(v15, v14);
      *&v47.fields.m_continueOnCapturedContext = System_Threading_Tasks_Task___Il2CppFullySharedGenericType___GetAwaiter(
                                                   v13,
                                                   Method_System_Threading_Tasks_Task_byte____GetAwaiter__); // awaiter = t.GetAwaiter();
          
      if ( System_Runtime_CompilerServices_ConfiguredTaskAwaitable_ConfiguredTaskAwaiter___Il2CppFullySharedGenericType___get_IsCompleted(
             &v47.fields.m_continueOnCapturedContext,
             Method_System_Runtime_CompilerServices_TaskAwaiter_byte____get_IsCompleted__) ) // 判断 IsCompleted
      {
LABEL_13: // 第一个 await
        Result = System_Runtime_CompilerServices_ConfiguredTaskAwaitable_ConfiguredTaskAwaiter_object___GetResult(
                   &v47.fields.m_continueOnCapturedContext,
                   Method_System_Runtime_CompilerServices_TaskAwaiter_byte____GetResult__); // 先 getResult
        if ( Result )
        {
          // 调 uploadFlagAsync
          uploadFlagAsync_5__2 = this->fields._uploadFlagAsync_5__2;
          if ( !uploadFlagAsync_5__2 )
            sub_1801579F0(0, v17);
          v20 = (uploadFlagAsync_5__2->fields.invoke_impl)(
                  uploadFlagAsync_5__2->fields.method_code,
                  Result,
                  uploadFlagAsync_5__2->fields.method);
          if ( !v20 )
            sub_1801579F0(v22, v21);
          v47.fields.m_task = System_Threading_Tasks_Task__GetAwaiter(v20, 0).fields.m_task;
          if ( !System_Runtime_CompilerServices_ConfiguredTaskAwaitable_ConfiguredTaskAwaiter___Il2CppFullySharedGenericType___get_IsCompleted(
                  &v47,
                  0) )
          {
            this->fields.__1__state = 1;
            this->fields.__u__2.fields.m_task = v47.fields.m_task;
            sub_180156C70(&this->fields.__u__2, 0);
            System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18_(
              &this->fields.__t__builder,
              &v47,
              this,
              Method_System_Runtime_CompilerServices_AsyncVoidMethodBuilder_AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18___);
            return;
          }
LABEL_17: // 第二个 await
          System_Runtime_CompilerServices_TaskAwaiter__GetResult(&v47, 0);
          // 调 runEncryptAsync
          runEncryptAsync_5__3 = this->fields._runEncryptAsync_5__3;
          if ( !runEncryptAsync_5__3 )
            sub_1801579F0(0, v23);
          v25 = (runEncryptAsync_5__3->fields.invoke_impl)(
                  runEncryptAsync_5__3->fields.method_code,
                  runEncryptAsync_5__3->fields.method);
          if ( !v25 )
            sub_1801579F0(v27, v26);
          v47.fields.m_task = System_Threading_Tasks_Task__GetAwaiter(v25, 0).fields.m_task;
          if ( !System_Runtime_CompilerServices_ConfiguredTaskAwaitable_ConfiguredTaskAwaiter___Il2CppFullySharedGenericType___get_IsCompleted(
                  &v47,
                  0) )
          {
            this->fields.__1__state = 2;
            this->fields.__u__2.fields.m_task = v47.fields.m_task;
            sub_180156C70(&this->fields.__u__2, 0);
            System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18_(
              &this->fields.__t__builder,
              &v47,
              this,
              Method_System_Runtime_CompilerServices_AsyncVoidMethodBuilder_AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18___);
            return;
          }
LABEL_20: // 第三个 await
          System_Runtime_CompilerServices_TaskAwaiter__GetResult(&v47, 0);
          // 调 runEncryptAsync
          v29 = this->fields._runEncryptAsync_5__3;
          if ( !v29 )
            sub_1801579F0(0, v28);
          v30 = (v29->fields.invoke_impl)(v29->fields.method_code, v29->fields.method);
          if ( !v30 )
            sub_1801579F0(v32, v31);
          v47.fields.m_task = System_Threading_Tasks_Task__GetAwaiter(v30, 0).fields.m_task;
          if ( !System_Runtime_CompilerServices_ConfiguredTaskAwaitable_ConfiguredTaskAwaiter___Il2CppFullySharedGenericType___get_IsCompleted(
                  &v47,
                  0) )
          {
            this->fields.__1__state = 3;
            this->fields.__u__2.fields.m_task = v47.fields.m_task;
            sub_180156C70(&this->fields.__u__2, 0);
            System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18_(
              &this->fields.__t__builder,
              &v47,
              this,
              Method_System_Runtime_CompilerServices_AsyncVoidMethodBuilder_AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18___);
            return;
          }
LABEL_23: // 第四个 await
          System_Runtime_CompilerServices_TaskAwaiter__GetResult(&v47, 0);
          // 调 runEncryptAsync
          v34 = this->fields._runEncryptAsync_5__3;
          if ( !v34 )
            sub_1801579F0(0, v33);
          v35 = (v34->fields.invoke_impl)(v34->fields.method_code, v34->fields.method);
          if ( !v35 )
            sub_1801579F0(v37, v36);
          v47.fields.m_task = System_Threading_Tasks_Task__GetAwaiter(v35, 0).fields.m_task;
          if ( !System_Runtime_CompilerServices_ConfiguredTaskAwaitable_ConfiguredTaskAwaiter___Il2CppFullySharedGenericType___get_IsCompleted(
                  &v47,
                  0) )
          {
            this->fields.__1__state = 4;
            this->fields.__u__2.fields.m_task = v47.fields.m_task;
            sub_180156C70(&this->fields.__u__2, 0);
            System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18_(
              &this->fields.__t__builder,
              &v47,
              this,
              Method_System_Runtime_CompilerServices_AsyncVoidMethodBuilder_AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18___);
            return;
          }
LABEL_26: // 第五个 await
          System_Runtime_CompilerServices_TaskAwaiter__GetResult(&v47, 0);
          // 调 runCheckAsync
          runCheckAsync_5__4 = this->fields._runCheckAsync_5__4;
          if ( !runCheckAsync_5__4 )
            sub_1801579F0(0, v38);
          v40 = (runCheckAsync_5__4->fields.invoke_impl)(
                  runCheckAsync_5__4->fields.method_code,
                  runCheckAsync_5__4->fields.method);
          if ( !v40 )
            sub_1801579F0(v42, v41);
          v47.fields.m_task = System_Threading_Tasks_Task__GetAwaiter(v40, 0).fields.m_task;
          if ( !System_Runtime_CompilerServices_ConfiguredTaskAwaitable_ConfiguredTaskAwaiter___Il2CppFullySharedGenericType___get_IsCompleted(
                  &v47,
                  0) )
          {
            this->fields.__1__state = 5;
            this->fields.__u__2.fields.m_task = v47.fields.m_task;
            sub_180156C70(&this->fields.__u__2, 0);
            System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18_(
              &this->fields.__t__builder,
              &v47,
              this,
              Method_System_Runtime_CompilerServices_AsyncVoidMethodBuilder_AwaitUnsafeOnCompleted_TaskAwaiter__Controller__Check_d__18___);
            return;
          }
LABEL_29: // 第六个 await
          // 清理字段,SetResult()
          System_Runtime_CompilerServices_TaskAwaiter__GetResult(&v47, 0);
        }
        else
        {
          if ( !_4__this )
            sub_1801579F0(v18, v17);
          klass = _4__this[7].klass;
          v44 = sub_180156CF0(int___TypeInfo, 1);
          if ( !v44 )
            sub_1801579F0(v46, v45);
          if ( !*(v44 + 24) )
            sub_1801579E0();
          *(v44 + 32) = 1;
          if ( !klass )
            sub_1801579F0(v46, v45);
          UnityEngine_ComputeBuffer__SetData(klass, v44, 0);
        }
        this->fields.__1__state = -2;
        this->fields._uploadFlagAsync_5__2 = 0;
        sub_180156C70(&this->fields._uploadFlagAsync_5__2, 0);
        this->fields._runEncryptAsync_5__3 = 0;
        sub_180156C70(&this->fields._runEncryptAsync_5__3, 0);
        this->fields._runCheckAsync_5__4 = 0;
        sub_180156C70(&this->fields._runCheckAsync_5__4, 0);
        System_Runtime_CompilerServices_AsyncVoidMethodBuilder__SetResult(&this->fields.__t__builder, 0);
      }
      else
      {
        this->fields.__1__state = 0;
        this->fields.__u__1.fields.m_task = *&v47.fields.m_continueOnCapturedContext;
        sub_180156C70(&this->fields.__u__1, 0);
        System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitUnsafeOnCompleted_TaskAwaiter_object___Controller__Check_d__18_( // OnCompleted 在这里
          &this->fields.__t__builder,
          &v47.fields.m_continueOnCapturedContext,
          this,
          Method_System_Runtime_CompilerServices_AsyncVoidMethodBuilder_AwaitUnsafeOnCompleted_TaskAwaiter_byte_____Controller__Check_d__18___);
      }
      return;
  }
}

async 函数启动器#

跟进 Controller___Check_b__18_x,很常规的 async 函数启动器。分析一个

System_Threading_Tasks_Task_byte____o *Controller___Check_b__18_0(Controller_o *this, const MethodInfo *method)
{
  const MethodInfo *v2; // r8
  UnityEngine_Bounds_o v5; // [rsp+20h] [rbp-58h] BYREF
  Controller___Check_b__18_0_d_o v6; // [rsp+38h] [rbp-40h] BYREF

  if ( !byte_180F4C9A5 )
  {
    sub_180157800();
    sub_180157800();
    sub_180157800();
    sub_180157800();
    byte_180F4C9A5 = 1;
  } // 初始化栅栏
    
  // v6 为 StateMachine 对象
  *&v6.fields.__1__state = 0;
  *&v6.fields.__4__this = 0;
  if ( !System_Runtime_CompilerServices_AsyncTaskMethodBuilder_byte____TypeInfo->_2.cctor_finished ) // C# 第一次用到某个类型前,CLR 会保证静态构造(.cctor)已经执行
    il2cpp_runtime_class_init(System_Runtime_CompilerServices_AsyncTaskMethodBuilder_byte____TypeInfo, method);
  
  // 这段莫名其妙,应该是反编译误判
  // v6.fields.__t__builder = AsyncTaskMethodBuiler<byte>.Create();
  TMPro_TMP_Text__GetCompoundBounds(
    &v5,
    Method_System_Runtime_CompilerServices_AsyncTaskMethodBuilder_byte____Create__,
    v2);
  v6.fields.__t__builder = v5;
  sub_180156C70(&v6.fields.__t__builder, 0);
    
  v6.fields.__4__this = this;
  sub_180156C70(&v6.fields.__4__this, this);
  v6.fields.__1__state = -1;
  // v6.__t__builder.Start(ref v6.builder, ref v6)
    
  // builder 启动方法
  System_Runtime_CompilerServices_AsyncTaskMethodBuilder_object___Start_Controller___Check_b__18_0_d_(
    &v6.fields.__t__builder,
    &v6,
    Method_System_Runtime_CompilerServices_AsyncTaskMethodBuilder_byte____Start_Controller___Check_b__18_0_d___);
  return System_Runtime_CompilerServices_AsyncTaskMethodBuilder_object___get_Task(
           &v6.fields.__t__builder,
           Method_System_Runtime_CompilerServices_AsyncTaskMethodBuilder_byte____get_Task__); // return v6.builder.Task 返回 Task
}

sub_180156C70 是 GC 写屏障(避免 GC 漏记引用)

Check() 业务逻辑#

Check_b__18_0:没有名字,就是从文本框取花括号内的文本转成 ASCII 写到 Result

Check_b__18_1:uploadFlagAsync,把输入当成宽 16,高 1 的 Texture2D 写入 TexF

Check_b__18_2:runEncryptAsync,就是 UnityEngine_ComputeShader__Dispatch(Shader0, _4__this->fields.K0, 1, 1, 1, 0);

Check_b__18_3:runCheckAsync,就是 UnityEngine_ComputeShader__Dispatch(Shader0, _4__this->fields.K1, 1, 1, 1, 0);

uploadFlagAsync#

Pixel#

pixel 是 Texture 里一个格子的颜色值,Texture2D 的像素通常按行排(线性索引第 i 个像素 x = i % width, y = i / width)

Color32#

Unity 由两种常用颜色结构:

  • Color:float 的 RGBA(0~1),精度高,性能低
  • Color32:byte 的 RGBA(0~255),更适合像素操作

Color32 本质就是 4 个 byte,r, g, b, a

v9 = sub_180156CF0(UnityEngine_Color32___TypeInfo, 16); // new 一个 Color32[16]
for ( i = 0; i < 16; i = (i + 1) )
{
  bytes = this->fields.bytes; // 输入
  if ( !bytes )
    sub_1801579F0();
  if ( i >= LODWORD(bytes->max_length) )
    sub_1801579E0(bytes, i, v8, v9);
  v12 = bytes->m_Items[i];
  LOBYTE(v17) = v12; // R: input
  *(&v17 + 1) = 0;
  HIBYTE(v17) = -1; // A: 255
  if ( !v9 )
    sub_1801579F0();
  if ( i >= *(v9 + 24) )
    sub_1801579E0(v12, i, v8, v9);
  *(v9 + 4LL * i + 32) = v17; // 把 v17 放到 Color32[16] 里面,+32 是在跳过对象头
}
v7 = sub_1800F5060(UnityEngine_Texture2D_TypeInfo, v6); // new 一个 宽 16 高 1 的 Texture2D
UnityEngine_Texture2D___ctor_6451756016(v7, 16, 1, 63, 0, 1, 0);
UnityEngine_Texture2D__SetPixels32_6451755632(v7, v9, 0); // 把 Pixel 写入纹理
UnityEngine_Texture2D__Apply_6451753536(v7, 0); // 上传到 GPU
TexF = _4__this->fields.TexF; // 取 TexF 字段指针
if ( !UnityEngine_Graphics_TypeInfo->_2.cctor_finished )
  il2cpp_runtime_class_init(UnityEngine_Graphics_TypeInfo, v13);
UnityEngine_Graphics__CopyTexture(v7, 0, 0, TexF, 0, 0, 0); // 把输入得到的 Texture copy 到 TexF

runCheckAsync#

Buffer0 = _4__this->fields.Buffer0; // 取 ComputeBuffer(Start 里面分配的)
v7 = sub_180156CF0(int___TypeInfo, 1);
if ( !Buffer0 )
  sub_1801579F0();
UnityEngine_ComputeBuffer__SetData(Buffer0, v7, 0); // 值设成 0
Shader0 = _4__this->fields.Shader0;
if ( !Shader0 )
  sub_1801579F0();
UnityEngine_ComputeShader__Dispatch(Shader0, _4__this->fields.K1, 1, 1, 1, 0);

信息整理#

K0: TexV(CoTex),TexF

K1: TexC(CiTex),TexF

K2: TexR(OutputTex),Tex0(CorrectTex),Tex1(WrongTex)

# dump_png.py
from PIL import Image

def dump_png(png_path):
    img = Image.open(png_path).convert("RGBA")
    w, h = img.size
    px = img.load()

    N = min(w, 1024)

    result = []
    for x in range(N):
        r,g,b,a = px[x,0]         
        result.append((r,g,b,a))

    for i,(op,y,z,w_) in enumerate(result[:50]):
        print(f"({op}, {y}, {z}, {w_}),")

dump_png("ExportedProject/Assets/Resources/coTex.png")
print()
dump_png("ExportedProject/Assets/Resources/ciTex.png")

"""
(0, 1, 0, 42),
(0, 2, 0, 0),
(1, 0, 2, 0),
(2, 0, 1, 0),
(3, 0, 2, 0),
(4, 0, 0, 7),
(6, 0, 2, 0),
(7, 2, 0, 0),
(6, 1, 0, 0),
(5, 2, 2, 1),
(8, 2, 0, 16),
(9, 0, 0, 247),
(10, 0, 0, 0),

(233, 0, 0, 255),
(142, 0, 0, 255),
(138, 0, 0, 255),
(138, 0, 0, 255),
(183, 0, 0, 255),
(231, 0, 0, 255),
(201, 0, 0, 255),
(224, 0, 0, 255),
(184, 0, 0, 255),
(151, 0, 0, 255),
(183, 0, 0, 255),
(75, 0, 0, 255),
(59, 0, 0, 255),
(33, 0, 0, 255),
(211, 0, 0, 255),
(124, 0, 0, 255),
"""

ComputeShader#

AssetRipper 把 asset 搞下来,跑一下 assets2dxbc,然后 HLSLDecompiler 拿到 kernel

K0 是 vm,coTex 是 opcode;K1 做校验,结果写入 Buffer;K2 根据 Buffer 渲染贴图

vm dump 下来大概是

0000: MOV   R01, #42
0001: MOV   R02, #0
0002: LDK   R00, KEY[R02 & 0x0F]
0003: XOR   R00, R01
0004: ROL8  R00, (R02 & 7)
0005: MUL   R00, R00, #7
0006: ADD   R00, R02
0007: STO   O[R02 & 0x0F], R00
0008: ADD   R01, R00
0009: ADDI  R02, R02, #1
0010: CMPLO R02, #16    ; flag = (R02 < 16)
0011: JLT   0002 (pc-9) ; if flag jump
0012: DB    10,0,0,0     ; unknown

逆过来

def vm(r_value: List[int]) -> List[int]:
    R1 = 42
    
    Data = r_value
    OutVar = [0] * 16    
    
    for i, d in enumerate(Data):
        x = (d - i) & 0xFF
        x = (x * 183) & 0xFF
        x = ror8(x, i & 7)
        x = x ^ R1
        OutVar[i] = x
        R1 = (R1 + d) & 0xFF
    print("Check:", OutVar)
    return OutVar

K1 的校验加了一步 TexF[i] == (TexC[i] + i) mod 256

if __name__ == "__main__":
    texc = [(233, 0, 0, 255),
    (142, 0, 0, 255),
    (138, 0, 0, 255),
    (138, 0, 0, 255),
    (183, 0, 0, 255),
    (231, 0, 0, 255),
    (201, 0, 0, 255),
    (224, 0, 0, 255),
    (184, 0, 0, 255),
    (151, 0, 0, 255),
    (183, 0, 0, 255),
    (75, 0, 0, 255),
    (59, 0, 0, 255),
    (33, 0, 0, 255),
    (211, 0, 0, 255),
    (124, 0, 0, 255)]
    texc_fixed = [r-i for i, (r,g,b,a) in enumerate(texc)]

    out = vm(vm(vm(texc_fixed)))
    print(''.join(chr(_) for _ in out))
    
"""
Check: [165, 25, 18, 163, 62, 189, 40, 59, 248, 25, 127, 183, 91, 50, 9, 119]
Check: [217, 219, 244, 246, 250, 159, 161, 152, 107, 203, 228, 21, 213, 6, 122, 104]
Check: [53, 104, 97, 100, 101, 114, 86, 77, 95, 82, 101, 112, 51, 97, 116, 33]
5haderVM_Rep3at!
"""

alictf{5haderVM_Rep3at!}