Programming C

【ELF形式】ELFヘッダ(2)

今回は、以下のソースコードをコンパイルして、そのバイナリを解析します。 /* sample.c */ 1 #include <stdio.h> 2 3 int main(void) 4 { 5 return 0; 6 } まずコンパイル。 $ gcc sample.c -o sample -Wallodコマンドで、実行ファイルの先頭にあるELFヘッダ(52バイ</stdio.h>…

関数の動的書き換え(1)

関数の内容を動的に書き換えることをやってみます。 サンプルソースは、「リンカ・ローダ実践開発テクニック」(p139)より。 /* overwrite.c */ 1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int value; 6 char buffer[100]; 7 8 void func1() { value += 1; }</stdlib.h></string.h></stdio.h>…

関数の動的書き換え(2)

前回は、関数の動的書き換えを行うプログラムを実行したところ、書き込み禁止領域に対して書き込もうとするため保護違反が発生し、プログラムはSIG_SEGVで終了しました。そこで、func1()が.textセクションではなく、.dataセクションに配置されるように、リン…

-128〜127、0〜255のビットの動き

char、unsigned charの表現可能な数値の範囲は、 -128 0 127 255 CHAR +++++++++++------------- UCHAR -------------+++++++++++++charの0〜127がunsigned charの0〜127とビット列を共有しているのは当然ですが、 charの-128〜-1は、unsigned charの128〜25…

【配列とポインタ】第9回 配列とは概念である

前回までで、メモリ上のデータの参照/代入に必要な情報は、「位置」「長さ」「解釈の仕方」だ、と書きましたが、逆に言うと、その3つさえ分かれば、何でもできてしまいます。それがC言語の恐ろしいところですが…以下は、アドレス0x804A024に位置する1バイト…

【配列とポインタ】第8回 そもそも変数とは何か?(2)

今回は、変数の残りのキモである「データの長さ」「データを正しく解釈する方法」についてです。データの長さ前回は、メモリ上の特定領域にアクセスするための「位置情報」(アドレス)について書きました。この位置情報を使って、特定領域にアクセスすると…

【配列とポインタ】第7回 そもそも変数とは何か?(1)

今まで、さんざんポインタと配列をいじり倒しながら試行錯誤してきましたが、そろそろ「そもそも変数とは何か?」という本質論について考える時が来たようです。プログラミングの入門書によくある説明で、変数とは「番号のついた箱」と言われます。データ格…

【配列とポインタ】第6回 char **argvを実装してみる(2)

前回の擬似argv実装を、いい感じにリライトしてみた(つもり)です。 マジックナンバーがかっこ悪いですが、動的配列の機能はC99からなので使用禁止。使用する変数は、以下の通り。 変数 役割 char string 文字列の実体を格納する領域 char *arg_plist[] 各…

【配列とポインタ】第5回 char **argvを実装してみる(1)

main()の引数である、「char **argv」の擬似変数「char **argv2」を実装してみます。 引数文字列の実体をスタックの連続領域に作成し、 各文字列の位置情報を管理するポインタ配列を作成し、 ポインタ配列の位置情報をargv2に格納する という流れでやってい…

【配列とポインタ】第4回 式の展開規則(配列・ポインタ編)

式の展開規則実際のコーディングでは、扱う配列はたかだか二次元配列までがほとんどなので、一次元・二次元配列の展開手順は反復練習で身体で覚えてしまって、それ以上の多次元配列になれば、以下の展開規則を再帰的に当てはめて展開していけばいいかと思い…

【配列とポインタ】第3回 間接演算子は関数である

今まで見てきたように、間接演算子*の作用は、以下のようにまとめられると思います。char型配列の配列arrが以下のようにあるとき、 +------+------+------+------+------+ arr | arr0 | arr1 | arr2 | … | arrN | +------+------+------+------+------+式中…

【配列とポインタ】第2回 配列へのポインタ

式中において、ポインタに間接演算子*をつけると、参照先の値に展開される…というのは当たり前ですが、int型変数へのポインタに*をつけると、int型の値に展開される。 char型変数へのポインタに*をつけると、char型の値に展開される。 ポインタ型変数へのポ…

【配列とポインタ】第1回 配列

C言語中級者にとっての鬼門と言っても過言ではない、配列とポインタ絡みのところが、いつまで立ってもモヤモヤして気持ち悪い!ので、何回かに分けて、基礎からまとめてみたいと思います。宣言と初期化char型配列の宣言時の初期化子には、以下例のように文字…

sizeof演算子

以下について、sizeofでサイズを調べてみます。 文字リテラル 空文字 文字列リテラル char型配列 int型配列 int型二次元配列 int型二次元配列の最上位次元の先頭要素の間接参照 int型二次元配列の間接参照 char型配列の集合を管理するポインタ配列 char型配…

constの修飾対象

間接演算子*と、constが混在した場合の変数宣言について、まとめてみました。 # syntax constの修飾対象 1 const chat *p ポインタの参照先オブジェクト 2 char * const p ポインタ変数そのもの 3 const char * const p 1かつ2 ちなみに、以下のような記述も…

配列要素を参照するシンタックスバリエーション

C言語では、以下のシンタックスは、すべて'B'に展開される。 (文字列リテラル"ABCDE"の格納領域の先頭アドレスを、0x8048560と仮定します。) # syntax 1 "ABCDE"[1] 2 ((char *)0x8048560)[1] 3 1["ABCDE"] 4 (&"ABCDE")[0][1] 5 0[(&"ABCDE")][1] 式中の…

