目录

八宝粥行动逆向笔记:UWorld、UEngine、GName加密分析及GObject分析

UWorld、UEngine加密分析

GWorld加密

IDA搜索字符串,定位到int32 FEnvQueryRequest::Execute方法

"Unknown owner of request: %s";
"Unable to access world with owner: %s";
int32 FEnvQueryRequest::Execute(EEnvQueryRunMode::Type RunMode, FQueryFinishedSignature const& FinishDelegate)
{
	if (Owner == NULL)
	{
		Owner = FinishDelegate.GetUObject();
		if (Owner == NULL)
		{
			UE_LOG(LogEQS, Warning, TEXT("Unknown owner of request: %s"), *GetNameSafe(QueryTemplate));
			return INDEX_NONE;
		}
	}

	if (World == NULL)
	{
         // 这里会返回UWorld*
		World = GEngine->GetWorldFromContextObject(Owner, EGetWorldErrorMode::ReturnNull);
		if (World == NULL)
		{
			UE_LOG(LogEQS, Warning, TEXT("Unable to access world with owner: %s"), *GetNameSafe(Owner));
			return INDEX_NONE;
		}
	}

	UEnvQueryManager* EnvQueryManager = UEnvQueryManager::GetCurrent(World);
	if (EnvQueryManager == NULL)
	{
		UE_LOG(LogEQS, Warning, TEXT("Missing EQS manager!"));
		return INDEX_NONE;
	}

	return EnvQueryManager->RunQuery(*this, RunMode, FinishDelegate);
}

八宝粥IDA在对应方法的伪代码为

