免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
本次游戏没法给
内容参考于:微尘网络安全
上一个内容:17.游戏逆向-pxxx-分析UProperty结构
上一个内容中Size和Offset的值是会被加密的,指不定那次更新就被加密了,所以这次写如果被加密了该怎么办
首先在UE源码中打开UProperty类中

先找Offset_Internal,也就是Offset,跟之前的找法一样,先找到一个在ida中可以用于搜索的字符串,然后通过字符串找到相关的函数,从而找到相关的内容,ida的使用基本上就这么一套流程,其它游戏也一样
搜索Offset_Internal,查看有哪些地方调用了它,然后再看调用它的地方哪里有字符串,只能这样硬找,所以这个过程是一个漫长的过程可能要找两三天才能找到
这里直接说答案,如果没有答案是要把搜索到的结果一个一个的查看,通过下图蓝框的搜索结果找到下图红框的函数

然后搜索 GetOffset_ForUFunction,然后通过下图蓝框得到下图红框的函数

然后搜索 InitializeDerivedMembers,然后通过下图蓝框找到下图红框,在它的父类中有一个字符串可以使用ida搜索

下图红框的字符串,可以看到知道答案找起来还是很费劲,如果不知道答案找两三天是短的

搜出来下图红框的就是,Scriptserializatio

然后点击下图红框进行跳转

跳转之后直接按F5

