RA2红色警戒红警2辅助

2024/01/02 sec 共 18077 字,约 52 分钟

开启日志

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

如意宝箱

参考:

分析思路:

  1. 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]
    
  2. 分析地址 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),逐一在反汇编窗口中跟随查看地址,并对应一下功能。

实现思路:

  1. 对地址4833C4起始的0x12个四字节内容统一修改为想要的功能地址表。例如想要金钱效果,当按下热键时全部修改为0x00482463,然后再去捡箱子,则无论是什么箱子都会执行的是捡到金钱的效果。

  2. 类推,可以设计一个主要的热键功能:

    金钱	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!");
	}
}

验证效果:

  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
    }
    

高级玩法

  1. 使用 Phobos 作为SDK。例如坦克单位的上述内存地址偏移,可以通过头文件 FootClass 及其父类层层查看对应偏移的字段说明,能对应的上 ,其他属性可以通过源码来判断其偏移地址。在编写工具的时候,开发阶段可以直接使用这些头文件,获取到的地址关联到对应的对象,就可以直接使用了,不需要自己计算地址的硬编码了,很是方便。
  2. 使用 CnCNet 搭建联网对战平台。这个有兴趣的可以玩玩,也可以参考其源码学习如何打补丁的。

参考

文档信息

Search

    Table of Contents