__int64 __fastcall sub_14AA57EB0(_QWORD *a1, unsigned int a2, v8_qjs::External *a3)
{
  __int64 (__fastcall ***v6)(_QWORD); // rax
  __int64 v7; // rax
  __int64 v8; // rax
  __int64 v9; // rcx
  __int64 v10; // rax
  void *v11; // rax
  __int64 v12; // rax
  __int64 v13; // rax
  void *v14; // rax
  int v15; // eax
  __int64 v16; // rsi
  __int64 v17; // rax
  __int64 v18; // rdx
  __int64 v19; // r8
  __int64 v20; // rax
  __int64 v21; // r14
  __int64 v22; // rsi
  _DWORD *v23; // rbx
  unsigned int v24; // edi
  v8_qjs::External *v25; // rdi
  __int64 v26; // r14
  __int64 v27; // rax
  __int64 v28; // rcx
  bool v29; // zf
  __int64 v31; // [rsp+30h] [rbp-10h] BYREF
  _DWORD *v32; // [rsp+38h] [rbp-8h]
  __int64 v33; // [rsp+70h] [rbp+30h] BYREF

  if ( a1[1] )
    goto LABEL_5;
  if ( !*((_DWORD *)a3 + 2) || (v6 = (__int64 (__fastcall ***)(_QWORD))off_1500540A0(a3)) == 0i64 )
  {
    a1[1] = 0i64;
LABEL_9:
    if ( (unsigned __int8)byte_150C78710 >= 3u )
    {
      if ( *a1 )
      {
        v33 = *(_QWORD *)(*a1 + 36i64);
        sub_148552FD0(&v33, &v31);
      }
      else
      {
        v31 = 0i64;
        v32 = 0i64;
        sub_14080A940((v8_qjs::External *)&v31);
        LODWORD(v32) = (_DWORD)v32 + 5;
        if ( (int)v32 > SHIDWORD(v32) )
          sub_14080A8A0((v8_qjs::External *)&v31);
        v10 = off_1500540A0(&v31);
        sub_1483F3110(v10, L"None", 10i64);
      }
      if ( (_DWORD)v32 )
        v11 = (void *)off_1500540A0(&v31);
      else
        v11 = &unk_14B95B5C8;
      sub_14848CEE0(
        (unsigned int)"Unknown",
        80,
        (unsigned int)&unk_150C78714,
        3,
        (__int64)L"Unknown owner of request: %s",
        v11);
      goto LABEL_31;
    }
    return 0xFFFFFFFFi64;
  }
  v7 = (**v6)(v6);
  a1[1] = v7;
  if ( !v7 )
    goto LABEL_9;
LABEL_5:
  v8 = a1[2];
  if ( !v8 )                                    // 这里是特征,判断World == NULL
      										// 跟源码对应上了,这里v8就是World
  {
   	// GEngine解密,这里v9就是GEngine
    if ( (unsigned __int8)off_150241E80() )
    {
      v33 = qword_150244E88;
      BYTE1(v33) = BYTE6(qword_150244E88) ^ 0x37;
      BYTE6(v33) = BYTE1(qword_150244E88) ^ 0x37;
      v9 = v33;
    }
    else
    {
      v9 = *(_QWORD *)qword_150242E88;
    }
      
    // GEngine作为参数1调call,返回的v8就是GWorld,下文继续分析sub_14A544E40这个call是什么
    v8 = sub_14A544E40(v9, a1[1], 0i64);
    a1[2] = v8;
    if ( !v8 )
    {
      if ( (unsigned __int8)byte_150C78710 >= 3u )
      {
        v12 = a1[1];
        if ( v12 )
        {
          v33 = *(_QWORD *)(v12 + 36);
          sub_148552FD0(&v33, &v31);
        }
        else
        {
          v31 = 0i64;
          v32 = 0i64;
          sub_14080A940((v8_qjs::External *)&v31);
          LODWORD(v32) = (_DWORD)v32 + 5;
          if ( (int)v32 > SHIDWORD(v32) )
            sub_14080A8A0((v8_qjs::External *)&v31);
          v13 = off_1500540A0(&v31);
          sub_1483F3110(v13, L"None", 10i64);
        }
        if ( (_DWORD)v32 )
          v14 = (void *)off_1500540A0(&v31);
        else
          v14 = &unk_14B95B5C8;
        sub_14848CEE0(
          (unsigned int)"Unknown",
          90,
          (unsigned int)&unk_150C78714,
          3,
          (__int64)L"Unable to access world with owner: %s",
          v14);
LABEL_31:
        off_1500540A0(&v31);
        sub_14080A880(&v31, 0i64);
        if ( off_1500540A0(&v31) )
        {
          v15 = off_1500540A0(&v31);
          sub_14841A4C0(v15);
        }
        return 0xFFFFFFFFi64;
      }
      return 0xFFFFFFFFi64;
    }
  }
  v16 = *(_QWORD *)(v8 + 344);
  if ( !v16
    || (v17 = sub_14AAD8D60(),
        v18 = *(_QWORD *)(v16 + 16),
        v19 = v17 + 56,
        v20 = *(int *)(v17 + 64),
        (int)v20 > *(_DWORD *)(v18 + 64))
    || *(_QWORD *)(*(_QWORD *)(v18 + 56) + 8 * v20) != v19 )
  {
    v16 = 0i64;
  }
  if ( !v16 || (v21 = *(_QWORD *)(v16 + 176)) == 0 )
  {
    if ( (unsigned __int8)byte_150C78710 >= 3u )
      sub_14848CEE0((unsigned int)"Unknown", 98, (unsigned int)&unk_150C78714, 3, (__int64)L"Missing EQS manager!");
    return 0xFFFFFFFFi64;
  }
  sub_14AA6E170(*(_QWORD *)(v16 + 176), &v31, a1, a2);
  v22 = v31;
  v23 = v32;
  if ( v31 )
  {
    sub_1408B7830((v8_qjs::External *)(v31 + 96), a3);
    v25 = (v8_qjs::External *)(v21 + 72);
    v26 = *(int *)(v21 + 80);
    *((_DWORD *)v25 + 2) = v26 + 1;
    if ( (int)v26 + 1 > *((_DWORD *)v25 + 3) )
      sub_140834340(v25);
    v27 = off_1500540A0(v25);
    v28 = 2 * v26;
    *(_QWORD *)(v27 + 8 * v28) = v22;
    *(_QWORD *)(v27 + 8 * v28 + 8) = v23;
    if ( v23 )
      ++v23[2];
    v24 = *(_DWORD *)(v22 + 48);
  }
  else
  {
    v24 = -1;
  }
  if ( v23 )
  {
    v29 = v23[2]-- == 1;
    if ( v29 )
    {
      (**(void (__fastcall ***)(_DWORD *))v23)(v23);
      v29 = v23[3]-- == 1;
      if ( v29 )
        (*(void (__fastcall **)(_DWORD *, __int64))(*(_QWORD *)v23 + 8i64))(v23, 1i64);
    }
  }
  return v24;
}

