0

SOの誰かが、関数を「隠す」方法を尋ねる質問を投稿しました。これが私の答えでした:

#include <stdio.h>
#include <stdlib.h>

int encrypt(void)
{
  char *text="Hello World";
  asm("push text");
  asm("call printf");
  return 0;
}

int main(int argc, char *argv[])
{
  volatile unsigned char *i=encrypt;
  while(*i!=0x00)
    *i++^=0xBE;
  return EXIT_SUCCESS;
}

しかし、問題があります:

encode.c:関数 `main'内:
encode.c:13:警告:互換性のないポインタ型からの初期化
C:\ DOCUME〜1 \ Aviral \ LOCALS〜1 \ Temp / ccYaOZhn.o:encode.c:(。text + 0xf):「テキスト」への未定義の参照
C:\ DOCUME〜1 \ Aviral \ LOCALS〜1 \ Temp / ccYaOZhn.o:encode.c:(。text + 0x14):`printf'への未定義の参照
collect2:ldが1つの終了ステータスを返しました

私の最初の質問は、なぜインラインアセンブリが失敗するのかということです...それを行う正しい方法は何でしょうか?他のこと-「ret」または「retn」のコードは0x00です、そうです...それがリターンに達するまで私のコードxorのもの...それでなぜそれはSEGFAULTingなのですか?

4

3 に答える 3

1

GCCインラインasmはAT&T構文を使用します(Intelのオプションを使用するための特定のオプションが選択されていない場合)。

次に例を示します。

 int a=10, b;
 asm ("movl %1, %%eax; 
       movl %%eax, %0;"
      :"=r"(b)        /* output */
      :"r"(a)         /* input */
      :"%eax"         /* clobbered register */
      );       

したがって、あなたの問題は、「テキスト」があなたの呼び出しから(そして指示に従うことも)識別できないということです。

参考までにこちらをご覧ください。

さらに、コードは32ビット環境と64ビット環境の間で移植できません。適切な分析を確実にするために-m32フラグを使用してコンパイルします(エラーが発生した場合、GCCはとにかく文句を言います)。

問題の完全な解決策は、GCCメーリングリストのこの投稿にあります。スニペットは次のとおりです。

for ( i = method->args_size - 1; i >= 0; i-- ) {
    asm( "pushl %0": /* no outputs */: \
         "g" (stack_frame->op_stack[i]) );
}

asm( "call *%0" : /* no outputs */ : "g" (fp) :
     "%eax", "%ecx", "%edx", "%cc", "memory" );

asm ( "movl %%eax, %0" : "=g" (ret_value) : /* No inputs */ );

Windowsシステムでは、追加の作業もありasm ( "addl %0, %%esp" : /* No outputs */ : "g" (method->args_size * 4) );ます。詳細についてはGoogleをご覧ください。

于 2009-10-03T08:10:34.833 に答える
1

大まかに言うと、インラインアセンブリを使用してprintfを単純に呼び出しようとしている理由はよくわかりません。これは、関数呼び出しの誤ったバージョンを作成するだけだからです(インラインが何かをプッシュします)。スタックをポップオフしないでください。問題が発生する可能性があります。GCCは、関数の途中でスタックポインターを変更したことを認識していません。これは簡単な例では問題ありませんが、明らかでないエラーが発生する可能性があります。より複雑な機能で)

top関数の正しい実装は次のとおりです。

int encrypt(void)
{
  char *text="Hello World";
  char *formatString = "%s\n";
  // volatile really isn't necessary but I just use it by habit
  asm volatile("pushl %0;\n\t"
               "pushl %1;\n\t"
            "call printf;\n\t"
               "addl $0x8, %%esp\n\t"          
               : 
               : "r"(text), "r"(formatString)
               );

  return 0;
}

最後の質問ですが、RETの通常のオペコードは「C3」ですが、さまざまなバリエーションがあります。http://pdos.csail.mit.edu/6.828/2009/readings/i386/RET.htmをご覧 ください。ランダムな命令セットにバイト0xC3が表示された場合、それはretに遭遇したことを意味しないという事実のために、RETを検索するという考えも誤りです。0xC3は単に別の命令のデータ/属性である可能性があるため(補足として、x86は1〜16バイトの命令長を持つCISCアーキテクチャであるため、実行しているときにx86命令を解析するのは特に困難です。 )。

別の注意点として、すべてのOSがテキスト/コードセグメント(実行可能命令が格納されている場所)の変更を許可しているわけではないため、メインにあるコードは関係なく機能しない可能性があります。

于 2009-10-03T08:39:30.363 に答える
0

printfではなく_printfです

于 2009-10-21T12:50:55.903 に答える