背景
ツールの使い方の備忘録として残したかった
Ghidraのインストール
ここから落とす
Javaのランタイムが必要なので入れてなかったら入れる
Ghidraでの解析
ghidraRun.bat
で起動できる
起動したらドラゴンくんを選択する
importFileで解析したいバイナリをインポート
いろいろ出るがとりあえずそのままOK押してくと解析してくれる
解析が終わると、ウィンドウ左のSymbolTreeのFunctionsを見てみる。
これがバイナリ内部の関数だ。
それらしい関数名はないので、main関数を選択する
真ん中のウィンドウはアセンブリなので今回は見ない。
右のDecompileウィンドウを見てみると、アセンブリをc言語に(機械的に)戻したものが表示される。
あくまでアセンブリになったものを無理くりcにしているので、変数名は適当なものが入る。
void main(void)
{
size_t sVar1;
char local_58 [23];
char local_41;
int local_2c;
FILE *local_28;
FILE *local_20;
uint local_14;
int local_10;
char local_9;
local_20 = fopen("flag.txt","r");
local_28 = fopen("rev_this","a");
if (local_20 == (FILE *)0x0) {
puts("No flag found, please make sure this is run on the server");
}
if (local_28 == (FILE *)0x0) {
puts("please run this on the server");
}
sVar1 = fread(local_58,0x18,1,local_20);
local_2c = (int)sVar1;
if ((int)sVar1 < 1) {
/* WARNING: Subroutine does not return */
exit(0);
}
for (local_10 = 0; local_10 < 8; local_10 = local_10 + 1) {
local_9 = local_58[local_10];
fputc((int)local_9,local_28);
}
for (local_14 = 8; (int)local_14 < 0x17; local_14 = local_14 + 1) {
if ((local_14 & 1) == 0) {
local_9 = local_58[(int)local_14] + '\x05';
}
else {
local_9 = local_58[(int)local_14] + -2;
}
fputc((int)local_9,local_28);
}
local_9 = local_41;
fputc((int)local_41,local_28);
fclose(local_28);
fclose(local_20);
return;
}
ここまでがGhidraでの操作。
解析したソースからフラグを推定する
ここからが本当のリバースエンジニアリング作業。
このままでは見づらいので、テキストエディタで変数名などを整形する。
たとえば、
local_20 = fopen("flag.txt","r");
local_28 = fopen("rev_this","a");
なんかはfopen
しているので入出力ファイルだとわかる。
sVar1 = fread(local_58,0x18,1,local_20);
fread
しているので、local_58
は入力ファイルの文字列が入ると推測できる。
そんなこんなで整形した結果が以下。
void main(void)
{
size_t sVar1;
char input_char_array [23];
char local_41;
int local_2c;
FILE *output_file;
FILE *input_file;
uint j;
int i;
char enc_char;
input_file = fopen("flag.txt","r");
output_file = fopen("rev_this","a");
if (input_file == (FILE *)0x0) {
puts("No flag found, please make sure this is run on the server");
}
if (output_file == (FILE *)0x0) {
puts("please run this on the server");
}
sVar1 = fread(input_char_array,0x18,1,input_file);
local_2c = (int)sVar1;
// 読み込んだ文字がないとき
if ((int)sVar1 < 1) {
/* WARNING: Subroutine does not return */
exit(0);
}
// 暗号化処理
for (i = 0; i < 8; i = i + 1) { // 8回ループ
// 1文字目~8文字目まで
enc_char = input_char_array[i];
fputc((int)enc_char,output_file);
}
for (j = 8; (int)j < 0x17; j = j + 1) {
// 8文字目~23文字目まで
if ((j & 1) == 0) { // 下位1ビット以外をマスクして0のとき=偶数インデックスのとき
// 奇数文字目
enc_char = input_char_array[(int)j] + '\x05';
}
else { // 奇数インデックス
// 偶数文字目
enc_char = input_char_array[(int)j] + -2;
}
fputc((int)enc_char,output_file);
}
enc_char = local_41;
fputc((int)local_41,output_file);
fclose(output_file);
fclose(input_file);
return;
1~8文字目はprefixなのでそのまま。
このプログラムの暗号化アルゴリズムは
- 奇数文字目:文字コードを+5する
- 偶数文字目:文字コードを-2する
ということが分かったので、
アウトプットのファイルrev_this
の文字列を、
- 奇数文字目:文字コードを-5する
- 偶数文字目:文字コードを+2する
してあげればいい。
以上。