继续分析sub_14A544E40

GCloud::CApolloBufferWriter *__fastcall sub_14A544E40(__int64 a1, __int64 a2, int a3)
{
  __int64 v4; // rdi
  __int64 v6; // rax
  __int64 v7; // rsi
  void *v8; // rbx
  __int64 v9; // rdx
  int v10; // eax
  int v11; // eax
  char v12[8]; // [rsp+20h] [rbp-28h] BYREF
  int v13; // [rsp+28h] [rbp-20h]
  char v14[8]; // [rsp+30h] [rbp-18h] BYREF
  int v15; // [rsp+38h] [rbp-10h]
  __int64 v16; // [rsp+58h] [rbp+10h] BYREF
  __int64 v17; // [rsp+68h] [rbp+20h]

  v4 = a2;
  if ( a2 )
  {
    LOBYTE(v16) = 1;
    if ( a3 == 2 )
      v6 = sub_1487DE0B0(a2, &v16);
    else
      v6 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a2 + 344i64))(a2);
    v7 = v6;
    if ( !(_BYTE)v16 )
      goto LABEL_26;
    if ( !v6 && a3 == 1 )
    {
      v17 = 0i64;
      sub_148837900(v4, v14, 0i64);
      v8 = &unk_14B95B5C8;
      if ( v15 != (_DWORD)v7 )
        off_1500540A0(v14);
      sub_1483DAA50((v8_qjs::External *)v12);
      if ( v13 )
        v8 = (void *)off_1500540A0(v12);
      LOBYTE(v9) = 3;
      sub_14883BF60(v8, v9, v17);
      off_1500540A0(v12);
      sub_14080A880(v12, 0i64);
      if ( off_1500540A0(v12) )
      {
        v10 = off_1500540A0(v12);
        sub_14841A4C0(v10);
      }
      off_1500540A0(v14);
      sub_14080A880(v14, 0i64);
      if ( off_1500540A0(v14) )
      {
        v11 = off_1500540A0(v14);
        sub_14841A4C0(v11);
      }
    }
    if ( !(_BYTE)v16 )
    {
LABEL_26:
      // 这里就是GWorld解密
      if ( !(unsigned __int8)off_150246E70() )
        return qword_150247E78;
      v16 = qword_150249E78;
      LOBYTE(v16) = HIBYTE(qword_150249E78) ^ 0x36;
      HIBYTE(v16) = qword_150249E78 ^ 0x36;
      return (GCloud::CApolloBufferWriter *)v16;
    }
    return (GCloud::CApolloBufferWriter *)v7;
  }
  else
  {
    if ( a3 == 1 )
    {
      v16 = 0i64;
      LOBYTE(a2) = 3;
      sub_14883BF60(L"UEngine::Get.World.4ContextObj", a2, 0i64);
    }
    return 0i64;
  }
}

GName加密分析

通过字符串定位(Ansi)

"ByteProperty"

搜索到引用函数后,再交叉引用即可

