N1CTF2021 - Easyre
很多汇编码 IDA 反编译不了,查询后得知 v 开头的一系列指令来自 AVX,遂给 IDA 安装 Micro AVX,然而无济于事。考虑直接逆汇编码。
步骤一:Extend
大体观察一下可以发现,整个流程就是对一个 int128 进行加密,但是储存加密结果的寄存器可能会改变,所以首先考虑写一个程序去追踪它被储存到了哪个寄存器,形如 aesenclast xmm4,xmm0,xmmX 这样,也就是操作数 1 与操作数 2 和操作数 3 都不相同的指令就意味着加密结果从 xmm0 转移到了 xmm4,把它拆分成两个指令,即 aesenclast xmm0,xmm0,xmmX 和 movdqa xmm4,xmm0。而另一种指令形如 aesenclast xmm0,xmm0,xmmX 则不意味着主寄存器的改变,于是我们把所有主寄存器改变的指令集中到了 vmovdqa 这一种上。
#include <cstdio>
#include <iostream>
#include <string>
#include <deque>
using namespace std;
string str;
deque<string> dq;
struct opt{
string opt0, opta, optb;
opt(){}
opt(const string &str){
opt0 = str.substr(str.find_last_of(' ') + 1, str.find(',') - str.find_last_of(' ') - 1);
opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
optb = str.substr(str.find_last_of(',') + 1);
if(optb.back() == 10) optb.pop_back();
}
opt(const string &str, bool dual){
opt0 = str.substr(str.find_last_of(' ') + 1, str.find(',') - str.find_last_of(' ') - 1);
opta = str.substr(str.find_last_of(',') + 1);
if(opta.back() == 10) opta.pop_back();
}
};
int line;
string reg = "xmm0";
int main(){
freopen("nottobeworked.asm", "r", stdin);
while(getline(cin, str)){
if(str.find("vmovdqa") != string::npos){
opt optnow(str, 1);
if(optnow.opta == reg) reg = optnow.opt0;
dq.push_back(str + "\n");
}else if(str.find("aesimc") != string::npos){
opt optnow(str, 1);
if(optnow.opt0 != reg && optnow.opta == reg){
cout << "ERROR";
dq.push_back(" aesimc " + reg + "," + reg + "\n");
dq.push_back(" vmovdqa " + optnow.opt0 + "," + reg + "\n");
reg = optnow.opt0;
}else dq.push_back(str + "\n");
}else{
opt optnow(str);
if(optnow.opt0 != reg && (optnow.opta == reg || optnow.optb == reg)){
str.replace(str.find(optnow.opt0), optnow.opt0.length(), reg);
dq.push_back(str + "\n");
dq.push_back(" vmovdqa " + optnow.opt0 + "," + reg + "\n");
reg = optnow.opt0;
}else dq.push_back(str + "\n");
}
if(reg != "xmm0") cout << reg << endl;
}
cout << reg;
freopen("extended.asm", "w", stdout);
while(!dq.empty()){
cout << dq.front();
dq.pop_front();
}
return 0;
}
步骤二:Replace
然后还有形如 vpxor xmm1,xmm2,xmm15 这样的指令,它不代表任何主寄存器的改变,仅仅是求了一个异或的中间结果,但是这样做的问题是 xmm1 寄存器上原来的值会丢失,因此我的做法是,先进行一次假加密操作,但是这次操作中会把每个被覆盖的值先备份到栈中,也就是把这条指令扩展成 movdqa [rsp+xxx],xmm1 和 vpxor xmm1,xmm2,xmm15。最后在解密操作的时候把它的值复原。
由于求中间结果这一步对于解密来说毫无意义,因此我们先考虑直接将 vpxor xmm1,xmm2,xmm15 替换成 movdqa [rsp+xxx],xmm1。
#include <cstdio>
#include <iostream>
#include <string>
using namespace std;
int alloc = 16;
string str;
string int2str(int x){
string res;
while(x){
int num = x % 16;
string pre;
pre.push_back(num < 10 ? '0' + num : 'A' + num - 10);
res = pre + res;
x /= 16;
}
if(isupper(res[0])) res = "0" + res;
return res;
}
struct opt{
string opt0, opta, optb;
opt(){}
opt(const string &str){
opt0 = str.substr(str.find_last_of(' ') + 1, str.find(',') - str.find_last_of(' ') - 1);
opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
optb = str.substr(str.find_last_of(',') + 1);
if(optb.back() == 10) optb.pop_back();
}
opt(const string &str, bool dual){
opt0 = str.substr(str.find_last_of(' ') + 1, str.find(',') - str.find_last_of(' ') - 1);
opta = str.substr(str.find_last_of(',') + 1);
if(opta.back() == 10) opta.pop_back();
}
};
int main(){
freopen("extended.asm", "r", stdin);
freopen("replaced.asm", "w", stdout);
while(getline(cin, str)){
if(str.find("aesimc") != string::npos || str.find("vmovdqa") != string::npos){
int offset = str.find("aesimc") != string::npos ? 8 : 9;
string reg = str.substr(offset, str.find(',') - offset);
string output = " vmovdqa ";
output += "[rsp+" + int2str(alloc) + "h]," + reg;
alloc += 16;
cout << output << endl;
if(str.find("vmovdqa") != string::npos) cout << str << endl;
}else if(str.find("vpxor") != string::npos){
string opt0 = str.substr(7, str.find(',') - 7);
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
string optb = str.substr(str.find_last_of(',') + 1);
if(opt0 != opta && opt0 != optb){
string reg = opt0;
string output = " vmovdqa ";
output += "[rsp+" + int2str(alloc) + "h]," + reg;
alloc += 16;
cout << output << endl;
}else cout << str << endl;
}else if(str.find("vpaddq") != string::npos || str.find("vpsubq") != string::npos){
string opt0 = str.substr(8, str.find(',') - 8);
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
string optb = str.substr(str.find_last_of(',') + 1);
if(opt0 != opta && opt0 != optb){
string reg = opt0;
string output = " vmovdqa ";
output += "[rsp+" + int2str(alloc) + "h]," + reg;
alloc += 16;
cout << output << endl;
}else cout << str << endl;
}else if(str.find("vpshufd") != string::npos){
string opt0 = str.substr(9, str.find(',') - 9);
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
if(opt0 != opta){
string reg = opt0;
string output = " vmovdqa ";
output += "[rsp+" + int2str(alloc) + "h]," + reg;
alloc += 16;
cout << output << endl;
}else cout << str << endl;
}else cout << str << endl;
}
}
步骤三:Add
这一步执行的就是上一步第一段提到的内容。需要注意不能从栈直接赋值到栈。
#include <cstdio>
#include <iostream>
#include <string>
using namespace std;
int alloc = 16;
string str, thereg;
string int2str(int x){
string res;
while(x){
int num = x % 16;
string pre;
pre.push_back(num < 10 ? '0' + num : 'A' + num - 10);
res = pre + res;
x /= 16;
}
if(isupper(res[0])) res = "0" + res;
return res;
}
int main(){
freopen("extended.asm", "r", stdin);
freopen("added.asm", "w", stdout);
while(getline(cin, str)){
thereg = "xmm15";
if(str.find("aesimc") != string::npos || str.find("vmovdqa") != string::npos){
int offset = str.find("aesimc") != string::npos ? 8 : 9;
string reg = str.substr(offset, str.find(',') - offset);
if(reg == thereg) thereg = "xmm14";
cout
<< " vmovdqa [rsp]," + thereg + "\n"
<< " vmovdqa " + thereg + "," << reg << '\n'
<< " vmovdqa [rsp+" << int2str(alloc) << "h]," + thereg + "\n"
<< " vmovdqa " + thereg + ",[rsp]\n"
;
alloc += 16;
}else if(str.find("vpxor") != string::npos){
string opt0 = str.substr(7, str.find(',') - 7);
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
string optb = str.substr(str.find_last_of(',') + 1);
if(opt0 != opta && opt0 != optb){
string reg = opt0;
if(reg == thereg) thereg = "xmm14";
cout
<< " vmovdqa [rsp]," + thereg + "\n"
<< " vmovdqa " + thereg + "," << reg << '\n'
<< " vmovdqa [rsp+" << int2str(alloc) << "h]," + thereg + "\n"
<< " vmovdqa " + thereg + ",[rsp]\n"
;
alloc += 16;
}
}else if(str.find("vpaddq") != string::npos || str.find("vpsubq") != string::npos){
string opt0 = str.substr(8, str.find(',') - 8);
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
string optb = str.substr(str.find_last_of(',') + 1);
if(opt0 != opta && opt0 != optb){
string reg = opt0;
if(reg == thereg) thereg = "xmm14";
cout
<< " vmovdqa [rsp]," + thereg + "\n"
<< " vmovdqa " + thereg + "," << reg << '\n'
<< " vmovdqa [rsp+" << int2str(alloc) << "h]," + thereg + "\n"
<< " vmovdqa " + thereg + ",[rsp]\n"
;
alloc += 16;
}
}else if(str.find("vpshufd") != string::npos){
string opt0 = str.substr(9, str.find(',') - 9);
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
if(opt0 != opta){
string reg = opt0;
if(reg == thereg) thereg = "xmm14";
cout
<< " vmovdqa [rsp]," + thereg + "\n"
<< " vmovdqa " + thereg + "," << reg << '\n'
<< " vmovdqa [rsp+" << int2str(alloc) << "h]," + thereg + "\n"
<< " vmovdqa " + thereg + ",[rsp]\n"
;
alloc += 16;
}
}
cout << str << endl;
}
return 0;
}
步骤四:Reverse
这一步需要做的是将每一条指令解释成对应的逆向指令,并用一个栈来倒指令。比较重要的是 AES 相关的指令,这些指令不能找到对应的逆向指令,但是可以用多个指令排列组合达到同样的效果。更具体一点,把每个指令分解成 AES 的四个经典步骤,然后排列组合。
#include <string>
#include <iostream>
#include <stack>
#include <map>
#include <vector>
#include <utility>
using namespace std;
vector<pair<string, string>> pr;
unsigned char cand[4] = {0, 1, 2, 3};
bool used[4];
unsigned char id[4];
char half2hex(unsigned char x){
return x < 10 ? x + '0' : x + 'A' - 10;
}
string char2hex(unsigned char x){
string ret;
ret.push_back(half2hex(x >> 4));
ret.push_back(half2hex(x & 0xF));
return ret;
}
void dfs(int i, unsigned char key){
if(i < 0){
unsigned char res = 0;
for(int i = 0; i < 4; i++)
res |= id[i] << (i << 1);
string keystr = char2hex(key);
string resstr = char2hex(res);
if(isupper(keystr[0])) keystr = "0" + keystr;
if(isupper(resstr[0])) resstr = "0" + resstr;
pr.push_back(make_pair(keystr, resstr));
return;
}
for(int j = 0; j < 4; j++) if(!used[j]){
used[j] = true;
id[cand[j]] = i;
dfs(i - 1, key | (cand[j] << (i << 1)));
used[j] = false;
}
}
void rev_shuf(){
dfs(3, 0);
}
string keep, reg, str, thereg;
stack<string> s;
int line;
int main(){
rev_shuf();
freopen("replaced.asm", "r", stdin);
while(getline(cin, str)){
thereg = "xmm15";
line++;
if(str.find("vmovdqa") != string::npos){
string opta = str.substr(9, str.find(',') - 9);
string optb = str.substr(str.find(',') + 1);
swap(opta, optb);
if(thereg == opta || thereg == optb) thereg = "xmm14";
if(thereg == opta || thereg == optb) thereg = "xmm13";
s.push(
" vmovdqa [rsp]," + thereg + "\n"
+ " vmovdqa " + thereg + "," + optb + "\n"
+ " vmovdqa " + opta + "," + thereg + "\n"
+ " vmovdqa " + thereg + ",[rsp]\n"
);
}else if(str.find("vpxor") != string::npos){
s.push(str + "\n");
}else if(str.find("vaesdeclast") != string::npos){
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
string optb = str.substr(str.find_last_of(',') + 1);
s.push(
" pxor " + opta + "," + optb + "\n"
+ " aesenclast " + opta + "," + optb + "\n"
+ " pxor " + opta + "," + optb + "\n"
);
}else if(str.find("vaesdec") != string::npos){
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
string optb = str.substr(str.find_last_of(',') + 1);
s.push(
" pxor " + opta + "," + optb + "\n"
+ " aesdeclast " + opta + "," + optb + "\n"
+ " pxor " + opta + "," + optb + "\n"
+ " aesenc " + opta + "," + optb + "\n"
+ " pxor " + opta + "," + optb + "\n"
+ " aesenclast " + opta + "," + optb + "\n"
+ " pxor " + opta + "," + optb + "\n"
);
}else if(str.find("vaesenclast") != string::npos){
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
string optb = str.substr(str.find_last_of(',') + 1);
s.push(
" pxor " + opta + "," + optb + "\n"
+ " aesdeclast " + opta + "," + optb + "\n"
+ " pxor " + opta + "," + optb + "\n"
);
}else if(str.find("vaesenc") != string::npos){
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
string optb = str.substr(str.find_last_of(',') + 1);
s.push(
" pxor " + opta + "," + optb + "\n"
+ " aesimc " + opta + "," + opta + "\n"
+ " aesdeclast " + opta + "," + optb + "\n"
+ " pxor " + opta + "," + optb + "\n"
);
}else if(str.find("vpshufd") != string::npos){
for(auto x : pr) if(str.find(x.first) != string::npos){
str.replace(str.find(x.first), x.first.length(), x.second);
break;
}
s.push(str + "\n");
}else if(str.find("vpaddq") != string::npos){
str.replace(str.find("add"), 3, "sub");
string opt0 = str.substr(8, str.find(',') - 8);
string opta = str.substr(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1);
string optb = str.substr(str.find_last_of(',') + 1);
if(opt0 != opta){
str.replace(str.find(',') + 1, str.find_last_of(',') - str.find(',') - 1, optb);
str.replace(str.find_last_of(',') + 1, str.length() - str.find_last_of(',') - 1, opta);
}
s.push(str + "\n");
}else if(str.find("vpsubq") != string::npos){
str.replace(str.find("sub"), 3, "add");
s.push(str + "\n");
}else cout << "ERROR " << line << endl;
}
freopen("reversework.asm", "w", stdout);
while(!s.empty()){
cout << s.top();
s.pop();
}
return 0;
}
步骤五
将 Add 的结果和 Reverse 的结果拼接起来,即可获得一套完整的加解密回路。

浙公网安备 33010602011771号