MOCTF RE 暗恋的烦恼

在moctf平台上,一道逆向题目很有意思,记录一下:

 

 文件链接:http://119.23.73.3:6001/re4/jiamiqi.exe

jiamiqi.exe文件执行效果:

执行到这里可以发现了,加密器输出的密文长度和明文长度是一致的,并且不加密空格。后面我们再去详细验证。

拖入IDA pro静态分析:

找到main函数,双击进入

再次双击,跳转

main函数反汇编代码:

.text:00401340 var_50          = byte ptr -50h
.text:00401340 var_10          = dword ptr -10h
.text:00401340 var_C           = dword ptr -0Ch
.text:00401340 var_8           = dword ptr -8
.text:00401340 Str             = dword ptr -4
.text:00401340
.text:00401340                 push    ebp
.text:00401341                 mov     ebp, esp
.text:00401343                 sub     esp, 50h
.text:00401346                 push    ebx
.text:00401347                 push    esi
.text:00401348                 push    edi
.text:00401349                 lea     edi, [ebp+var_50]
.text:0040134C                 mov     ecx, 14h
.text:00401351                 mov     eax, 0CCCCCCCCh
.text:00401356                 rep stosd
.text:00401358                 push    0FFh            ; unsigned int
.text:0040135D                 call    ??2@YAPAXI@Z    ; operator new(uint)
.text:00401362                 add     esp, 4
.text:00401365                 mov     [ebp+var_C], eax
.text:00401368                 mov     eax, [ebp+var_C]
.text:0040136B                 mov     [ebp+Str], eax
.text:0040136E                 push    1Ah             ; unsigned int
.text:00401370                 call    ??2@YAPAXI@Z    ; operator new(uint)
.text:00401375                 add     esp, 4
.text:00401378                 mov     [ebp+var_10], eax
.text:0040137B                 mov     ecx, [ebp+var_10]
.text:0040137E                 mov     [ebp+var_8], ecx
.text:00401381                 push    offset sub_40102D
.text:00401386                 push    offset aIFIG    ; "请输入您的明文:"
.text:0040138B                 push    offset unk_439920
.text:00401390                 call    sub_4010AA      ;打印
.text:00401395                 add     esp, 8
.text:00401398                 mov     ecx, eax
.text:0040139A                 call    sub_40107D
.text:0040139F                 mov     edx, [ebp+Str]
.text:004013A2                 push    edx             ; Buffer
.text:004013A3                 call    _gets
.text:004013A8                 add     esp, 4
.text:004013AB                 push    offset sub_40102D
.text:004013B0                 push    offset aIFIG_0  ; "请输入您的密匙:"
.text:004013B5                 push    offset unk_439920
.text:004013BA                 call    sub_4010AA
.text:004013BF                 add     esp, 8
.text:004013C2                 mov     ecx, eax
.text:004013C4                 call    sub_40107D
.text:004013C9                 mov     eax, [ebp+var_8]
.text:004013CC                 push    eax             ; Buffer
.text:004013CD                 call    _gets
.text:004013D2                 add     esp, 4
.text:004013D5                 mov     ecx, [ebp+var_8]
.text:004013D8                 push    ecx             ; int
.text:004013D9                 mov     edx, [ebp+Str]
.text:004013DC                 push    edx             ; Str
.text:004013DD                 call    j_encrypt_func  ; 调用函数获得加密信息,传入buffer
.text:004013E2                 add     esp, 8
.text:004013E5                 mov     [ebp+Str], eax  ; eax返回加密信息
.text:004013E8                 mov     eax, [ebp+Str]
.text:004013EB                 push    eax
.text:004013EC                 push    offset Format   ; "%s\n"
.text:004013F1                 call    _printf
.text:004013F6                 add     esp, 8
.text:004013F9                 call    sub_419590
.text:004013FE                 xor     eax, eax
.text:00401400                 pop     edi
.text:00401401                 pop     esi
.text:00401402                 pop     ebx
.text:00401403                 add     esp, 50h
.text:00401406                 cmp     ebp, esp
.text:00401408                 call    __chkesp
.text:0040140D                 mov     esp, ebp
.text:0040140F                 pop     ebp
.text:00401410                 retn
.text:00401410 _main_0         endp

一键F5,查看伪代码

进入函数encrypt_func也就是加密函数,再次F5产看代码(已优化命名):

加密函数内,先分配了加密数组enc_buffer的内存地址,也分别计算了明文、密钥的长度;然后进入for循环,i是递增的循环变量,也是明文字符串索引,循环次数取决于明文长度;v6 是密钥索引,由if 判断来重置密钥的值,这就避免了密钥长度短于明文长度带来的不便,简单的循环密钥思想。

重点是这一行加密代码:

*(_BYTE *)(i + enc_buffer) = sub_401005(plain[i], K[v6++]);

可见,他是明文字符 plain[i] 和密钥字符 K[v6] 加密填入enc_buffer[i],因此密文长度等于明文长度,验证了前面的判断。

好,现在重点分析sub_401005地址,又是一个跳转,我命名之为encrypt_char函数

双击进入函数(优化命名后)、F5:

逻辑十分清楚了,名文字符、密钥字符取得大写,判断不是空格后开始加密(再次验证开头关于空格不加密的判断),由明文的ascii-‘A’的ascii(65)得到这一次函数循环的次数,循环更简单,就是每次把密钥的ascii值加1。我们通过函数流图看看后续操作(伪代码后面的结束步骤返回不详细)。

(局部)

(loc_401200 程序结束)

分析完毕。

 然后根据题目要求来写出解密脚本,只需要写出最后的encrypt_char函数解密脚本:

enc_text = "QWDRILDWNTW"
K =        "ILOVEMOCTFI" #直接取大写,并且循环连接密钥使K长度和明文一致,简化后面操作
x = 0 #索引
decoded_text = ""         #目标解密字符串
while x < len(K):
    if enc_text[x] == " ":  #空格直接回填
        decoded_text += " "
        continue
    temp_num = 0          #保存ascii码值
    #正常情况下,密文是密钥K若干次加一得到的,要大于K;否则说明密文经过-0x19操作
    if enc_text[x] < K[x]: 
        temp_num = ord(enc_text[x]) + 0x19
    else:
        temp_num = ord(enc_text[x])
    cycle_time = temp_num - ord(K[x])  #还原循环次数
    decoded_text += chr(cycle_time+65) #通过ascii值+64 还原明文
    x += 1
print(repr(decoded_text))

结果:

 

    moctf{ILOVEYOUTOO}

参考:https://bbs.pediy.com/thread-250320.htm

posted @ 2019-04-20 18:05  爱吃砂糖橘的白龙  阅读(469)  评论(0编辑  收藏  举报