[!WARNING]
本文仅用于交流学习请勿用于不法用途,如有侵权联系Euarno@outlook.com 或Euarno@qq.com 要求删除
本文由Euarno原创,转载请标明出处
1.在Steam 下载并安装POLYGON 2.找到游戏目录POLYGON\POLYGON\Binaries\Win64 3.找到游戏文件POLYGON-Win64-Shipping.exe 4.拖进IDA 等待漫长分析过程
1.Visual Studio 2022 2.C++20
1.Cheat Engine 2.IDA Pro 3.Inject Tool
[!TIP]
游戏有EasyAntiCheat 保护
[!NOTE]
在同一台设备上,Dx 虚表位置**“固定”**
[!IMPORTANT]
有关GName 的寻找原理请参见前文,本文不做赘述
先通过字符串熟练地找到void __fastcall FNamePool_FNamePool(__int64 a1)
分析交叉引用,如下表:
[!NOTE]
Down
表示当前函数调用了 FNamePool_FNamePool
函数。
Up
表示 FNamePool_FNamePool
函数被当前函数调用。
依次看看几个Up调用内容,不要找太长的函数,也尽量避开明显提到其他组件的函数,要时时刻刻切记我们寻找的只是简单的通过
这种方式进行的构造函数调用,在为数不多的Up调用中寻找到以下符合要求的伪函数
GName 偏移通过计算 0x1480AD880-0x140000000=0x80AD880 得到,偏移为 0x80AD880
我们开CheatEngine 简单检验一下,也确实是这个结果,我们有充分的理由认为,GName 地址是 “POLYGON-Win64-Shipping.exe”+0x80AD880
通过以下代码验证GName 正确性:
得到输出:
在UnrealEngine.cpp 源代码中寻找如下函数
它以UWorld *作为返回值,在函数中有大量明文字符串可用来作为特征寻找该函数
[!NOTE]
如果你发现你在IDA 中无法搜索到这些字符串,请设置一下识别的字符串风格,把unicode 加进去
锁定return qword_1482ACFD0
,直接计算0x1482ACFD0-0x0x140000000=0x82ACFD0 ,偏移为0x82ACFD0
还是先看引擎源码,在UObjectHash.cpp ,有GObject 的定义
分析对GUObjectArray 的引用,有很多含有字符串的函数可以作为寻找UObject 的跳板,我选择了这个函数
在IDA中定位到源码后,分析这部分
在MaxObjectsNotConsideredByGC = InMaxObjectsNotConsideredByGC时,类成员变量被赋值,对应伪代码
定位类索引首地址,就是类全局变量的地址,用0x148153F28-0x8-0x140000000=0x8153F20
ID
Description
ShowAsSigned
VariableType
Address
0
无描述
0
4 Bytes
d3d11.dll
1
无描述
0
4 Bytes
d3d12.dll
2
无描述
0
4 Bytes
d3d9.dll
ID
Description
ShowAsSigned
ShowAsHex
VariableType
Address
0
SwapChain
1
8 Bytes
1C8BD143B20
ID
Description
LastState Value
LastState RealAddress
VariableType
Address
Offset(倒序)
0
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+08157630
0, 70, 90, 68, 10
1
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+08244B40
0, 70, 80, 528, 10
3
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+0827D5B8
0, 70, 20, 778, 110
4
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+0827D5C0
0, 70, 20, 778, 110
5
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+08296C30
0, 70, 20, 748, 110
6
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+08296C38
0, 70, 20, 748, 110
7
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+082C7F60
0, 130, 10, 390, 110
8
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+082C7F60
0, 130, 10, 398, 110
9
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+082C7F68
0, 130, 10, 390, 110
10
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+082C7F68
0, 130, 10, 398, 110
11
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+0817B270
0, 10, 0, 110, 160
12
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+0817B270
0, 10, 8, 110, 160
13
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+082312C8
0, 10, 0, 110, 160
14
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"POLYGON-Win64-Shipping.exe"+082312C8
0, 10, 8, 110, 160
15
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"opencv_world455.dll"+02723310
0, 10, 0, 110, 3E8
16
指针扫描结果
618754048
1C8BD143B20
4 Bytes
"opencv_world455.dll"+02723310
0, 10, 8, 110, 3E8
ID
Description
LastState Value
LastState RealAddress
VariableType
Address
Offset(倒序)
0
指针扫描结果
618754048
1ED4E835FB0
4 Bytes
"POLYGON-Win64-Shipping.exe"+0817B270
0, 10, 0, 110, 160
1
指针扫描结果
618754048
1ED4E835FB0
4 Bytes
"POLYGON-Win64-Shipping.exe"+082312C8
0, 10, 0, 110, 160
ID
Description
VariableType
Address
Offset(倒序)
0
指针扫描结果
4 Bytes
"POLYGON-Win64-Shipping.exe"+0817B270
0, 10, 0, 110, 160
Direction
Type
Address
Text
Down
p
sub_142B09630+47
call FNamePool_FNamePool
Down
p
sub_142B09740+48
call FNamePool_FNamePool
Down
p
sub_142B0A890+30
call FNamePool_FNamePool
Down
p
sub_142B0A950+30
call FNamePool_FNamePool
Down
p
sub_142B0AA10+30
call FNamePool_FNamePool
Down
p
sub_142B0B100+4E
call FNamePool_FNamePool
Down
p
sub_142B0BCF0+4E
call FNamePool_FNamePool
Down
p
sub_142B0C260+30
call FNamePool_FNamePool
Down
p
sub_142B0CCB0+95
call FNamePool_FNamePool
Down
p
sub_142B0CE90+1F
call FNamePool_FNamePool
Down
p
sub_142B0D0B0+21
call FNamePool_FNamePool
Down
p
sub_142B0D110+21
call FNamePool_FNamePool
Down
p
sub_142B0D540+28
call FNamePool_FNamePool
Down
p
sub_142B0D5D0+28
call FNamePool_FNamePool
Down
p
sub_142B0D670+28
call FNamePool_FNamePool
Down
p
sub_142B0D700+28
call FNamePool_FNamePool
Down
p
sub_142B0DA90+28
call FNamePool_FNamePool
Down
p
sub_142B10080+72
call FNamePool_FNamePool
Down
p
sub_142B159A0+25B
call FNamePool_FNamePool
Down
p
sub_142B16D10+3A
call FNamePool_FNamePool
Down
p
sub_142B16D10+D1
call FNamePool_FNamePool
Down
p
sub_142B16EA0+3F
call FNamePool_FNamePool
Down
p
sub_142B16F90+30
call FNamePool_FNamePool
Down
o
.pdata:0000000148513348
RUNTIME_FUNCTION <rva FNamePool_FNamePool, rva algn_142B092C5,
Up
p
sub_1407D8E60+154
call FNamePool_FNamePool
Up
p
sub_1407D90C0+154
call FNamePool_FNamePool
Up
p
sub_142B04A20+A3
call FNamePool_FNamePool
Up
p
sub_142B04B20+C3
call FNamePool_FNamePool
Up
p
sub_142B04D00+FE
call FNamePool_FNamePool
Up
p
sub_142B04D00+25B
call FNamePool_FNamePool
Up
p
sub_142B05050+A5
call FNamePool_FNamePool
static
bool
bNamePoolInitialized;
alignas(FNamePool)
static
uint8 NamePoolData[
sizeof
(FNamePool)];
static
bool
bNamePoolInitialized;
alignas(FNamePool)
static
uint8 NamePoolData[
sizeof
(FNamePool)];
_DWORD *__fastcall sub_142B04A20(_DWORD *a1, _BYTE *a2)
{
bool
v2;
_BYTE *v3;
__int64
v5;
int
v6;
RTL_SRWLOCK *v7;
_DWORD *result;
const
char
*v9;
int
v10;
char
v11;
int
v12;
int
v13;
v2 = *a2 == 0;
v3 = a2 + 2;
v9 = a2 + 2;
v5 = -1i64;
if
( v2 )
{
do
++v5;
while
( v3[v5] );
v11 = 0;
}
else
{
do
++v5;
while
( *(_WORD *)&v3[2 * v5] );
v11 = 1;
}
v10 = v5;
if
( (unsigned
int
)v5 < 0x400 )
{
if
( byte_148089CF9 )
{
v7 = &stru_1480AD880;
}
else
{
FNamePool_FNamePool((
__int64
)&stru_1480AD880);
byte_148089CF9 = 1;
}
sub_142B16A60(v7, &v12, &v9);
v13 = v12;
v6 = v12;
}
else
{
v10 = 24;
v9 =
"ERROR_NAME_SIZE_EXCEEDED"
;
v11 = 0;
v6 = sub_142B0CCB0(&v9, 1i64);
}
*a1 = v6;
result = a1;
a1[1] = 0;
return
result;
}
_DWORD *__fastcall sub_142B04A20(_DWORD *a1, _BYTE *a2)
{
bool
v2;
_BYTE *v3;
__int64
v5;
int
v6;
RTL_SRWLOCK *v7;
_DWORD *result;
const
char
*v9;
int
v10;
char
v11;
int
v12;
int
v13;
v2 = *a2 == 0;
v3 = a2 + 2;
v9 = a2 + 2;
v5 = -1i64;
if
( v2 )
{
do
++v5;
while
( v3[v5] );
v11 = 0;
}
else
{
do
++v5;
while
( *(_WORD *)&v3[2 * v5] );
v11 = 1;
}
v10 = v5;
if
( (unsigned
int
)v5 < 0x400 )
{
if
( byte_148089CF9 )
{
v7 = &stru_1480AD880;
}
else
{
FNamePool_FNamePool((
__int64
)&stru_1480AD880);
byte_148089CF9 = 1;
}
sub_142B16A60(v7, &v12, &v9);
v13 = v12;
v6 = v12;
}
else
{
v10 = 24;
v9 =
"ERROR_NAME_SIZE_EXCEEDED"
;
v11 = 0;
v6 = sub_142B0CCB0(&v9, 1i64);
}
*a1 = v6;
result = a1;
a1[1] = 0;
return
result;
}
std::string GetName(uint32_t Id)
{
uint32_t Block = Id >> 16;
uint32_t Offset = Id & 65535;
uint8_t* GameBase = (uint8_t*)GetModuleHandleA(
"POLYGON-Win64-Shipping.exe"
);
uint8_t** GName = (uint8_t**)(GameBase + 0x80AD880);
FNameEntry* Info = (FNameEntry*)((GName)[2 + Block] + 2 * Offset);
return
std::string(Info->AnsiName, Info->Len);
}
printf
(
"Name:%s\n"
, GetName(0).c_str());
std::string GetName(uint32_t Id)
{
uint32_t Block = Id >> 16;
uint32_t Offset = Id & 65535;
uint8_t* GameBase = (uint8_t*)GetModuleHandleA(
"POLYGON-Win64-Shipping.exe"
);
uint8_t** GName = (uint8_t**)(GameBase + 0x80AD880);
FNameEntry* Info = (FNameEntry*)((GName)[2 + Block] + 2 * Offset);
return
std::string(Info->AnsiName, Info->Len);
}
printf
(
"Name:%s\n"
, GetName(0).c_str());
Name:None
UWorld* UEngine::GetWorldFromContextObject(
const
UObject* Object, EGetWorldErrorMode ErrorMode)
const
{
if
(Object == nullptr)
{
switch
(ErrorMode)
{
case
EGetWorldErrorMode::Assert:
check(Object);
break
;
case
EGetWorldErrorMode::LogAndReturnNull:
FFrame::KismetExecutionMessage(TEXT(
"A null object was passed as a world context object to UEngine::GetWorldFromContextObject()."
), ELogVerbosity::Warning);
break
;
case
EGetWorldErrorMode::ReturnNull:
break
;
}
return
nullptr;
}
bool
bSupported =
true
;
UWorld* World = (ErrorMode == EGetWorldErrorMode::Assert) ? Object->GetWorldChecked(
bSupported) : Object->GetWorld();
if
(bSupported && (World == nullptr) && (ErrorMode == EGetWorldErrorMode::LogAndReturnNull))
{
FFrame::KismetExecutionMessage(*FString::Printf(TEXT(
"No world was found for object (%s) passed in to UEngine::GetWorldFromContextObject()."
), *GetPathNameSafe(Object)), ELogVerbosity::Warning);
}
return
(bSupported ? World : GWorld);
}
UWorld* UEngine::GetWorldFromContextObject(
const
UObject* Object, EGetWorldErrorMode ErrorMode)
const
{
if
(Object == nullptr)
{
switch
(ErrorMode)
{
case
EGetWorldErrorMode::Assert:
check(Object);
break
;
case
EGetWorldErrorMode::LogAndReturnNull:
FFrame::KismetExecutionMessage(TEXT(
"A null object was passed as a world context object to UEngine::GetWorldFromContextObject()."
), ELogVerbosity::Warning);
break
;
case
EGetWorldErrorMode::ReturnNull:
break
;
}
return
nullptr;
}
bool
bSupported =
true
;
UWorld* World = (ErrorMode == EGetWorldErrorMode::Assert) ? Object->GetWorldChecked(
bSupported) : Object->GetWorld();
if
(bSupported && (World == nullptr) && (ErrorMode == EGetWorldErrorMode::LogAndReturnNull))
{
FFrame::KismetExecutionMessage(*FString::Printf(TEXT(
"No world was found for object (%s) passed in to UEngine::GetWorldFromContextObject()."
), *GetPathNameSafe(Object)), ELogVerbosity::Warning);
}
return
(bSupported ? World : GWorld);
}
.rdata:00000001470E6BE0 aANullObjectWas: ; DATA XREF: sub_144FEE480+1F↑o
.rdata:00000001470E6BE0 text
"UTF-16LE"
,
'A null object was passed as a world context object '
.rdata:00000001470E6C46 text
"UTF-16LE"
,
'to UEngine::GetWorldFromContextObject().'
,0
.rdata:00000001470E6BE0 aANullObjectWas: ; DATA XREF: sub_144FEE480+1F↑o
.rdata:00000001470E6BE0 text
"UTF-16LE"
,
'A null object was passed as a world context object '
.rdata:00000001470E6C46 text
"UTF-16LE"
,
'to UEngine::GetWorldFromContextObject().'
,0
__int64
__fastcall sub_144FEE480(
__int64
a1,
__int64
a2,
int
a3)
{
__int64
v4;
__int64
v6;
__int64
v7;
const
wchar_t
*v8;
const
wchar_t
*v9;
__int64
v10;
const
wchar_t
*v11;
int
v12;
const
wchar_t
*v13;
int
v14;
__int64
v15;
v4 = a2;
if
( a2 )
{
LOBYTE(v15) = 1;
if
( a3 == 2 )
v6 = sub_142C63C80(a2, &v15);
else
v6 = (*(
__int64
(__fastcall **)(
__int64
))(*(_QWORD *)a2 + 392i64))(a2);
v7 = v6;
if
( !(_BYTE)v15 )
return
qword_1482ACFD0;
if
( !v6 && a3 == 1 )
{
sub_142CD1520(v4, &v13, 0i64);
v8 = &chText;
v9 = &chText;
if
( v14 != (_DWORD)v7 )
v9 = v13;
sub_1429C9990(&v11, L
"No world was found for object (%s) passed in to UEngine::GetWorldFromContextObject()."
, v9);
LOBYTE(v10) = 3;
if
( v12 != (_DWORD)v7 )
v8 = v11;
sub_142CAF290(v8, v10, 0i64);
if
( v11 )
sub_142A062C0();
if
( v13 )
sub_142A062C0();
}
if
( !(_BYTE)v15 )
return
qword_1482ACFD0;
return
v7;
}
else
{
if
( a3 == 1 )
{
v15 = 0i64;
LOBYTE(a2) = 3;
sub_142CAF290(
L
"A null object was passed as a world context object to UEngine::GetWorldFromContextObject()."
,
a2,
0i64);
}
return
0i64;
}
}
__int64
__fastcall sub_144FEE480(
__int64
a1,
__int64
a2,
int
a3)
{
__int64
v4;
__int64
v6;
__int64
v7;
const
wchar_t
*v8;
const
wchar_t
*v9;
__int64
v10;
const
wchar_t
*v11;
int
v12;
const
wchar_t
*v13;
int
v14;
__int64
v15;
v4 = a2;
if
( a2 )
{
LOBYTE(v15) = 1;
if
( a3 == 2 )
v6 = sub_142C63C80(a2, &v15);
else
v6 = (*(
__int64
(__fastcall **)(
__int64
))(*(_QWORD *)a2 + 392i64))(a2);
v7 = v6;
if
( !(_BYTE)v15 )
return
qword_1482ACFD0;
if
( !v6 && a3 == 1 )
{
sub_142CD1520(v4, &v13, 0i64);
v8 = &chText;
v9 = &chText;
if
( v14 != (_DWORD)v7 )
v9 = v13;
sub_1429C9990(&v11, L
"No world was found for object (%s) passed in to UEngine::GetWorldFromContextObject()."
, v9);
LOBYTE(v10) = 3;
if
( v12 != (_DWORD)v7 )
v8 = v11;
sub_142CAF290(v8, v10, 0i64);
if
( v11 )
sub_142A062C0();
if
( v13 )
sub_142A062C0();
}
if
( !(_BYTE)v15 )
return
qword_1482ACFD0;
return
v7;
}
else
{
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
上传的附件: