[CISCN2021]华北区_ctf_re_imnotavirus

题目来源和附件

Written by Poilzero(blog:poilzero.cn)

  • 同步转发到简书和CSDN

题目来源:

  • 2021年第十四届全国大学生信息安全竞赛——创新实践能力赛
  • 华北赛区
  • re方向第一题
  • imnotavirus

题目附件:

https://poil.lanzoui.com/i0iImqjom6h

个人解题目录和部分所需程序:

https://poil.lanzoui.com/i9txPqz69pg

总体分析

文件是一个用python写的程序并且使用pyinstaller打包难点在于

  • 打包的时候加了-key参数,这样打包的时候会对主程序import的所有库进行加密可能是CFB加密?
  • python3.9无uncompyle6而源程序是用python3.9编写
  • 源码部分用python调用了win32API并且使用了多线程编程
  • 真正的flag生成过程实际上是使用了SMC技术(看雪《加密与解密17.2.2》)

总体分解过程是step1-5

  1. upx.exe脱压缩壳
  2. *使用pyinstxtractor.py将exe文件逆向成pyc等文件(python3.9 x64)发现import的相关库被加密
  3. 改文件头使用python38的uncompyle6生成main.py发现核心代码是是调用import的sign文件(被加密)
  4. *解密sign文件(准确的来说是这个文件:main.exe_extracted\PYZ-00.pyz_extracted\sign.pyc.encrypted)得到源码
  5. *分析源码写出注册机(使用了SMC技术)

step23逆向成py文件

使用python38 pyinstxtractor.py:python pyinstxtractor.py [filename]

  • 脚本会根据文件头提示:Please run this script in Python39 to prevent extraction errors during unmarshalling
  • 所以使用python39(实测不用python39会不生成import的对应加密库)

使用python39 pyinstxtractor.py:python pyinstxtractor.py [filename]

  • 再次运行脚本会生成一系列如:[!] Error: Failed to decompress PYZ-00.pyz_extracted\__future__.pyc, probably encrypted. Extracting as is.
  • 这其实是在逆向获得主文件import库的时候出错,因为这些文件加密了,即使用pyinstaller打包的时候加了-key参数
  • 但是正常生成文件了,解密(step4)也没法通过这个脚本(没有提供这个功能,等我考完试看看能不能添加一下这个功能
  • 主文件main.exe_extracted\main.pyc

使用python38 uncompyle6库(pip install uncompyle):uncompyle6 [org_filename] > [out_filename]

  • 会报错,原因是如上的源项目是python3.9写的
  • 但是python39没有 uncompyle库
  • 我这里的解决方法是修改pyc文件头

【*】修改pyc文件头,标其为python3.8的pyc文件

使用二进制编辑器打开(如01editor winhex),参考我自己写的python38写的程序的反汇编pyc修改前16个字节为

image.png

可以看到主文件其实调用了的是sign文件中的main()函数,因此下一步我们需要解密这个被加密的sign文件

step4解密sign核心源码

文件位于:main.exe_extracted\PYZ-00.pyz_extracted\sign.pyc.encrypted

主办方提示:pyimod02_archive.py文件

定位文件在pyinstaller项目源码:imnotavirus\pyinstaller-4.2\PyInstaller\loader\pyimod02_archive.py

以及逆向生成的pyc文件:main.exe_extracted\pyimod02_archive.pyc

分析得知:pyimod02_archive.py中有解密函数,参考其调用规则,在该文件末编写如下代码

inf = open('sign.pyc.encrypted', 'rb')
c = Cipher()

buf = c.decrypt(inf.read())
buf = zlib.decompress(buf) # 查看这个代码文件能发现密文是用zlib压缩过的所以需要解压缩

out = open('sign.pyc', 'wb')
out.write(buf)
print('written down %n bytes' % len(buf))

inf.close()
out.close()

将相关文件移入main.exe_extracted目录中

  • 加密文件:main.exe_extracted\PYZ-00.pyz_extracted\sign.pyc.encrypted
  • 解密文件:imnotavirus\pyinstaller-4.2\PyInstaller\loader\pyimod02_archive.py

使用python39进入main.exe_extracted目录中,依次运行

  • 使用pyc加载相关的文件所以要在逆向目录中(比如密钥文件):pyimod02_archive.pyc
  • pyimod02_archive.py

提示written down 3457 bytes代表正确生成了文件

main.exe_extracted目录中的sign.py就是解压后的文件,参考前面的 “【*】修改pyc文件头,标其为python3.8的pyc文件”

在文件最开始的地方插入十六个同前面的字节,然后用python38反编译得到核心源码uncompyle6 sign.pyc > sign.py

核心代码如下

# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.5 (default, Sep  3 2020, 21:29:08) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: sign.py
import ctypes, urllib, base64, hashlib, ast

def ppp(bbb):
    qaq = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(bbb)), ctypes.c_int(12288), ctypes.c_int(64))
    bbb[31:35] = (qaq + 59).to_bytes(4, 'little')
    bbb[36:40] = (qaq + 178).to_bytes(4, 'little')
    qwq = (ctypes.c_char * len(bbb)).from_buffer(bbb)
    eval(base64.b64decode('Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQzMihxYXEpLHF3cSxjdHlwZXMuY19pbnQobGVuKGJiYikpKQ==').decode())
    handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0), ctypes.c_int(0), ctypes.c_uint32(qaq), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)))
    ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))


def aaa(ttt, ggg):
    ggg = hashlib.md5(ggg).hexdigest()
    ttt = base64.b64decode(ttt)
    result = b''
    ggg_len = len(ggg)
    box = list(range(256))
    j = 0
    for i in range(256):
        j = (j + box[i] + ord(ggg[(i % ggg_len)])) % 256
        box[i], box[j] = box[j], box[i]
    else:
        i = j = 0
        for iii in ttt:
            i = (i + 1) % 256
            j = (j + box[i]) % 256
            box[i], box[j] = box[j], box[i]
            k = iii ^ box[((box[i] + box[j]) % 256)]
            result += bytes([k])
        else:
            return result


def main():
    ctypes.windll.kernel32.VirtualAlloc.restyle = ctypes.c_uint32
    fff = input('Your input: ')
    b = 'pB2M/eG2iz1pB4kej4yXXCYvTFV3b/6NDPvMVc+iOs2QwI7Tg7QcItIK6KtB5seaZhd67NGYh6xyMPAocLhd0NeJhweg5/rsEuYnzxFxqMrysaizRAiD6HQhe50rwF5UnByay04giUxuLxy6zL8me5sAQqaUAuCv0c1EsDKBxv1B1zV8MEDav5bkgsTd2t3X3Jt0+lfGKk98bxg1FIXoUhtyZjhV489SpMi+Gzs2+/zaZ0d7p12KoppTYPNs3sj9l74Q8EJPYIAecUEnMSmKDF7yPaKIFloCdW5ghVaSeiaskr5OzfzfeccpvRPevL7PW9uW1R8WmcW2oWjN9aWsinwG2Gk1B7JPa+HusBGpIxxSQhK0wBrEjQpYlIMC7fTpFZca373+p/A2oXuaXqfmOoWtE62JCM74tWqZFrSWyLdnu1/vHaClrzcdzpHLum9shEOcNHYi88Dj11mYufJxH/sEx0CBtWkTCHwEhxVs1sYkGBHlDFUpFbfpY0UagbyPJdq+bmXBdmLKEhJ/M/2cCjmsjuma6IKvo+riA7/B8+T3GB06x31G5tibi3rJMDb4bVWBswzI3gg06mc4Q9EW5dQ4+/SRKbVoYhAfbGKlBeuxSIARKzSAuJPlfm+dmJ6pHx9a4qek2UIEKr4zxA0bSMALoYOSETDF1JWZX+K0HEJLY/hXajmzq5qSTX0EAKBLJtIPkJ0e+XsaQCXyhy4Cg8mYNlQGIORNo5vyNe4QPAD8d3GKr/PZnEMwJ5WsgyoSBOGDke4PGUJd70thEyGHKN9QfTXWknC8HBZdcEojvtC3Prj7LlxXI6y8uZ7ie/1HltGogj3EsUkqU0d3WDuBPZec1Tzj/7Vs44MFGRjEJ0IuSA0U0vOCShyeHUB23qhrXqODsrO/t+s/Zohmd2H0xS46qdoquQj8L1RY2fCt3H0US3Wffk0FKf7qYboKeW/7vlkOYlchgP/HXf0Mfo5gBXhJg3e9jGJ8K5J0gt6Zra9dhPINGgekDMIoxXE='
    c = aaa(b, 'blackhand'.encode('utf-8'))
    c = ast.literal_eval("b'" + c.decode().strip() + "'")
    qaq = eval(base64.b64decode('Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5WaXJ0dWFsQWxsb2MoY3R5cGVzLmNfaW50KDApLCBjdHlwZXMuY19pbnQobGVuKGZmZikpLCBjdHlwZXMuY19pbnQoMTIyODgpLCBjdHlwZXMuY19pbnQoNjQpKQ==').decode())
    qwq = (ctypes.c_char * (len(fff) + 1)).from_buffer(bytearray(fff.encode()) + bytes([0]))
    eval(base64.b64decode('Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQzMihxYXEpLHF3cSxjdHlwZXMuY19pbnQobGVuKGZmZikpKQ==').decode())
    c = bytearray(c)
    c[55:59] = qaq.to_bytes(4, 'little')
    ppp(c)
    r = ctypes.create_string_buffer(len(fff))
    ctypes.windll.kernel32.ReadProcessMemory(ctypes.windll.kernel32.GetCurrentProcess(), ctypes.c_uint32(qaq), ctypes.byref(r), ctypes.c_ulong(len(fff)), 0)
    if r.raw == b'J\x04`~~s Q"Y!C [j\x05e\x06aB&N#B!E Qp\\ S{\x05{\x05{\x05':
        print('Yes! You got it!')
    else:
        print('Nope. Try harder :)')
