Analysis process and safety protection of fast-casting magic through walls in seconds in retro decisive battle

"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.

Guess you like

Origin blog.csdn.net/qq_43355637/article/details/130447589