hiziriAI’s blog

自分用雑記

Lua

basic

コメント

-- comment

--[[
multiple line comment
ok.
]]

変数

-- num
x = 10
x = 1.0
x = 10e-1

-- string
s = "AAA"
s = 'AAA\n'

-- function
-- table
t = {}

-- boolean
b = true
b = false

テーブル

t = {}
t["str"] = "string"
t["boolean"] = false
t["number"] = 1

-- number is accepted as index
-- index should start from 1, lua expects that beginning of index is 1.
t[1] = "one"
t[2] = true
t[3] = 3

イテレータ

t = {}
t["1"] = "one"
t["2"] = "two"
t["3"] = "three"

for i, val in pairs(t) do
    print(val)
end   

t[1] = "one"
t[2] = "two"
t[3] = "three"

for i, val in ipairs(t) do -- using ipairs
    print(val) -- output "one", "two", "three"
end

演算子

-- math
1 + 1
1 - 1
1 * 1
1 / 1
1 ^ 1
1 % 1

-- cmp
1 < 1
1 > 1
1 <= 1
1 >= 1
1 == 1
1 ~= 1

-- logical
true and true
true or false
not false

-- string
"str1" .. "str2" -- "str1str2"

if

a = 3
b = 2

if a > b then
    print("a")
end

if a > b then
    print("a")
elseif a == b then
    print("ab")
else
    print("b")
end   

for

for i = 0, 4, 1 do
    print(i) -- 0, 1, 2, 3, 4
end

a = 0
b = 4
while a <= b do
    print(a) -- 0, 1, 2, 3, 4
    a = a + 1
end

a = 0
b = 5
repeat
    print(a) -- 0, 1, 2, 3, 4
    a = a + 1
until a == b

while true do
    break -- break should be before end
end

while true do
    do
        break
    end
    
    a = 10
end

関数

function funcname (arg)
    return arg + 1
end

function returnmultival(arg1, arg2)
    return arg1, arg2
end

a, b = returnmultival(1, 2)

-- extendable arg
function func(...)
    t = {...}
end

-- lambda
f = function(args)
       return args
    end
    
f(3)

コルーチン(co-routine)

function col(a, b, c)
    sum = 0
    sum = sum + a
    coroutine.yield(sum)
    sum = sum + b
    coroutine.yield(sum)
    sum = sum + c   
    return sum
end  

local co = ccoroutine.wrap(col)
ret = co(3, 2, 1) -- return a ?
ret = co(3, 2, 1) -- return a + b
ret = co(3, 2, 1) -- return a + b + c

バッファオーバーフローでeipが書き換えられない

自分の環境で脆弱性のあるプログラムを再現できなかった

pwnの練習をしようとして脆弱性のあるプログラムを書いたら、何故かその脆弱性を(幸か不幸か)exploitできなかった。 解決するまでに結構かかってしまった上、日本語の情報が見つからなかったから残しておく。 参考というか、丸パクリ元はここ

環境

自分の環境は以下の通りである。

x86_64 GNU/Linux Ubuntu LTS-16.04

やろうとしたこと

やろうとしていたことはバッファオーバーフローによるeipを書き換えだったけど、実際にやるとクラッシュしてもeipが書き換えられてなかった。 プログラム自体はfgetsしてバッファ書き換えるだけの簡単もので、32bitアーキテクチャSSPは無効にしていた。 ASLRは無効にしてなかったけど、gdbで実行していたので無効の状態のはず。

#include <stdio.h>

int main(int argc, char** argv){
  char buffer[32];
  
  fgets(buffer, 128, stdin);
  
  return 0;
}

コンパイルは、gcc -m32 -fno-stack-protector -o overflow overflow.c

gdbで調べたり、逆アセンブルして解析した結果、どうもなんらかのセキュリティが働いているような感じだった。 逆アセンブルしてみてみると、main関数の最初とret周りの処理が怪しい。

0804843b <main>:
 804843b: 8d 4c 24 04           lea    ecx,[esp+0x4] ;; 1
 804843f: 83 e4 f0                 and    esp,0xfffffff0
 8048442:  ff 71 fc                 push   DWORD PTR [ecx-0x4]
 8048445:  55                     push   ebp
 8048446:  89 e5                   mov    ebp,esp
 8048448:  51                     push   ecx
 8048449:  83 ec 24               sub    esp,0x24
 804844c: a1 20 a0 04 08         mov    eax,ds:0x804a020
 8048451:  83 ec 04               sub    esp,0x4
 8048454:  50                     push   eax
 8048455:  68 80 00 00 00         push   0x80
 804845a: 8d 45 d8              lea    eax,[ebp-0x28]
 804845d: 50                     push   eax
 804845e: e8 ad fe ff ff           call   8048310 <fgets@plt>
 8048463:  83 c4 10               add    esp,0x10
 8048466:  b8 00 00 00 00           mov    eax,0x0
 804846b: 8b 4d fc                 mov    ecx,DWORD PTR [ebp-0x4] ;; 2
 804846e: c9                       leave  
 804846f: 8d 61 fc              lea    esp,[ecx-0x4] ;; 3
 8048472:  c3                       ret    
 8048473:  66 90                 xchg   ax,ax
 8048475:  66 90                 xchg   ax,ax
 8048477:  66 90                 xchg   ax,ax
 8048479:  66 90                 xchg   ax,ax
 804847b: 66 90                 xchg   ax,ax
 804847d: 66 90                 xchg   ax,ax
 804847f: 90                     nop
  • 1で[esp+0x4]をecxに格納
  • 2でecxが復帰
  • 3のret前でespに[ecx-0x4]を格納