这里把F5后的代码保存下来,后续可以找特征
c++
__int64 __fastcall sub_7FF7A1F3123C(unsigned int *a1, __int64 a2)
{
__int64 v4; // rax
__int64 v5; // r8
unsigned __int64 v6; // rdx
__int64 v7; // r9
__int64 result; // rax
__int64 v9; // rcx
__int64 v10; // r12
__int64 v11; // rcx
_BYTE *v12; // r14
__int64 v13; // rdx
int v14; // r13d
__int64 v15; // rax
int v16; // r8d
__int64 v17; // r14
__int64 v18; // r15
__int64 v19; // rcx
__int64 v20; // rsi
__int64 v21; // r14
__int64 (__fastcall *v22)(); // r8
__int64 v23; // rdx
__int64 v24; // rcx
unsigned int v25; // eax
signed int v26; // r9d
__int64 v27; // rdi
__int64 v28; // rdx
__int64 v29; // [rsp+20h] [rbp-99h]
__int64 v30; // [rsp+30h] [rbp-89h] BYREF
__int64 v31; // [rsp+38h] [rbp-81h]
_BYTE v32[208]; // [rsp+40h] [rbp-79h] BYREF
unsigned int v33; // [rsp+120h] [rbp+67h] BYREF
signed int v34; // [rsp+128h] [rbp+6Fh] BYREF
__int64 v35; // [rsp+130h] [rbp+77h] BYREF
sub_7FF7A1B5A5E8();
(*(void (__fastcall **)(__int64, unsigned int *))(*(_QWORD *)a2 + 40LL))(a2, a1 + 10);
(*(void (__fastcall **)(unsigned int *, __int64))(*(_QWORD *)a1 + 672LL))(a1, a2);
(*(void (__fastcall **)(__int64, unsigned int *))(*(_QWORD *)a2 + 40LL))(a2, a1 + 54);
if ( (*(_BYTE *)(a2 + 40) & 1) != 0 )
{
sub_7FF7A1F31350(&v30, a1, a2);
sub_7FF7A1F3143C(&v30, a1, a2);
v4 = sub_7FF7A1B6271C();
v5 = 0x3868F90A279CB28ALL;
v6 = (((*((_QWORD *)a1 + 1) ^ 0x9A6C533B35CDE3BFuLL) & 0xFFFFFFFFFFFF8000uLL) << 17)
^ 0x3868F90A279CB28ALL
^ __ROR8__(*((_QWORD *)a1 + 1) ^ 0x9A6C533B35CDE3BFuLL, 15);
v7 = v4 + 224;
result = *(int *)(v4 + 232);
if ( (int)result > *(_DWORD *)(v6 + 232)
|| (v9 = result, result = *(_QWORD *)(v6 + 224), *(_QWORD *)(result + 8 * v9) != v7)
|| !a1 )
{
if ( (*(_DWORD *)(a2 + 48) & 0x1000) == 0 )
{
LOBYTE(v5) = 1;
return (*(__int64 (__fastcall **)(unsigned int *, __int64, __int64))(*(_QWORD *)a1 + 600LL))(a1, a2, v5);
}
}
return result;
}
result = a1[40];
v33 = a1[40];
v10 = -1;
if ( (*(_BYTE *)(a2 + 40) & 2) != 0 )
{
v11 = *(_QWORD *)(a2 + 8);
if ( (unsigned __int64)(*(_QWORD *)v11 + 4LL) > *(_QWORD *)(v11 + 8) )
{
(*(void (__fastcall **)(__int64, unsigned int *, __int64))(*(_QWORD *)a2 + 72LL))(a2, &v33, 4);
v12 = (_BYTE *)(a2 + 41);
if ( (*(_BYTE *)(a2 + 41) & 8) != 0 )
sub_7FF7A3FD352C(a2, &v33, 4);
}
else
{
v33 = **(_DWORD **)v11;
*(_QWORD *)v11 += 4LL;
v12 = (_BYTE *)(a2 + 41);
}
v34 = 0;
result = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a2 + 136LL))(a2);
v10 = result;
v13 = *(_QWORD *)(a2 + 8);
if ( (unsigned __int64)(*(_QWORD *)v13 + 4LL) > *(_QWORD *)(v13 + 8) )
{
result = (*(__int64 (__fastcall **)(__int64, signed int *, __int64))(*(_QWORD *)a2 + 72LL))(a2, &v34, 4);
if ( (*v12 & 8) != 0 )
result = sub_7FF7A3FD352C(a2, &v34, 4);
}
else
{
v34 = **(_DWORD **)v13;
*(_QWORD *)v13 += 4LL;
}
}
if ( !byte_7FF7B2C29202 )
{
v34 = 0;
v14 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a2 + 136LL))(a2);
if ( (*(_BYTE *)(a2 + 40) & 0x20) == 0 || !(*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a2 + 128LL))(a2) )
{
while ( 1 )
{
result = v33;
v26 = v34;
if ( v34 >= (int)v33 )
break;
(*(void (__fastcall **)(unsigned int *, signed int *, __int64))(*(_QWORD *)a1 + 648LL))(a1, &v34, a2);
}
goto LABEL_47;
}
v15 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a2 + 128LL))(a2);
v17 = v15;
if ( !v15 || *(_DWORD *)(v15 + 152) != 2 )
v17 = 0;
v18 = *(_QWORD *)(v17 + 728);
v30 = 0;
v31 = 0;
v35 = 0;
LOBYTE(v16) = (*(_BYTE *)(a2 + 40) & 0x20) != 0;
sub_7FF7A1EB1ED8((unsigned int)v32, (unsigned int)&v30, v16, 0, 0);
*(_QWORD *)(v17 + 728) = v32;
while ( v34 < (int)v33 )
(*(void (__fastcall **)(unsigned int *, signed int *, __int64))(*(_QWORD *)a1 + 648LL))(a1, &v34, a2);
*(_QWORD *)(v17 + 728) = v18;
(*(void (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a2 + 72LL))(a2, v30, (int)v31);
v19 = *(_QWORD *)(v17 + 584);
if ( v19 && (_DWORD)v31 )
sub_7FF7A1C73ABC(v19, v30);
sub_7FF7A1B3BD78(v32);
v20 = v30;
if ( !v30 )
{
LABEL_44:
result = v33;
v26 = v34;
LABEL_47:
if ( v26 != (_DWORD)result )
{
sub_7FF7A3FD30A8(
"D:\\wk\\cwd1b\\build\\UnrealEngine\\Engine\\Source\\Runtime\\CoreUObject\\Private\\UObject\\Class.cpp",
1310,
L"Script serialization mismatch: Got %i, expected %i");
LODWORD(v29) = v34;
result = sub_7FF7A3FD2B00(
(unsigned int)&unk_7FF7B0A7288E,
(unsigned int)"D:\\wk\\cwd1b\\build\\UnrealEngine\\Engine\\Source\\Runtime\\CoreUObject\\Private\\UObject\\Class.cpp",
1310,
(unsigned int)L"Script serialization mismatch: Got %i, expected %i",
v29,
v33);
}
if ( (*(_BYTE *)(a2 + 40) & 2) != 0 )
{
v27 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a2 + 136LL))(a2);
(*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)a2 + 168LL))(a2, v10);
LODWORD(v35) = v27 - v14;
v28 = *(_QWORD *)(a2 + 8);
if ( (unsigned __int64)(*(_QWORD *)v28 + 4LL) > *(_QWORD *)(v28 + 8) )
{
(*(void (__fastcall **)(__int64, __int64 *, __int64))(*(_QWORD *)a2 + 72LL))(a2, &v35, 4);
if ( (*(_BYTE *)(a2 + 41) & 8) != 0 )
sub_7FF7A3FD352C(a2, &v35, 4);
}
else
{
LODWORD(v35) = **(_DWORD **)v28;
*(_QWORD *)v28 += 4LL;
}
return (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a2 + 168LL))(a2, v27);
}
return result;
}
v21 = qword_7FF7B25C14B0;
if ( !qword_7FF7B25C14B0 )
{
sub_7FF7A24A9C94();
(*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)qword_7FF7B25C14B0 + 32LL))(qword_7FF7B25C14B0, v20);
goto LABEL_44;
}
v22 = *(__int64 (__fastcall **)())(*(_QWORD *)qword_7FF7B25C14B0 + 32LL);
if ( v22 != sub_7FF7A28FCF50 )
{
((void (__fastcall *)(__int64, __int64))v22)(qword_7FF7B25C14B0, v30);
goto LABEL_44;
}
if ( (_WORD)v30 )
{
if ( dword_7FF7B25B90A4 )
{
v23 = MEMORY[0x7FFBFB544510]();
if ( v23 )
{
if ( *(_BYTE *)((v20 & 0xFFFFFFFFFFFF0000uLL) + 3) == 0xE3 )
{
v24 = 32LL * *(unsigned __int8 *)((v20 & 0xFFFFFFFFFFFF0000uLL) + 2);
v25 = *(_DWORD *)(v24 + v23 + 16);
if ( v25 < 0x40 && *(unsigned __int16 *)(v20 & 0xFFFFFFFFFFFF0000uLL) * v25 < 0x10000 )
goto LABEL_41;
if ( !*(_QWORD *)(v24 + v23 + 24) )
{
*(_OWORD *)(v24 + v23 + 24) = *(_OWORD *)(v24 + v23 + 8);
*(_QWORD *)(v24 + v23 + 8) = 0;
*(_DWORD *)(v24 + v23 + 16) = 0;
LABEL_41:
*(_QWORD *)v20 = *(_QWORD *)(v24 + v23 + 8);
*(_QWORD *)(v24 + v23 + 8) = v20;
*(_DWORD *)(v20 + 8) = ++*(_DWORD *)(v24 + v23 + 16);
goto LABEL_44;
}
}
}
}
}
sub_7FF7A28FD010(v21, v20);
goto LABEL_44;
}
return result;
}
鼠标左键单击下图红框,然后按X可以看有哪些地方调用了它,可以记录一下从别处找它的方式