# okay decompiling sign.pyc

step5注册机

sign源码审计

审计后发现代码内容主要做以下几件事

  • 读取输入值
  • *使用win32api操作

    • 把输入值和汇编形式的指针函数写入内存中
    • 使用线程调用这个指针函数从而加密输入值
  • 最后把加密结果和一个量比较判断是否正确

其中指针函数在ppp()函数中,我写了个myproof函数把这个指针函数作为一个文件保存下来

提示如下时代码正常运行

require:

  • python3.9 x32版本
  • 标准库:ctypes, urllib, base64, hashlib, ast
Your input: 1111
> written down 191 bytes
> written in 0x3950000
Nope. Try harder :)

Process finished with exit code 0

接下来就是审计用汇编写的指针函数了

已添加注释如下,也可在附件中查看
# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.5 (default, Sep  3 2020, 21:29:08) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: sign.py
import ctypes, urllib, base64, hashlib, ast

def my_proof(QAQ, add):
    # save qaq function
    out = open('qaq_function', 'wb')
    out.write(QAQ)
    out.close(),print('> written down {} bytes'.format(len(QAQ)))
    print('> written in {}'.format(hex(add)))

def getStringFromMem(source, length):
    r = ctypes.create_string_buffer(length)
    ctypes.windll.kernel32.ReadProcessMemory(
        ctypes.windll.kernel32.GetCurrentProcess()  # hinstance
        , ctypes.c_uint32(source)                   # from        address
        , ctypes.byref(r)                           # destination address
        , ctypes.c_ulong(length)                  # nSize
        , 0
    )
    # print('in getStringFromMem() qaq\'s address is {}'.format(hex(source)))
    return r.raw

# converted by c(bbb)
def ppp(bbb):
    # qaq = malloc(len(bbb))
    qaq = ctypes.windll.kernel32.VirtualAlloc(
        ctypes.c_int(0)          # destination address
        , ctypes.c_int(len(bbb)) # dwSize
        , ctypes.c_int(12288)    # type of allocation
        , ctypes.c_int(64)
    )      # type of access protection
    bbb[31:35] = (qaq + 59).to_bytes(4, 'little')
    bbb[36:40] = (qaq + 178).to_bytes(4, 'little')
    # qwq = bbb
    qwq = (ctypes.c_char * len(bbb)).from_buffer(bbb)
    # qaq = qwq (=bbb)
    ctypes.windll.kernel32.RtlMoveMemory(
        ctypes.c_uint32(qaq)     # destination address
        ,qwq                     # source address
        ,ctypes.c_int(len(bbb))  # nSize
    )
    # bbb is code written to mem-add at qaq
    my_proof(bbb, qaq)  # to touch qaq=qwq=bbb written by me