こんな感じの処理がされているらしい。 つまり、一番最初に格納したスタックポインタの値をleaveとretの間で復帰させることで、書き換えられたリターンアドレスがeipに格納されることを防いでいるのではないかと思われる。

また、callした後のスタックの状態は以下のようになるので、ebpを変更せずにリターンアドレスを変更することはできない(ebpの整合性を保つような値で上書きすることは可能かな?)。 したがって、リターンアドレスがバッファオーバーフローで書き換えられていると、ebpが異常値になって正しくespを復帰させられなくなる。 その結果としてeipが書き換えられなくなっている。

stack
argn
arg1
return-address
saved-ebp
local-variable

解決策(?)

解決というか問題の再発というか、脆弱性を仕込むことができなければ練習できないので調べた。 幸いにも参考にしたページのコメントに解決できそうなやり方が載っていたので、ありがたく参考にさせてもらう。 このespの保存と復帰はmain関数でしか行なわれないらしいので、別の関数に脆弱性のある処理を書いてmainから呼べばいいらしい。

早速試す。 プログラムを書き換えて、以下のような感じにした。 main関数の中身を丸ごと取り出しただけで、処理は同じになる。

#include <stdio.h>

void vulnerable_func(){
  char buffer[32];  
  fgets(buffer, 128, stdin);
  return;
}

int main(int argc, char** argv){
  vulnerable_func();
  return 0;
}

gdbで実行し、50文字の文字列を入力するとeipが書き変わっているのを確認できた。 これで問題なく練習できるはず。

Boostnoteからメモを取り出す

その名の通りBoostnoteからメモを取り出す。 ホームディレクトリにあるBoostnote内にデータがあるが、ディレクトリやファイル名はハッシュか何かで変更されているため、そのままじゃ識別できない。 しかし、jsonとcsonで管理しているようなので、pythonで取り出してくるスクリプトを書いた。 githubにあげておく。

cl-mstdn

cl-mstdn

common-lisp製mastodonAPI用ライブラリ。 どんどん変更されるはず。

(defparameter client-token (request-client-token "インスタンス名")) ;; クライアントごとに一回でオーケー
(defparameter access-token (register-client "インスタンス名" client-token "たぶん登録したメアド" "パスワード")) ;; このトークンは以後インスタンスサーバへのアクセスで使う、保存しておく(保存するメソッドはない)

随時加筆

C++ 記法

C++

C言語は分かっているものとして書いているので、変数の宣言なんかは説明しない。 自分向け。 参考文献は、猫でもわかる C++プログラミング

ライブラリのインクルード

#include <iostream>

Cのライブラリは、xxx.hならcxxxとしてインクルードできる。

ネームスペース

using namespace std;

  • Integer
    • short
    • int
    • long
  • Floating Point
    • float
    • double
    • long double
  • Char
    • char
  • Boolean
    • bool
      • true
      • false

制御構文

for

for(int i = 0; i < 10: i++){
    cout << i << endl;
}

初期化部で変数宣言が可能に

関数

プロトタイプ宣言

int add(int);

引数の指定は型名だけで十分

デフォルト値

int func(int a = 100, int b = 100){
...
}

引数が渡されなかった場合、指定した値が使われる。 関数プロトタイプ宣言のときは、プロトタイプ宣言でデフォルト値を指定する。

int func(int = 10, int = 3);

...

int func(int a, int b){
...
}

オーバーロード

引数の個数や型が異なれば同じ名前の関数を定義できる。 ただし、戻り値だけが異なる場合は同じものと見做される。 デフォルト引数を取る同名の関数との間で分かりづらいコンパイルエラーや動作を引き起しうる。 同時に使用しないようにするべき。

const

引数にconstを付けると

記憶クラス指定子

  • static
    • プログラム終了まで同一の参照を保持する
  • extern
    • 別ファイルで宣言された変数を参照したいときに
  • auto
    • 自動変数はスタック領域に保存される、ブロック内で有効
    • たんなるローカル変数
  • register

ポインタ

参照

別名を付けられる

int a;
int &alias = a;

/*
 int a;
 int &alias;
 alias = a;
 error
 */

