Análisis de llamadas Aardio-API

Nota: El siguiente contenido es solo mi entendimiento actual, no se garantiza que sea correcto y puede actualizarse en cualquier momento.

Dé la bienvenida a todos los entusiastas del aardio para que se comuniquen, estudien juntos el aardio, sepan qué es lo correcto y corrijan los errores, y progresen juntos.

Que el aardio se fortalezca.

Resolución de llamadas a API

1. Parámetros de tipo de datos estáticos

1. Los tipos de datos estáticos (número, puntero, etc.) son valores pasados ​​durante las llamadas a la API, ya sean declarados o no, y solo se pueden usar como parámetros de entrada.

2. Si desea pasar un puntero (dirección) como un "parámetro de salida", debe utilizar una estructura en su lugar.

3. Use la tabla para definir la estructura de la estructura y defina el tipo estático en la estructura.

4. A todos los miembros de la estructura se les deben asignar valores iniciales (para verificarlos, vea la pequeña prueba a continuación).

Como:

Puntero numérico: {int abc} o {int abc = 123}.

Puntero a matriz numérica: {int data [4]}.

Puntero a matriz de bytes: {byte data [4]} o raw.buffer () 

// 通常,我们使用class来定义API函数中需要用到的结构体:

class POINT{
int x = 0; // 结构体成员必须设定初始值
int y = 0; 
}

// 创建一个结构体对象
pt = POINT();

// 每个结构体会创建一个_struct只读字段记录类型定义,语义如下:
pt = { _struct = "int x;int y" }

Prueba pequeña: a todos los miembros de la estructura se les deben asignar valores iniciales

Conclusión de la prueba: incluso si no se da un valor inicial a los miembros de la estructura, todo está bien.

Creo que el tipo de datos de longitud fija como int no importa si no hay un valor inicial.

Si es una matriz, la longitud de la estructura debe determinarse de antemano.

 

Código dll de idioma fácil:

 

 

código de llamada aardio:

 

 

No se da ningún valor inicial a la estructura, el valor del tipo int predeterminado es 0


 

Después de pasar a la api, el valor de la variable también se puede modificar normalmente.

 

 

2. Parámetros de tipo de datos dinámicos

      1. El tipo dinámico (cadena, búfer) en sí mismo es un puntero, por lo que ya no se puede pasar la dirección.

            En otras palabras, en la superficie, solo se pueden usar como parámetros de entrada, no como parámetros de salida.

            Sin embargo, a pesar de esto, debido a que es un puntero en sí mismo, su valor también se puede modificar en la api.

      2. Cuando struct declara API, se puede usar como parámetro de entrada o salida, cuando no se declara API, es obligatorio como parámetro de salida.

            Cuando se utiliza como parámetro de entrada, el valor se pasa (pasando por valor) y no se devuelve como un valor de retorno de la API.

            Cuando se utiliza como parámetro de salida, se pasa un puntero (dirección de paso) y se devuelve como valor de retorno de la API.

3. Llame directamente a la API sin declaración:

1. Parámetros:

1. Llame directamente a la API sin declararla y podrá cambiar de forma flexible el tipo de parámetro según sea necesario, lo que es más conveniente y ahorra recursos.

2. La convención de llamada se especifica en los parámetros de carga de DLL y se admite el número variable de parámetros cdecl.

3. El parámetro nulo no se puede omitir.

4. Todos los parámetros numéricos se tratan como enteros int de 32 bits. (Los valores enteros, los tipos enumerados, los valores bool de 8 o 32 bits inferiores a 32 bits son compatibles con los valores de int de 32 bits)

5. Un entero de 64 bits se puede representar mediante un objeto math.size64. (O use dos parámetros numéricos para representar un parámetro de valor entero de 64 bits, donde el primer parámetro representa el valor bajo de 32 bits y el segundo parámetro representa el valor alto de 32 bits)

6. Los punteros numéricos (parámetros de salida) siempre se representan mediante estructuras.

7. Puntero de matriz, use puntero de estructura en su lugar, como {int data [4]}.

8. La estructura siempre se procesa como un parámetro de salida y se devuelve en el valor de retorno aardio.

9. Otros tipos, excepto la estructura, solo se pueden utilizar como parámetros de entrada.

0. Tenga en cuenta que en aardio, cualquier estructura pasada en la llamada API es el puntero de estructura (dirección de paso) (para ser verificado más a fondo, porque en el siguiente código de prueba de estructura numérico simple, cuando se realiza la prueba de no aprobado, la API No se pudo cambiar el valor de la variable real.

      El significado aquí es muy probable: cualquier estructura que se pasa en la llamada directa a la API es un puntero de estructura ).