####### threadprocess
    # qaq is a function!!!
    handle = ctypes.windll.kernel32.CreateThread(
        ctypes.c_int(0)         # must be NULL
        , ctypes.c_int(0)       # 0 means createThread STACK_SIZE_PARAM_IS_A_RESERVATION means as virtual memeory
        , ctypes.c_uint32(qaq)  # function pointer!!!!
        , ctypes.c_int(0)       # function's param lpvThreadParam
        , ctypes.c_int(0)       # type of CreateThread
        , ctypes.pointer(ctypes.c_int(0)) # lpRecevedReternValue if 0 it will not return to it
    )
    ctypes.windll.kernel32.WaitForSingleObject(
        ctypes.c_int(handle)
        , ctypes.c_int(-1)
    )
    return None

'''
return is constance
'''
def aaa(ttt, ggg):
    ggg = hashlib.md5(ggg).hexdigest() # ggg = md5(ggg,16,little)
    ttt = base64.b64decode(ttt)
##########
    result = b''
    ggg_len = len(ggg)
    box = list(range(256))
    j = 0
    for i in range(256):
        j = (j + box[i] + ord(ggg[(i % ggg_len)])) % 256
        box[i], box[j] = box[j], box[i]
    else:
        i = j = 0
        for iii in ttt:
            i = (i + 1) % 256
            j = (j + box[i]) % 256
            box[i], box[j] = box[j], box[i]
            k = iii ^ box[((box[i] + box[j]) % 256)]
            result += bytes([k])
        else:
            return result

