"Decisive Battle" is a very old RPG game. As a popular game in the same period as the legend of blood, it is also deeply loved by the post-7080 generation.
When I played this game more than ten years ago, I also used the assistance of functions such as teleportation through walls, fast casting and instant monsters.
Next, we will use a stand-alone version built by ourselves to review the auxiliary development process of the year, and analyze the protection of these functions from a security perspective.
1. Find the plaintext sending function
First, we analyze the data requirements of the function.
Teleporting through the wall is a mage's patent. Under normal circumstances, the mage's teleportation cannot pass through the wall. In order to realize the function of passing through the wall, we must start with sending a packet, so the primary goal is to find the plaintext sending function.
Then there is fast casting. Since there is this function, it means that this function is also likely to be realized through fast protocol retransmission, so we also start with the plaintext sending function.
xdbg32 add-on game, disconnected under send, and disconnected from walking game
Repeatedly interrupting, taking medicine or teleporting actions, observing the stack and finding that the stack returns are different, indicating that the game does not have a thread to send packets
Execute multiple times to return, find the position near the public CALL and function CALL, and send the function header as the plaintext packet
The above are basic operations for novices, nothing difficult.
2. Analysis of teleportation through the wall
Interrupt and teleport in the skill CALL, let's observe the parameters
The plaintext function has 2 parameters, the first is 0, and the second is a structure
In the structure, +8 is another structure, which is used as a plaintext packet structure, and +C is the packet length, and the rest can be written as fixed values.
Analyze the plaintext packet structure
6D of +0 means skill header, BYTE type
+1 is 2712, it should be role ID, DWORD type
07 of +5 is the teleportation skill ID, which can be observed by comparing with other skills
Then +6 and +A are the X and Y coordinates of the destination, DWORD type
We apply for the memory to build the structure by ourselves, and use the code injector to test, and found that this packet can pass through the wall, but normal games will hit the wall
This shows that there is some code above that checks and corrects this coordinate
We analyze upwards and first find the package function
The parameter of this function is the XY coordinates, and it is corrected
Then let's go after the source of these two coordinates
The corrected X and Y come from ecx+8 and ecx+C respectively, and the final source is the return of the above call 0x004A2C20
We are looking at the parameters of call 0x004A2C20
It is found that the four parameters of this CALL are the current XY and the destination XY respectively, and after this CALL, the destination XY will be corrected to coordinates that cannot penetrate the wall
If we want to manually play through the wall, we can use 4EFA9D under HOOK to pass the coordinates of the destination to the corrected coordinates
If you think the HOOK code is troublesome, you can also use other methods, that is, rewrite the machine code at 004EFA9D to 8B 49 90
Because we found after the break at this position, the structure stored in ecx-70 is exactly the structure of the destination coordinates before correction, and [ecx-70]+8 and +C point to the destination coordinates
In this way we only need to write to memory.
After this processing, we can teleport through the wall. No other data is used here. Next, we analyze the fast casting.
3. Realization of fast casting
We return to the position of the skill CALL, release the skill on a monster, and observe the parameters
The packet structure is very similar to teleportation, where the packet length becomes 6
+0 is also Baotou 0x6D
+1 is monster ID, DWORD type
+5 is 04, representing the skill "Ice Soul", BYTE type
We used a code injector to quickly release this skill, and found that the frequency of skill release has increased significantly. The following is a comparison between normal situations and direct contract delivery
In this plaintext packet, our skill ID can be fixed, but what needs to be passed is just a recent monster ID
So we are going to analyze the data of the character and surrounding objects
4. Analysis of role objects
Usually, we start with the character's blood volume for the character object, but when you scan the character's blood volume, you will find that you scan directly to the base address of a non-game module
Whether we switch equipment or take medicine, the blood volume obtained is the base address. At this time, some people may think that they have found the role object, so they can make do with it.
In fact, this data is not the answer we want, because the blood volume of the object in this game will only be written correctly when it is attacked by melee, and it will remain unchanged or have errors at other times. Not just characters, but monsters too.
So if we want to find the character object, we need to let the monster attack and lose blood before scanning.
Rescanning can result in a non-base address
To access the breakpoint under this result, you can get +58 offset
CE scanning rbx can get two base addresses, the result of one of the base addresses may change, so we choose another
Access 005780C4, you can get 1ACC offset and base address 0x5765F8
HP formula [0x5765F8+1ACC]+58
The role object is done, then analyze the surrounding traversal
5. Surrounding object traversal
The traversal of surrounding objects still starts with the blood volume. Since it was mentioned earlier that long-range attacks cannot change the blood volume of the monster object, then we will find a monster to cut him
Use CE to scan the monster's blood volume
Use xdbg to observe the address and find that the structure of the monster object and the character object is basically the same, that is to say, the offset of the first layer is also +58
Access breakpoints above and below health
Analyze eax upwards to get the source [esp+10], and continue to analyze to get the return from CALL
In this CALL, we can get a binary tree traversal, the process will not be analyzed in detail, the final formula is as follows
[[0x5765F8+1B0C]+4] root node==[0x00590790] end traversal
+0 left subtree
+8 right subtree
+C ID
+10 objects
Subject +58 current blood volume (useless data)
[Object+2C]+0 name ASCII
Object +224 X
Object +228 Y
These data are the most basic and there is no difficulty. Through observation and comparison, it can be obtained that +158 is the death mark. After the monster dies,
6. Classification of Surrounding Objects
The importance of object type is often overlooked, but when you use it, you will find that this data has no way to start.
If we don't judge the type, we will send skill packs to NPCs, buildings, and even ourselves, so this data is indispensable.
The most common starting point is to analyze by interacting with objects. Such as opening NPC, attacking monsters and so on.
We found an NPC and broke through the plaintext sending function to open NPCCALL
back again
Next, find a monster, attack him with the left button, and get a normal attack CALL
After returning again, I found that it was the same as opening the outer layer of the NPC
This shows that normal attack and opening NPC are under the same function, we need to find a jump to distinguish these two functions.
Break off at the head, break off immediately, single-step execution to find the jump from jumping out to returning, then bypass this jump, and break down below to open the NPC action
When stepping into a dec code, it is found that the NPC does not jump when it is turned on, and the attacking monster jumps
Go to CALL to analyze the source of eax
The ecx jumped here determines the returned eax value, so we only need to analyze the source of ecx, analyze upwards, after an inc, it comes from +248 offset
This offset is just under the object, after observation of multiple objects, we came to the following conclusions
The buildings here refer to portals and the like, unknowns are some invisible objects
The classification is solved, and the next step is the death sign of the monster. This data is obtained by observing the properties of the object, because the monster will change about 0.5 seconds after death, so if you search too quickly with CE, this sign may be filtered out
Deathmark's offset is +158
7. Encapsulate data, write logic, and realize functions
After getting the data, we can realize the function of automatically releasing skills to fight monsters.
First of all, we need to read the information of the surrounding objects through the binary tree
void objTree(DWORD node)//遍历周围对象二叉树
{
try
{
DWORD left = (DWORD)(node + 0);
DWORD right = (DWORD)(node + 8);
DWORD ObjID = (DWORD)(node + 0xC);
DWORD dObj = (DWORD)(node + 0x10);
objarr[i].setID(ObjID);
objarr[i].setDeath(*(BYTE*)(dObj + 0x158));
objarr[i].setType(*(DWORD*)(dObj + 0x248));
objarr[i].setCoord(*(DWORD*)(dObj + 0x228), *(DWORD*)(dObj + 0x22C));
char* name = (char*)(*(DWORD*)(dObj + 0x2C));
objarr[i].setName(name);
char* isdeath = "";
if (objarr[i].getDeath())
isdeath = "死亡";
else
isdeath = "活着";
DbgPrintf_Mine("%X ,%s ,%X ,%s ,%d ,%d ,%s", dObj, objarr[i].getName(), objarr[i].getID(), objarr[i].getType(), objarr[i].getCoord().X, objarr[i].getCoord().Y, isdeath);
i++;
if (left != *(DWORD*)Base_End)
{
objTree(left);
}
if (right != *(DWORD*)Base_End)
{
objTree(right);
}
}
catch (...)
{
DbgPrintf_Mine("二叉树读取异常" );
}
}
Then we need to filter out the nearest, alive, and monsters within the range of the skill range from these objects
Object getNearest()//取最近距离的,活着的怪物
{
getObj();
DWORD a = 1000;
DWORD b = 1000;
Object cNest = Object();
for (int j = 0; j < i; j++)
{
b = getdistance(objarr[j]);
if ((a > b) && (objarr[j].getDeath() == 0) && (objarr[j].getTypeid() == 0))
{
a = b;
cNest = objarr[j];
}
}
return cNest;
}
In the code for sending packets, we add range judgment and release skills to monsters with a range of 15 or less
void sendPacket() //发送封包
{
Object obj = getNearest();
if (getdistance(obj) < 15)
{
static char buf[0x500] = { 0 };
static char buf1[0x500] = { 0 };
memset(buf, 0, 0x500);
memset(buf1, 0, 0x500);
packet1* pkt1 = (packet1*)buf;
packet2* pkt2 = (packet2*)buf1;
//组包过程
pkt2->packHead = 0x6D;
pkt2->id = obj.getID();
if (pkt2->id == 0)
return;
pkt2->num = 2;
pkt1->base = 0x517788;
pkt1->a1 = 1;
pkt1->pack2 = pkt2;
pkt1->lenth = 6;
pkt1->a2 = 1;
pkt1->a3 = 0x400;
pkt1->skillId = 0x6A5A;
//内联汇编调用明文发包函数
DWORD call1 = call_mingwen;
DWORD b = (DWORD)&buf;
__asm
{
pushad
mov eax, b
push eax
push 0
mov eax, call1
call eax
popad
}
}
}
Finally, we only need to open a thread to continuously call the skill function
int ii = 0;
void myThread()
{
ii = 0;
while(ii == 0)
{
Sleep(100);
sendPacket();
}
}
void endThread()
{
ii = 1;
}
The above is the whole process of realizing this function, which is very simple, except that the process of data analysis is a little longer, and the logic code is very little
Later we can also add some functions of remote transmission and automatic killing of monsters
8. Confrontation from the perspective of game security
Although the above functions are very violent, it is very simple to detect from the perspective of game security
1. First of all, the part of teleportation through the wall, we can verify it through CRC to ensure that the code will not be rewritten by ordinary people.
2. The second is fast casting. We can add a timestamp to the packet, or check the packet interval on the server. This is actually very easy to handle.
3. If remote transmission is used later, we can also check the transmission distance. Generally speaking, the maximum distance of packet transmission is the longest distance that can be traversed.
The distance that the player can achieve is the distance that can be seen within a screen.
In fact, as long as all functions are strictly verified on the server, it is difficult for similar bugs to appear, and local detection is difficult to play a role in front of the protocol.