hi, siri~

reverse

puzzle

mips32,ida7.5可反汇编 。

输入首先经过变种base64解码,解码后必须是0~9,然后走check函数。

image-20201228082701679

check函数:

image-20201228082739340

table的数据为4, 0, 3, 7, 2, 6, 8, 1, 5。

base64解码后的字符必须是2468,来控制方向。

就是个滑块,棋盘共9块区域,3*3,然后只有8块拼图,一块空白,拼图可以移动到空白处。

sub_400ffc验证table是否是123456780:

image-20201228083023801

所以就是移动拼图,最终状态为123456780。

QQ图片20201228084841

算法上这叫八数码。网上找个轮子直接跑状态。

#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

image-20201228083529955

跟进去

image-20201228083730699

其实就是查找字符的第一次出现的地址,减去字符串起始地址,得到偏移值,在模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)))

Snipaste_2020-12-27_22-42-58

re123

给出一个没有后缀名的文件。查文件头,是chm文件。

image-20201228084130864

改成chm后,运行,杀软提示调用了cmd和powershell。

7z可以打开chm,提取出文件。

image-20201228084324266

其中doc.htm中有构造的cmd命令,调用了powershell。

image-20201228084415686

,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的队友帮忙解码。

image-20201228084955709

如果你把本题的文件改名为doc.chm,并放到$pwd目录下,会在$env:temp目录中生成一个2020.tmp。

image-20201228085351265

本题文件的后半段是base64编码的,解码后发现是个没有文件头的pe程序。这个命令就是把这个文件解码并释放出来。

其实大多数人都是直接发现文件后半段有base64,直接解码吧。

添加pe头之后,发现是dll。里面有aes。

image-20201228090005327

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。

爆异常:

image-20201228093409216

来到此处:

image-20201228093506414

把7FFF53CC1B64的0xcc改成原来的0x48。

然后改过0xcc的这个地方,set ip。因为本来运行了0xcc,爆了异常,此时rip是0x7FFF53CC1B65了,我们得把rip改回7FFF53CC1B64。

在7FFF53CC1B64右键,然后点set ip:

image-20201228100909902

(本dll改不改都行是个特例,因为

image-20201228101156048

image-20201228101139623

刚好运行到这里时,rbx高位是0。)

然后继续运行,点NO,这个0xcc异常不要传给程序。

image-20201228093615071

然后往后就是在dllEntryPoint这个函数里了,正常调试就行。

crash

给出的是core转储文件。这题是队友做的。

我看出有个md5算法,然后找到了几个md5的值,都能在cmd5上查到,但是要收费。还走了个异或常数。

等题解吧。听说是md5查表拿到原文后再异或常数,就是flag。但我没舍得花钱去反查cmd5.

Last modification:December 28th, 2020 at 10:13 am