def main():
    ctypes.windll.kernel32.VirtualAlloc.restyle = ctypes.c_uint32
    # fff = 'Y3sImD3f1n2t3ly2v1ru5y0u2r3fck3dh2h2h2'
    fff = input('Your input: ')
    b = 'pB2M/eG2iz1pB4kej4yXXCYvTFV3b/6NDPvMVc+iOs2QwI7Tg7QcItIK6KtB5seaZhd67NGYh6xyMPAocLhd0NeJhweg5/rsEuYnzxFxqMrysaizRAiD6HQhe50rwF5UnByay04giUxuLxy6zL8me5sAQqaUAuCv0c1EsDKBxv1B1zV8MEDav5bkgsTd2t3X3Jt0+lfGKk98bxg1FIXoUhtyZjhV489SpMi+Gzs2+/zaZ0d7p12KoppTYPNs3sj9l74Q8EJPYIAecUEnMSmKDF7yPaKIFloCdW5ghVaSeiaskr5OzfzfeccpvRPevL7PW9uW1R8WmcW2oWjN9aWsinwG2Gk1B7JPa+HusBGpIxxSQhK0wBrEjQpYlIMC7fTpFZca373+p/A2oXuaXqfmOoWtE62JCM74tWqZFrSWyLdnu1/vHaClrzcdzpHLum9shEOcNHYi88Dj11mYufJxH/sEx0CBtWkTCHwEhxVs1sYkGBHlDFUpFbfpY0UagbyPJdq+bmXBdmLKEhJ/M/2cCjmsjuma6IKvo+riA7/B8+T3GB06x31G5tibi3rJMDb4bVWBswzI3gg06mc4Q9EW5dQ4+/SRKbVoYhAfbGKlBeuxSIARKzSAuJPlfm+dmJ6pHx9a4qek2UIEKr4zxA0bSMALoYOSETDF1JWZX+K0HEJLY/hXajmzq5qSTX0EAKBLJtIPkJ0e+XsaQCXyhy4Cg8mYNlQGIORNo5vyNe4QPAD8d3GKr/PZnEMwJ5WsgyoSBOGDke4PGUJd70thEyGHKN9QfTXWknC8HBZdcEojvtC3Prj7LlxXI6y8uZ7ie/1HltGogj3EsUkqU0d3WDuBPZec1Tzj/7Vs44MFGRjEJ0IuSA0U0vOCShyeHUB23qhrXqODsrO/t+s/Zohmd2H0xS46qdoquQj8L1RY2fCt3H0US3Wffk0FKf7qYboKeW/7vlkOYlchgP/HXf0Mfo5gBXhJg3e9jGJ8K5J0gt6Zra9dhPINGgekDMIoxXE='
    c = aaa(b, 'blackhand'.encode('utf-8'))
    # ast.literal_eval equal try-catch-mode eval()
    # strip() 去除首尾的所有 或\n
    c = ast.literal_eval("b'" + c.decode().strip() + "'") # c is constance
    # qaq = malloc(len(fff))
    qaq = ctypes.windll.kernel32.VirtualAlloc(
        ctypes.c_int(0)             # destination address
        , ctypes.c_int(len(fff))    # dwSize
        , ctypes.c_int(12288)       # type of allocation
        , ctypes.c_int(64)          # type of access protection
    )
    # qwq=fff+'0'
    qwq = (ctypes.c_char * (len(fff) + 1)).from_buffer(bytearray(fff.encode()) + bytes([0]))
    # qaq = qwq(=fff+'\x0')
    ctypes.windll.kernel32.RtlMoveMemory(
        ctypes.c_uint32(qaq)     # destination address
        ,qwq                     # source address
        ,ctypes.c_int(len(fff))  # nSize
    )
    '''
    c variable change start
    '''
    c = bytearray(c) # transefer to bytes list struct (like list)
    # c[55:59] = qaq_address[0:4]
    c[55:59] = qaq.to_bytes(4, 'little')
    # print(getStringFromMem(qaq,len(fff)))
    ppp(c)
    # print(getStringFromMem(qaq,len(fff)))
    '''
    change end
    '''
    # r.raw = qaq
    r = ctypes.create_string_buffer(len(fff))
    ctypes.windll.kernel32.ReadProcessMemory(
        ctypes.windll.kernel32.GetCurrentProcess() # hinstance
        , ctypes.c_uint32(qaq)       # from        address
        , ctypes.byref(r)            # destination address
        , ctypes.c_ulong(len(fff))   # nSize
        , 0
    )
    # compare
    if r.raw == b'J\x04`~~s Q"Y!C [j\x05e\x06aB&N#B!E Qp\\ S{\x05{\x05{\x05':
        print('Yes! You got it!')
    else:
        print('Nope. Try harder :)')
# okay decompiling sign.pyc

main()

指针函数汇编审计

涉及

  • smc
  • 花指令

ida打开qaq_function发现有乱码:

seg000:00000000 ; ===========================================================================
seg000:00000000
seg000:00000000 ; Segment type: Pure code
seg000:00000000 seg000          segment byte public 'CODE' use32
seg000:00000000                 assume cs:seg000
seg000:00000000                 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
seg000:00000000                 push    ebp
seg000:00000001                 mov     ebp, esp
seg000:00000003                 sub     esp, 0E4h
seg000:00000009                 push    ebx
seg000:0000000A                 push    esi
seg000:0000000B                 push    edi
seg000:0000000C                 lea     edi, [ebp-0E4h]
seg000:00000012                 mov     ecx, 39h ; '9'
seg000:00000017                 mov     eax, 0CCCCCCCCh
seg000:0000001C                 rep stosd
seg000:0000001E                 mov     eax, 3BB003Bh
seg000:00000023                 mov     ecx, 3BB00B2h
seg000:00000028                 sub     ecx, eax
seg000:0000002A
seg000:0000002A loc_2A:                                 ; CODE XREF: seg000:00000032↓j
seg000:0000002A                 mov     ebx, [eax]
seg000:0000002C                 xor     ebx, 77h
seg000:0000002F                 mov     [eax], ebx
seg000:00000031                 inc     eax
seg000:00000032                 loop    loc_2A
seg000:00000034                 mov     dword ptr [ebp+8], 3BA0000h
seg000:0000003B                 mov     al, 32h ; '2'
seg000:0000003B ; ---------------------------------------------------------------------------
seg000:0000003D                 db 8Fh, 2 dup(77h)
seg000:00000040                 db  77h ; w
seg000:00000041                 db 77h, 0B0h, 32h
seg000:00000044                 dd 7777779Bh, 0FC7E9C77h, 0B7F49B32h, 9B32FE76h, 747F32FCh
seg000:00000044                 dd 0C9789B32h, 3BEF27Fh, 8F32FC7Ch, 0FE76B7F4h, 0A89C8F32h
seg000:00000044                 dd 779732B0h, 9C777777h, 9732FC7Eh, 0FE75B7F4h, 32FC9732h
seg000:00000044                 dd 8F324C97h, 32FC5B0Ah, 9732747Fh, 0F47FC978h, 22FC6486h
seg000:00000044                 dd 9722747Fh, 32FC7DFFh, 9732747Fh, 763FC978h, 0FC4086F4h
seg000:00000044                 dd 22747F22h, 763DFF97h, 5E5FB49Ch, 0E4C4815Bh, 8B000000h
seg000:000000BC                 db 0E5h, 5Dh, 0C3h
seg000:000000BC seg000          ends
seg000:000000BC
seg000:000000BC
seg000:000000BC                 end

