Visual C++で記号プログラミング
2010-12-22
hotpepsiです。
x86のWindowsで動作するhello.cを生成するプログラムを書いてみました。
#include <windows.h> #include <stdio.h> static const char *Hexnums[]={"' '-' '","'!'-' '","'#'-'!'","'#'-' '", "'$'-' '","'%'-' '","'&'-' '","'('-'!'","'('-' '", "')'-' '","'*'-' '", "'+'-' '","','-' '","'-'-' '","'.'-' '","'/'-' '","'='-'-'"}; #define H(n) Hexnums[(n)&0xF] #define S Hexnums[4] static void PutHex32(FILE *fp, unsigned int value) { fprintf(fp,"((((((%s)<<(%s))|(%s))<<(%s))|\n(((%s)<<(%s))|(%s)))<<(%s))|\n" "(((((%s)<<(%s))|(%s))<<(%s))|\n(((%s)<<(%s))|(%s))),\n", H(value>>28),H(4),H(value>>24),H(8),H(value>>20),H(4),H(value>>16),Hexnums[0x10], H(value>>12),H(4),H(value>>8),H(8),H(value>>4),H(4),H(value)); } int main(int argc, char *argv[]) { FILE *fp = stdout; fprintf(fp, "__[]={\n"); PutHex32(fp, 0x6C6C6548); // Hell PutHex32(fp, 0x77202C6F); // o, w PutHex32(fp, 0x646C726F); // orld PutHex32(fp, 0x00000A21); // !\n fprintf(fp, "};\n_[]={\n"); PutHex32(fp, 0x006A006A); // push 0 / push 0 PutHex32(fp, 0x68900E6A); // push 0xE / push __ fprintf(fp, "__,\n"); PutHex32(fp, 0xB890F56A); // push -11 / mov eax, GetStdHandle PutHex32(fp, (unsigned int)GetStdHandle); PutHex32(fp, 0xB850D0FF); // call eax / push eax / mov eax, WriteFile PutHex32(fp, (unsigned int)WriteFile); PutHex32(fp, 0x90C3D0FF); // call eax / ret fprintf(fp, "};\n"); return 0; }
Windows XP SP3で実行させたところ、以下の記号だけのhello.cが生成されました。
__[]={ (((((('&'-' ')<<('$'-' '))|(','-' '))<<('('-' '))| ((('&'-' ')<<('$'-' '))|(','-' ')))<<('='-'-'))| ((((('&'-' ')<<('$'-' '))|('%'-' '))<<('('-' '))| ((('$'-' ')<<('$'-' '))|('('-' '))), (((((('('-'!')<<('$'-' '))|('('-'!'))<<('('-' '))| ((('#'-'!')<<('$'-' '))|(' '-' ')))<<('='-'-'))| ((((('#'-'!')<<('$'-' '))|(','-' '))<<('('-' '))| ((('&'-' ')<<('$'-' '))|('/'-' '))), (((((('&'-' ')<<('$'-' '))|('$'-' '))<<('('-' '))| ((('&'-' ')<<('$'-' '))|(','-' ')))<<('='-'-'))| ((((('('-'!')<<('$'-' '))|('#'-'!'))<<('('-' '))| ((('&'-' ')<<('$'-' '))|('/'-' '))), ((((((' '-' ')<<('$'-' '))|(' '-' '))<<('('-' '))| (((' '-' ')<<('$'-' '))|(' '-' ')))<<('='-'-'))| (((((' '-' ')<<('$'-' '))|('*'-' '))<<('('-' '))| ((('#'-'!')<<('$'-' '))|('!'-' '))), }; _[]={ ((((((' '-' ')<<('$'-' '))|(' '-' '))<<('('-' '))| ((('&'-' ')<<('$'-' '))|('*'-' ')))<<('='-'-'))| (((((' '-' ')<<('$'-' '))|(' '-' '))<<('('-' '))| ((('&'-' ')<<('$'-' '))|('*'-' '))), (((((('&'-' ')<<('$'-' '))|('('-' '))<<('('-' '))| (((')'-' ')<<('$'-' '))|(' '-' ')))<<('='-'-'))| (((((' '-' ')<<('$'-' '))|('.'-' '))<<('('-' '))| ((('&'-' ')<<('$'-' '))|('*'-' '))), __, (((((('+'-' ')<<('$'-' '))|('('-' '))<<('('-' '))| (((')'-' ')<<('$'-' '))|(' '-' ')))<<('='-'-'))| ((((('/'-' ')<<('$'-' '))|('%'-' '))<<('('-' '))| ((('&'-' ')<<('$'-' '))|('*'-' '))), (((((('('-'!')<<('$'-' '))|(','-' '))<<('('-' '))| ((('('-' ')<<('$'-' '))|('!'-' ')))<<('='-'-'))| ((((('#'-'!')<<('$'-' '))|('/'-' '))<<('('-' '))| (((','-' ')<<('$'-' '))|(')'-' '))), (((((('+'-' ')<<('$'-' '))|('('-' '))<<('('-' '))| ((('%'-' ')<<('$'-' '))|(' '-' ')))<<('='-'-'))| ((((('-'-' ')<<('$'-' '))|(' '-' '))<<('('-' '))| ((('/'-' ')<<('$'-' '))|('/'-' '))), (((((('('-'!')<<('$'-' '))|(','-' '))<<('('-' '))| ((('('-' ')<<('$'-' '))|('!'-' ')))<<('='-'-'))| (((((' '-' ')<<('$'-' '))|('.'-' '))<<('('-' '))| ((('!'-' ')<<('$'-' '))|('('-'!'))), ((((((')'-' ')<<('$'-' '))|(' '-' '))<<('('-' '))| (((','-' ')<<('$'-' '))|('#'-' ')))<<('='-'-'))| ((((('-'-' ')<<('$'-' '))|(' '-' '))<<('('-' '))| ((('/'-' ')<<('$'-' '))|('/'-' '))), };
ビルドするには、Win32コンソールアプリケーションのプロジェクトを作成してから、プロジェクトのプロパティを開き、構成プロパティ→リンカ→詳細の「エントリポイント」の欄に _ を設定します。
VC2008以降の場合には、DEPの設定を「イメージはDEPと互換性がない」にする必要があります。
動作原理はLinux/gcc版とほぼ同じです。
記号だけの変数を定義して、変数 _ をエントリポイントの関数とみなして実行させます。
APIの名前を指定するという高度な機能が使えないため、GetStdHandleとWriteFileのアドレスを代入して呼び出すという、アセンブリ言語よりも低レベルな方法を採用しています。