#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?? ");
}
Isso chama indiretamente main
? quão?
c
c-preprocessor
obfuscation
Rajeev Singh
fonte
fonte
Respostas:
A linguagem C define o ambiente de execução em duas categorias: independente e hospedado . Em ambos os ambientes de execução, uma função é chamada pelo ambiente para a inicialização do programa.
Em um ambiente autônomo, a função de inicialização do programa pode ser definida pela implementação, enquanto no ambiente hospedado deveria ser
main
. Nenhum programa em C pode ser executado sem a função de inicialização do programa nos ambientes definidos.No seu caso,
main
está oculto pelas definições do pré-processador.begin()
irá expandir para odecode(a,n,i,m,a,t,e)
qual ainda será expandidomain
.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)
é uma macro parametrizada com 7 parâmetros. A lista de substituição para esta macro ém##s##u##t
.m, s, u
et
são 4º , 1º , 3º e 2º parâmetros usados na lista de substituição.s, t, u, m, p, e, d 1 2 3 4 5 6 7
O descanso é inútil ( apenas para ofuscar ). O argumento passado para
decode
é " a , n , i , m , a, t, e", portanto, os identificadoresm, s, u
et
são substituídos pelos argumentosm, a, i
en
, respectivamente.fonte
_start()
. Ou ainda em um nível mais baixo, posso tentar apenas alinhar o início do meu programa com o endereço para o qual o IP é definido após a inicialização.main()
é a biblioteca C Standard . O próprio C não impõe restrições sobre isso.decode(a,n,i,m,a,t,e)
se tornoum##a##i##n
? Substitui personagens? Você pode fornecer um link para a documentação dadecode
função? Obrigado.begin
é definido para ser substituído pelodecode(a,n,i,m,a,t,e)
que foi definido antes. Esta função pega os argumentoss,t,u,m,p,e,d
e os concatena nesta formam##s##u##t
(##
significa concatenar). Ou seja, ele ignora os valores de p, e e d. Conforme você "chama"decode
com s = a, t = n, u = i, m = m, ele efetivamente substituibegin
pormain
.Tente usar
gcc -E source.c
, a saída termina com:int main() { printf("Ha HA see how it is?? "); }
Portanto, uma
main()
função é gerada pelo pré-processador.fonte
O programa em questão faz chamada
main()
devido à expansão macro, mas o seu pressuposto é falho - que não tem que chamarmain()
em tudo!A rigor, você pode ter um programa C e compilá-lo sem ter um
main
símbolo.main
é algo para o qual oc library
espera saltar, depois de concluir sua própria inicialização. Normalmente, você salta para amain
partir do símbolo libc conhecido como_start
. É sempre possível ter um programa muito válido, que simplesmente execute assembly, sem ter um principal. Dê uma olhada neste:/* 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 o acima com
gcc -nostdlib without_main.c
e veja a impressãoHello World!
na tela apenas emitindo chamadas de sistema (interrupções) em assembly embutido.Para obter mais informações sobre este problema específico, verifique o blog ksplice
Outra questão interessante, é que você também pode ter um programa que compila sem que o
main
símbolo corresponda a uma função C. Por exemplo, você pode ter o seguinte como um programa C muito válido, que só faz o compilador reclamar quando você sobe o nível de Avisos./* 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 };
Os valores na matriz são bytes que correspondem às instruções necessárias para imprimir Hello World na tela. Para um relato mais detalhado de como esse programa específico funciona, dê uma olhada neste post do blog , que é onde eu também o li primeiro.
Quero fazer um último aviso sobre esses programas. Não sei se eles se registram como programas C válidos de acordo com a especificação da linguagem C, mas compilá-los e executá-los é certamente muito possível, mesmo que eles violem a própria especificação.
fonte
_start
parte de um padrão definido ou é apenas específico da implementação? Certamente o seu "principal como um array" é específico da arquitetura. Também importante, não seria irracional que seu truque "principal como um array" falhasse em tempo de execução devido a restrições de segurança (embora isso seja mais provável se você não usar oconst
qualificador, e muitos sistemas ainda permitiriam)._start
não está no padrão ELF, embora o AMD64 psABI contenha uma referência_start
em 3.4 Inicialização do processo . Oficialmente, ELF só conhece o endereço eme_entry
no cabeçalho ELF,_start
é apenas um nome que a implementação escolheu.const
não importa nem um pouco - o nome do símbolo nesse arquivo executável binário émain
. Nem mais nem menos.const
é uma construção C que não significa nada em tempo de execução.Alguém está tentando agir como mágico. Ele acha que pode nos enganar. Mas todos nós sabemos, a execução do programa c começa com
main()
.O
int begin()
será substituídodecode(a,n,i,m,a,t,e)
por uma passagem do estágio de pré-processador. Então, novamente,decode(a,n,i,m,a,t,e)
será substituído por m ## a ## i ## n. Como por associação posicional de chamada de macro,s
vontade tem um valor de carátera
. Da mesma forma,u
será substituído por 'i' et
será substituído por 'n'. E é assim quem##s##u##t
vai se tornarmain
Quanto ao
##
símbolo na expansão da macro, é o operador de pré-processamento e realiza a colagem do token. Quando uma macro é expandida, os dois tokens de cada lado de cada operador '##' são combinados em um único token, que então substitui o '##' e os dois tokens originais na expansão da macro.Se você não acredita em mim, você pode compilar seu código com
-E
flag. Isso interromperá o processo de compilação após o pré-processamento e você poderá ver o resultado da colagem do token.fonte
decode(a,b,c,d,[...])
embaralha os primeiros quatro argumentos e os junta para obter um novo identificador, na ordemdacb
. (Os três argumentos restantes são ignorados.) Por exemplo,decode(a,n,i,m,[...])
fornece o identificadormain
. Observe que é assim que abegin
macro é definida.Portanto, a
begin
macro é simplesmente definida comomain
.fonte
Em seu exemplo, a
main()
função está realmente presente, porquebegin
é uma macro que o compilador substitui pordecode
macro que por sua vez é substituída pela expressão m ## s ## u ## t. Usando expansão macro##
, você alcançará a palavramain
dedecode
. Este é um traço:É apenas um truque para se ter
main()
, mas usar o nomemain()
para a função de entrada do programa não é necessário na linguagem de programação C. Depende de seus sistemas operacionais e do vinculador como uma de suas ferramentas.No Windows, você nem sempre usa
main()
, mas simWinMain
ouwWinMain
, embora possa usarmain()
, mesmo com o conjunto de ferramentas da Microsoft . No Linux, pode-se usar_start
.Cabe ao vinculador, como ferramenta do sistema operacional, definir o ponto de entrada, e não o idioma em si. Você pode até definir nosso próprio ponto de entrada e criar uma biblioteca que também seja executável !
fonte
main()
função à linguagem de programação C, o que não é correto.