开启日志
0x4068E0
函数是一个空函数,开发阶段应该是输出调试日志的,发布阶段函数体是空的。而它的后续有足够的空间,可以设置一个hook。
004068E0 $ C3 retn
004068E1 90 nop
004068E2 90 nop
004068E3 90 nop
004068E4 90 nop
具体代码如下:
// 打印错误信息
void printLastError(DWORD error, const char* tag = NULL) {
char buffer[1024] = {};
sprintf_s(buffer, sizeof(buffer), "Tag: %s, Error Code: %lu", tag, error);
OutputDebugStringA(buffer);
}
#pragma optimize("", off)
BOOL installHook(LPVOID addr, DWORD newFunc) {
byte buff[5];
DWORD* offset = (DWORD*) & buff[1];
DWORD dwWrittenSize = 0;
DWORD dwOldProtect = 0;
buff[0] = 0xE9; // jmp
*offset = newFunc - (DWORD)addr - 5;
if (VirtualProtect(addr, 5u, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
WriteProcessMemory(GetCurrentProcess(), addr, &buff, 5u, &dwWrittenSize);
if (!VirtualProtect(addr, 5u, dwOldProtect, &dwOldProtect)) {
printLastError(GetLastError(), "Failed 2!");
}
} else {
printLastError(GetLastError(), "Failed 1!");
}
return true;
}
void __cdecl log(const char* Format, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) {
if (!strstr(Format, "Theme")) {
char buff1[1024] = {};
char buff2[1024] = {};
DWORD* pArgs = (DWORD*)((DWORD)&Format - 4); //&p是参数栈的地址,&p-4是返回地址
sprintf_s(buff1, Format, a1, a2, a3, a4, a5, a6, a7, a8, a9);
sprintf_s(buff2, "[Ra2] %08X %s", pArgs[0], buff1);
OutputDebugStringA(buff2);
}
}
byte backupCode[5] = {};
bool TrainerFunctions::OpenLog() {
OutputDebugStringA("[Ra2] OpenLog");
void* oldFunc = (byte*)GetModuleHandleA(0) + 0x68E0; // 0x4068E0 是一个空函数,就一个retn
memcpy(&backupCode, oldFunc, 5u);
return installHook(oldFunc, (DWORD)&log);
}
#pragma optimize("", on)
运行并hook后,输出的日志信息:
[Ra2] OpenLog
[Ra2] 00000000 sendto 1.0.0.0 ; 0
[Ra2] 00000000 Frame 272, Receive packet uncompressed into 0 commmands
[Ra2] 00000000 Theme::AI(Next song = 0)
[Ra2] 00000000 Theme::Stop(0)
[Ra2] 00000000 Theme::PlaySong(0) - Repeating
[Ra2] 00000000 Theme::AI(Next song = 0)
[Ra2] 00000000 Theme::Stop(0)
[Ra2] 00000000 Theme::PlaySong(0) - Repeating
[Ra2] 00000000 Crate at 96,53 contains 'reveal'
[Ra2] 00000000 Crate at 71,96 contains money
[Ra2] 00000000 Crate at 139,153 contains firepower
[Ra2] 00000000 Crate at 124,76 contains a unit
[Ra2] 00000000 Crate at 96,53 contains speed
[Ra2] 00000000 Crate at 84,167 contains veterancy(TM)
[Ra2] 00000000 Response time = 2
[Ra2] 00000000 IPX Manager: RetryDelta = 12
[Ra2] 00000000 MaxAhead is 24
[Ra2] 00000000 Send_Packets - created packet with 1 commands on frame 384
[Ra2] 00000000 NumSendAck = 4
[Ra2] 00000000 Received ACK for packet 4
[Ra2] 00000000 Sending ACK for packet 4 to be forwarded to 00
后续可以结合日志进行分析,比较容易聚焦,提高效率。
通信数据
人机对战不会产生通信数据,人人对战才会有通信数据。
结合日志分析:
sendto 1.0.0.0 ; 0
Send_Packets - added total of 1 commands on frame 384
Frame 384, my sent = 5
Frame 384, Receive packet uncompressed into 1 commmands
Received ACK for packet 4
Sending ACK for packet 4 to be forwarded to 00
Send_Packets - created packet with 1 commands on frame 384
Send_Packets - added total of 1 commands on frame 384
找到:
007B3D4F . 68 D0E48400 push 0084E4D0 ; sendto %d.%d.%d.%d ; %d\n
007B3D54 . E8 872BC5FF call 004068E0
007B3D59 . 8B43 10 mov eax, dword ptr [ebx+10]
007B3D5C . 8B7424 50 mov esi, dword ptr [esp+50]
007B3D60 . 83C4 18 add esp, 18
007B3D63 . 8D5424 20 lea edx, dword ptr [esp+20]
007B3D67 . 83C0 04 add eax, 4
007B3D6A . 8D4B 1C lea ecx, dword ptr [ebx+1C]
007B3D6D . 6A 10 push 10 ; /ToLength = 10 (16.)
007B3D6F . 52 push edx ; |pTo
007B3D70 . 6A 00 push 0 ; |Flags = 0
007B3D72 . 50 push eax ; |DataSize
007B3D73 . 51 push ecx ; |Data
007B3D74 . 56 push esi ; |Socket
007B3D75 . E8 364C0100 call <jmp.&WSOCK32.#20> ; \sendto
地图全开
00481F9D |> \0FBF56 26 movsx edx, word ptr [esi+26]
00481FA1 |. 0FBF46 24 movsx eax, word ptr [esi+24]
00481FA5 |. 52 push edx
00481FA6 |. 50 push eax
00481FA7 |. 68 10D08100 push 0081D010 ; ASCII "Crate at %d,%d contains 'reveal'",LF
00481FAC |. E8 2F49F8FF call 004068E0 ; 空函数
00481FB1 |. 8B4D 08 mov ecx, dword ptr [ebp+8]
00481FB4 |. 83C4 0C add esp, 0C
00481FB7 |. 8B91 1C020000 mov edx, dword ptr [ecx+21C]
00481FBD |. B9 E8F78700 mov ecx, 0087F7E8
00481FC2 |. 52 push edx
00481FC3 |. E8 C85D0F00 call 00577D90 ; 地图全开
2024年1月3日:已验证可行性。
// 全地图内联代码
void Map_Assemble() {
_asm {
pushad
mov eax,0x00A83D4C
mov edx,[eax]
mov ecx,0x0087F7E8
push edx
mov eax,0x00577D90
call eax
popad
}
}
雷达开启
以下汇编仅作过程分析的参考:
00508DF0 /$ C681 79570000>mov byte ptr [ecx+5779], 0
00508DF7 |. A1 4C3DA800 mov eax, dword ptr [A83D4C]
00508DFC |. 83EC 0C sub esp, 0C
00508DFF |. 3BC8 cmp ecx, eax
00508E01 |. 0F85 4D010000 jnz 00508F54
00508E07 |. 8B81 B8020000 mov eax, dword ptr [ecx+2B8]
00508E0D |. 53 push ebx
00508E0E |. 55 push ebp
00508E0F |. 56 push esi
00508E10 |. 8BB1 B0020000 mov esi, dword ptr [ecx+2B0]
00508E16 |. 57 push edi
00508E17 |. 83FE FF cmp esi, -1
00508E1A |. C64424 10 00 mov byte ptr [esp+10], 0
00508E1F |. 74 0E je short 00508E2F
00508E21 |. 8B15 84EDA800 mov edx, dword ptr [A8ED84]
00508E27 |. 2BD6 sub edx, esi
00508E29 |. 3BD0 cmp edx, eax
00508E2B |. 7D 0A jge short 00508E37
00508E2D |. 2BC2 sub eax, edx
00508E2F |> 85C0 test eax, eax
00508E31 |. 0F85 F8000000 jnz 00508F2F
00508E37 |> A1 30B2A800 mov eax, dword ptr [A8B230]
00508E3C |. 8A90 A4340000 mov dl, byte ptr [eax+34A4]
00508E42 |. 84D2 test dl, dl ; 雷达开启判断
00508E44 |. 0F85 E0000000 jnz 00508F2A
2024年1月4日:已验证可行性,一键开启全图和雷达,无须修改指令。
//全地图内联代码
#pragma pack(1)
void Map_Assemble() {
_asm {
pushad
mov eax, 0x00A83D4C
mov edx, [eax]
mov ecx, 0x0087F7E8
push edx
mov eax,0x00577D90
call eax
popad
}
}
// 雷达全开 [[0x00A8B230] + 0x34A4] = 1
void RadarOn_Assemble() {
_asm {
pushad
mov eax, 0x00A8B230
mov eax, [eax]
mov byte ptr [eax + 0x34A4], 0x1
popad
}
}
// 判断游戏是否运行
bool isGameRunning() {
bool isRunning = false;
__try {
_asm {
pushad
mov eax, 0x00A83D4C
mov eax, [eax]
mov eax, 0x0087F7E8
mov eax, [eax]
popad
}
isRunning = true;
} __except (EXCEPTION_EXECUTE_HANDLER) {
isRunning = false;
}
return isRunning;
}
#pragma pack()
调用:
//地图全开
void openAllMap() {
if (isGameRunning()) {
Map_Assemble();
RadarOn_Assemble();
} else {
::Beep(523, 400); // do
}
}
其他:
- 对 00577D90 函数下断点,创建雷达后断下来跟踪。
- 找到地址: 0x00A83D4C 0x0087F7E8 0x00A8B230 0x34A4 之间的关系和来源,最好能从一个地址出发找到其他地址。
科技全开
参考:
[建筑类]
0=GAPOWR;盟军发电厂
1=GAREFN ;盟军矿厂
2=GAPILE ;盟军兵营
3=GAWEAP ;盟军兵工厂
4=GAAIRC ;盟军空军指挥部
5=AMRADR ;美国空军指挥部--AmericanParaDropSpecial
6=GADEPT ;盟军维修厂
7=GAYARD ;盟军船厂
8=GATECH ;盟军实验室
9=GAROBO; 控制中心 ---
10=GAOREP; 矿石精鍊器
11=GAWALL; 盟军围墙
12=GAPILL; 机枪碉堡---Vulcan2
13=NASAM ;爱国者飞弹 --RedEye2
14=GAGAP ;裂缝产生器
15=ATESLA ;光棱塔----PrismShot / PrismSupport
16=GASPYSAT; 间谍卫星
17=GACNST ;盟军建造场
18=GTGCAN ;法国巨炮 GrandCannonWeapon
19=GACSPH ;超时空传送仪 --- ChronoSphereSpecial
20=GAWEAT ;天气控制器 --- LightningStormSpecial
21=GASAND ;沙袋
22=GAGATE_A; 闸门
23=;===苏军======;
24=NAPOWR; 磁能反应炉
25=NAREFN ;苏军矿厂
26=NAHAND; 苏军兵营
27=NAWEAP; 苏军兵工厂
28=NARADR ;苏军雷达 --- SpyPlaneSpecial
29=NADEPT ;苏军维修厂
30=NAYARD ;苏军造船厂
31=NATECH ;苏军实验室
32=NANRCT ;核子反应堆 --- NukePayload
33=NAINDP ;工业工厂
34=NAWALL ;苏军围墙
35=NABNKR ;战斗碉堡
36=NALASR ;哨戒炮--Vulcan
37=NAFLAK ;防空炮--FlakWeapon
38=TESLA ;磁暴线圈 -- CoilBolt / OPCoilBolt
39=NACNST ;苏军建造厂
40=NAIRON ;铁幕 --- IronCurtainSpecial
41=NAMISL ;核弹发射井 -- NukeSpecial
42=NAPSYB ;心灵信标
43=;===尤里======;
44=YAPOWR ;生化反应炉
45=YAREFN ;奴隶矿厂
46=YABRCK ;尤里兵营
47=YAWEAP ;尤里兵工厂
48=NAPSIS ;心灵感应器 --- PsychicRevealSpecial
49=YAYARD ;尤里船厂
50=YAGRND ;部队回收厂
51=YATECH ;尤里实验室
52=GAFWLL ;尤里围墙
53=NATBNK ;坦克碉堡
54=YAGGUN ;盖特机炮
55=YAPSYT ;心灵控制塔----MultipleMindControlTower
56=NACLON ;复制中心
57=YAGNTC ;基因突变器 --- GeneticConverterSpecial
58=YAPPET ;心灵控制器 --- PsychicDominatorSpecial
59=YACNST ;尤里建造场
60=YAROCK ;不明建筑物
61=YACOMD ;尤里指挥中心
62=;===平======;
63=GASAND;沙墙
64=CAAIRP;科技机场---ParaDropSpecial
65=CAOILD;=科技钻油厂
66=CAPARS01;=艾菲尔铁塔
67=CAEAST02;=尤里雕像----PrismShot
68=CATRAN03;=尤里要塞
69=CAEAST01;=复活岛石像
[步兵类代码]
0=E1;美国大兵
1=E2;苏联动员兵
2=SHK;磁爆步兵
3=ENGINEER;盟军工程师
4=JUMPJET;火箭飞行兵
5=GHOST;海豹部队
6=YURI;尤里
7=IVAN;疯狂伊万
8=DESO;生化步兵
9=DOG;苏联军犬
10=CIV1;平民1
11=CIV2;平民2
12=CIV3;平民3
13=CTECH;技师
14=WEEDGUY;防IE挂载(没用)
15=CLEG;超时空兵团
16=SPY;间谍
17=CCOMAND;超时空突击队
18=PTROOP;伞兵
19=CIVAN;超时空伊万
20=YURIPR;尤里改
21=SNIPE;狙击手
22=COW;奶牛
23=ALL ;鳄鱼
24=TANY;谭雅
25=FLAKT;防空步兵
26=TERROR;恐怖分子
27=SENGINEER;苏联工程师
28=ADOG;盟军军犬
29=VLADIMIR;VLADIMIR
30=PENTGEN;PENTGEN
31=PRES;总统
32=SSRV;终级保镖
33=CIVA;德克萨斯平民A
34=CIVB;德克萨斯平民B
35=CIVC;德克萨斯平民C
36=CIVBBP;棒员运动员
37=CIVBFM;海滩肥男
38=CIVBF;海滩女
39=CIVBTM;海滩瘦男
40=CIVSFM;雪中肥男
41=CIVSF;雪中肥女
42=CIVSTM;雪中瘦男
43=POLARB;北极熊
44=JOSH;猴子
45=YENGINEER;尤里工程师
46=GGI;重装大兵
47=INIT;尤里新兵
48=BORIS;鲍裏斯
49=BRUTE;狂兽人
50=VIRUS;病毒狙击手
51=CLNT;快枪手
52=ARND;终结者
53=STLN;蓝波
54=CAML;骆驼
55=EINS;爱因斯坦
56=MUMY;木乃伊
57=RMNV;洛马诺夫总理
58=LUNR;登月火箭兵
59=DNOA;暴龙
60=DNOB;暴龙
61=SLAV;奴隶矿工
62=WWLF;(木乃伊)
63=YDOG;尤里军犬
64=YADOG;尤里军犬
65=CIVFM;海滩肥女
[战车类代码]
0=AMCV;盟军移动基地车
1=HARV;尤里奴隶采矿车
2=APOC;天启坦克
3=HTNK;犀牛坦克
4=SAPC;装甲运输船
5=CAR;汽车
6=BUS;校车
7=WINI;wini
8=PICK;小货车
9=MTNK;灰熊坦克
10=HORV;武装采矿车
11=TRUCKA;货车A
12=TRUCKB;货车B
13=CARRIER;航空母舰
14=V3;V3火箭车
15=ZEP;基洛夫空艇
16=DRON;恐怖机器人
17=HTK;防空履带车
18=DEST;驱逐舰
19=SUB;飓风级战舰
20=AEGIS;宙斯盾战舰
21=LCRF;盟军运输船
22=DRED;无畏级战舰
23=SHAD;夜鹰直升机
24=SQD;乌贼
25=DLPH;海豚
26=SMCV;苏联移动机基车
27=TNKD;坦克杀手
28=HOWI;榴弹炮
29=TTNK;磁爆坦克
30=LTNK;轻坦克
31=CMON;超时空采矿车(不回)
32=CMIN;超时空采矿车
33=SREF;光棱坦克
34=XCOMET;位置标定器
35=HYD;海蝎
36=MGTK;幻影坦克
37=FV;多功能步兵车
38=VLAD;维拉迪摩指挥舰
39=DTRUCK;自爆卡车
40=PROPA;宣传车
41=CONA;挖掘机
42=COP;cop
43=EUROC;欧洲汽车
44=LIMO;豪华轿车
45=STANG;小轿车
46=SUVB;小汽车A
47=SUVW;小汽车B
48=TAXI;出租车
49=PTRUCK;货车C
50=CRUISE;巡游船
51=TUG;拖船
52=CDEST;海岸巡逻船
53=YHVR;尤里气垫船
54=PCV;尤里机动基地车
55=SMIN;尤里奴隶矿厂
56=SMON;超时空采矿车
57=YCAB;黄色计程车
58=YTNK;盖特炮坦克
59=BFRT;战斗要塞
60=TELE;磁电坦克
61=CAOS;神经突袭车
62=DDBX;巴士
63=BCAB;黑色计程车
64=BSUB;雷鸣潜艇
65=SCHP;武装直升机
66=JEEP;卡车
67=MIND;精神控制车
68=DISK;镭射幽浮
69=UTNK;激光坦克
70=ROBO;遥控坦克
71=SCHD;武装直升机
72=DOLY;摄影车
73=CBLC;电车
74=FTRK;救火车
75=AMBU;救护车
76=CIVP;民航机
77=V3V3;V3火箭车
78=TURCKB;货车B
[飞机类代码]
0=APACHE;阿帕奇
1=ORCA;入侵者战机
2=HORNET;大黄蜂
3=V3ROCKET;V3火箭
4=ASW;舰载反潜机
5=DMISL;无畏级导弹
6=PDPLANE;运输机
7=BEAG;黑鹰战机
8=BPLN;米格战机(鲍里斯的飞机)
9=SPYP;侦察机
10=CMISL;雷鸣导弹
void _openTechOne(int objType, int index) {
_asm {
pushad
mov ecx, 0x0087F7E8
push index
push objType
mov eax, 0x006A6300
call eax
popad
}
}
void _openTechAll(int objType, int count) {
_asm {
pushad
xor esi, esi
label:
push esi
push objType // 0x7是建筑;0x10(也可能是0xF)是步兵;0x28是车船;0x1F=武器
mov ecx, 0x0087F7E8
mov eax, 0x006A6300
call eax
inc esi
cmp esi, count // 可以填一个编号上限值,参考上述ini
jle label
popad
}
}
// 科技全开
void OpenTech() {
//_openTechAll(7, 69); // 建筑全开
//_openTechAll(0x10, 30); // 步兵全开
//_openTechAll(0x28, 40); // 坦克全开
_openTechOne(0x28, 0); // 盟军基地车
_openTechOne(0x28, 26); // 苏军基地车
//_openTechOne(0x28, 54); // 尤里基地车
_openTechOne(0x28, 2); // 天启
_openTechOne(0x28, 16); // 恐怖机器人(蜘蛛)
_openTechOne(0x28, 33); // 光棱坦克
_openTechOne(0x28, 36); // 幻影
_openTechOne(0x28, 37); // 多功能
}
或者,将如下指令修改为jmp
:
006AA788 |. /0F85 DA020000 |jnz 006AAA68 ;; 测试有效
或者,用CE使用如下注入代码:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,64)
label(returnhere)
label(originalcode)
label(exit)
newmem: //this is allocated memory, you have read,write,execute access
//place your code here
jmp 004F7B96
originalcode:
mov ecx,[00A8B238]
exit:
jmp returnhere
"{proc}"+F7B6C:
jmp newmem
nop
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"{proc}"+F7B6C:
mov ecx,[00A8B238]
//Alt: db 8B 0D 38 B2 A8 00
心灵探测
判断是否有选中单位(选中的单位数量大于零),然后判断单位是否是建筑物类型(通过对象虚函数表),然后对单位的某个属性字段做修改(具体看代码),心灵探测的字段为一个word类型,因此最大的有符号正数是0x7FFF
。使用时,要选中一下建筑。
// 开启心灵探测
void OpenPsychicDetection() {
_asm {
pushad
mov eax, 0x00A8ECC8
mov eax, [eax] //选中单位的数量
cmp eax, 0 //是否有选中单位
je exit1
mov eax, 0x00A8ECBC
mov eax, [eax]
mov eax, [eax] // +0
mov eax, [eax] // +0
cmp eax, 0x007E3EBC // Building vt
jnz exit1
mov eax, 0x00A8ECBC
mov eax, [eax]
mov eax, [eax] // +0
mov eax, [eax + 0x520] // +0x520
mov word ptr[eax + 0x170C], 0x7FFF // 0x170C PsychicDetectionRadius
exit1:
popad
}
}
无限信标
联网对战的时候最多只能放置三个信标,当超过三个时需要把多余的删除掉才能放置新的信标,这样就会造成一个麻烦:战况激烈的时候,想放置信标的时候放置不了,还需要在地图上删除一个信标才能继续放置。
解决思路:RA2YurisRevengeTrainer/issues/19
00430CFF |. 6A 00 push 0
00430D01 |. 68 E1000000 push 0E1
00430D06 |. 68 46400000 push 4046
00430D0B |. 52 push edx
00430D0C |. 68 2B030000 push 32B
00430D11 |. 68 D88A8100 push 00818AD8 ; ASCII "D:\ra2mdpost\Beacon.CPP"
00430D16 |. 33D2 xor edx, edx
00430D18 |. B9 C48A8100 mov ecx, 00818AC4 ; ASCII "TXT_BEACON_HELP1"
00430D1D |. E8 3E413000 call 00734E60
00430D22 |. 50 push eax
00430D23 |. 6A 00 push 0
00430D25 |. 6A 00 push 0
00430D27 |. B9 60BCA800 mov ecx, 00A8BC60
00430D2C |. E8 6F2E1A00 call 005D3BA0
00430D31 |. A1 4C3DA800 mov eax, dword ptr [A83D4C]
00430D36 |. 6A 00 push 0
00430D38 |. 68 E1000000 push 0E1
00430D3D |. 68 46400000 push 4046
00430D42 |. 8B88 54600100 mov ecx, dword ptr [eax+16054]
00430D48 |. 33D2 xor edx, edx
00430D4A |. 51 push ecx
00430D4B |. 68 2C030000 push 32C
00430D50 |. 68 D88A8100 push 00818AD8 ; ASCII "D:\ra2mdpost\Beacon.CPP"
00430D55 |. B9 B08A8100 mov ecx, 00818AB0 ; ASCII "TXT_BEACON_HELP2"
00430D5A |. E8 01413000 call 00734E60
00430D5F |. 50 push eax
00430D60 |. 6A 00 push 0
00430D62 |. 6A 00 push 0
00430D64 |. B9 60BCA800 mov ecx, 00A8BC60
00430D69 |. E8 322E1A00 call 005D3BA0
00430D6E |. 6A FF push -1
00430D70 |. 83CA FF or edx, FFFFFFFF
00430D73 |. B9 9C8A8100 mov ecx, 00818A9C ; ASCII "EVA_BeaconPlaced"
00430D78 |. E8 83193200 call 00752700
00430D7D |. A1 E0718800 mov eax, dword ptr [8871E0]
00430D82 |. 6A 00 push 0
00430D84 |. BA 00200000 mov edx, 2000
00430D89 |. 68 0000803F push 3F800000
00430D8E |. 8B88 CC010000 mov ecx, dword ptr [eax+1CC]
00430D94 |. E8 87FB3100 call 00750920
00430D99 |. 8B0D 78DAA800 mov ecx, dword ptr [A8DA78]
00430D9F |. C74424 18 200>mov dword ptr [esp+18], 20
00430DA7 |. 8B09 mov ecx, dword ptr [ecx]
00430DA9 |. E8 12433000 call 007350C0
00430DAE |. 8BF8 mov edi, eax
00430DB0 |. 83C9 FF or ecx, FFFFFFFF
00430DB3 |. 33C0 xor eax, eax
00430DB5 |. 8D5424 1C lea edx, dword ptr [esp+1C]
00430DB9 |. F2:AE repne scas byte ptr es:[edi]
00430DBB |. F7D1 not ecx
00430DBD |. 2BF9 sub edi, ecx
00430DBF |. 8BC1 mov eax, ecx
00430DC1 |. 8BF7 mov esi, edi
00430DC3 |. 8BFA mov edi, edx
00430DC5 |. 8B9424 F00100>mov edx, dword ptr [esp+1F0]
00430DCC |. C1E9 02 shr ecx, 2
00430DCF |. F3:A5 rep movs dword ptr es:[edi], dword p>
00430DD1 |. 8BC8 mov ecx, eax
00430DD3 |. 8A8424 F40100>mov al, byte ptr [esp+1F4]
00430DDA |. 83E1 03 and ecx, 3
00430DDD |. F3:A4 rep movs byte ptr es:[edi], byte ptr>
00430DDF |. 8B8C24 EC0100>mov ecx, dword ptr [esp+1EC]
00430DE6 |. 884424 53 mov byte ptr [esp+53], al
00430DEA |. A1 84DAA800 mov eax, dword ptr [A8DA84]
00430DEF |. BE 01000000 mov esi, 1
00430DF4 |. 3BC6 cmp eax, esi
00430DF6 |. 896C24 47 mov dword ptr [esp+47], ebp
00430DFA |. 894C24 4B mov dword ptr [esp+4B], ecx
00430DFE |. 895424 4F mov dword ptr [esp+4F], edx
00430E02 |. 885C24 54 mov byte ptr [esp+54], bl
00430E06 |. 0F8E 14010000 jle 00430F20
00430E0C |> 8B0D 78DAA800 /mov ecx, dword ptr [A8DA78]
00430E12 |. 8B14B1 |mov edx, dword ptr [ecx+esi*4]
00430E15 |. 52 |push edx
00430E16 |. 68 7C8A8100 |push 00818A7C ; sending beacon placement to %s\n
00430E1B |. E8 C05AFDFF |call 004068E0
如意宝箱
参考:
分析思路:
OD导入,分析字符串,相关联的字符串释义如下:
veterancy # 老兵 veteran # 老兵 base healing # 加血
找到如下的信息:
00481EA3 push 0081D058 crate at %d,%d contains tiberium\n 00481F77 push 0081D034 crate at %d,%d contains 'shroud'\n 00481FA7 push 0081D010 crate at %d,%d contains 'reveal'\n 0048204B push 0081CFF0 crate at %d,%d contains a unit\n 0048246F push 0081CFD0 crate at %d,%d contains money\n 0048256F push 0081CFAC crate at %d,%d contains explosives\n 00482728 push 0081CF8C crate at %d,%d contains napalm\n 0048284A push 0081CF60 crate at %d,%d contains cloaking device\n 0048297C push 0081CF38 crate at %d,%d contains veterancy(tm)\n 00482B99 push 0081CF10 crate at %d,%d contains base healing\n 00482CAB push 0081CEF0 crate at %d,%d contains icbm\n 00482D60 push 0081CED0 crate at %d,%d contains armor\n 00482EB6 mov ecx, 0081CEB8 eva_unitarmorupgraded 00482F40 push 0081CE98 crate at %d,%d contains speed\n 004830A5 mov ecx, 0081CE80 eva_unitspeedupgraded 0048312F push 0081CE5C crate at %d,%d contains firepower\n
以
veterancy
为例,定位到相关汇编代码:00482972 |> \0FBF46 26 movsx eax, word ptr [esi+26] 00482976 |. 0FBF4E 24 movsx ecx, word ptr [esi+24] 0048297A |. 50 push eax 0048297B |. 51 push ecx 0048297C |. 68 38CF8100 push 0081CF38 ; crate at %d,%d contains veterancy(tm)\n 00482981 |. E8 5A3FF8FF call 004068E0
00482972
表明这个从其他地方跳转过来的,找到跳转源头:00481DE0 |. FF2495 C43348>jmp dword ptr [edx*4+4833C4]
分析地址
4833C4
,是一个跳转表:004833C4 00482463 ; crate at %d,%d contains money\n 004833C8 00482041 ; crate at %d,%d contains a unit\n 004833CC 00482B8F ; crate at %d,%d contains base healing\n 004833D0 00482840 ; crate at %d,%d contains cloaking device\n 004833D4 00482565 ; crate at %d,%d contains explosives\n 004833D8 0048271E ; crate at %d,%d contains napalm\n 004833DC 004832F5 ; ??? 004833E0 00481F6D ; crate at %d,%d contains 'shroud'\n 004833E4 00481F9D ; crate at %d,%d contains 'reveal'\n 004833E8 00482D56 ; crate at %d,%d contains armor\n 004833EC 00482F36 ; crate at %d,%d contains speed\n 004833F0 00483125 ; crate at %d,%d contains firepower\n 004833F4 00482CA1 ; crate at %d,%d contains icbm\n 004833F8 004832F5 ; ??? 004833FC 00482972 ; crate at %d,%d contains veterancy(tm)\n 00483400 004832F5 ; ??? 00483404 00481DE7 ; crate at %d,%d contains poison gas\n 00483408 00481E99 ; crate at %d,%d contains tiberium\n 0048340C 90909090 ; nop
一共有
0x12
个地址((0048340C - 004833C4) / 4
),逐一在反汇编窗口中跟随查看地址,并对应一下功能。
实现思路:
对地址
4833C4
起始的0x12
个四字节内容统一修改为想要的功能地址表。例如想要金钱效果,当按下热键时全部修改为0x00482463
,然后再去捡箱子,则无论是什么箱子都会执行的是捡到金钱的效果。类推,可以设计一个主要的热键功能:
金钱 00482463 加血 00482B8F 经验 00482972 防御 00482D56 火力 00483125 速度 00482F36
实现代码:
// 所有捡箱子效果:金钱
void TrainerFunctions::SetBoxAllMoney() {
const LPVOID MethodTableAddr = (LPVOID)0x004833C4;
const size_t MethodTableCount = 0x12;
const SIZE_T MethodTableSize = MethodTableCount * sizeof(DWORD);
const DWORD JumpToAddr = 0x00482463; // 捡到的是金钱的跳转地址
// 修改代码保护属性
DWORD dwOldProtect = 0;
if (VirtualProtect(MethodTableAddr, MethodTableSize, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
DWORD* p = (DWORD*)MethodTableAddr;
for (size_t i = 0; i < MethodTableCount; i++) {
*(p + i) = JumpToAddr;
}
// 恢复代码保护属性
if (!VirtualProtect(MethodTableAddr, MethodTableSize, dwOldProtect, &dwOldProtect)) {
printLastError(GetLastError(), "Failed 2!");
}
} else {
printLastError(GetLastError(), "Failed 1!");
}
}
验证效果:
单机玩有效果,但是联网对战不行,会卡,可能是触发了什么机制。
如何使用
编译项目 Ra2Tool ,使用任意注入工具把 Ra2Dll.dll
模块注入到游戏进程即可(RATrainer 用不到)。
如果使用HookLoader工具,可以参考如下使用步骤:
【使用说明】 1、运行exe 2、进入游戏之后按下快捷键【ALT + H】即可,一定要进入游戏,保险起见是在出现基地之后,其他场景没测试过。
【作者有话】 全图功能我认为对级别低的玩家比较有帮助,对级别高的玩家帮助没有那么大。其他影响游戏平衡的变态功能全部删除了,只保留了全图这个功能,不然就没有可玩性了。
内存地址
- 0x00A83D4C :玩家。
[0x00A83D4C] + 0x30
是玩家ID。 - 0x00A8022C :
- 0x00A8ECBC : 选中的单位有关。
[0x00A8ECBC]
是选中单位的数组地址。如果要遍历选择的单位,则结合个数,每次取一个四字节,就是具体单位对象的地址。 - 0x00A8ECC8 : 选中单位的数量,也即:
0x00A8ECBC + 0x0C
。 - 0x6C : 单位血量。
- 0x150 :等级。无星是0,三星是:0x40000000。
- 0x21C :单位归属。
- 0x584 : 疑似速度。
- 0x007E3EBC:建筑物的虚函数表地址,可以通过虚函数表是否是该值来判断对象是否是建筑物。
单机功能
可以参考:AdjWang/RA2YurisRevengeTrainer: 红色警戒2 尤里的复仇v1.001 内存修改器 ,主要是单机版可用的功能,联网对战中很多功能不可用,主要是借鉴参考。
游戏速度:
0x00A8EB60
地址的内容是速度(4字节)。金钱:
[0x00A83D4C] + 0x30C
地址的内容是金钱(4字节)。快速建造:
[0x00A83D4C] + 0x5378
地址起始的连续 5 个四字节内容是速度,修改为15可以实现快速建造。删除选中的单位:
__asm { pushad mov eax,0x00A8ECC8 mov eax,[eax] //选中单位数量 cmp eax,0 //是否选中单位 je exit1 mov eax,0x00A8ECBC mov eax,[eax] mov ecx,[eax] mov eax,[ecx] add eax,0xF8 mov ebx,[eax] call ebx exit1: popad }
选中单位归属为我:
__asm { pushad mov eax,0x00A8ECC8 //选中单位数量 mov eax,[eax] cmp eax,0 //是否选中单位 je exit1 push 0 // mov ebx,0x00A83D4C mov eax,[ebx] push eax mov ebx,0x00A8ECBC mov eax,[ebx] mov ecx,[eax] mov ebx,[ecx] add ebx,0x3D4 mov ebx,[ebx] call ebx exit1: popad }
选中单位升级:
__asm { pushad mov eax,0x00A8ECBC mov ebx,[eax] //选中单位首地址 mov eax,0 process: mov ecx,0x00A8ECC8 cmp eax,[ecx] //选中单位数量为零判断 jge brek //>=跳转 mov ecx,[ebx+eax*4] //当前单位地址 mov edx,0x40000000 //三星的数值 add ecx,0x150 mov [ecx],edx //修改单位经验 add eax,1 jmp process brek: popad }
高级玩法
- 使用 Phobos 作为SDK。例如坦克单位的上述内存地址偏移,可以通过头文件
FootClass
及其父类层层查看对应偏移的字段说明,能对应的上 ,其他属性可以通过源码来判断其偏移地址。在编写工具的时候,开发阶段可以直接使用这些头文件,获取到的地址关联到对应的对象,就可以直接使用了,不需要自己计算地址的硬编码了,很是方便。 - 使用 CnCNet 搭建联网对战平台。这个有兴趣的可以玩玩,也可以参考其源码学习如何打补丁的。
参考
- 红警2共和国之辉开启调试功能
- RA2YurisRevengeTrainer 红色警戒2 尤里的复仇v1.001 内存修改器
- Phobos-developers/Phobos: Ares-compatible C&C Red Alert 2: Yuri’s Revenge engine extension
- CnCNet
- Ra2Tool
文档信息
- 本文作者:zhupite
- 本文链接:https://zhupite.com/sec/red-alert2.html
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)