argvのメモリ構造

main関数のargvの構造がどうなっているのか調べてみた。 1 #include <stdio.h> 2 3 int main(int argc, char *argv[]) 4 { 5 int i; 6 7 printf("%p : argc %d\n", &argc, argc); 8 printf("%p : argv %p\n", &argv, argv); 9 printf("%p : argv+1 %p\n", &argv+1, *(</stdio.h>…

T型の配列

「ポインタ虎の巻」を読んでいたら、今更ながら、こんな盲点に気づかされた。 /* !!! 間違ったコード例 */ int data[3][5]; int **p; p = data; /* Syntax Error !!! */ 二次元配列の値を、ポインタのポインタ変数に代入しているので、なんとな〜く正しそう…

free(3)

#include <stdlib.h> void free(void *ptr); free(3)で注意すべきは、 引数ptrの値は、malloc、calloc、またはreallocの返却値と同じでなければならない。 すでに解放された領域に対してfree(3)すること(二重解放)は動作未定義である。 NULLポインタに対するfree(3)</stdlib.h>…

sizeof演算子

以下について、sizeofでサイズを調べてみます。 文字列リテラル 初期値を持つchar型配列 初期値を持つint型配列 char型配列を指すポインタ変数 文字リテラル 空文字列 1 #include <stdio.h> 2 3 int main() 4 { 5 char data[] = "ABCDE"; 6 int data2[] = {1,2,3,4,5}</stdio.h>…

gcc4.7.3におけるvoid型の実装

void型について実験。gccのバージョンは、以下です。 gcc バージョン 4.7.3 (Ubuntu/Linaro 4.7.3-1ubuntu1) われわれはvoidを当たり前のように使っているけど、そもそもvoidとは何だろ?実装レベルで、どのようにコンパイラに解釈されているのか、また逆ア…

逆アセンブルで遊んでみる(9)構造体変数の返却

構造体変数をreturnする関数の内部処理を解析する。です。 1 struct d { 2 int num1; 3 int num2; 4 int num3; 5 }; 6 7 struct d func() 8 { 9 struct d d1; 10 11 d1.num1 = 0x7FFFFFFF; 12 d1.num2 = 0x8FFFFFFF; 13 d1.num3 = 0x9FFFFFFF; 14 15 return …

逆アセンブルで遊んでみる(8)long long型の自動変数

スタック上に確保した自動変数領域に、long long型(16バイト)のデータを格納する。です(エピローグとプロローグ、SSPのスタック破壊検知コードについては省略しています)。 1 void func() 2 { 3 long long n = 0x1234567890ABCDEF; 4 char str[] = "0123…

逆アセンブルで遊んでみる(7)スタック破壊検出コードの自動生成

今回は、関数内で配列を確保する。です。 1 void func() 2 { 3 char array[] = "12345678"; 4 } 00000000 <func>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 18 sub $0x18,%esp 6: 65 a1 14 00 00 00 mov %gs:0x14,%eax c: 89 45 f4 mov %eax,-0xc(%ebp) f</func>…

システムコールの発行について

アセンブリにおけるシステムコール発行は、プログラミング言語における関数呼出しと同じで、 ・関数呼出時の引数指定は、レジスタへの値セットに相当する。 ・使用するレジスタ(どのレジスタに何の値をセットするか)は固定。 ・EAXレジスタにセットする値…

逆アセンブルで遊んでみる(6)関数内の処理を追いかけてみた

今回は、関数呼出しにおけるスタックの実装について、逆アセンブルを執拗に解析していきます。スタックという概念については、「積み上げられた本を取り出す時、一番上にある本から優先して取り出しが行われる」というたとえ話で説明されることが多いですが…

逆アセンブルで遊んでみる(5)自動変数の領域を16バイトを超えて確保

「自動変数の領域を16バイトを超えて確保」です。 void func(void) { int i = 0x7F; int j = 0x8F; int k = 0x9F; int l = 0xAF; int m = 0xBF; } 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 20 sub $0x20,%esp 6: c7 45 ec 7f 00 00 00 movl $0x7f,-…

逆アセンブルで遊んでみる(4)自動変数に値をセット

「自動変数に値をセット」です。 /* asm004.c */ void func(void) { int n = 0x7F; } 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 10 sub $0x10,%esp 6: c7 45 fc 7f 00 00 00 movl $0x7f,-0x4(%ebp) d: c9 leave e: c3 ret3行目で、スタックポインタ…

逆アセンブルで遊んでみる(3)signed long longの最大値を返す関数

「signed long longの最大値を返す関数」です。 unsigned long long func(void) { return 0x7FFFFFFFFFFFFFFFLL; } 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: b8 ff ff ff ff mov $0xffffffff,%eax 8: ba ff ff ff 7f mov $0x7fffffff,%edx d: 5d pop %ebp…

逆アセンブルで遊んでみる(2)signed intの最大値を返す関数

「signed intの最大値(2,147,483,647)を返す関数」です。 int func(void) { return 0x7FFFFFFF; } 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: b8 ff ff ff 7f mov $0x7fffffff,%eax 8: 5d pop %ebp 9: c3 ret intの最大値は「0x7FFFFFFF」ですが、バイナ…