Proceso de análisis y protección de seguridad de la magia de lanzamiento rápido a través de las paredes en segundos en una batalla decisiva retro

"Decisive Battle" es un juego de rol muy antiguo. Como un juego popular en el mismo período que la leyenda de la sangre, también es muy querido por la generación posterior a 7080.

Cuando jugué este juego hace más de diez años, también usé la asistencia de funciones como teletransportación a través de paredes, lanzamiento rápido y monstruos instantáneos.

A continuación, utilizaremos una versión independiente construida por nosotros mismos para revisar el proceso de desarrollo auxiliar del año y analizar la protección de estas funciones desde una perspectiva de seguridad.

1. Encuentra la función de envío de texto sin formato

Primero, analizamos los requisitos de datos de la función.

Teletransportarse a través de la pared es una patente de mago. En circunstancias normales, la teletransportación del mago no puede atravesar la pared. Para realizar la función de atravesar la pared, debemos comenzar con el envío de un paquete, por lo que el objetivo principal es encontrar la función de envío de texto sin formato.

Luego está la transmisión rápida. Dado que existe esta función, significa que es probable que esta función también se realice a través de la retransmisión rápida del protocolo, por lo que también comenzamos con la función de envío de texto sin formato.

Juego adicional xdbg32, desconectado al enviar y desconectado del juego para caminar

Interrumpir repetidamente, tomar medicamentos o teletransportarse, observar la pila y descubrir que los retornos de la pila son diferentes, lo que indica que el juego no tiene un hilo para enviar paquetes.

Ejecute varias veces para regresar, encuentre la posición cerca de la LLAMADA pública y la función LLAMADA, y envíe el encabezado de la función como el paquete de texto sin formato

Las anteriores son operaciones básicas para principiantes, nada difícil.

2. Análisis de la teletransportación a través del muro.

Interrumpir y teletransportarse en la habilidad CALL, observemos los parámetros

La función de texto plano tiene 2 parámetros, el primero es 0 y el segundo es una estructura

En la estructura, +8 es otra estructura, que se utiliza como estructura de paquete de texto sin formato, y +C es la longitud del paquete, y el resto se puede escribir como valores fijos.

Analizar la estructura del paquete de texto sin formato

6D de +0 significa encabezado de habilidad, tipo BYTE

+1 es 2712, debe ser ID de rol, tipo DWORD

07 de +5 es la identificación de habilidad de teletransportación, que se puede observar al comparar con otras habilidades

Entonces +6 y +A son las coordenadas X e Y del destino, tipo DWORD

Solicitamos la memoria para construir la estructura por nosotros mismos, y usamos el inyector de código para probar, y descubrimos que este paquete puede atravesar la pared, pero los juegos normales golpearán la pared.

Esto muestra que hay un código arriba que verifica y corrige esta coordenada

Analizamos hacia arriba y primero encontramos la función del paquete.

El parámetro de esta función son las coordenadas XY, y se corrige

Entonces vamos tras la fuente de estas dos coordenadas.

Los X e Y corregidos provienen de ecx+8 y ecx+C respectivamente, y la fuente final es el retorno de la llamada anterior 0x004A2C20

Estamos viendo los parámetros de la llamada 0x004A2C20

Se encuentra que los cuatro parámetros de esta LLAMADA son el XY actual y el destino XY respectivamente, y después de esta LLAMADA, el destino XY será corregido a coordenadas que no pueden penetrar la pared.

Si queremos jugar manualmente a través de la pared, podemos usar 4EFA9D debajo de HOOK para pasar las coordenadas del destino a las coordenadas corregidas

Si cree que el código HOOK es problemático, también puede usar otros métodos, es decir, reescribir el código de máquina en 004EFA9D a 8B 49 90

Debido a que encontramos después de la ruptura en esta posición, la estructura almacenada en ecx-70 es exactamente la estructura de las coordenadas de destino antes de la corrección, y [ecx-70]+8 y +C apuntan a las coordenadas de destino

De esta manera solo necesitamos escribir en la memoria.