这里通过上图蓝框找下图红框的函数,注意上图是在下图蓝框的函数中

首先看下图红框的,双击下图红框跳转

来到最后,如下图红框,看着大体逻辑可以对的起来Ar=a2,a1=InitializeDerivedMembers的this

然后看下一个

它的代码逻辑对不上

然后下一个

第三个如下图红框,通过三个对比,它是最符合的,也就是说 7FF7A1F32BFC是InitializeDerivedMembers

鼠标左键单击下图红框,给它改名InitializeDerivedMembers,然后双击下图红框进入InitializeDerivedMembers

通过下图红框可以看出找对了,Next的偏移正是40

然后如下图红框,它正好有Size

GetSize里也有我们要找的ElementSize,所以这俩都在一起,可以一起找

还能看到PropertyFlags的偏移

首先找GetOffset_ForUFunction,如下图红框有一个++可以定位到NumParms++

下图红框的就是GetOffset_ForUFunction和 ArrayDim和ElementSize偏移,这里是没有加密的,如果有加密的话,下图红框的后面就是加密算法,会很复杂

如下图是一个加密版本v9就是offset,通过一个Switch语句进行不同的解密,这个代码很长
解密代码很长,但是这些解密都会统一调用某一个函数,在上图找到的位置我们没办法使用,我们期望的是给一个函数传入加密后的offset,然后得到解密后的offset,UE源码中找不到这样的函数,但是在ida中通过对统一调用的解密函数按X,得到调用它的位置,然后一个一个去看,就能找到一个传进加密的offset得到一个解密的offset
然后再通过特征码定位解密函数的地址,但这种方式需要注入
如果上述的话,读了一遍没有画面,那就说明,没有使用过ida中的x