其中smc代码,修改了运行时的程序代码,规则是与0x77异或,即以下代码

seg000:0000001E                 mov     eax, 3BB003Bh
seg000:00000023                 mov     ecx, 3BB00B2h
seg000:00000028                 sub     ecx, eax
seg000:0000002A
seg000:0000002A loc_2A:                                 ; CODE XREF: seg000:00000032↓j
seg000:0000002A                 mov     ebx, [eax]
seg000:0000002C                 xor     ebx, 77h
seg000:0000002F                 mov     [eax], ebx
seg000:00000031                 inc     eax
seg000:00000032                 loop    loc_2A

编写代码直接代替执行异或,对文件直接修改生成正确的运行时指针函数

l = 0x3b
r = 0xb2
fi = open('qaq_function', 'rb')
fo = open('qaq_function_smced', 'wb')
cipher = fi.read()
for i in range(len(cipher)):
    if i>=0x3b and i<0xb2:
        per = cipher[i]^0x77
    else:
        per = cipher[i]
    per = hex(per)
    per = "b'\\"+per[1:]+"'"
    if len(per)==6:
        per = per[:-2]+'0'+per[-2:]
    print('> written byte:', per)
    per = eval(per)
    fo.write(per)

fi.close(),fo.close()

得到qaq_function_smced文件ida打开如下:

跳转流程:

loc_54-》loc_6C(花指令本质上是强制跳转)-》loc_7E-》

