niscCTF_2019 write up

发布于 2019-08-18  1379 次阅读


Reverse

flat

flat_bdc004aa1104e443b3b9c90a6b98d5e6.zip

初步阅读f5代码,发现程序是由大量switch语句构成的,其中每个case都套上了很奇怪的数字,不方便静态阅读,所以我采取了动态调试的方式

发现大量诸如以下的语句

00040109E loc_40109E:                             ; CODE XREF: main:loc_4013B3↓j
.text:000000000040109E                 mov     eax, [rbp+var_154]
.text:00000000004010A4                 mov     ecx, eax
.text:00000000004010A6                 sub     ecx, 88070986h
.text:00000000004010AC                 mov     [rbp+var_164], eax
.text:00000000004010B2                 mov     [rbp+var_168], ecx
.text:00000000004010B8                 jz      loc_401270
.text:00000000004010BE                 jmp     $+5
.text:00000000004010C3 ; ---------------------------------------------------------------------------
.text:00000000004010C3
.text:00000000004010C3 loc_4010C3:                             ; CODE XREF: main+7E↑j
.text:00000000004010C3                 mov     eax, [rbp+var_164]
.text:00000000004010C9                 sub     eax, 916CBF8Ch
.text:00000000004010CE                 mov     [rbp+var_16C], eax
.text:00000000004010D4                 jz      loc_401382
.text:00000000004010DA                 jmp     $+5

动态调试后可知程序把大量的汇编代码通过这样的方式混淆了,然后再通过操作码来读取对应的功能。

000401270 loc_401270:                             ; CODE XREF: main+78↑j
.text:0000000000401270                 lea     rdi, [rbp+s]
.text:0000000000401274                 mov     rax, offset aJ  ; "J"
.text:000000000040127E                 mov     ecx, 90h
.text:0000000000401283                 mov     edx, ecx        ; n
.text:0000000000401285                 lea     rsi, [rbp+dest]
.text:000000000040128C                 mov     [rbp+var_198], rdi
.text:0000000000401293                 mov     rdi, rsi        ; dest
.text:0000000000401296                 mov     rsi, rax        ; src
.text:0000000000401299                 call    _memcpy
.text:000000000040129E                 mov     rdi, [rbp+var_198] ; char *
.text:00000000004012A5                 call    _Z10fun_check1Pc ; fun_check1(char *)
.text:00000000004012AA                 mov     ecx, 916CBF8Ch
.text:00000000004012AF                 mov     r8d, 70B25450h
.text:00000000004012B5                 test    al, 1
.text:00000000004012B7                 cmovnz  ecx, r8d
.text:00000000004012BB                 mov     [rbp+var_154], ecx
.text:00000000004012C1                 jmp     loc_4013B3
.text:00000000004012C6 ; ---------------------------------------------------------------------------
.text:00000000004012C6
.text:00000000004012C6 loc_4012C6:                             ; CODE XREF: main+190↑j
.text:00000000004012C6                 lea     rdi, [rbp+s]    ; char *
.text:00000000004012CA                 call    _Z10fun_check2Pc ; 检测格式
.text:00000000004012CF                 mov     ecx, 916CBF8Ch
.text:00000000004012D4                 mov     edx, 48DC1E73h
.text:00000000004012D9                 test    al, 1
.text:00000000004012DB                 cmovnz  ecx, edx
.text:00000000004012DE                 mov     [rbp+var_154], ecx
.text:00000000004012E4                 jmp     loc_4013B3
.text:00000000004012E9 ; ---------------------------------------------------------------------------
.text:00000000004012E9
.text:00000000004012E9 loc_4012E9:                             ; CODE XREF: main+174↑j
.text:00000000004012E9                 lea     rdi, [rbp+s]    ; char *
.text:00000000004012ED                 call    _Z10fun_check3Pc ; 限制格式
.text:00000000004012F2                 mov     ecx, 916CBF8Ch
.text:00000000004012F7                 mov     edx, 9FDEA8ECh
.text:00000000004012FC                 test    al, 1
.text:00000000004012FE                 cmovnz  ecx, edx
.text:0000000000401301                 mov     [rbp+var_154], ecx
.text:0000000000401307                 jmp     loc_4013B3
.text:000000000040130C ; ---------------------------------------------------------------------------
.text:000000000040130C
.text:000000000040130C loc_40130C:                             ; CODE XREF: main+B0↑j
.text:000000000040130C                 lea     rdi, [rbp+s]    ; char *
.text:0000000000401310                 call    _Z10fun_check4Pc ; 检查格式
.text:0000000000401315                 mov     ecx, 916CBF8Ch
.text:000000000040131A                 mov     edx, 0CDC4B0D0h
.text:000000000040131F                 test    al, 1
.text:0000000000401321                 cmovnz  ecx, edx
.text:0000000000401324                 mov     [rbp+var_154], ecx
.text:000000000040132A                 jmp     loc_4013B3
.text:000000000040132F ; ---------------------------------------------------------------------------
.text:000000000040132F
.text:000000000040132F loc_40132F:                             ; CODE XREF: main+104↑j
.text:000000000040132F                 lea     rsi, [rbp+dest] ; int *
.text:0000000000401336                 lea     rdi, [rbp+var_B0] ; char *
.text:000000000040133D                 call    _Z10fun_check5PcPi ; fun_check5(char *,int *)
.text:0000000000401342                 mov     ecx, 916CBF8Ch
.text:0000000000401347                 mov     edx, 0F465F43Bh
.text:000000000040134C                 test    al, 1
.text:000000000040134E                 cmovnz  ecx, edx
.text:0000000000401351                 mov     [rbp+var_154], ecx
.text:0000000000401357                 jmp     loc_4013B3

