#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?? ");
}
Czy to pośrednio dzwoni main
? w jaki sposób?
c
c-preprocessor
obfuscation
Rajeev Singh
źródło
źródło
Odpowiedzi:
Język C definiuje środowisko wykonawcze w dwóch kategoriach: wolnostojące i hostowane . W obu środowiskach wykonawczych środowisko wywołuje funkcję w celu uruchomienia programu.
W środowisku wolnostojącym funkcję uruchamiania programu można zdefiniować za pomocą implementacji, podczas gdy w środowisku hostowanym tak powinno być
main
. Żaden program w języku C nie może działać bez funkcji uruchamiania programu w określonych środowiskach.W twoim przypadku
main
jest ukryty przez definicje preprocesora.begin()
rozszerzy się, dodecode(a,n,i,m,a,t,e)
którego dalej zostanie rozszerzonymain
.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)
to sparametryzowane makro z 7 parametrami. Lista zastępcza tego makra tom##s##u##t
.m, s, u
it
są 4 p , 1 st , 3 rd i 2 ND parametr stosowany na liście zastępczej.s, t, u, m, p, e, d 1 2 3 4 5 6 7
Reszta na nic się nie przyda ( tylko do zaciemniania ). Przekazany argument
decode
to „ a , n , i , m , a, t, e”, więc identyfikatorym, s, u
it
są zastępowane odpowiednio argumentamim, a, i
in
.źródło
_start()
. Lub nawet na niższym poziomie mogę spróbować po prostu wyrównać początek mojego programu z adresem, na który ustawiany jest adres IP po uruchomieniu.main()
jest biblioteką C Standard . Sam C nie nakłada na to ograniczeń.decode(a,n,i,m,a,t,e)
się stałom##a##i##n
? Czy zastępuje znaki? Czy możesz podać link do dokumentacjidecode
funkcji? Dzięki.begin
jest zdefiniowany do zastąpienia, przezdecode(a,n,i,m,a,t,e)
który zdefiniowano wcześniej. Ta funkcja pobiera argumentys,t,u,m,p,e,d
i łączy je w tej formiem##s##u##t
(##
oznacza konkatenację). Tzn. Ignoruje wartości p, e i d. A ty "połączenia"decode
z y = A, T = N, U = l, m = M skutecznie zastępujebegin
sięmain
.Spróbuj użyć
gcc -E source.c
, wyjście kończy się na:int main() { printf("Ha HA see how it is?? "); }
Zatem
main()
funkcja jest faktycznie generowana przez preprocesor.źródło
Program, o którym mowa , wywołuje z
main()
powodu rozszerzenia makr, ale twoje założenie jest błędne - wcale nie musi wywoływaćmain()
!Ściśle mówiąc, możesz mieć program w C i być w stanie skompilować go bez
main
symbolu.main
jest czymś, do czegoc library
oczekuje skok po zakończeniu własnej inicjalizacji. Zwykle skaczeszmain
z symbolu libc znanego jako_start
. Zawsze można mieć bardzo poprawny program, który po prostu wykonuje asemblację, bez posiadania pliku main. Spójrz na to:/* 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 */ }
Skompiluj powyższe z
gcc -nostdlib without_main.c
i zobacz, jak wyświetla sięHello World!
na ekranie, po prostu wywołując wywołania systemowe (przerwania) w asemblerze wbudowanym.Aby uzyskać więcej informacji na temat tego konkretnego problemu, odwiedź blog ksplice
Inną interesującą kwestią jest to, że możesz również mieć program, który kompiluje się bez
main
odpowiadania symbolowi funkcji C. Na przykład możesz mieć następujący program jako bardzo poprawny program w C, który spowoduje, że kompilator będzie jęczeć tylko po podniesieniu poziomu Ostrzeżenia./* 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 };
Wartości w tablicy to bajty, które odpowiadają instrukcjom potrzebnym do wydrukowania Hello World na ekranie. Aby uzyskać bardziej szczegółowe informacje o tym, jak działa ten konkretny program, spójrz na ten wpis na blogu , w którym również go przeczytałem.
Chcę jeszcze raz zwrócić uwagę na te programy. Nie wiem, czy rejestrują się jako prawidłowe programy w C zgodnie ze specyfikacją języka C, ale ich kompilacja i uruchamianie jest z pewnością bardzo możliwe, nawet jeśli naruszają samą specyfikację.
źródło
_start
częścią zdefiniowanego standardu, czy jest to tylko specyficzna dla implementacji? Z pewnością twój "main jako tablica" jest specyficzny dla architektury. Co więcej, nie byłoby nierozsądne, gdyby sztuczka „główna jako tablica” zawodziła w czasie wykonywania z powodu ograniczeń bezpieczeństwa (chociaż byłoby to bardziej prawdopodobne, gdybyś nie użyłconst
kwalifikatora, a wiele systemów by na to pozwalało)._start
nie jest w standardzie ELF, że 64-bitową psABI zawiera odniesienie do_start
co 3,4 proces inicjalizacji . Oficjalnie ELF wie tylko o adresiee_entry
w nagłówku ELF,_start
to tylko nazwa wybrana przez implementację.const
to nie ma znaczenia - nazwa symbolu w tym binarnym pliku wykonywalnym tomain
. Nie więcej nie mniej.const
jest konstrukcją C, która nic nie znaczy w czasie wykonywania.Ktoś próbuje zachowywać się jak Mag. Myśli, że może nas oszukać. Ale wszyscy wiemy, że wykonanie programu c zaczyna się od
main()
.int begin()
Zostaną zastąpionedecode(a,n,i,m,a,t,e)
jednym przejściu etapu preprocesora. Z drugiej stronydecode(a,n,i,m,a,t,e)
zostanie zastąpiony przez m ## a ## i ## n. Podobnie jak w przypadku pozycyjnego skojarzenia wywołania makra,s
will ma wartość znakua
. Podobnieu
zostanie zastąpione przez „i” it
zastąpione przez „n”. I takm##s##u##t
się staniemain
Jeśli chodzi o
##
symbol w rozwijaniu makra, jest to operator przetwarzania wstępnego i wykonuje wklejanie tokenu. Kiedy makro jest rozwinięte, dwa tokeny po obu stronach każdego operatora „##” są łączone w jeden token, który następnie zastępuje „##” i dwa oryginalne tokeny w rozwinięciu makra.Jeśli mi nie wierzysz, możesz skompilować swój kod z
-E
flagą. Zatrzyma proces kompilacji po wstępnym przetwarzaniu i możesz zobaczyć wynik wklejania tokenu.źródło
decode(a,b,c,d,[...])
tasuje pierwsze cztery argumenty i łączy je w celu uzyskania nowego identyfikatora w określonej kolejnościdacb
. (Pozostałe trzy argumenty są ignorowane.) Na przykładdecode(a,n,i,m,[...])
podaje identyfikatormain
. Zwróć uwagę, że właśniebegin
tak zdefiniowano makro.Dlatego
begin
makro jest po prostu zdefiniowane jakomain
.źródło
W twoim przykładzie
main()
funkcja jest faktycznie obecna, ponieważbegin
jest makrem, które kompilator zastępujedecode
makrem, które z kolei zastępuje wyrażeniem m ## s ## u ## t. Używając rozszerzenia makro##
, dotrzesz do słowamain
zdecode
. To jest ślad:To tylko sztuczka
main()
, ale używanie nazwymain()
funkcji wejścia programu nie jest konieczne w języku programowania C. Zależy to od systemów operacyjnych i konsolidatora jako jednego z jego narzędzi.W systemie Windows nie zawsze używasz
main()
, ale raczejWinMain
lubwWinMain
, chociaż możeszmain()
, nawet z łańcuchem narzędzi Microsoft . W Linuksie można użyć_start
.To zależy od konsolidatora jako narzędzia systemu operacyjnego, aby ustawić punkt wejścia, a nie sam język. Możesz nawet ustawić własny punkt wejścia i możesz stworzyć bibliotekę, która jest również wykonywalna !
źródło
main()
funkcję z językiem programowania C, co nie jest poprawne.