• XDCTF2015_re100


    去年做的一道 CTF,清理文档

    0x01 The .init and .fini Sections

    参考下面链接:
    http://www.ru.j-npcs.org/usoft/WWW/www_debian.org/Documentation/elf/node3.html

    简单说一下,即.init中的指令,在main()函数之前执行,.fini中的指令,在main()函数返回后执行;

    一段代码验证之:


    #include <stdio.h> void my_init() __attribute__((constructor)); void my_fini() __attribute__((destructor)); void my_init() { printf("init? "); } void my_fini() { printf("fini? "); } int main() { printf("I'm in main() "); return 0; }


    输出结果:

    ez[@ubuntu](http://my.oschina.net/u/555627):~/xdctf$ ./init 
    init?
    I'm in main()
    fini?
    

    下面链接,有更详细介绍:

    https://www.codeaurora.org/git/projects/qrd-gb-ssss-7225/repository/revisions/e34c19e51778b1f44682192040db577967da636b/entry/android/bionic/linker/README.TXT

    DT_INIT:ELF loaded时执行
    DT_INIT_ARRAY:其中的函数被顺序执行,完成main()前的初始化操作,通常存放于.init_array段
    DT_FINI:ELF unloaded时或进程结束时执行
    DT_FINI_ARRAY:与DT_INIT_ARRAY类似,用于main()后扫尾
    

    0x02 IDA静态分析

    用IDA打开re100,发现是一个Linux x86_64程序;没有main()函数,可能编译时修改了入口函数,可参考:

    http://stackoverflow.com/questions/8116648/why-is-the-elf-entry-point-0x8048000-not-changeable-with-the-ld-e-option?lq=1

    http://stackoverflow.com/questions/4272316/in-an-elf-file-how-does-the-address-for-start-get-detemined

    没有_start函数,但发现有一处start函数与_start函数类似,猜测ld时被修改,具体可参考:

    http://eli.thegreenplace.net/2012/08/13/how-statically-linked-programs-run-on-linux/

    我们平常所说的__libc_start_main()函数,其实在_start()中被调用:


    .text:0000000000400590 public _start .text:0000000000400590 _start proc near .text:0000000000400590 xor ebp, ebp .text:0000000000400592 mov r9, rdx .text:0000000000400595 pop rsi .text:0000000000400596 mov rdx, rsp .text:0000000000400599 and rsp, 0FFFFFFFFFFFFFFF0h .text:000000000040059D push rax .text:000000000040059E push rsp .text:000000000040059F mov r8, offset __libc_csu_fini .text:00000000004005A6 mov rcx, offset __libc_csu_init .text:00000000004005AD mov rdi, offset main .text:00000000004005B4 call ___libc_start_main .text:00000000004005B9 hlt .text:00000000004005B9 _start endp

    比较一下正常的_start与该题目中的start函数发现,入口被从main()修改为了sub_4008E1():

    ### 修改后 start
    .text:000000000040053E                 push    rsp
    .text:000000000040053F                 mov     r8, offset nullsub_1
    .text:0000000000400546                 mov     rcx, offset loc_400AA0
    .text:000000000040054D                 mov     rdi, offset sub_4008E1
    .text:0000000000400554                 call    ___libc_start_main
    
    ### 正常的 _start
    .text:000000000040059E                 push    rsp
    .text:000000000040059F                 mov     r8, offset __libc_csu_fini
    .text:00000000004005A6                 mov     rcx, offset __libc_csu_init
    .text:00000000004005AD                 mov     rdi, offset main
    .text:00000000004005B4                 call    ___libc_start_main
    

    继续分析sub_4008E1()函数,其开始处有ptrace()的简单反调试;如果需要gdb调试,则nop之即可,这里静态分析暂不管它;

    初步分析此函数功能,首先栈上有两个字符串,分别为命名为g_key和g_enc:


    0x0601280 g_key "\|Gq\@?BelTtK5L`\|D`d42;" 0x6012A0 g_enc ";%#848N!0Z?7'%23]/5#1"YX"


    sub_4008E1()函数将用户输入的12个字符flag与g_key进行一系列异或运算后,与g_enc进行比较,如果相等,则输入的flag验证通过;

    如果这样做,得出的结果永远是错的,其实题目也有个Hint,Don’t believe your eyes..因为我们忽略了.init_array和.fini_array中的代码,而这两段中的函数,对g_key和异或算法均有影响;由上面收集的资料可知,其分别会在sub_4008E1()函数之前和之后执行;

    .init_array:0000000000601000 ; =============================================================
    .init_array:0000000000601000
    .init_array:0000000000601000 ; Segment type: Pure data
    .init_array:0000000000601000 ; Segment permissions: Read/Write
    .init_array:0000000000601000 ; Segment alignment 'qword' can not be represented in assembly
    .init_array:0000000000601000 _init_array     segment para public 'DATA' use64
    .init_array:0000000000601000                 assume cs:_init_array
    .init_array:0000000000601000                 ;org 601000h
    .init_array:0000000000601000 off_601000      dq offset sub_400600    ; DATA XREF: .text:0000000000400AB1 o
    .init_array:0000000000601008                 dq offset sub_400669
    .init_array:0000000000601008 _init_array     ends
    .init_array:0000000000601008
    .fini_array:0000000000601010 ; ===============================================================
    .fini_array:0000000000601010
    .fini_array:0000000000601010 ; Segment type: Pure data
    .fini_array:0000000000601010 ; Segment permissions: Read/Write
    .fini_array:0000000000601010 ; Segment alignment 'qword' can not be represented in assembly
    .fini_array:0000000000601010 _fini_array     segment para public 'DATA' use64
    .fini_array:0000000000601010                 assume cs:_fini_array
    .fini_array:0000000000601010                 ;org 601010h
    .fini_array:0000000000601010 off_601010      dq offset sub_4005E0    ; DATA XREF: .text:0000000000400AB9 o
    .fini_array:0000000000601018                 dq offset sub_400787
    .fini_array:0000000000601018 _fini_array     ends
    .fini_array:0000000000601018
    

    分析发现sub_400669()函数对g_key进行了异或操作,也就是我们用来异或的g_key并不是内存中的内容:


    size_t sub_400669() { size_t result; // rax@1 int i; // [sp+Ch] [bp-14h]@2 result = dword_601340; if ( dword_601340 != 1 ) { dword_601340 = 1; for ( i = 0; ; ++i ) { result = strlen(key_str_1); if ( i >= result ) break; key_str_1[i] ^= 6u; } } return result; }

    而sub_400787()函数中才是我们真正需要破解的代码:


    char *sub_400787() { char *result; // rax@13 signed int v1; // [sp+8h] [bp-18h]@9 int i; // [sp+Ch] [bp-14h]@1 unsigned int j; // [sp+Ch] [bp-14h]@4 unsigned int k; // [sp+Ch] [bp-14h]@9 for ( i = 0; i < strlen(key_str_1); ++i ) char_array_24[i] = key_str_1[i] ^ char_array_16[i - 12 * (((0x0AAAAAAAAAAAAAAABLL * i) >> 64) >> 3)]; sub_4006D5(char_array_24, 24, 12); for ( j = 0; j <= 0x17; ++j ) { if ( char_array_24[j] <= 31 ) char_array_24[j] += 32; } v1 = 1; for ( k = 0; ; ++k ) { result = k; if ( k > 0x17 ) break; if ( char_array_24[k] != key_str_2[k] ) v1 = 0; } if ( v1 ) { result = output_; if ( output_ ) { output_[15] = '!'; puts(output_); exit(0); } } return result; }


    根据算法逆推脚本如下: 


    def get_flag(g_enc, g_key): flag = [] size = 24 for i in xrange(0, size, 1): if ( ord(g_enc[i]) - 0x20) <= 0x1F: flag.append(ord(g_enc[i])-0x20) else: flag.append(ord(g_enc[i])) for i in xrange(0,size/2,1): flag[i] ^= flag[17-i] flag[17-i] ^= flag[i] flag[i] ^= flag[17-i] for i in xrange(0,size,1): flag[i] = flag[i%(size/2)] ^ (ord(g_key[i])^6) for i in xrange(0,size/2,1): flag[i] = chr(flag[i]) return "".join(flag[:size/2]) if __name__ == '__main__': g_key = "\|Gq\@?BelTtK5L`\|D`d42;" g_enc = ";%#848N!0Z?7'%23]/5#1"YX" flag = get_flag(g_enc, g_key) print flag
    ez[@ubuntu](http://my.oschina.net/u/555627):~/xdctf$ python get_flag.py 
    U'Re_AwEs0Me
    

    根据提示结果均为小写,最终flag为:XDCTF{u’re_awes0me}

  • 相关阅读:
    git pull origin master命令后发生冲突
    计算属性和侦听器
    微信小程序 wx.navigateBack携带参数
    小程序----路由
    quill工具栏出现提示的功能
    quill修改字体大小
    quill报错,"RangeError: Maximum call stack size exceeded"
    quill添加字体
    新建一个vue项目(补充了vue-cli4.0快速搭建一个项目【站在巨人的肩膀上并亲测】)
    codeforce round 7
  • 原文地址:https://www.cnblogs.com/gm-201705/p/9864050.html
Copyright © 2020-2023  润新知