[GXYCTF2019]シンプルCPP
ステップ
- 定期検査、64ビットプログラム、シェルなし
- プログラムを実行して、一般的な状況を確認してください
- 64ビットのidaがロードされたら、最初に文字列を取得してフラグに関する情報を表示します。フラグに関する
文字列によると、キー関数
main()が長すぎるため、直接投稿しません。セクションを見てください。セクションごと。 - 逆に、プログラムを下から見なければなりません。
1つ目はフラグの判定についてです。168の判定が満たされれば正しいフラグです
。sub_7FF603EF19C0()関数でフラグがGXY {}でラップされています。関数が長すぎます。計算ではありません。 。自分で作成した関数関数である必要があります。sub_7FF603EF19C0と組み合わせて呼び出すと、この関数の関数はパラメータ文字列を出力することであると推測されます。171〜175によると、フラグの文字列はブロックに格納されていると推測できます - 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()))
- 私たちの目的は、ブロック内の値を見つけ、
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_7FF603EF6048
XORして、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の使い方がわかりません