if ( byte_154A8182C )
{
    v4 = &unk_154A81B00;
}
else
{
    v4 = (_QWORD *)sub_14AEA1CE0((__int64)&unk_154A81B00);	// GName = 154A81B00
    byte_154A8182C = 1;									 // 全局初始化标志位
}
v5 = (_WORD *)(v4[v3 + 1] + (unsigned int)(2 * v12));
v6 = v5 + 1;
v7 = (unsigned __int64)(unsigned __int16)*v5 >> 6;
if ( (*v5 & 1) != 0 )	// FNAME_ENTRY.bIsWide
{
    v8 = v7;
    sub_14E327C7D(v13, v6, 2 * v7);	// memcpy
    sub_14AEABF60(v13, (unsigned __int16)*v5 >> 6);// WideChar解密
    if ( (unsigned int)sub_14AD5CEE0(v13, a2, v7) )
        return 0;
    v9 = a2[v8];
    if ( !v14 )
        return v9 == 0;
    return v9 == 95 && (unsigned __int8)sub_14AEA0F40((unsigned int)(v14 - 1), &a2[v8 + 1]);
}
else
{
    sub_14E327C7D(v13, v6, (unsigned __int64)(unsigned __int16)*v5 >> 6);// memcpy
    sub_14AEABD50(v13, (unsigned __int16)*v5 >> 6);// Ansi解密
    if ( (unsigned int)sub_14AD5CE60(v13, a2, v7) )
        return 0;
    v11 = a2[v7];
    if ( !v14 )
        return v11 == 0;
    return v11 == 95 && (unsigned __int8)sub_14AEA0F40((unsigned int)(v14 - 1), &a2[v7 + 1]);
}

特征码

  1. GName特征码
"48 8D 15 ?? ?? ?? ?? EB ?? 48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B D0 C6 05 ?? ?? ?? ?? 01"
DeltaForce_20260626_Ansi.exe+1528E3CB - 48 8D 15 EE96A90B     - lea rdx,[DeltaForce_20260626_Ansi.exe+20D27AC0]		// GName
DeltaForce_20260626_Ansi.exe+1528E3D2 - EB 16                 - jmp DeltaForce_20260626_Ansi.exe+1528E3EA
DeltaForce_20260626_Ansi.exe+1528E3D4 - 48 8D 0D E596A90B     - lea rcx,[DeltaForce_20260626_Ansi.exe+20D27AC0]		// GName
DeltaForce_20260626_Ansi.exe+1528E3DB - E8 F0130000           - call DeltaForce_20260626_Ansi.exe+1528F7D0
DeltaForce_20260626_Ansi.exe+1528E3E0 - 48 8B D0              - mov rdx,rax
DeltaForce_20260626_Ansi.exe+1528E3E3 - C6 05 1295A90B 01     - mov byte ptr [DeltaForce_20260626_Ansi.exe+20D278FC],01
DeltaForce_20260626_Ansi.exe+1528E3EA - 48 8B 44 24 20        - mov rax,[rsp+20]
  1. 解密特征码
"E8 ?? ?? ?? ?? 0F B7 16 48 8D 4C 24 ?? C1 EA ?? E8 ?? ?? ?? ?? 4C 8B C7 48 8D 4C 24 ?? 48 8B D3 E8"
DeltaForce_20260626_Ansi.exe+1528E415 - E8 337AAF03           - call DeltaForce_20260626_Ansi.exe+18D85E4D
DeltaForce_20260626_Ansi.exe+1528E41A - 0FB7 16               - movzx edx,word ptr [rsi]
DeltaForce_20260626_Ansi.exe+1528E41D - 48 8D 4C 24 30        - lea rcx,[rsp+30]
DeltaForce_20260626_Ansi.exe+1528E422 - C1 EA 06              - shr edx,06
DeltaForce_20260626_Ansi.exe+1528E425 - E8 066D0000           - call DeltaForce_20260626_Ansi.exe+15295130	// WideChar解密Call

GObject

IDA搜索字符串(Ansi)

"UObjectBaseInit"

搜索到引用后,直接转伪代码