変更は元の引数にも反映される 関数を呼び出した先でも同じことができる

関数の呼び出し先で変数の値を変更して欲しくないときは、constを付ける

メモリ

動的確保

int *a;
a = new int[4]; // malloc
delete [] a; // free

クラス

宣言

class Test {
private:
  char* private_name = "private";
  char* privateFunc() {
    return private_name;
  }
  
public:
  char* public_name = "public";
  void printPrivate(){
    std::cout << private_name << endl;
    return;
  }

  void callPrivate(){
    std::cout << privateFunc() << endl;
    return;
  }
};

次の様に関数の中身を分離できる。

class Test {
private:
  char* private_name = "private";
  char* privateFunc();
  
public:
  char* public_name = "public";
  void printPrivate();
  void callPrivate();
};

char* Test::privateFunc(){
  return private_name;
}

void Test::printPrivate(){
  std::cout << private_name << endl;
  return;
}

void Test::callPrivate(){
  std::cout << privateFunc() << endl;
  return;
}

inlineを付ければ、クラス内に書いたのと同じ扱いになる。

変数宣言

Test t;

コンストラクタ

コンストラクタはオーバロードできる

引数なし

class Const {
public:
  int a;
  Const();
};

Const::Const() {
  a = 100;
}

int main(){
  Const c;
  std::cout << c.a << endl;
  return 0;
}

引数あり

class Const {
public:
  int a;
  Const(int);
};

Const::Const(int i) {
  a = i;
}

int main(){
  Const c(101);
  std::cout << c.a << endl;
  return 0;
}

コピーコンストラクタ

代入の際には、tのコピーコンストラクタが呼ばれる

Test t;
Test t_copy = t;

定義は以下

class Test {
public:
  int* array = {0, 1, 2, 3, 4, 5};
  Test();
  Test(const Test &t);
};

Test::Test(){
  array[0] = 10;
}

Test::Test(const Test &t){
  array[0] = t.array[0];
}

自分と同じ型の参照を受け取るコンストラクタとして実装される

デストラクタ

class Test {
public:
  Test();
  ~Test(); // destractor
}

thisポインタ

メンバ関数内で、自分自身への参照を保持する

static

クラス関数、クラス変数を定義する

メンバポインタ

int x::*ptr;
ptr = &x::a;

継承

class Parent {
private:
  int p = 1;
protected:
  int protect = 2;
public:
  int pub = 3; 
};

class Child : public Parent {
    ...
}
コンストラクタ

コンストラクタを指定する

Test() : Base(4) {
    ...
}
仮想関数

virtualと付ける こうすると、ポインタが実際に指している型のメンバにアクセスできる

純仮想関数

virtual funcName = 0; 抽象クラスのような使い方

フレンド関数

friendと付けて指定する。 クラス内で定義することで、そのクラスの非公開メンバにアクセスすることのできる関数を指定する friend int funcName(int arg);

フレンドクラス

クラスA内で定義する

friend class B;

B内ではAの非公開メンバにアクセスできる。 Bを継承したクラスではB経由でAにアクセスすることはできるが、Aに直接アクセスすることはできない。

多重継承

できる

菱形継承

できる

演算子オーバーロード

予約語operatorを使う 加算はこんな感じ int operator + (int a, int b){return a + b}

代入演算子も複合代入演算子オーバーロードできる。 メンバ関数として宣言することも、通常の関数としても宣言でき、中置記法で記述できる。

フレンド関数

通常の関数として宣言し、クラス内でフレンド関数としておけば、非公開メンバへのアクセスもしやすい。

単項演算子

クラス内では引数なしの関数のように宣言するとできる。

インクリメンタル

Vector:operator ++();
Vector:operator ++(int);

ファイルI/O

lib-fstream

#include <fstream>

int main(){
    ofstream of;
    of.open("filename", ios:app) // append
    if(!of)
        return -1;
    of << "test" << endl;
    of.close();
    
    return 0;
}

テンプレート

型抽象化 予約語templateを使う template <typename T> return-type funcname(args){......}

テンプレートクラス

同様 typeidを使うと、現在の型が分かる

標準テンプレートライブラリ

例外処理

try{
    if(...)
        throw val;      
} catch(type <val-name>) {
    error-handling
}

ラムダ式

[]でラムダ式の宣言を始める。 続いて引数、返り値の型(省略可能)、関数本体を定義する。 呼び出すときは通常の関数のように引数を渡す。

int main(){
  [](int a, int b) -> int {return a + b;}(1, 3);

  return 0;
}

の中に&を付けると、スコープ内の変数を参照として補足する。 =を付けると、値をコピーする。=を付けたとき、引数の()の後にmutableと付けると、補足した変数を変更できるようになる。ただし、参照元は変更されない。 の中では、変数ごとに参照かコピーかを決定できる。参照の場合は&を変数名のあとに付ける。