リバース エンジニアリングの演習の一環として、以下のプログラムを満たすユーザー名とパスワードを見つける Z3 ソルバーを作成しようとしています。誰もが参照している z3py チュートリアル (rise4fun) がダウンしているため、これは特に困難です。
#include <iostream>
#include <string>
using namespace std;
int main() {
string name, pass;
cout << "Name: ";
cin >> name;
cout << "Pass: ";
cin >> pass;
int sum = 0;
for (size_t i = 0; i < name.size(); i++) {
char c = name[i];
if (c < 'A') {
cout << "Lose: char is less than A" << endl;
return 1;
}
if (c > 'Z') {
sum += c - 32;
} else {
sum += c;
}
}
int r1 = 0x5678 ^ sum;
int r2 = 0;
for (size_t i = 0; i < pass.size(); i++) {
char c = pass[i];
c -= 48;
r2 *= 10;
r2 += c;
}
r2 ^= 0x1234;
cout << "r1: " << r1 << endl;
cout << "r2: " << r2 << endl;
if (r1 == r2) {
cout << "Win" << endl;
} else {
cout << "Lose: r1 and r2 don't match" << endl;
}
}
バイナリのアセンブリからそのコードを取得しました。間違っているかもしれませんが、ソルバーの作成に集中したいと思います。最初の部分から始めて、 を計算するだけですr1
。これが私が持っているものです:
from z3 import *
s = Solver()
sum = Int('sum')
name = Array('name', IntSort(), IntSort())
for c in name:
s.add(c < 65)
if c > 90:
sum += c - 32
else:
sum += c
r1 = Xor(sum, 0x5678)
print s.check()
print s.model()
私が主張しているのは、配列に「A」未満の文字がないということだけです。そのため、65 より大きい数値を持つ任意のサイズの配列が返されることを期待しています。
主に無限ループであるため、明らかにこれは完全に間違っています。また、合計が 0 に初期化されているかどうかわからないため、合計を正しく計算しているかどうかもわかりません。誰かがこの最初のループを機能させる方法を理解するのを手伝ってくれますか?
編集:上記の C++ コードに近い z3 スクリプトを取得できました。
from z3 import *
s = Solver()
sum = 0
name = Array('name', BitVecSort(32), BitVecSort(32))
i = Int('i')
for i in xrange(0, 1):
s.add(name[i] >= 65)
s.add(name[i] < 127)
if name[i] > 90:
sum += name[i] - 32
else:
sum += name[i]
r1 = sum ^ 0x5678
passwd = Array('passwd', BitVecSort(32), BitVecSort(32))
r2 = 0
for i in xrange(0, 5):
s.add(passwd[i] < 127)
s.add(passwd[i] >= 48)
c = passwd[i] - 48
r2 *= 10
r2 += c
r2 ^= 0x1234
s.add(r1 == r2)
print s.check()
print s.model()
このコードにより、正しいユーザー名とパスワードを得ることができました。ただし、ユーザー名には 1、パスワードには 5 の長さをハードコーディングしました。長さをハードコードする必要がないように、スクリプトを変更するにはどうすればよいですか? また、プログラムを実行するたびに異なるソリューションを生成するにはどうすればよいでしょうか?