这是第七届职业技能竞赛山东省赛的一道赛题, 价值2分.
逆向分析
首先拖入Jadx中看源码, 代码很简单, 也没做混淆. 主要逻辑的代码如下所示:
//.....
byte[] passwordhash = MainActivity.stringToMD5(password.toString());
Toast.makeText(MainActivity.this.getApplicationContext(), "解密flag文件,如果你输入的口令正确,那么你会在解密文件中看到flag的:)", 1).show();
InputStream infile = MainActivity.this.getResources().openRawResource(R.raw.flag);
int data_lenth = 0;
byte[] buffer = new byte[0];
try {
data_lenth = infile.available();
buffer = new byte[data_lenth];
infile.read(buffer);
infile.close();
} catch (IOException e) {
e.printStackTrace();
}
for (int i = 0; i < data_lenth; i++) {
buffer[i] = (byte) (buffer[i] ^ passwordhash[i % 16]);
}
try {
FileOutputStream outfile = MainActivity.this.openFileOutput("flag.docx", 1);
outfile.write(buffer);
outfile.close();
//.....
由于不知道密码, 遂打算设法直接爆破. 通过代码可以看到, flag存储在资源文件flag.docx, 该文件是加密存储的. 加密方法是每16个字节均依次与一个长度为16的byte数组(key)进行异或运算. 因此, 只需要设法找到这个长度为16的key数组, 即可解密该文件.
解密思路是, 找一个正常的docx文件, 根据它的文件结构, 找出docx文件中固定的部分, 将加密文件中相应位置与这部分进行异或运算, 最终得到key. 要注意的是, 这里在寻找固定部分时, 应该确保能够使地址 mod 16
将0..15
这个数组填满.
使用HxD打开两个不同的正常的docx文件:
文件1 | 文件2 |
---|---|
可以发现, 在地址为
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
ps: 这里打开的docx文件是用office 2019版默认保存的docx文件, 尝试过使用WPS默认保存的docx文件, 但是发现其结构和题目中的docx文件是不一样的, 如下所示:
WPS的文件 | Office的文件 | 加密的文件 |
---|---|---|
很明显加密文件的结构和Office的文件更相似
在apk中提取出加密文件flag.docx, 编写程序解密该文件.
$flagdoc = [System.IO.File]::ReadAllBytes(".\flag.docx");
$realdoc = [System.IO.File]::ReadAllBytes(".\real.docx");
$key = @();
$output = @();
foreach ($i in 0x40..0x4f) {
$key += $flagdoc[$i]
}
foreach($i in 0..$flagdoc.Count) {
$output += ($flagdoc[$i] -bxor $key[$i % 16] );
}
[System.IO.File]::WriteAllBytes("./output.docx", $output);
最后使用Word打开output.docx, 即可得到flag (其实就是key数组:
至于这个hash的源值是什么, 在cmd5.com上查询为付费记录, 其它平台查询均无果. 用hashcat跑了跑, 奈何字典不够大, 也是没能跑出来. 就当是个谜吧.