[BUUCTF] REVERSE- [GXYCTF2019] simple CPP

[GXYCTF2019]シンプルCPP

別館

ステップ

  1. 定期検査、64ビットプログラム、シェルなし
    ここに画像の説明を挿入します
  2. プログラムを実行して、一般的な状況を確認してください
    ここに画像の説明を挿入します
  3. 64ビットのidaがロードされたら、最初に文字列を取得してフラグに関する情報を表示します。フラグに関する
    ここに画像の説明を挿入します
    文字列によると、キー関数
    main()が長すぎるため、直接投稿しません。セクションを見てください。セクションごと。
  4. 逆に、プログラムを下から見なければなりません。
    1つ目はフラグの判定についてです。168の判定が満たされれば正しいフラグです
    ここに画像の説明を挿入します
    。sub_7FF603EF19C0()関数でフラグがGXY {}でラップされています。関数が長すぎます。計算ではありません。 。自分で作成した関数関数である必要があります。sub_7FF603EF19C0と組み合わせて呼び出すと、この関数の関数はパラメータ文字列を出力することであると推測されます。171〜175によると、フラグの文字列はブロックに格納されていると推測できます
  5. if判定のさまざまなパラメータがどこから来ているかを調べて確認します
    ここに画像の説明を挿入します

アルゴリズムは少し面倒です。整理して
くださいv38 = v18 [2];
v19 = v18 [1];
v20 = v18 [0];

v22 = v19&v20
v21 [0] = v19&v20
getv22=v21[0]= v19 & v20=v18[1]&v18[0]

v23 = v38&〜v20
v21 [1] = v23;
getv23 =v21[1]= v38 & ~v20=v18[2]&~v18[0]

v24 = 〜v19;
取得v24 = ~v19=~v18[1]

v25 = v38&v24;
v21 [2] = v38&v24;
取得v21[2] = v38 & v24=v25=v18[2]& ~v18[1]

v26 = v20&v24;
v21 [3] = v26;
getv26 = v20 & v24=v21[3]=v18[0]&~v18[1]

v23 = v21 [1] = 1176889593874
getv18[2]&~v18[0]=1176889593874

v27 = v23 | v22 | v25 | v26
取得v27 =(v18[1]&v18[0]) | (v18[1]&v18[0]) |(v18[2]& ~v18[1]) | (v18[0]&~v18[1])=4483974544037412639

V18 = V28 [。1];
V29 = V18 [2];
V30&V25 = V18 * |&V29(V22 | V28&V18 *〜|〜(V28 | V18 *))= 577031497978884115i64
givev30 =(v18[2]& ~v18[1]) & v18[0] | v18[2] & ((v18[1]&v18[0]) | v18[1] & ~v18[0] | ~(v18[1] | v18[0]))=577031497978884115

v31 = v27 == 4483974544037412639i64;
v27 ^ v18 [3] == 4483974543195470111i64)を
取得するにはv183^(v18[1]&v18[0]) | (v18[1]&v18[0]) |(v18[2]& ~v18[1]) | (v18[0]&~v18[1])=4483974544037412639

最後に、すべての式がv18 [0]、v18 [1]、v18 [2]、v18 [3]に置き換えられて表現されます。z3ライブラリを使用して、この方程式を解きます
。x、y、z、wはv18 [0] 〜v18 [3]を表します。

 from z3 import *

x,y,z,w=BitVecs('x y z w',64)

s=Solver()

s.add((~x)&z==1176889593874)
s.add(((z&~x)|(x&y)|(z&(~y))|(x&(~y)))^w==4483974543195470111)
s.add(((z&~y)&x|z&((x&y)|y&~x|~(y|x)))==577031497978884115)
s.add(((z&~x)|(x&y)|(z&~y)|(x&~y))==4483974544037412639)
s.add(((z&(~x)) | (x&y) | y & z) == (((~x)& z)|864693332579200012))

s.check()
m = s.model()
for i in m:
    print("%s = 0x%x"%(i,m[i].as_long()))