2. Valor de retorno:

1. Si se llama directamente a la API, el valor de retorno predeterminado es int type.

2. Puede utilizar [API tail] para cambiar el valor de retorno a otros tipos.

3. La función API no declarada es solo un objeto de función aardio ordinario y no se puede pasar al parámetro API como un parámetro de puntero de función (el objeto de función API declarado está bien).

4. La estructura siempre se utiliza como parámetro de salida y se devuelve después del valor de retorno de la API.

3. Cola de API:

1. Al llamar a la API directamente sin una declaración, si el final del nombre de la función de la API no es un carácter en mayúsculas, puede utilizar un carácter en mayúscula específico (este es el final de la API) para modificar la regla de llamada de API predeterminada.

2. Agregar una cola después del nombre de la función API no afectará el resultado de la búsqueda de la función API. Aardio puede encontrar la función real independientemente de la API real con o sin la cola especificada.

3. Todos los [Sufijo API] y las reglas representativas disponibles son las siguientes:

  • dll.ApiName W () Cambiar a la versión Unicode, cadena conversión bidireccional UTF8-UTF16
  • dll.ApiName A () Cambie a la versión ANSI, la cadena no se convertirá
  • dll.ApiName L () El valor de retorno es un tipo LONG de 64 bits
  • dll.ApiName P () El valor de retorno es el tipo de puntero
  • dll.ApiName D () El valor de retorno es un número de coma flotante doble
  • dll.ApiName F () El valor de retorno es un número de coma flotante flotante
  • dll.ApiName B () El valor de retorno es de tipo byte (bool de 8 bits en C ++)

4. Para las funciones API que ya tienen una cola W, se pueden usar otras colas al llamar, y la versión Unicode de la función API aún se puede detectar y cambiar correctamente.

Cuarto, usa cadenas

1. Las matrices de bytes de tipo cadena y búfer se utilizan generalmente como punteros de cadena.

2. Si la API necesita escribir datos en la memoria a la que apunta la cadena, la función raw.buffer () debe usarse para crear una matriz de bytes de longitud fija.

3. La memoria apuntada por la cadena de aardio ordinaria está prohibida de escribir ( esta declaración es solo para operaciones regulares de aardio, mire el siguiente código de prueba de API, puede modificar con éxito el valor de la cadena) , modifique los caracteres ordinarios en aardio String devolverá un nuevo objeto de cadena en lugar de modificar los datos en la memoria original.

4. Para la versión Ansi de la API, la cadena se ingresa directamente a los datos originales (el texto está codificado en UTF8 de forma predeterminada).

      Para la versión Unicode de la API, las cadenas de cadenas se verán obligadas a convertirse a Unicode (UTF16).

      Los parámetros de tipo de búfer siempre usan datos sin procesar para interactuar con la API en modo binario (sin conversión de codificación de texto).

Cinco, Ansi y Unicode

1. Al cargar DLL en raw.loadDll (), puede agregar ", unicode" a la convención de llamada para que utilice la API Unicode de forma predeterminada.

2. Puede agregar la cola "A" o "W" después del nombre de la función para declarar la versión Ansi o Unicode de la API.

      Si tanto la versión final como la versión acordada con loadDll se especifican al mismo tiempo, prevalecerá la versión final.

      Cuando aardio no puede encontrar la función API de esta versión, eliminará la cola, pero aún codificará la cadena de acuerdo con la versión de cola especificada.

4. Cuando algunas API reciben parámetros como cadenas y matrices de bytes, normalmente el siguiente parámetro necesita especificar la longitud de la memoria. Cuando se usa el operador # en aardio para obtener la longitud de la cadena y el búfer, se devuelven todos los bytes. La API puede requerir que ingrese la cantidad de caracteres. Si es una versión Unicode de la API, un carácter son dos bytes. Para una cadena UTF8, debe usar la función string.len () para obtener la longitud real de los caracteres, mientras que la cadena Unicode usa # Obtenga la longitud del byte y multiplíquela por 2.

Prueba de estructura numérica simple

Código dll escrito en un lenguaje fácil:

código de llamada aardio:

1. Cuando se define como parámetros de entrada [No pasar dirección]:

// 结构体未定义为输出参数:

import console; 

testdll = raw.loadDll("C:\Users\Administrator\Desktop\test.dll")
test=testdll.api("test","int(struct)","stdcall")

var i = {int abc=123}

var a,b = test (i)
console.dump("a:",a)
console.dump("b:",b)
console.dump("i:",i)

