#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
¿Esto llama indirectamente main
? ¿cómo?
c
c-preprocessor
obfuscation
Rajeev Singh
fuente
fuente
Respuestas:
El lenguaje C define el entorno de ejecución en dos categorías: independiente y alojado . En ambos entornos de ejecución, el entorno llama a una función para el inicio del programa.
En un entorno independiente , la función de inicio del programa se puede definir por implementación, mientras que en el entorno alojado debería serlo
main
. Ningún programa en C puede ejecutarse sin la función de inicio del programa en los entornos definidos.En su caso,
main
está oculto por las definiciones del preprocesador.begin()
se expandirá a lodecode(a,n,i,m,a,t,e)
que más se expandirámain
.int begin() -> int decode(a,n,i,m,a,t,e)() -> int m##a##i##n() -> int main()
decode(s,t,u,m,p,e,d)
es una macro parametrizada con 7 parámetros. La lista de reemplazo para esta macro esm##s##u##t
.m, s, u
yt
son 4 º , 1 st , 3 rd y 2 nd parámetro utilizado en la lista de reemplazo.s, t, u, m, p, e, d 1 2 3 4 5 6 7
El descanso no sirve de nada ( solo para ofuscar ). El argumento que se pasa a
decode
es " a , n , i , m , a, t, e", por lo que los identificadoresm, s, u
yt
se reemplazan con argumentosm, a, i
yn
, respectivamente.fuente
_start()
. O incluso en un nivel más bajo, puedo intentar alinear el inicio de mi programa con la dirección a la que se establece la IP después del inicio.main()
es la biblioteca C estándar . C en sí mismo no impone restricciones sobre esto.decode(a,n,i,m,a,t,e)
conviertem##a##i##n
? ¿Reemplaza a los personajes? ¿Puede proporcionar un enlace a la documentación de ladecode
función? Gracias.begin
se define para ser reemplazado por lodecode(a,n,i,m,a,t,e)
que se define antes. Esta función toma los argumentoss,t,u,m,p,e,d
y los concatena en esta formam##s##u##t
(##
significa concatenar). Es decir, ignora los valores de p, e y d. A medida que "llama"decode
con s = a, t = n, u = i, m = m, efectivamente reemplazabegin
conmain
.Intente usar
gcc -E source.c
, la salida termina con:int main() { printf("Ha HA see how it is?? "); }
Entonces, una
main()
función es realmente generada por el preprocesador.fuente
El programa en cuestión hace la llamada
main()
debido a la expansión de la macro, pero su suposición es errónea - que no tiene que llamarmain()
a todos!Estrictamente hablando, puede tener un programa en C y poder compilarlo sin tener un
main
símbolo.main
es algo a lo quec library
espera saltar, después de que haya terminado su propia inicialización. Por lo general, saltamain
desde el símbolo libc conocido como_start
. Siempre es posible tener un programa muy válido, que simplemente ejecute ensamblado, sin tener un main. Mira esto:/* This must be compiled with the flag -nostdlib because otherwise the * linker will complain about multiple definitions of the symbol _start * (one here and one in glibc) and a missing reference to symbol main * (that the libc expects to be linked against). */ void _start () { /* calling the write system call, with the arguments in this order: * 1. the stdout file descriptor * 2. the buffer we want to print (Here it's just a string literal). * 3. the amount of bytes we want to write. */ asm ("int $0x80"::"a"(4), "b"(1), "c"("Hello world!\n"), "d"(13)); asm ("int $0x80"::"a"(1), "b"(0)); /* calling exit syscall, with the argument to be 0 */ }
Compile lo anterior con
gcc -nostdlib without_main.c
, y véalo imprimiendoHello World!
en la pantalla simplemente emitiendo llamadas al sistema (interrupciones) en ensamblado en línea.Para obtener más información sobre este problema en particular, consulte el blog de ksplice
Otro tema interesante, es que también puedes tener un programa que compile sin que el
main
símbolo corresponda a una función C. Por ejemplo, puede tener lo siguiente como un programa en C muy válido, que solo hace que el compilador gime cuando sube el nivel de Advertencias./* These values are extracted from the decimal representation of the instructions * of a hello world program written in asm, that gdb provides. */ const int main[] = { -443987883, 440, 113408, -1922629632, 4149, 899584, 84869120, 15544, 266023168, 1818576901, 1461743468, 1684828783, -1017312735 };
Los valores de la matriz son bytes que corresponden a las instrucciones necesarias para imprimir Hello World en la pantalla. Para obtener una descripción más detallada de cómo funciona este programa específico, eche un vistazo a esta publicación de blog , que es donde también la leí primero.
Quiero hacer un último aviso sobre estos programas. No sé si se registran como programas C válidos de acuerdo con la especificación del lenguaje C, pero compilarlos y ejecutarlos es ciertamente muy posible, incluso si violan la especificación en sí.
fuente
_start
parte de un estándar definido o es solo una implementación específica? Ciertamente, su "principal como matriz" es específico de la arquitectura. También es importante que no sea irrazonable que su truco "main as an array" falle en tiempo de ejecución debido a restricciones de seguridad (aunque eso sería más probable si no usara elconst
calificador, y aún así muchos sistemas lo permitirían)._start
no está en el estándar ELF, aunque el AMD64 psABI contiene una referencia a_start
al 3.4 proceso de inicialización . Oficialmente, ELF solo conoce la direccióne_entry
en el encabezado ELF,_start
es solo un nombre que eligió la implementación.const
no importará ni un bit, el nombre del símbolo en ese archivo ejecutable binario esmain
. Ni mas ni menos.const
es una construcción de C que no significa nada en el momento de la ejecución.Alguien está tratando de actuar como un mago. Cree que puede engañarnos. Pero todos sabemos que la ejecución del programa c comienza con
main()
.El
int begin()
será reemplazadodecode(a,n,i,m,a,t,e)
por una pasada de la etapa de preprocesador. Luego, nuevamente,decode(a,n,i,m,a,t,e)
será reemplazado por m ## a ## i ## n. Como por asociación posicional de llamada macro,s
tendrá un valor de caráctera
. Asimismo,u
será reemplazado por 'i' yt
será reemplazado por 'n'. Y así es comom##s##u##t
se convertirámain
En cuanto al
##
símbolo en la expansión macro, es el operador de preprocesamiento y realiza el pegado de tokens. Cuando se expande una macro, los dos tokens a cada lado de cada operador '##' se combinan en un solo token, que luego reemplaza el '##' y los dos tokens originales en la expansión de macro.Si no me cree, puede compilar su código con
-E
flag. Detendrá el proceso de compilación después del preprocesamiento y podrá ver el resultado de pegar el token.fuente
decode(a,b,c,d,[...])
mezcla los primeros cuatro argumentos y los une para obtener un nuevo identificador, en el ordendacb
. (Los tres argumentos restantes se ignoran). Por ejemplo,decode(a,n,i,m,[...])
proporciona el identificadormain
. Tenga en cuenta que asíbegin
se define la macro.Por lo tanto, la
begin
macro se define simplemente comomain
.fuente
En su ejemplo, la
main()
función está realmente presente, porquebegin
es una macro que el compilador reemplaza con unadecode
macro que a su vez reemplaza por la expresión m ## s ## u ## t. Usando la expansión macro##
, llegará a la palabramain
dedecode
. Este es un rastro:Es solo un truco
main()
, pero usar el nombremain()
de la función de entrada del programa no es necesario en el lenguaje de programación C. Depende de sus sistemas operativos y del vinculador como una de sus herramientas.En Windows, no siempre se utiliza
main()
, pero en lugarWinMain
owWinMain
, aunque se puede utilizarmain()
, incluso con cadena de herramientas de Microsoft . En Linux, se puede usar_start
.Depende del vinculador como herramienta del sistema operativo establecer el punto de entrada, y no el idioma en sí. ¡Incluso puede establecer nuestro propio punto de entrada y puede crear una biblioteca que también sea ejecutable !
fuente
main()
función al lenguaje de programación C, lo cual no es correcto.