ここに画像の説明を挿入します

  1. 私たちの目的は、ブロック内の値を見つけ、
    ここに画像の説明を挿入します
    v18のデータがどこから来ているかを調べ続けることです。v18のデータv11、v12、v13、v14から来ており、v11のデータの値は関連していますV40とV6に。、ルックアップするために続けます。
    ここに画像の説明を挿入します
    プログラムは、我々は、操作の開始時に見た2つの文の出力に先頭にsub_7FF603EF19C0()を呼び出します。実行時に推測によると、sub_7FF603EF1DE0()の関数は、私たちにできるようにすることですデータを入力し、ブロックに保存します。51行目と53行目の推測によると、v40-5は25より大きく、5はGXY {}である必要があるため、フラグの長さは25で、v40に格納されているフラグの長さです。
    次に、76行目から76行目から85行目まで、入力したデータをqword_7FF603EF6048XORして、v6に格納する必要があります。
    qword_7FF603EF6048を直接クリックして、値が0であることを確認しますが、0 XORの任意の数はそれ自体であり、それ自体が間違っていると感じますが、それでも試しました.0に等しい場合、v11の値は値ですv6では。これは入力した文字列です。つまり、z3で解く数値がフラグであり、文字列
    変換するのは明らかに間違っています
    ここに画像の説明を挿入します
    したがって、qword_7FF603EF6048を割り当てる関数がプログラムに存在する必要があります。
    パラメータを右クリックし、[外部参照にジャンプ]を選択して、パラメータ
    ここに画像の説明を挿入します
    参照されている場所を確認します
    ここに画像の説明を挿入します
    。2つの関数に割り当てられているsub_7FF603EF1720関数があることがわかりますmemcpyとmemoveの方が目立ちますが、バイトのコピーによく使用されるので、qword_7FF603EF6048 = i_will_check_is_debug_or_notの場合は、i_will_check_is_debug_or_notをqword_7FF603EF6048にコピーする必要があります。
Dst = 'i_will_check_is_debug_or_noi_wil'
flag=[0x3E,0x3A,0x46,0x05,0x33,0x28,0x6F,0x0D,0x8C,0x00,0x8A,0x09,0x78,0x49,0x2C,0xAC,0x08,0x02,0x07,0x17,0x15,0x3E,0x30,0x13,0x32,0x31,0x06]
s=''
for i in range(len(flag)):
    s+=chr(ord(Dst[i]) ^ flag[i])
    #s+=chr(flag[i])
print(s)

ここに画像の説明を挿入します
途中で文字化けしたコードを見ると、奇妙で​​、トリックはありません。Baiduは、他のマスターwpを見た後、方程式が複数の解であり、2番目の部分の結果が競技中のe!P0or_aであることを学びました。

from z3 import *

x,y,z,w=BitVecs('x y z w',64)

s=Solver()

s.add((~x)&z==1176889593874)
s.add(((z&~x)|(x&y)|(z&(~y))|(x&(~y)))^w==4483974543195470111)
s.add(((z&~y)&x|z&((x&y)|y&~x|~(y|x)))==577031497978884115)
s.add(((z&~x)|(x&y)|(z&~y)|(x&~y))==4483974544037412639)
s.add(((z&(~x)) | (x&y) | y & z) == (((~x)& z)|864693332579200012))

s.check()
m = s.model()
for i in m:
    print("%s = 0x%x"%(i,m[i].as_long()))

w = [0x32,0x31,0x06]
z = [0x08,0x02,0x07,0x17,0x15,0x3E,0x30,0x13]
y = "e!P0or_a"
x = [0x3e,0x3a,0x46,0x05,0x33,0x28,0x6f,0x0d]
Dst = 'i_will_check_is_debug_or_noi_wil'

flag=[0x3E,0x3A,0x46,0x05,0x33,0x28,0x6F,0x0D,0x8C,0x00,0x8A,0x09,0x78,0x49,0x2C,0xAC,0x08,0x02,0x07,0x17,0x15,0x3E,0x30,0x13,0x32,0x31,0x06]
s=''
for i in range(len(flag)):
    s+=chr(ord(Dst[i]) ^ flag[i])
    #s+=chr(flag[i])
print(s)

b=""
for i in range(len(x)):
    b+=chr(ord(Dst[i]) ^ x[i])
print(b)

ここに画像の説明を挿入します
We1l_D0nから、äeéb'_óをeに変更します!P0or_aが最後のフラグです

フラグ{We1l_D0ne!P0or_algebra_am_i}

優先wp:https://www.cnblogs.com/LLeaves/p/13522069.html

このマスターはこの問題を動的に実行し、プログラムのロジックを理解する方が簡単であることがわかりました。ええと、実はidaの使い方がわかりません
ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/mcmuyanga/article/details/113628506