check1~5是整个程序的核心部分,其中check1 ~check4都是对flag格式的限制

我们只需要将call checkx 打上断点,在这个call执行完成之后,观察al的值即可判断格式是否符合要求,最终发现flag的格式是flag{},总长度共42.

最重要的是check5函数,其中

.text:0000000000400D90 loc_400D90:                             ; CODE XREF: fun_check5(char *,int *)+E9↑j
.text:0000000000400D90                 mov     eax, 0B92BF994h
.text:0000000000400D95                 mov     ecx, 0AC151DE7h
.text:0000000000400D9A                 mov     rdx, [rbp+var_10]
.text:0000000000400D9E                 movsxd  rsi, [rbp+var_E4]
.text:0000000000400DA5                 movsx   edi, byte ptr [rdx+rsi] ; 读取下一个字符
.text:0000000000400DA9                 cmp     edi, 30h
.text:0000000000400DAC                 cmovge  eax, ecx
.text:0000000000400DAF                 mov     [rbp+var_E8], eax
.text:0000000000400DB5                 jmp     loc_40102D
.text:0000000000400DBA ; ---------------------------------------------------------------------------
.text:0000000000400DBA
.text:0000000000400DBA loc_400DBA:                             ; CODE XREF: fun_check5(char *,int *)+79↑j
.text:0000000000400DBA                 mov     eax, 0B92BF994h
.text:0000000000400DBF                 mov     ecx, 0E37ECC40h
.text:0000000000400DC4                 mov     rdx, [rbp+var_10]
.text:0000000000400DC8                 movsxd  rsi, [rbp+var_E4]
.text:0000000000400DCF                 movsx   edi, byte ptr [rdx+rsi]
.text:0000000000400DD3                 cmp     edi, 39h        ; 判断是否为数字
.text:0000000000400DD6                 cmovle  eax, ecx
.text:0000000000400DD9                 mov     [rbp+var_E8], eax
.text:0000000000400DDF                 jmp     loc_40102D
.text:0000000000400DE4 ; ---------------------------------------------------------------------------
.text:0000000000400DE4
.text:0000000000400DE4 loc_400DE4:                             ; CODE XREF: fun_check5(char *,int *)+159↑j
.text:0000000000400DE4                 mov     rax, [rbp+var_10]
.text:0000000000400DE8                 movsxd  rcx, [rbp+var_E4]
.text:0000000000400DEF                 movsx   edx, byte ptr [rax+rcx]
.text:0000000000400DF3                 sub     edx, 1D854A80h
.text:0000000000400DF9                 add     edx, 11h
.text:0000000000400DFC                 add     edx, 1D854A80h
.text:0000000000400E02                 movsxd  rax, [rbp+var_E4]
.text:0000000000400E09                 mov     [rbp+rax*4+var_E0], edx
.text:0000000000400E10                 mov     [rbp+var_E8], 72D256E3h
.text:0000000000400E1A                 jmp     loc_40102D