__int64 sub_14B217330()
{
  int v0; // edi
  __int64 v1; // rax
  __int64 v2; // r9
  __int64 v3; // rax
  const wchar_t *v4; // rcx
  __int64 v5; // rax
  unsigned int v6; // ebx
  char v7; // si
  int v8; // ebx
  unsigned __int128 v9; // rax
  __int64 v10; // rbx
  unsigned __int128 v11; // rax
  __int64 v12; // rax
  __int64 v13; // rsi
  __int64 v14; // rcx
  int v15; // eax
  __int64 v16; // rbx
  int v17; // eax
  int v18; // ebx
  int v20; // [rsp+50h] [rbp-30h] BYREF
  __int64 v21[2]; // [rsp+58h] [rbp-28h] BYREF
  char v22[24]; // [rsp+68h] [rbp-18h] BYREF
  char v23; // [rsp+B0h] [rbp+30h] BYREF
  char v24; // [rsp+B8h] [rbp+38h] BYREF
  int v25; // [rsp+C0h] [rbp+40h] BYREF
  int v26; // [rsp+C8h] [rbp+48h] BYREF

  sub_14092DD90(v22, "UObjectBaseInit");
  v0 = 0;
  v20 = 0;
  v25 = 0;
  v23 = 0;
  v24 = 0;
  v26 = 0x200000;
  sub_14AE1B0B0(
    qword_154A73948,
    (unsigned int)L"/Script/Engine.GarbageCollectionSettings",
    (unsigned int)L"gc.EnabledGCOffsetByPC",
    (unsigned int)&v24,
    (__int64)&unk_154A7DD08);
  byte_154A9AFF4 = v24;
  v21[0] = 0i64;
  v21[1] = 0i64;
  v1 = sub_14AE1AA80();
  LOBYTE(v2) = 1;
  if ( (unsigned __int8)sub_14AE85600(v1, L"-filehostip=", v21, v2) )
  {
    dword_153FA8E48 = 0;
  }
  else
  {
    sub_14AE1C7B0(
      qword_154A73948,
      (unsigned int)L"/Script/Engine.GarbageCollectionSettings",
      (unsigned int)L"gc.MaxObjectsNotConsideredByGC",
      (unsigned int)&v20,
      (__int64)&unk_154A7DD08);
    sub_14AE1C7B0(
      qword_154A73948,
      (unsigned int)L"/Script/Engine.GarbageCollectionSettings",
      (unsigned int)L"gc.SizeOfPermanentObjectPool",
      (unsigned int)&v25,
      (__int64)&unk_154A7DD08);
  }
  sub_14AE1C7B0(
    qword_154A73948,
    (unsigned int)L"/Script/Engine.GarbageCollectionSettings",
    (unsigned int)L"gc.MaxObjectsInGame",
    (unsigned int)&v26,
    (__int64)&unk_154A7DD08);
  sub_14AE1B0B0(
    qword_154A73948,
    (unsigned int)L"/Script/Engine.GarbageCollectionSettings",
    (unsigned int)L"gc.PreAllocateUObjectArray",
    (unsigned int)&v23,
    (__int64)&unk_154A7DD08);
  off_153F960A0(v21);
  sub_1408EF720(v21, 0i64);
  if ( off_153F960A0(v21) )
  {
    v3 = off_153F960A0(v21);
    sub_14AD7B320(v3);
  }
  if ( (unsigned __int8)byte_154A7DFA0 >= 6u )
  {
    v4 = L"Presizing";
    if ( v23 )
      v4 = L"Pre-allocating";
    sub_14ADF5040(
      (unsigned int)"Unknown",
      1153,
      (unsigned int)&unk_154A7DFA4,
      6,
      (__int64)L"%s for max %d objects, including %i objects not considered by GC, pre-allocating %i bytes for permanent pool.",
      v4,
      v26,
      v20,
      v25);
  }
  dword_154A9AE30 = v25;
  v5 = sub_14AD87600(v25, 0i64);
  v6 = v26;
  v7 = v23;
  qword_154A9AE38 = v5;
  qword_154A9AE40 = v5;
  qword_154A9AE48 = v5;
  dword_154A9B220 = v20;
  dword_154A9B218 = (v20 <= 0) - 1;
  if ( v26 <= 0 )
  {
    sub_14ADF5040(
      (unsigned int)"Unknown",
      45,
      (unsigned int)&unk_154A9AE54,
      1,
      (__int64)L"Max UObject count is invalid. It must be a number that is greater than 0.");
    sub_14AE28230();
  }
  v8 = sub_1408F39D0(v6) + 1;
  dword_154A9B228 = v8;
  dword_154A9B250 = sub_14B1EFC30((unsigned int)v8);
  v9 = (unsigned __int64)v8 * (unsigned __int128)8ui64;
  if ( !is_mul_ok(v8, 8ui64) )
    *(_QWORD *)&v9 = -1i64;
  qword_154A9B238 = sub_1426411F0(v9, *((_QWORD *)&v9 + 1));// GObject = 154A9B238
  sub_14E327C89(qword_154A9B238, 0i64, 8i64 * dword_154A9B228);
  if ( v7 )
  {
    v10 = dword_154A9B250;
    v11 = (unsigned __int64)dword_154A9B250 * (unsigned __int128)0x18ui64;
    if ( !is_mul_ok(dword_154A9B250, 0x18ui64) )
      *(_QWORD *)&v11 = -1i64;
    v12 = sub_1426411F0(v11, *((_QWORD *)&v11 + 1));
    v13 = v12;
    if ( v12 )
    {
      if ( v10 )
      {
        v14 = v12 + 12;
        do
        {
          *(_BYTE *)v14 &= 0xF0u;
          *(_QWORD *)(v14 - 12) = 0i64;
          *(_DWORD *)(v14 - 4) = 0;
          *(_QWORD *)(v14 + 4) = 0i64;
          v14 += 24i64;
          --v10;
        }
        while ( v10 );
      }
    }
    else
    {
      v13 = 0i64;
    }
    v15 = dword_154A9B228;
    qword_154A9B248 = v13;
    if ( dword_154A9B228 > 0 )
    {
      v16 = 0i64;
      while ( 1 )
      {
        v17 = sub_14B1EFC30((unsigned int)v0);
        v16 += 8i64;
        ++v0;
        // 这里也是特征,明显循环给qword_154A9B238填充对象
        *(_QWORD *)(v16 + qword_154A9B238 - 8) = v13 + 24i64 * v17;// GObject = 154A9B238
        v15 = dword_154A9B228;
        if ( v0 >= dword_154A9B228 )
          break;
        v13 = qword_154A9B248;
      }
    }
    HIDWORD(qword_154A9B22C) = v15;
  }
  v18 = dword_154A9B220;
  if ( dword_154A9B220 > 0 )
  {
    sub_14B1FE340(&dword_154A9B228, (unsigned int)(dword_154A9B220 + qword_154A9B22C - 1));
    LODWORD(qword_154A9B22C) = v18 + qword_154A9B22C;
  }
  sub_14B045AB0();
  byte_154A9B084 = 1;
  sub_14B219750();
  return sub_1408F5840((v8_qjs::External *)v22);
}

特征码

"48 89 05 ?? ?? ?? ?? E8 ?? ?? ?? ?? 40 84 F6 0F 84 ?? ?? ?? ?? 48 63 1D ?? ?? ?? ?? B8"
155602069 - 48 89 05 30D0740B     - mov [160D4F0A0],rax		// 160D4F0A0就是GObject
155602070 - E8 E43D7803           - call 158D85E59
155602075 - 40 84 F6              - test sil,sil
155602078 - 0F84 A0000000         - je 15560211E
15560207E - 48 63 1D 33D0740B     - movsxd  rbx,dword ptr [160D4F0B8]
155602085 - B8 18000000           - mov eax,00000018
八宝粥行动 虚幻引擎 逆向工程