#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?? ");
}
Ruft dies indirekt auf main
? Wie?
c
c-preprocessor
obfuscation
Rajeev Singh
quelle
quelle
Antworten:
Die Sprache C definiert die Ausführungsumgebung in zwei Kategorien: freistehend und gehostet . In beiden Ausführungsumgebungen wird von der Umgebung eine Funktion zum Programmstart aufgerufen.
In einer freistehenden Umgebung kann die Startfunktion eines Programms definiert werden, während dies in einer gehosteten Umgebung der Fall sein sollte
main
. Kein Programm in C kann ohne Programmstartfunktion in den definierten Umgebungen ausgeführt werden.In Ihrem Fall
main
wird durch die Präprozessordefinitionen ausgeblendet.begin()
wird erweitert, aufdecode(a,n,i,m,a,t,e)
die weiter erweitert wirdmain
.decode(s,t,u,m,p,e,d)
ist ein parametrisiertes Makro mit 7 Parametern. Ersatzliste für dieses Makro istm##s##u##t
.m, s, u
undt
gibt 4 th , 1 st , 3 rd und 2 nd Parameter in der Ersatzliste verwendet.Rest nützt nichts ( nur um zu verschleiern ). Argument übergeben
decode
ist " a , n , i , m , a, t, e" , so werden die Kennungenm, s, u
undt
ersetzt werden mit Argumentenm, a, i
undn
, respectively.quelle
_start()
. Oder noch niedriger kann ich versuchen, den Start meines Programms einfach an der Adresse auszurichten, auf die die IP nach dem Start eingestellt ist.main()
ist C Standard Bibliothek . C selbst unterwirft dies nicht.decode(a,n,i,m,a,t,e)
es wirdm##a##i##n
? Ersetzt es Zeichen? Können Sie einen Link zur Dokumentation derdecode
Funktion bereitstellen ? Vielen Dank.begin
ist so definiert, dass es durchdecode(a,n,i,m,a,t,e)
das zuvor definierte ersetzt wird. Diese Funktion nimmt die Argumentes,t,u,m,p,e,d
und verkettet sie in dieser Formm##s##u##t
(##
bedeutet verketten). Das heißt, es ignoriert die Werte von p, e und d. Wie Sie "Call"decode
mit s = a, t = n, u = i, m = m ersetzt er effektivbegin
mitmain
.Versuchen Sie es mit
gcc -E source.c
, die Ausgabe endet mit:Eine
main()
Funktion wird also tatsächlich vom Präprozessor erzeugt.quelle
Das betreffende Programm wird
main()
aufgrund einer Makroerweiterung aufgerufen , aber Ihre Annahme ist fehlerhaft - es muss überhaupt nicht aufgerufen werdenmain()
!Genau genommen können Sie ein C-Programm haben und es kompilieren können, ohne ein
main
Symbol zu haben.main
ist etwas, in das derc library
erwartet, zu springen, nachdem er seine eigene Initialisierung abgeschlossen hat. Normalerweise springt manmain
vom libc-Symbol, das als bekannt ist_start
. Es ist immer möglich, ein sehr gültiges Programm zu haben, das einfach die Assembly ausführt, ohne ein Hauptprogramm zu haben. Schau dir das an:Kompilieren Sie das Obige mit
gcc -nostdlib without_main.c
und sehen Sie, wie esHello World!
auf dem Bildschirm gedruckt wird, indem Sie Systemaufrufe (Interrupts) in der Inline-Assembly ausgeben.Weitere Informationen zu diesem speziellen Problem finden Sie im ksplice-Blog
Ein weiteres interessantes Problem ist, dass Sie auch ein Programm haben können, das kompiliert wird, ohne dass das
main
Symbol einer C-Funktion entspricht. Zum Beispiel können Sie Folgendes als sehr gültiges C-Programm verwenden, das den Compiler nur dann zum Jammern bringt, wenn Sie die Warnstufe erhöhen.Die Werte im Array sind Bytes, die den Anweisungen zum Drucken von Hello World auf dem Bildschirm entsprechen. Um eine detailliertere Darstellung der Funktionsweise dieses speziellen Programms zu erhalten, werfen Sie einen Blick auf diesen Blog-Beitrag , in dem ich ihn auch zuerst gelesen habe.
Ich möchte noch einen letzten Hinweis zu diesen Programmen geben. Ich weiß nicht, ob sie sich gemäß der C-Sprachspezifikation als gültige C-Programme registrieren, aber das Kompilieren und Ausführen dieser Programme ist sicherlich sehr gut möglich, selbst wenn sie gegen die Spezifikation selbst verstoßen.
quelle
_start
Teils eines definierten Standards oder ist das nur implementierungsspezifisch? Sicherlich ist Ihr "main als Array" architekturspezifisch. Wichtig ist auch, dass es nicht unangemessen ist, wenn Ihr Trick "main as a array" zur Laufzeit aufgrund von Sicherheitsbeschränkungen fehlschlägt (obwohl dies wahrscheinlicher wäre, wenn Sie dasconst
Qualifikationsmerkmal nicht verwenden würden und viele Systeme dies dennoch zulassen würden)._start
ist nicht im ELF-Standard enthalten, obwohl der AMD64 psABI einen Verweis auf_start
bei 3.4 Process Initialization enthält . Offiziell kennt ELF nur die Adressee_entry
im ELF-Header,_start
ist nur ein Name, den die Implementierung gewählt hat.const
keine Rolle - der Symbolname in dieser ausführbaren Binärdatei lautetmain
. Nicht mehr und nicht weniger.const
ist ein C-Konstrukt, das zur Ausführungszeit nichts bedeutet.Jemand versucht, sich wie ein Magier zu verhalten. Er glaubt, er kann uns austricksen. Aber wir alle wissen, dass die Ausführung des c-Programms mit beginnt
main()
.Das
int begin()
wirddecode(a,n,i,m,a,t,e)
durch einen Durchgang der Präprozessorstufe ersetzt. Andererseitsdecode(a,n,i,m,a,t,e)
wird durch m ## a ## i ## n ersetzt. Wie durch die Positionszuordnung des Makroaufrufs hat ders
Wille einen Zeichenwerta
. Ebensou
wird durch 'i' undt
durch 'n' ersetzt. Und som##s##u##t
wird es werdenmain
In Bezug auf das
##
Symbol in der Makroerweiterung ist es der Vorverarbeitungsoperator und führt das Einfügen von Token durch. Wenn ein Makro erweitert wird, werden die beiden Token auf beiden Seiten jedes '##'-Operators zu einem einzigen Token kombiniert, das dann die' ## 'und die beiden ursprünglichen Token in der Makroerweiterung ersetzt.Wenn Sie mir nicht glauben, können Sie Ihren Code mit
-E
flag kompilieren . Der Kompilierungsprozess wird nach der Vorverarbeitung gestoppt und Sie können das Ergebnis des Einfügens von Token sehen.quelle
decode(a,b,c,d,[...])
mischt die ersten vier Argumente und verbindet sie, um eine neue Kennung in der Reihenfolge zu erhaltendacb
. (Die verbleibenden drei Argumente werden ignoriert.) Gibt beispielsweisedecode(a,n,i,m,[...])
den Bezeichner anmain
. Beachten Sie, dass dasbegin
Makro so definiert ist.Daher wird das
begin
Makro einfach definiert alsmain
.quelle
In Ihrem Beispiel ist die
main()
Funktion tatsächlich vorhanden, dabegin
es sich um ein Makro handelt, das der Compiler durch ein Makro ersetzt,decode
das wiederum durch den Ausdruck m ## s ## u ## t ersetzt wird. Mit der Makro-Erweiterung##
erreichen Sie das Wortmain
vondecode
. Dies ist eine Spur:Es ist nur ein Trick
main()
, aber die Verwendung des Namensmain()
für die Eingabefunktion des Programms ist in der Programmiersprache C nicht erforderlich. Dies hängt von Ihren Betriebssystemen und dem Linker als einem seiner Tools ab.Unter Windows verwenden Sie nicht immer
main()
, sondern eherWinMain
oderwWinMain
, obwohl Sie verwenden könnenmain()
, auch mit Microsofts Toolchain . Unter Linux kann man verwenden_start
.Es liegt am Linker als Betriebssystem-Tool, den Einstiegspunkt und nicht die Sprache selbst festzulegen. Sie können sogar unseren eigenen Einstiegspunkt festlegen und eine Bibliothek erstellen, die auch ausführbar ist !
quelle
main()
Funktion an die Programmiersprache C bindet , die nicht korrekt ist.