如果输入是数字,就将数字转换为对应的大写字母

.text:0000000000400E1F loc_400E1F:                             ; CODE XREF: fun_check5(char *,int *)+B1↑j
.text:0000000000400E1F                 mov     eax, 966647E9h
.text:0000000000400E24                 mov     ecx, 0BA6BE5FDh
.text:0000000000400E29                 mov     rdx, [rbp+var_10]
.text:0000000000400E2D                 movsxd  rsi, [rbp+var_E4]
.text:0000000000400E34                 movsx   edi, byte ptr [rdx+rsi]
.text:0000000000400E38                 cmp     edi, 2Dh
.text:0000000000400E3B                 cmovz   eax, ecx
.text:0000000000400E3E                 mov     [rbp+var_E8], eax
.text:0000000000400E44                 jmp     loc_40102D

'-'符号不作处理

.text:0000000000400E75 loc_400E75:                             ; CODE XREF: fun_check5(char *,int *)+41↑j
.text:0000000000400E75                 mov     eax, 67B6BD28h
.text:0000000000400E7A                 mov     ecx, 0B80842FBh
.text:0000000000400E7F                 mov     rdx, [rbp+var_10]
.text:0000000000400E83                 movsxd  rsi, [rbp+var_E4]
.text:0000000000400E8A                 movsx   edi, byte ptr [rdx+rsi]
.text:0000000000400E8E                 cmp     edi, 61h
.text:0000000000400E91                 cmovge  eax, ecx
.text:0000000000400E94                 mov     [rbp+var_E8], eax
.text:0000000000400E9A                 jmp     loc_40102D
.text:0000000000400E9F ; ---------------------------------------------------------------------------
.text:0000000000400E9F
.text:0000000000400E9F loc_400E9F:                             ; CODE XREF: fun_check5(char *,int *)+95↑j
.text:0000000000400E9F                 mov     eax, 67B6BD28h
.text:0000000000400EA4                 mov     ecx, 7CFC4F40h
.text:0000000000400EA9                 mov     rdx, [rbp+var_10]
.text:0000000000400EAD                 movsxd  rsi, [rbp+var_E4]
.text:0000000000400EB4                 movsx   edi, byte ptr [rdx+rsi]
.text:0000000000400EB8                 cmp     edi, 7Ah
.text:0000000000400EBB                 cmovle  eax, ecx
.text:0000000000400EBE                 mov     [rbp+var_E8], eax
.text:0000000000400EC4                 jmp     loc_40102D
.text:0000000000400EC9 ; ---------------------------------------------------------------------------
.text:0000000000400EC9
.text:0000000000400EC9 loc_400EC9:                             ; CODE XREF: fun_check5(char *,int *)+271↑j
.text:0000000000400EC9                 mov     rax, [rbp+var_10]
.text:0000000000400ECD                 movsxd  rcx, [rbp+var_E4]
.text:0000000000400ED4                 movsx   edx, byte ptr [rax+rcx]
.text:0000000000400ED8                 sub     edx, 50577E63h
.text:0000000000400EDE                 sub     edx, 30h
.text:0000000000400EE1                 add     edx, 50577E63h
.text:0000000000400EE7                 movsxd  rax, [rbp+var_E4]
.text:0000000000400EEE                 mov     [rbp+rax*4+var_E0], edx
.text:0000000000400EF5                 mov     [rbp+var_E8], 67B6BD28h
.text:0000000000400EFF                 jmp     loc_40102D

如果是字母就转换为数字

最后在转换完成的字符串上面下一个内存断点,就可找到这个字符串与目标加密字符串比较的过程

加密字符串:

flag{J2261C63-3I2I-EGE4-IBCC-IE41A5I5F4HB}

解密即可。

en c = "J2261C63-3I2I-EGE4-IBCC-IE41A5I5F4HB"
flag = "flag{"

