web-dev-qa-db-ja.com

この難読化されたCコードは、main()なしで実行すると主張していますが、実際には何をしますか?

#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?? ");
}

これは間接的にmainを呼び出しますか?どうやって?

85
Rajeev Singh

C言語は、2つのカテゴリで実行環境を定義します:freestandingおよびhosted 両方の実行環境で、関数はプログラムの起動のために環境によって呼び出されます。
自立環境では、hosted環境はmainである必要があります。 Cのプログラムは、定義された環境でプログラム起動機能なしでは実行できません。

あなたの場合、mainはプリプロセッサ定義によって隠されています。 begin()decode(a,n,i,m,a,t,e)に展開され、さらに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)は、7つのパラメーターを持つパラメーター化されたマクロです。このマクロの置換リストは_m##s##u##t_です。 _m, s, u_およびtは4番目、1st、3rd および2nd 置換リストで使用されるパラメーター。

_s, t, u, m, p, e, d
1  2  3  4  5  6  7
_

残りは役に立たない(単に難読化する)。 decodeに渡される引数は "anim、a、t 、e "ですので、識別子_m, s, u_とtは、それぞれ引数_m, a, i_とnに置き換えられます。

_ m --> m  
 s --> a 
 u --> i 
 t --> n
_
193
haccks

_gcc -E source.c_を使用してみてください。出力は次で終わります:

_int main()
{
    printf("Ha HA see how it is?? ");
}
_

したがって、main()関数は実際にはプリプロセッサによって生成されます。

72
jdarthenay

問題のプログラムdoesはマクロ展開のためにmain()を呼び出しますが、あなたの仮定に誤りがあります-itmain()を呼び出す必要はまったくありません!

厳密に言うと、Cプログラムを使用して、mainシンボルなしでコンパイルできます。 mainは、c libraryが独自の初期化を完了した後、ジャンプするものです。通常、_startとして知られるlibcシンボルからmainにジャンプします。メインがなくても、単にアセンブリを実行するだけの非常に有効なプログラムを作成することは常に可能です。これを見てください:

/* 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 */
}

上記をgcc -nostdlib without_main.cでコンパイルし、インラインアセンブリでシステムコール(割り込み)を発行するだけで、画面にHello World!が出力されるのを確認します。

この特定の問題の詳細については、 ksplice blog をご覧ください。

別の興味深い問題は、mainシンボルをC関数に対応させずにコンパイルするプログラムを使用できることです。たとえば、非常に有効なCプログラムとして次のものを使用できます。これにより、警告レベルを上げたときにのみコンパイラーが泣き叫びます。

/* 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
};

配列内の値は、画面にHello Worldを印刷するために必要な指示に対応するバイトです。この特定のプログラムがどのように機能するかについてのより詳細な説明については、この ブログ投稿 をご覧ください。

これらのプログラムについて最後にお知らせしたいと思います。 C言語仕様に従って有効なCプログラムとして登録されているかどうかはわかりませんが、仕様自体に違反していても、これらをコンパイルして実行することは確かに非常に可能です。

37
NlightNFotis

誰かがマジシャンのように行動しようとしています。彼は私たちをだますことができると考えています。しかし、cプログラムの実行はmain()で始まります。

int begin()は、プリプロセッサステージの1つのパスによってdecode(a,n,i,m,a,t,e)に置き換えられます。次に、decode(a,n,i,m,a,t,e)がm ## a ## i ## nに置き換えられます。マクロ呼び出しの位置関連付けと同様に、sの値は文字aになります。同様に、uは 'i'に置き換えられ、tは 'n'に置き換えられます。そして、それがどのように、m##s##u##tmainになります

に関して、##記号はマクロ展開で使用され、前処理演算子であり、トークンの貼り付けを実行します。マクロが展開されると、各「##」演算子の両側にある2つのトークンが単一のトークンに結合され、マクロ展開の「##」と2つの元のトークンが置き換えられます。

あなたが私を信じないなら、あなたは-E 国旗。前処理後にコンパイルプロセスを停止し、トークンの貼り付けの結果を確認できます。

gcc -E FILENAME.c
30
abhiarora

decode(a,b,c,d,[...])は最初の4つの引数をシャッフルし、それらを結合してdacbの順序で新しい識別子を取得します。 (残りの3つの引数は無視されます。)たとえば、decode(a,n,i,m,[...])は識別子mainを提供します。これはbeginマクロが定義されているものであることに注意してください。

したがって、beginマクロは、単にmainとして定義されます。

11
Frxstrem

あなたの例では、main()関数が実際に存在します。なぜなら、beginは、コンパイラーがdecodeマクロに置き換えるマクロであり、これは式m ## s ## u#に置き換えられるためです。 #t。マクロ展開##を使用すると、mainからWord decodeに到達します。これはトレースです:

begin --> decode(a,n,i,m,a,t,e) --> m##parameter1##parameter3##parameter2 ---> main

main()を使用するのは単なるトリックですが、Cプログラミング言語では、プログラムのエントリ関数に名前main()を使用する必要はありません。ツールの1つとして、オペレーティングシステムとリンカーに依存します。

Windowsでは、常にmain()を使用するわけではありませんが、- -WinMainまたはwWinMain を使用しますが、 main()、Microsoftのツールチェーンでも 。 Linuxでは、_startを使用できます。

言語そのものではなく、エントリポイントを設定するのはオペレーティングシステムツールとしてのリンカ次第です。 独自のエントリポイントを設定し、実行可能なライブラリを作成することもできます

2
Ho1