console.pause(true);

Resultados de la

 

Resultado de ejecución: el valor pasado es normal, la modificación de los datos de la estructura falla, el valor de retorno es normal, la estructura no se devuelve como valor de retorno;

2. Cuando se define como un parámetro de salida [Dirección]:

import console; 

testdll = raw.loadDll("C:\Users\Administrator\Desktop\test.dll")
test=testdll.api("test","int(struct&)","stdcall")

var i = {int abc=123}

var a,b = test (i)
console.dump("a:",a)
console.dump("b:",b)
console.dump("i:",i)

console.pause(true);

Resultados de:

Resultado de la ejecución: el valor de la API entrante es normal; los datos de la variable de estructura modificada en la API son normales; el valor de retorno es normal; la estructura se devuelve correctamente como valor de retorno;

3. Al llamar a funciones de API directamente:

import console; 

testdll = raw.loadDll("C:\Users\Administrator\Desktop\test.dll")

var i = {int abc=123}
var a,b = testdll.test(i)

console.dump("a:",a)
console.dump("b:",b)
console.dump("i:",i)

console.pause(true);

El resultado es el mismo que el anterior. Porque la estructura se ve obligada a pasar la dirección como parámetro de salida.

 Prueba de dirección de cadena simple

1. El código dll escrito en un lenguaje sencillo:

2. Ejemplo de llamada Aardio (transferencia de dirección):

import console; 

testdll = raw.loadDll("C:\Users\Administrator\Desktop\test.dll")
test=testdll.api("test","string(string&)","stdcall")

var i =..string.fromto("张三丰")

var a,b = test(i)

console.dump("a:",a)
console.dump("b:",b)
console.dump("i:",i)

console.pause(true);

3. Resultados de la implementación:

Resultado de la ejecución: el texto de la API entrante es normal; la modificación de los datos de la variable de texto en la API falla; el valor de retorno es normal; los datos modificados del parámetro saliente se devuelven como valor de retorno.

4. Ejemplo de llamada Aardio (sin dirección):

Resultado de la ejecución: el texto de la API entrante es normal; la modificación de los datos de la variable de texto en la API es exitosa; el valor de retorno es normal; no hay valor de retorno adicional.

Mi audaz conjetura

1. Puede ser con el propósito de proteger la seguridad de los datos de la memoria u otros propósitos. Aardio protege todos los tipos de datos, excepto las estructuras, y prohíbe el direccionamiento para evitar que sus datos sean manipulados ilegalmente.

2. Cuando estos tipos de datos son forzados a direccionarse, aardio solicitará variables temporales consistentes con sus datos, pasará la dirección a api para su ejecución y obtendrá el resultado de ejecución de la api y los datos de la variable temporal modificada, y volverá a aardio.

3. Los llamados "solo lectura" y "no modificable" son solo términos relativos, porque no puede proporcionar una forma precisa y eficaz de obtener la dirección de una variable como var i = 123, por lo que cree que no se puede modificar.

4. Para verificar esta conjetura, haga una prueba lstrcpyn:

Se puede ver en los resultados de la prueba que la API modificó y devolvió con éxito el valor de la variable numérica pasada, pero de hecho el valor de la variable real no ha cambiado. Debido a que la api cambia el valor de la variable temporal, aardio también toma el valor de la variable temporal.

5. Pase la prueba lstrcpyn y la prueba de direccionamiento de texto anterior, y los resultados inicialmente cumplen con la conjetura anterior.

 

Prueba en profundidad de direccionamiento de texto y no direccionamiento:

También escriba un dll en un lenguaje fácil, función:

1. Muestra el valor de la variable de texto pasada

2. Muestra la dirección de la variable de texto pasada

3. Modificar los datos de memoria de la variable de texto pasada

4. Devuelve un valor de texto

1. Utilice dos variables de cadena con [el mismo contenido] para probar [sin dirección].

Paso 1: Pase la primera variable t1, la API indica: El contenido es "Zhang Sanfeng" y la dirección de la variable es 36655216

Paso 2: Modifica el contenido de la variable t1 en la api a "Estoy bien", la consola en aardio también lo muestra simultáneamente, el valor de retorno es "jajaja", t1 es "Estoy bien", OK, correcto.

Paso 3: Pase la segunda variable t2, la API indica: El contenido es "Estoy bien" y la dirección de la variable es 36655216.

Entonces la pregunta es, ¿por qué t2 no es "Zhang Sanfeng"?

Eso es porque en aardio, las variables de texto con el mismo contenido comparten la misma dirección de memoria.