for c in enc:
    if c == '-':
        fuck = 0
    elif c >= 'A' and c <= 'J':
        c = chr(ord(c) - 0x11)
    else:
        c = chr(ord(c) + 48)
    flag = flag + c

flag += '}'
print(flag)

src_leak

src_leak_2886f9ce0464ae67bbeb52d939c9979d.zip

没学过c++语法,看这些东西看蒙了

就大概猜了一下,发现算法流程并不是很复杂

x1~x5都是用一个函数跑出来的

x6用另一个

    cout << func3< func2<x1> > << endl;
    cout << func3< func2<x2> > << endl;
    cout << func3< func2<x3> > << endl;
    cout << func3< func2<x4> > << endl;
    cout << func3< func2<x5> > << endl;

    // output: 1 1 1 1 1

    cout << _func1<x1>::result << endl;
    cout << _func1<x2>::result << endl;
    cout << _func1<x3>::result << endl;
    cout << _func1<x4>::result << endl;
    cout << _func1<x5>::result << endl;

    //output: 963 4396 6666 1999 3141

说几个比较关键的地方

template <bool Flag, class MaybeA, class MaybeB> class IfElse;

template <class MaybeA, class MaybeB>
class IfElse<true, MaybeA, MaybeB> {
public:
    using ResultType = MaybeA;
};

template <class MaybeA, class MaybeB>
class IfElse<false, MaybeA, MaybeB> {
public:
    using ResultType = MaybeB;

看起来这么大一堆,实际上就表达了一个意思

result = flag ? MaybeA:MaybeB

template <uint N, uint L> struct func1<N, L, L> { enum { result = L }; };

template<>
constexpr size_t func2<0> = 0;

template<uint m>struct TEST<0, m> {
    const static uint value = 0;
};

template<uint n>struct TEST<n, 0> {
    const static uint value = 1;
};

template<>struct func4<1> {
    const static uint value = 0;
};
template<>struct func4<2> {
    const static uint value = 1;
};

以上是各个函数的边界条件,一定不能省略,否则跑不出来或者跑出来不对,尤其是func4的1和2

因为c的执行效率比较高,抱着试一试的心态,写了个暴力算法进行求解,在int范围下尝试失败(因为涉及到了数据的平方,int类型会溢出),于是更换为long long 进行求解,虽然效率不高,但还是可以在十秒中左右得出结果。

#include<cstdio>
typedef long long LL;

LL func2(LL n){
    if(n==0) return 0;

    return n%2 + func2(n/2);
}

LL func3(LL n){
    return n%2;
}

LL func1(LL n, LL l, LL r){
    if(l==r) return l;
    LL mid = (l+r+1)/2;
    if(n < mid*mid)
        return func1(n,l,mid-1);
    else
        return func1(n,mid,r);
}

LL _func1(LL n){
    return func1(n,1,n);
}

LL nextn(LL n, LL m){
    return ((n % m != 0) * n);
}

LL nextm(LL n, LL m){
    return (m * m <= n ? (m + 1) : 0);
}

LL test(LL n,LL m){
    if(n == 0) return 0;
    if(m == 0) return 1;
    return test(nextn(n,m) , nextm(n,m));
}

LL func4(LL n){
    if(n==1) return 0;
    if(n==0) return 1;
    return test(n,2);
}

int main(){

    /************
    x1 ~ x5

    //input: 963 4396 6666 1999 3141

    // ans =  1927 8792 13332 3998 6282 

    **********/
    LL input[] = {963,4396,6666,1999,3141};
    printf("flag{");
    for(LL j = 0; j<=4 ; j++){
        LL ans = input[j];
        for(LL i=0;i<=300000000;i++){
            if(func3(func2(i))==1)
                if(_func1(i) == ans){
                    printf("%lld",i);
                    break;
                }
        /*  if(i % 100000 == 0)
                printf("%d finished\n",i);*/
        }
        printf("-");
    }

    /************
        x6 = 1229

    **********/

    LL cnt=1;
    for(LL i=3;i<=10000;i++)
        if(func4(i)==1)
            cnt++;
    printf("%lld}",cnt);

    //flag{927369-19324816-44435556-3996001-9865881-1229}
}

CTFer|NOIPer|CSGO|摸鱼|菜鸡