ofuscacion - ofuscar codigo c#
Este código C ofuscado dice que se ejecuta sin main(), pero ¿qué hace realmente? (6)
Alguien está tratando de actuar como mago.
Él cree que puede engañarnos.
Pero todos sabemos, la ejecución del programa c comienza con
main()
.
int begin()
será reemplazado con
decode(a,n,i,m,a,t,e)
por un paso de la etapa de preprocesador.
Por otra parte, la
decode(a,n,i,m,a,t,e)
se reemplazará con m ## a ## i ## n.
Como por asociación posicional de llamada macro,
s
tendrá un valor de carácter
a
.
Del mismo modo,
u
será reemplazado por ''i''
t
será reemplazado por ''n''.
Y así es como,
m##s##u##t
se convertirá en
main
En cuanto al símbolo
##
en la expansión de 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 la macro.
Si no me cree, puede compilar su código con la
-E
.
Se detendrá el proceso de compilación después del preprocesamiento y puede ver el resultado del pegado de tokens.
gcc -E FILENAME.c
#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 indirectamente llama
main
?
¿cómo?
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 programa de entorno
independiente
, la función de inicio puede definirse como implementación, mientras que en el entorno
hospedado
debe ser
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 de preprocesador.
begin()
se expandirá para
decode(a,n,i,m,a,t,e)
que luego se expandirá a
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 es
m##s##u##t
.
m, s, u
y
t
son los parámetros
4º
, 1º,
3º
y
2º
utilizados 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 pasado para
decode
es "
a
,
n
,
i
,
m
, a, t, e", por lo que los identificadores
m, s, u
y
t
se reemplazan con los argumentos
m, a, i
y
n
, respectivamente.
m --> m
s --> a
u --> i
t --> n
El programa en cuestión llama a
main()
debido a la expansión de macro, pero su suposición es errónea: ¡
no
tiene que llamar a
main()
en absoluto!
Estrictamente hablando, puede tener un programa en C y poder compilarlo sin tener un símbolo
main
.
main
es algo a lo que la
c library
espera acceder, después de que haya terminado su propia inicialización.
Por lo general, saltas a
main
desde el símbolo de libc conocido como
_start
.
Siempre es posible tener un programa muy válido, que simplemente ejecute el ensamblaje, 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 vea cómo imprime
Hello World!
en la pantalla simplemente emitiendo llamadas al sistema (interrupciones) en el ensamblaje en línea.
Para obtener más información sobre este problema en particular, consulte el blog ksplice
Otra cuestión interesante es que también puede tener un programa que se compila sin que el símbolo
main
corresponda a una función C.
Por ejemplo, puede tener lo siguiente como un programa C muy válido, que solo hace que el compilador se queje 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 en 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 lo leí primero.
Quiero hacer un aviso final 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 misma.
En su ejemplo, la función
main()
está realmente presente, porque
begin
es una macro que el compilador reemplaza con macro de
decode
que a su vez reemplaza por la expresión m ## s ## u ## t.
Usando la expansión de macro
##
, llegará a la palabra
main
desde
decode
.
Este es un rastro:
begin --> decode(a,n,i,m,a,t,e) --> m##parameter1##parameter3##parameter2 ---> main
Es solo un truco tener
main()
, pero usar el nombre
main()
para la función de entrada del programa no es necesario en el lenguaje de programación C.
Depende de sus sistemas operativos y del enlazador como una de sus herramientas.
En Windows, no siempre usa
main()
, sino
WinMain
o
wWinMain
, aunque
puede usar
main()
, incluso con la cadena de herramientas de Microsoft
.
En Linux, uno puede usar
_start
.
Depende del enlazador 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 !
Intente usar
gcc -E source.c
, la salida termina con:
int main()
{
printf("Ha HA see how it is?? ");
}
Entonces, una función
main()
es realmente generada por el preprocesador.
decode(a,b,c,d,[...])
baraja los primeros cuatro argumentos y los une para obtener un nuevo identificador, en el orden
dacb
.
(Los tres argumentos restantes se ignoran). Por ejemplo, la
decode(a,n,i,m,[...])
proporciona el identificador
main
.
Tenga en cuenta que así es como se define la macro de
begin
.
Por lo tanto, la macro de
begin
se define simplemente como
main
.