Si cambia uno, cambia todo.

Es por eso que las "direcciones de variables" que se pasan a estas dos variables son las mismas.

Por lo tanto, lo que se pasa a la API aquí es la "dirección real" de la variable de cadena.

El cuarto paso: la ejecución está completa, no hay suspenso.

La conclusión es: t1 y t2 tienen el mismo contenido y la misma dirección, si la api cambia los datos de memoria de una variable, es equivalente a todos los cambios. En el caso de "no address", la dirección real se pasa a la API en su lugar.

2. Utilice dos variables de cadena con [ contenido diferente ] para probar [sin direccionamiento].

Paso 1: pase la primera variable t1, la API indica: el contenido es "Zhang Sanfeng" y la dirección de la variable es 27875392

Paso 2: Modifica el contenido de la variable t1 en la api a "Estoy bien", la consola en aardio también lo muestra simultáneamente, el valor de retorno es "jajaja", t1 es "Estoy bien", OK, correcto.

Paso 3: Pase la segunda variable t2, la API indica: El contenido es "Zhang Wuji" y la dirección de la variable es 27875424.

Paso 4: Después de la ejecución, el contenido de la variable t2 se convierte en "Estoy bien".

Después de la ejecución, la conclusión es: los contenidos de t1 y t2 son diferentes, y las direcciones también son diferentes. En el caso de "transmisión sin dirección", todas las direcciones reales se pasan a la api y ambas son modificadas con éxito por la api.

3. Utilice dos variables de cadena con el mismo contenido para realizar la prueba [ transferencia de dirección ].

Debido a que la dirección de la cadena será devuelta por aardio como el valor de retorno, lo que significa que la api tendrá múltiples valores de retorno, por lo que en nuestro código de prueba a continuación, coloque el resultado de ejecución de la api en el último parámetro de console.log para que pueda ser todo monitor.

Paso 1: Pase la primera variable t1, la API indica: El contenido es "Zhang Sanfeng" y la dirección de la variable es 27423792.

Paso 2: El contenido de la variable t1 en la API no se ha modificado correctamente a "Estoy bien", y el contenido real de t1 sigue siendo "Zhang Sanfeng". El valor de retorno es "jajaja", pero el t1 en el valor de retorno es "Estoy bien". 

El tercer paso: pase la segunda variable t2, la API indica: El contenido es "Zhang Sanfeng" y la dirección de la variable es 27423792.

Del mismo modo, t1 y t2 tienen el mismo contenido y la misma dirección, porque t1 no se ha modificado, por lo que t2 se pasa y sigue siendo "Zhang Sanfeng".

Por lo tanto, lo que se pasa a la API aquí no debería ser la "dirección real" de la variable de cadena t1.

Pero la API se modificó con éxito y devolvió el resultado "correcto" modificado, por lo que supongo que aquí se usó una variable temporal para "engañar" a la API en lugar del t1 real.

Además, las direcciones variables temporales de las dos transferencias son las mismas, pero después de la primera transferencia, obviamente se modifica el contenido, pero se restaura la segunda transferencia.

Esto muestra que: cuando aardio pasa la variable temporal a la api, primero sincroniza el valor de la variable real con la variable temporal.

Paso 4: Una vez completada la ejecución, la conclusión es: En el caso de "pasar la dirección", primero copie el valor de la variable real a la variable temporal y luego pase la variable temporal a la api. La api cambia el valor de la variable temporal y devuelve el valor de la variable temporal.

4. Utilice dos variables de cadena con contenido [diferente] para realizar la prueba [ transferencia de dirección ].

Paso 1: Pase la primera variable t1, la API indica: El contenido es "Zhang Sanfeng" y la dirección de la variable es 36533296.

Paso 2: El contenido de la variable t1 en la API no se ha modificado correctamente a "Estoy bien", y el contenido real de t1 sigue siendo "Zhang Sanfeng". El valor de retorno es "jajaja", pero el t1 en el valor de retorno es "Estoy bien". 

Paso 3: Pase la segunda variable t2, la API indica: El contenido es "Zhang Wuji" y la dirección de la variable es 36533296. Bien, una ola de variables temporales.

Paso 4: Después de la ejecución, el contenido real de t2 sigue siendo "Zhang Wuji" y el valor devuelto es "jajaja", pero t2 en el valor devuelto es "Estoy bien". 

La conclusión es: lo mismo que el anterior, las variables temporales van en una ola ~~~

Supongo que te gusta

Origin blog.csdn.net/sdlgq/article/details/112465234
Recomendado
Clasificación