seg000:00000000 ; ===========================================================================
seg000:00000000
seg000:00000000 ; Segment type: Pure code
seg000:00000000 seg000          segment byte public 'CODE' use32
seg000:00000000                 assume cs:seg000
seg000:00000000                 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
seg000:00000000                 push    ebp
seg000:00000001                 mov     ebp, esp
seg000:00000003                 sub     esp, 0E4h
seg000:00000009                 push    ebx
seg000:0000000A                 push    esi
seg000:0000000B                 push    edi
seg000:0000000C                 lea     edi, [ebp-0E4h]
seg000:00000012                 mov     ecx, 39h ; '9'
seg000:00000017                 mov     eax, 0CCCCCCCCh
seg000:0000001C                 rep stosd
seg000:0000001E                 mov     eax, 3BB003Bh
seg000:00000023                 mov     ecx, 3BB00B2h
seg000:00000028                 sub     ecx, eax
seg000:0000002A
seg000:0000002A loc_2A:                                 ; CODE XREF: seg000:00000032↓j
seg000:0000002A                 mov     ebx, [eax]
seg000:0000002C                 xor     ebx, 77h
seg000:0000002F                 mov     [eax], ebx
seg000:00000031                 inc     eax
seg000:00000032                 loop    loc_2A
seg000:00000034                 mov     dword ptr [ebp+8], 3BA0000h
seg000:0000003B                 mov     dword ptr [ebp-8], 0
seg000:00000042                 mov     dword ptr [ebp-14h], 0
seg000:00000049                 jmp     short loc_54
seg000:0000004B ; ---------------------------------------------------------------------------
seg000:0000004B
seg000:0000004B loc_4B:                                 ; CODE XREF: seg000:0000006A↓j
seg000:0000004B                 mov     eax, [ebp-14h]
seg000:0000004E                 add     eax, 1
seg000:00000051                 mov     [ebp-14h], eax
seg000:00000054
seg000:00000054 loc_54:                                 ; CODE XREF: seg000:00000049↑j
seg000:00000054                 mov     eax, [ebp+8]
seg000:00000057                 add     eax, [ebp-14h]
seg000:0000005A                 movsx   ecx, byte ptr [eax]
seg000:0000005D                 test    ecx, ecx
seg000:0000005F                 jz      short loc_6C    ; 花指令,其实是强制跳转jmp
seg000:00000061                 mov     eax, [ebp-8]
seg000:00000064                 add     eax, 1
seg000:00000067                 mov     [ebp-8], eax
seg000:0000006A                 jmp     short loc_4B
seg000:0000006C ; ---------------------------------------------------------------------------
seg000:0000006C
seg000:0000006C loc_6C:                                 ; CODE XREF: seg000:0000005F↑j
seg000:0000006C                 mov     dword ptr [ebp-20h], 0
seg000:00000073                 jmp     short loc_7E
seg000:00000075 ; ---------------------------------------------------------------------------
seg000:00000075
seg000:00000075 loc_75:                                 ; CODE XREF: seg000:000000B0↓j
seg000:00000075                 mov     eax, [ebp-20h]
seg000:00000078                 add     eax, 2
seg000:0000007B                 mov     [ebp-20h], eax
seg000:0000007E
seg000:0000007E loc_7E:                                 ; CODE XREF: seg000:00000073↑j
seg000:0000007E                 mov     eax, [ebp-20h]
seg000:00000081                 cmp     eax, [ebp-8]
seg000:00000084                 jge     short loc_B2
seg000:00000086                 mov     eax, [ebp+8]
seg000:00000089                 add     eax, [ebp-20h]
seg000:0000008C                 movsx   ecx, byte ptr [eax]
seg000:0000008F                 xor     ecx, 13h
seg000:00000092                 mov     edx, [ebp+8]
seg000:00000095                 add     edx, [ebp-20h]
seg000:00000098                 mov     [edx], cl
seg000:0000009A                 mov     eax, [ebp+8]
seg000:0000009D                 add     eax, [ebp-20h]
seg000:000000A0                 movsx   ecx, byte ptr [eax+1]
seg000:000000A4                 xor     ecx, 37h
seg000:000000A7                 mov     edx, [ebp+8]
seg000:000000AA                 add     edx, [ebp-20h]
seg000:000000AD                 mov     [edx+1], cl
seg000:000000B0                 jmp     short loc_75
seg000:000000B2 ; ---------------------------------------------------------------------------
seg000:000000B2
seg000:000000B2 loc_B2:                                 ; CODE XREF: seg000:00000084↑j
seg000:000000B2                 pop     edi
seg000:000000B3                 pop     esi
seg000:000000B4                 pop     ebx
seg000:000000B5                 add     esp, 0E4h
seg000:000000BB                 mov     esp, ebp
seg000:000000BD                 pop     ebp
seg000:000000BE                 retn
seg000:000000BE seg000          ends
seg000:000000BE
seg000:000000BE
seg000:000000BE                 end

本质上是将输入值偶奇位分别于0x13和0x37进行异或,编写注册机:

s = 'J\x04`~~s Q"Y!C [j\x05e\x06aB&N#B!E Qp\\ S{\x05{\x05{\x05'
ans = ''
for i in range(len(s)):
    per = ord(s[i])
    if i%2 ==0:
        ans += chr(per^0x13)
    else:
        ans += chr(per^0x37)

print(ans)
Y3sImD3f1n2t3ly2v1ru5y0u2r3fck3dh2h2h2

Process finished with exit code 0

所有相关过程文件在附件中

Last modification:January 22, 2022
如果觉得我的文章对你有用,请随意赞赏。咖啡(12RMB)进度+100%,一块巧克力(1RMB)进度+6%。
(赞赏请备注你的名称哦!后台记录中来自理工小菜狗)