Después de este procesamiento, podemos teletransportarnos a través de la pared. No se utilizan otros datos aquí. A continuación, analizamos el lanzamiento rápido.

3. Realización de colada rápida

Volvemos a la posición de la habilidad CALL, liberamos la habilidad en un monstruo y observamos los parámetros.

La estructura del paquete es muy similar a la teletransportación, donde la longitud del paquete se convierte en 6

+0 también es Baotou 0x6D

+1 es ID de monstruo, tipo DWORD

+5 es 04, que representa la habilidad "Ice Soul", tipo BYTE

Utilizamos un inyector de código para liberar rápidamente esta habilidad y descubrimos que la frecuencia de liberación de la habilidad ha aumentado significativamente. La siguiente es una comparación entre situaciones normales y entrega de contrato directo.

En este paquete de texto sin formato, nuestra ID de habilidad se puede arreglar, pero lo que se necesita pasar es solo una ID de monstruo reciente

Entonces vamos a analizar los datos del personaje y los objetos que lo rodean.

4. Análisis de objetos de rol

Por lo general, comenzamos con el volumen de sangre del personaje para el objeto del personaje, pero cuando escaneas el volumen de sangre del personaje, encontrarás que escaneas directamente a la dirección base de un módulo que no es del juego.

Ya sea que cambiemos de equipo o tomemos medicamentos, el volumen de sangre obtenido es la dirección base.En este momento, algunas personas pueden pensar que han encontrado el objeto de rol, por lo que pueden arreglárselas con él.

De hecho, este dato no es la respuesta que queremos, porque el volumen de sangre del objeto en este juego solo se escribirá correctamente cuando sea atacado cuerpo a cuerpo, y permanecerá sin cambios o tendrá errores en otros momentos. No solo personajes, también monstruos.

Entonces, si queremos encontrar el objeto del personaje, debemos dejar que el monstruo ataque y pierda sangre antes de escanear.

Volver a escanear puede resultar en una dirección no base

Para acceder al punto de interrupción bajo este resultado, puede obtener una compensación de +58

El escaneo CE rbx puede obtener dos direcciones base, el resultado de una de las direcciones base puede cambiar, por lo que elegimos otra

Acceda a 005780C4, puede obtener un desplazamiento de 1ACC y una dirección base 0x5765F8

Fórmula HP [0x5765F8+1ACC]+58

El objeto de rol está hecho, luego analice el recorrido circundante

5. Recorrido de objetos circundantes

El recorrido de los objetos circundantes aún comienza con el volumen de sangre. Ya que se mencionó anteriormente que los ataques de largo alcance no pueden cambiar el volumen de sangre del objeto monstruo, entonces encontraremos un monstruo para cortarlo.

Usa CE para escanear el volumen de sangre del monstruo

Use xdbg para observar la dirección y encuentre que la estructura del objeto monstruo y el objeto personaje es básicamente la misma, es decir, el desplazamiento de la primera capa también es +58

Acceda a puntos de interrupción por encima y por debajo de la salud

Analice eax hacia arriba para obtener la fuente [esp+10] y continúe analizando para obtener el retorno de CALL

En este CALL, podemos obtener un recorrido de árbol binario, el proceso no se analizará en detalle, la fórmula final es la siguiente

[[0x5765F8+1B0C]+4] nodo raíz==[0x00590790] fin de recorrido

+0 subárbol izquierdo

+8 subárbol derecho

+C identificador

+10 objetos

Sujeto +58 volumen de sangre actual (datos inútiles)

[Objeto+2C]+0 nombre ASCII

Objeto +224 X

Objeto +228 Y

Estos datos son los más básicos y no hay dificultad. A través de la observación y la comparación, se puede obtener que +158 es la marca de la muerte. Después de que el monstruo muere,

6. Clasificación de los objetos circundantes

La importancia del tipo de objeto a menudo se pasa por alto, pero cuando lo usa, encontrará que estos datos no tienen forma de comenzar.

Si no juzgamos el tipo, enviaremos paquetes de habilidades a NPC, edificios e incluso a nosotros mismos, por lo que este dato es indispensable.

El punto de partida más común es analizar interactuando con objetos. Como abrir NPC, atacar monstruos, etc.

Encontramos un NPC y rompimos la función de envío de texto sin formato para abrir NPCCALL

de nuevo

A continuación, encuentra un monstruo, atácalo con el botón izquierdo y recibe un ataque normal LLAMADA

Después de regresar nuevamente, descubrí que era lo mismo que abrir la capa exterior del NPC

Esto muestra que el ataque normal y el NPC de apertura están bajo la misma función, necesitamos encontrar un salto para distinguir estas dos funciones.

Rompa en la cabeza, interrumpa inmediatamente, ejecución de un solo paso para encontrar el salto desde el salto hasta el regreso, luego omita este salto y rompa a continuación para abrir la acción NPC

Al ingresar a un código de dec, se descubre que el NPC no salta cuando está encendido, y el monstruo atacante salta

Ir a CALL para analizar la fuente de eax

El ecx saltado aquí determina el valor eax devuelto, por lo que solo necesitamos analizar la fuente de ecx, analizar hacia arriba, después de un inc, proviene de una compensación de +248

Este desplazamiento está justo debajo del objeto, después de la observación de múltiples objetos, llegamos a las siguientes conclusiones

Los edificios aquí se refieren a portales y similares, los desconocidos son algunos objetos invisibles.

La clasificación está resuelta, y el siguiente paso es el signo de la muerte del monstruo. Este dato se obtiene al observar las propiedades del objeto, porque el monstruo cambiará aproximadamente 0.5 segundos después de la muerte, por lo que si busca demasiado rápido con CE, este signo puede filtrarse.

La compensación de Deathmark es +158

7. Encapsule datos, escriba lógica y realice funciones

Después de obtener los datos, podemos realizar la función de liberar automáticamente habilidades para luchar contra monstruos.

En primer lugar, necesitamos leer la información de los objetos circundantes a través del árbol binario.

    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("二叉树读取异常" );
     }
}

Luego, debemos filtrar los monstruos más cercanos, vivos y dentro del rango del rango de habilidad de estos objetos.

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;
}

En el código para enviar paquetes, añadimos juicio de rango y habilidades de liberación para monstruos con un rango de 15 o menos.

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
        }
    }
}

Finalmente, solo necesitamos abrir un hilo para llamar continuamente a la función de habilidad

int ii = 0;
void myThread()
{    
        ii = 0;
        while(ii == 0)
        {
        Sleep(100);
        sendPacket();
        }
}

void endThread()
{
        ii = 1;
}

Lo anterior es todo el proceso de realización de esta función, que es muy simple, excepto que el proceso de análisis de datos es un poco más largo y el código lógico es muy pequeño.

Más adelante también podemos agregar algunas funciones de transmisión remota y eliminación automática de monstruos.

8. Confrontación desde la perspectiva de la seguridad del juego

Aunque las funciones anteriores son muy violentas, es muy simple de detectar desde la perspectiva de la seguridad del juego.

1. En primer lugar, la parte de la teletransportación a través de la pared, podemos verificarla a través de CRC para asegurarnos de que la gente común no reescriba el código.

2. La segunda es la transmisión rápida. Podemos agregar una marca de tiempo al paquete o verificar el intervalo del paquete en el servidor. Esto es realmente muy fácil de manejar.

3. Si se usa la transmisión remota más tarde, también podemos verificar la distancia de transmisión.En términos generales, la distancia máxima de transmisión de paquetes es la distancia más larga que se puede recorrer.

La distancia que el jugador puede alcanzar es la distancia que se puede ver dentro de una pantalla.

De hecho, siempre que todas las funciones se verifiquen estrictamente en el servidor, es difícil que aparezcan errores similares, y es difícil que la detección local desempeñe un papel frente al protocolo.

Supongo que te gusta

Origin blog.csdn.net/qq_43355637/article/details/130447589
Recomendado
Clasificación