游戏介绍:
RIMWORLD(环世界)游戏是一款模拟经营游戏,上手容易,玩家可以创造一个属于自己的世界指挥你的殖民者在陌生星球建立据点,并发展下去统领全球。有兴趣可以玩一下。
准备工作:
这款游戏是我在以前上学的时候放假打发时间用的,建造经营感觉还不错,今天就拿这个游戏练练手,逆向分析然后修改游戏数据,做一个简单的游戏修改器。
目标:
1.修改物品
2.修改人物状态
3.加速研究建造速
需要CE任何版本的都可以,
环世界游戏,我准备了单机环世界:
链接:https://pan.baidu.com/s/1xhlgiYs2HS2Fdzq-7ffnDQ
提取码:h910
编程使用的VS2017
修改的方法是DLL注入,注入工具大家可以随便在网上找找,一大堆,随便一个就可以。
首先运行游戏,新建游戏,创建一个剧本,
普通剧情简单模式
跳过不重要的选项,选择小地图便于分析
进入游戏后存档,准备完成,游戏的界面:
修改物品逆向
在游戏界面中,我们可以发现由每个物品都有数量,使用CE进行搜索木材54,然后随便建造一点使用木材的建筑,根据更改搜索变动。
用同样的方法搜索另一个类型的白银,会得到白银和木头的地址,将他们的结构保存下来进行每个字节的修改来判断物品的每个字段是什么类型,具体方法就是将白银的相同位置改到木头上,查看游戏变化,来判断那个位置属于什么,当然这种只能判断一部分,另一部分可以在带用他们的代码上进行分析。这一部分是比较消耗时间的,因为需要一点点判断,找出有用信息。
例如修改蓝色区域就能判断出为坐标,一点一点查找可以用到的字段
医药物品:
84 9E C2 19 00 00 00 00 20 D9 FD 19 00 00 00 00 | 20 D9 FD 19 *物品类型 | 84 9E C2 19 |
00 00 00 00 40 1B 8E 1A F8 54 CB 43 74 71 00 00 | 40 1B 8E 1A *物品贴图 | 74 71 00 00 像是序号 |
00 00 00 00 58 00 00 00 00 00 00 00 60 00 00 00 | 58、60 该物品座标 |
|
00 00 00 00 05 00 00 00 3C 00 00 00 | 05 00 00 00物品数量 | 3C 00 00 00耐久度 |
经过动态更改和比较能够判断出物品结构体部分类型
typedef struct _GoodsInfo{
int unknown0;
int unknown4;
PGoodsTypes GoodsType; //物品类型
int unknown12;
int unknown16;
int GoodsIconId; //物品贴图
int unknown24;
int unknown28;
int unknown32;
int GoodsX; //座标 从左至右变大
int x_null;
int GoodsY; //座标 从下之上变大
int y_null;
int GoodsNumber; //物品数量
int GoodsDurable; //物品耐久
}*PGoodsInfo, GoodsInfo;
找到了物品的地址就使用CE进行附加查看访问该位置的地方来查看那部分的代码进行分析。
我们在这个位置发现会访问物品的信息,并且是个循环线程,一直遍历访问所有物品,所以对这个函数进行hook就可以不断获取所有的物品。
HOOK的代码我会放在项目hook.cpp中 这个函数的第二个参数就是物品的地址。使用结构体指向这里就可以获取所有的信息。
因为这里的函数是动态内存中随机地址,所以不能通过模块+偏移的方式确定位置,所以我们需要遍历特征找到这个函数的位置,定义一个特征:
/*调用物品信息的函数特征,他的第二个参数是我能获得的物品结构地址,
这个函数是一个大循环里面的,会一直执行
028F1790 55 push ebp
028F1791 8BEC mov ebp,esp
028F1793 83EC 08 sub esp,0x8
028F1796 8B4D 0C mov ecx,dword ptr ss:[ebp+0xC]
028F1799 8B41 34 mov eax,dword ptr ds:[ecx+0x34]
028F179C 8B49 08 mov ecx,dword ptr ds:[ecx+0x8]
028F179F 83EC 04 sub esp,0x4
028F17A2 51 push ecx
028F17A3 50 push eax
028F17A4 FF75 08 push dword ptr ss:[ebp+0x8]
*/
char mem_of_goods[] = { 0x55,0x8B,0xEC,0x83,0xEC,0x08,0x8B,0x4D,0x0C,0x8B,0x41,0x34,0x8B,0x49,0x08,0x83,0xEC,0x04,0x51,0x50,0xFF,0x75,0x08 };
遍历所有的内存模块找到这个特征地址进行定位代码过长就放在项目的EnumAllMemoryBlocks函数和MemoryCmpToFeatureCode函数配合查找位置。这个游戏中我们需要的内存块都是固定属性的,所以我们只遍历该属性内存块就可以:
// 经过分析我们需要的内存块属性分别是是PAGE_EXECUTE_READWRITE\MEM_COMMIT\MEM_PRIVATE,所以将不必要的内存块排除掉
if (memInfo.Protect == PAGE_EXECUTE_READWRITE)
if (memInfo.State == MEM_COMMIT)
if (memInfo.Type == MEM_PRIVATE)
memories.push_back(memInfo);// 将内存块信息追加到 vector 数组尾部
有了位置修改物品的数量就轻而易举。因为hook函数是一只循环的,所以我根据第一次遍历到的地址保存,再次遍历到这个地址为一次循环,结束遍历,通过控件消息可重新更新遍历。
效果图:
人物状态逆向
点击人物,显示需求,会看到0.63/1的信息
同样使用CE进行搜索,不过是浮点类型的,所有介于两者之间,变化一点搜索一点,找到地址后,同样使用谁访问过这个位置,有很多地方都访问,这个位置是一个人物结构的状态,人物的结构指向这个位置,这个结构中装有很多人物信息,所以找到访问这个人物的代码,从中选择一个循环遍历所有人物的代码就可以,然后更改他跳转到我们的写好的改变状态代码上就可了。
调用任务信息的理想函数:
所对这个函数做特征,然后将1的位置跳转到我们的代码执行后在跳转回来就可以了
/*循环访问所有人物结构特征
24ADB2C8 - 55 - push ebp
24ADB2C9 - 8B EC - mov ebp,esp
24ADB2CB - 83 EC 08 - sub esp,08 { 00000008 }
24ADB2CE - 8B 45 08 - mov eax,[ebp+08]
24ADB2D1 - 8B 40 08 - mov eax,[eax+08]
24ADB2D4 - 8B 40 64 - mov eax,[eax+64]
24ADB2D7 - 8B 40 10 - mov eax,[eax+10]
24ADB2DA - 85 C0 - test eax,eax*/
char mem_of_callpeople[] = { 0x55,0x8b,0xec,0x83, 0xec, 0x08, 0x8b, 0x45, 0x08, 0x8b, 0x40, 0x08, 0x8b, 0x40, 0x64, 0x8b, 0x40, 0x10, 0x85, 0xc0,0x00 };
void InitPeopleStateData()
{
peopleStateMem = (CHAR*)malloc(0x200);//申请我们的代码
/*
56 push esi//跳转到这里
8B 70 10 mov esi, [eax+10h]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 5D 01 00 00 jle loc_147016D
8B 76 0C mov esi, [esi+0Ch]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 4E 01 00 00 jle loc_147016D
8B 76 0C mov esi, [esi+0Ch]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 3F 01 00 00 jle loc_147016D
8B 76 08 mov esi, [esi+8]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 30 01 00 00 jle loc_147016D
8B 76 08 mov esi, [esi+8]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 21 01 00 00 jle loc_147016D
81 7E 0C 63 00 6F 00 cmp dword ptr [esi+0Ch], 6F0063h//判断人物信息
0F 84 1F 00 00 00 jz loc_1470078
81 7E 0C 74 00 72 00 cmp dword ptr [esi+0Ch], 720074h
0F 84 12 00 00 00 jz loc_1470078
81 7E 0C 50 00 6C 00 cmp dword ptr [esi+0Ch], 6C0050h
0F 84 05 00 00 00 jz loc_1470078
E9 F5 00 00 00 jmp loc_147016D
; ---------------------------------------------------------------------------
loc_1470078:
83 3D 78 01 47 01 00 cmp ds:people_mood, 0//判断开启
0F 84 16 00 00 00 jz loc_147009B
8B 70 10 mov esi, [eax+10h]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E D9 00 00 00 jle loc_147016D
C7 46 14 00 00 80 3F mov dword ptr [esi+14h], 3F800000h//将人物相关结构位置的数据改为浮点1
loc_147009B:
83 3D 7C 01 47 01 00 cmp ds:people_food, 0
0F 84 16 00 00 00 jz loc_14700BE
8B 70 14 mov esi, [eax+14h]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E B6 00 00 00 jle loc_147016D
C7 46 14 00 00 80 3F mov dword ptr [esi+14h], 3F800000h
loc_14700BE:
83 3D 80 01 47 01 00 cmp ds:people_rest, 0
0F 84 16 00 00 00 jz loc_14700E1
8B 70 18 mov esi, [eax+18h]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 93 00 00 00 jle loc_147016D
C7 46 14 00 00 80 3F mov dword ptr [esi+14h], 3F800000h
loc_14700E1:
83 3D 84 01 47 01 00 cmp ds:people_joy, 0
0F 84 16 00 00 00 jz loc_1470104
8B 70 1C mov esi, [eax+1Ch]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 70 00 00 00 jle loc_147016D
C7 46 14 00 00 80 3F mov dword ptr [esi+14h], 3F800000h
loc_1470104:
83 3D 88 01 47 01 00 cmp ds:people_beauty, 0
0F 84 16 00 00 00 jz loc_1470127
8B 70 20 mov esi, [eax+20h]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 4D 00 00 00 jle loc_147016D
C7 46 14 00 00 80 3F mov dword ptr [esi+14h], 3F800000h
loc_1470127:
83 3D 8C 01 47 01 00 cmp ds:people_space, 0
0F 84 16 00 00 00 jz loc_147014A
8B 70 24 mov esi, [eax+24h]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 2A 00 00 00 jle loc_147016D
C7 46 14 00 00 80 3F mov dword ptr [esi+14h], 3F800000h
loc_147014A:
83 3D 90 01 47 01 00 cmp ds:people_comfort, 0
0F 84 16 00 00 00 jz loc_147016D
8B 70 28 mov esi, [eax+28h]
81 FE 00 00 10 00 cmp esi, 100000h
0F 8E 07 00 00 00 jle loc_147016D
C7 46 14 00 00 80 3F mov dword ptr [esi+14h], 3F800000h
loc_147016D:
5E pop esi
8B 40 10 mov eax, [eax+10h]//原来的代码要正确执行
85 C0 test eax, eax
E9 ?? ?? ?? ?? jmp --old address//计算原来的位置跳回去
sub_1470000 endp
; ---------------------------------------------------------------------------
00 00 00 00 people_mood dd 1
00 00 00 00 people_food dd 1
00 00 00 00 people_rest dd 1
00 00 00 00 people_joy dd 1
00 00 00 00 people_beauty dd 0
00 00 00 00 people_space dd 0
01 00 00 00 people_comfort dd 1
00 00 00 00
3F800000代表单浮点数的1
*/
char changePeopleStateByte[] = {
0x56,0x8B,0x70,0x10,0x81,0xFE,0x00,0x00,0x10,0x00,0x0F,0x8E,0x5D,0x01,0x00,0x00,
0x8B,0x76,0x0C,0x81,0xFE,0x00,0x00,0x10,0x00,0x0F,0x8E,0x4E,0x01,0x00,0x00,0x8B,
0x76,0x0C,0x81,0xFE,0x00,0x00,0x10,0x00,0x0F,0x8E,0x3F,0x01,0x00,0x00,0x8B,0x76,
0x08,0x81,0xFE,0x00,0x00,0x10,0x00,0x0F,0x8E,0x30,0x01,0x00,0x00,0x8B,0x76,0x08,
0x81,0xFE,0x00,0x00,0x10,0x00,0x0F,0x8E,0x21,0x01,0x00,0x00,0x81,0x7E,0x0C,0x63,
0x00,0x6F,0x00,0x0F,0x84,0x1F,0x00,0x00,0x00,0x81,0x7E,0x0C,0x74,0x00,0x72,0x00,
0x0F,0x84,0x12,0x00,0x00,0x00,0x81,0x7E,0x0C,0x50,0x00,0x6C,0x00,0x0F,0x84,0x05,
0x00,0x00,0x00,0xE9,0xF5,0x00,0x00,0x00,0x83,0x3D,0x78,0x01,0x47,0x01,0x00,0x0F,
0x84,0x16,0x00,0x00,0x00,0x8B,0x70,0x10,0x81,0xFE,0x00,0x00,0x10,0x00,0x0F,0x8E,
0xD9,0x00,0x00,0x00,0xC7,0x46,0x14,0x00,0x00,0x80,0x3F,0x83,0x3D,0x7C,0x01,0x47,
0x01,0x00,0x0F,0x84,0x16,0x00,0x00,0x00,0x8B,0x70,0x14,0x81,0xFE,0x00,0x00,0x10,
0x00,0x0F,0x8E,0xB6,0x00,0x00,0x00,0xC7,0x46,0x14,0x00,0x00,0x80,0x3F,0x83,0x3D,
0x80,0x01,0x47,0x01,0x00,0x0F,0x84,0x16,0x00,0x00,0x00,0x8B,0x70,0x18,0x81,0xFE,
0x00,0x00,0x10,0x00,0x0F,0x8E,0x93,0x00,0x00,0x00,0xC7,0x46,0x14,0x00,0x00,0x80,
0x3F,0x83,0x3D,0x84,0x01,0x47,0x01,0x00,0x0F,0x84,0x16,0x00,0x00,0x00,0x8B,0x70,
0x1C,0x81,0xFE,0x00,0x00,0x10,0x00,0x0F,0x8E,0x70,0x00,0x00,0x00,0xC7,0x46,0x14,
0x00,0x00,0x80,0x3F,0x83,0x3D,0x88,0x01,0x47,0x01,0x00,0x0F,0x84,0x16,0x00,0x00,
0x00,0x8B,0x70,0x20,0x81,0xFE,0x00,0x00,0x10,0x00,0x0F,0x8E,0x4D,0x00,0x00,0x00,
0xC7,0x46,0x14,0x00,0x00,0x80,0x3F,0x83,0x3D,0x8C,0x01,0x47,0x01,0x00,0x0F,0x84,
0x16,0x00,0x00,0x00,0x8B,0x70,0x28,0x81,0xFE,0x00,0x00,0x10,0x00,0x0F,0x8E,0x2A,
0x00,0x00,0x00,0xC7,0x46,0x14,0x00,0x00,0x80,0x3F,0x83,0x3D,0x90,0x01,0x47,0x01,
0x00,0x0F,0x84,0x16,0x00,0x00,0x00,0x8B,0x70,0x2C,0x81,0xFE,0x00,0x00,0x10,0x00,
0x0F,0x8E,0x07,0x00,0x00,0x00,0xC7,0x46,0x14,0x00,0x00,0x80,0x3F,0x5E,0x8B,0x40,
0x10,0x85,0xC0,0xE9,0x90,0x90,0x90,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
memcpy(peopleStateMem, changePeopleStateByte, 0x200);
DWORD dwOldAttrubet;
VirtualProtect(peopleStateMem,
0x200,
PAGE_EXECUTE_READWRITE,
&dwOldAttrubet);
g_pStateOfPeo = (PStateData)(peopleStateMem + 0x178);
*(DWORD*)(peopleStateMem + 0x7a) = (DWORD)(&g_pStateOfPeo->mood);//用于判断是否开启
*(DWORD*)(peopleStateMem + 0x9d) = (DWORD)(&g_pStateOfPeo->food);
*(DWORD*)(peopleStateMem + 0xc0) = (DWORD)(&g_pStateOfPeo->rest);
*(DWORD*)(peopleStateMem + 0xe3) = (DWORD)(&g_pStateOfPeo->joy);
*(DWORD*)(peopleStateMem + 0x106) = (DWORD)(&g_pStateOfPeo->beauty);
*(DWORD*)(peopleStateMem + 0x129) = (DWORD)(&g_pStateOfPeo->space);
*(DWORD*)(peopleStateMem + 0x14c) = (DWORD)(&g_pStateOfPeo->comfort);
}
将要跳转的地址计算填入特征位置,跳转回去的地址填入位置,计算位置这个看代码吧不好描述。
bool ChangePeopleMem(char* changePeopleMem)
{
HANDLE selfProHandle = GetCurrentProcess();
DWORD dwOffset;
/*
address + 0000 0000 - 55 - push ebp
address + 0000 0001 - 8B EC - mov ebp,esp
address + 0000 0003 - 83 EC 08 - sub esp,08
address + 0000 0006 - 8B 45 08 - mov eax,[ebp+08]
address + 0000 0009 - 8B 40 08 - mov eax,[eax+08]
address + 0000 000C - 8B 40 64 - mov eax,[eax+64]
address + 0000 000F - 8B 40 10 - mov eax,[eax+10] ---------------------------》改到自己的内存 E9 ?? ?? ?? ??
address + 0000 0012 - 85 C0 - test eax,eax
address + 0000 0014 - 75 08 - jne 24D70D26 《----------------------------运行完自己的逻辑在跳转回来
*/
char changePeopleByte[] = { 0xE9,0x90,0x90 ,0x90 ,0x90 };
bool flag_research = true;
int addressOffset = 0;
int offset_addr = 0;
if (EnumAllMemoryBlocks(selfProHandle, vec)) {
for (int i = 0; i < vec.size(); i++) {
// 查找特征
addressOffset = MemoryCmpToFeatureCode(vec[i].BaseAddress, vec[i].RegionSize, mem_of_callpeople);
if (addressOffset != -1)
{
for (int j = 0; j < vecCodeAddr.size(); j++) {
int int_value = (int)vec[i].BaseAddress + (int)vecCodeAddr[j];
g_changePeopleAddress = (PDWORD)(int_value + 0xF);//定位到修改
dwOffset = ((DWORD)changePeopleMem - ((DWORD)g_changePeopleAddress)) - 0x5;//计算位置
*(DWORD*)(changePeopleByte + 0x1) = dwOffset;
memcpy(g_changePeopleAddress, changePeopleByte, 0x5);
g_changePeopleAddress = (PDWORD)(int_value + 0x14);
dwOffset =(DWORD)g_changePeopleAddress - (DWORD)(changePeopleMem + 0x173)-0x5;//跳转回来
*(DWORD*)(changePeopleMem + 0x173 + 0x1) = dwOffset;
return true;
}
}
}
}
else
{
MessageBox(NULL, "memallpeople failed.", "failed", NULL);
return false;
}
}
对应图:
快速研究及建造
研究也是按照同样的方法搜索,不知道类型就搜索所有类型根据改变一点一点确定范围,找到访问的位置
函数特征
/*访问研究数值修改位置的特征
1607BBC5 - 8D 4C B1 10 - lea ecx,[ecx+esi*4+10]
1607BBC9 - D9 19 - fstp dword ptr [ecx]
1607BBCB - 8B 48 30 - mov ecx,[eax+30] -------》跳到我们地址
1607BBCE - 41 - inc ecx
1607BBCF - 89 48 30 - mov [eax+30],ecx
1607BBD2 - 8D 65 F4 - lea esp,[ebp-0C]
1607BBD5 - 5E - pop esi
1607BBD6 - 5F - pop edi
1607BBD7 - 5B - pop ebx
1607BBD8 - C9 - leave
1607BBD9 - C3 - ret
*/
char mem_of_fast_research[] = { 0x8D ,0x4C, 0xB1, 0x10,0xD9, 0x19,0x8B, 0x48 ,0x30,0x41,0x89, 0x48, 0x30,0x8D, 0x65, 0xF4,0x5E,0x5F,0x5B,0xC9,0xC3,0x00 };
但是这个访问的位置不一定是研究,还有其他的数据,我们只修改研究,所以要先判断修改的数据地址是否在数据地址附近,说实话这个方法不好,正常应该找到像人物数据一样的结构存放,我这里偷懒值直接特征码数据获取地址进行判断。
同样跳转到自己的代码
void InitFastResearchData()
{
fastResearchMem = (CHAR*)malloc(0x2A);
/*
0208153B - D9 45 10 - fld dword ptr [ebp+10]
0208153E - 39 71 0C - cmp [ecx+0C],esi
02081541 - 0F86 3F000000 - jbe 02081586
--------跳到下面这
02081547 - 8D 4C B1 10 - lea ecx,[ecx+esi*4+10]
0208154B - D9 19 - fstp dword ptr [ecx]
0040EB19 8B09 - mov ecx,dword ptr ds:[ecx]
0040EB1B 81C1 FFFF0000 - add ecx,0xFFFF
0208154D - 8B 48 30 - mov ecx,[eax+30]
02081550 - 41 - inc ecx
02081551 - 89 48 30 - mov [eax+30],ecx
02081554 - 8D 65 F4 - lea esp,[ebp-0C]
02081557 - 5E - pop esi
02081558 - 5F - pop edi
02081559 - 5B - pop ebx
0208155A - C9 - leave
0208155B - C3 - ret
0040EAEA 81F9 ???????? cmp ecx,0x12345678 大 判断是不是数据的地址不是跳过
0040EAF0 77 10 ja short 0040EB02
0040EAF2 81F9 ???????? cmp ecx,0x12345678 小 判断是不是数据的地址不是跳过
0040EAF8 76 08 jbe short 0040EB02
0040EAFA 8B09 mov ecx,dword ptr ds:[ecx]
0040EAFC 81C1 FFFF0000 add ecx,0xFFFF
0040EB02 90 nop
0040EB03 8B48 30 mov ecx,dword ptr ds:[eax+0x30]
0040EB06 41 inc ecx
0040EB07 8948 30 mov dword ptr ds:[eax+0x30],ecx
0040EB0A 8D65 F4 lea esp,dword ptr ss:[ebp-0xC]
0040EB0D 5E pop esi
0040EB0E 5F pop edi
0040EB0F 5B pop ebx
0040EB10 C9 leave
0040EB11 C3 retn
81F978563412771081F97856341276088B0981C1FFFF0000908B4830418948308D65F45E5F5BC9C3
*/
char changeByte[] = { 0x81,0xF9,0x78,0x56,0x34,0x12,0x77,0x13,0x81,0xF9,0x78,0x56,0x34,0x12,0x76,0x0B,0x50,0x8B,0x01,0x05,0xFF,0xFF,0x00,0x00,0x89,0x01,0x58,0x8B,0x48,0x30,0x41,0x89,0x48,0x30,0x8D,0x65,0xF4,0x5E,0x5F,0x5B,0xC9,0xC3 };
//char changeByte[] = { 0x8D ,0x4C ,0xB1 ,0x10 ,0xD9 ,0x19 ,0x8B,0x09 ,0x81,0xC1 ,0xFF,0xFF,0x00,0x00,0x8B ,0x48 ,0x30 ,0x41 ,0x89 ,0x48 ,0x30,0x8d,0x65,0xf4,0x5e,0x5f,0x5b,0xc9,0xc3 };
memcpy(fastResearchMem, changeByte, 0x2A);
DWORD dwOldAttrubet;
VirtualProtect(fastResearchMem,
0x2A,
PAGE_EXECUTE_READWRITE,
&dwOldAttrubet);
}
最后是快速建造,方法大家研究吧,因为写这个的时候也没怎么做笔记,从新记录下来会丢失很多细节,这些都是再边逆向,边编程解决的,不过大致方法和原理都是一样的。
找到关键函数,直接NOP掉2中的代码就会建造成功。
特征:
/*
seg000:0C4EEE56 D9 43 58 fld dword ptr [ebx+58h]
seg000:0C4EEE59 D9 45 F0 fld [ebp+var_10]
seg000:0C4EEE5C DE C1 faddp st(1), st
seg000:0C4EEE5E D9 5B 58 fstp dword ptr [ebx+58h]
seg000:0C4EEE61 D9 43 58 fld dword ptr [ebx+58h]
seg000:0C4EEE64 D9 45 E0 fld [ebp+var_20]
seg000:0C4EEE67 DF F1 fcomip st, st(1)
seg000:0C4EEE69 DD D8 fstp st
seg000:0C4EEE6B 7A 23 jp short loc_C4EEE90 ---nop
seg000:0C4EEE6D 77 21 ja short loc_C4EEE90 ---nop
seg000:0C4EEE6F 83 EC 08 sub esp, 8
seg000:0C4EEE72 56 push esi
seg000:0C4EEE73 53 push ebx
seg000:0C4EEE74 39 1B cmp [ebx], ebx
*/
char mem_of_fast_bulid[] = { 0xD9, 0x43, 0x58, 0xD9, 0x45, 0xF0, 0xDE, 0xC1, 0xD9, 0x5B, 0x58, 0xD9, 0x43, 0x58, 0xD9, 0x45, 0xE0, 0xDF, 0xF1, 0xDD, 0xD8, 0x7A, 0x23, 0x77, 0x21, 0x83, 0xEC, 0x08, 0x56, 0x53, 0x39, 0x1B ,0x00 };
对应图:
值得注意的点
快速研究游戏中必须要有工作台才能分析,特征中不能含有00,我的是遍历字符串自动算长度,所以我改成了如特征中由00的话,需要传特征长度到函数
//匹配内存中参数特征的位置
long MemoryCmpToFeatureCode(PVOID BaseAddress, long mem_size, PCHAR Strings, int isstrlen = 0)
{
vecCodeAddr.clear();
vecCodeAddr.reserve(200);
int index = 0;
int flag = -1;
int strlens;
//获取特征长度
if (isstrlen == 0)
{
strlens = strlen(Strings);
}
else
{
strlens = isstrlen;
}
for (long j = 0; j < mem_size - strlens; j++)
{
index = 0;
for (int i = 0; i < strlens; i++)
{
char* compare = (char*)BaseAddress;
//判断内存特征与传进来的特征对比
if (compare[j + i] != Strings[i])
{
break;
}
index++;
}
if (index == strlens)
{
vecCodeAddr.push_back(j);
flag = 1;
}
}
return flag;
}
我将遍历到的所有地址保存到了容器中,然后再从容器中循环获取地址,因为遍历的特征可能不止一处。可能还有遗漏,但是暂时想不到了,这个帖子适用新手,当当练手,大神就算了。还可以扩展很多功能,这些也够了。文章并没有十分详细,只有大概过程思路。还有就是游戏一定要载入地图后再注入,因为我没有做是否已经再入地图的判断,和游戏结束的判断。
代码工程在这:https://github.com/Cc28256/GameAnalysisOfRimWorld
环世界游戏:
链接:https://pan.baidu.com/s/1xhlgiYs2HS2Fdzq-7ffnDQ
提取码:h910
CE和dll注入器大家应该都有吧。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工
作,每周日13:00-18:00直播授课
最后于 2020-5-16 16:44
被Cc28256编辑
,原因: