首页
社区
课程
招聘
[原创]CTF2017 第六题loudy writeup
2017-11-5 09:28 8801

[原创]CTF2017 第六题loudy writeup

2017-11-5 09:28
8801
……这题真的是做的我想砸墙……前前后后花了不下24小时……各种坑……放弃HITCON一直在刚……
这题是一道数学 + 密码学 + lua + C++ STL的极其恶心人的一道题……大概一共分为三大部分,其中掺杂了各种不常见的库函数和更改过的库函数……这题经验就是善用工具……

首先惯例直接上strings

跟着引用找到main函数,如下




大概可以看出逻辑如下,我们的输入会经过两个大结构的变换,第一个是des_decrypt(之后我们会说怎么识别他),第二个是MIRACL的大整数操作,我们输入做了以下两个操作以后,会扔进lua里面进行第三次操作,最终进行比较,大致逻辑就是这样

首先先说一下库函数都是怎么识别出来的吧(DES是人工扒算法和常数扒出来的……),这里用到了bindiff这个工具https://www.zynamics.com/software.html ,他会比较两个idb之间每个函数的相似性,并列出最相似的函数,对于像MIRACL和LuaJIT这种开源程序来说很好用。我们需要做的就是下载对应版本的源码,编译成exe/dll (因为linux下calling convention不同,所以bindiff可能识别没那么准确(大概))以后开IDA执行一遍初始分析,然后存储idb后退出。注意这里每个版本都最好建debug build,这样大部分的函数名称会被保留,并生成pdb。bindiff目前还没有支持IDA7.0……也就是我为什么换回了IDA 6.95……

LuaJIT很好编译……直接扔vs command line tools里面跑他的mvcsbuild就好了,这里查看build的bat脚本后发现假如第一个参数是debug的话,他就会建成debug build(即 mvsbuild.exe debug)非常方便,因此lua库函数立马就标完了(不过这里实际执行的时候遇到了问题……luaji2.1.0-beta3使用的是2.0版的Opcode按理说并不能跑起来……即使改成1.0版也还是会出错……不知道为什么程序里面可以用2.1.0-beta3这个build……)。lua库函数所在的文件是lua51.dll,不是luajit.exe,不过lua库函数改动不大,所以bindiff识别准确率还是挺高的。所有lua的C API函数基本上都是bindiff识别的

MIRACL这个东西就dt了……我们知道用了MIRACL是因为strings里面的这句话

MIRACL的build只有一个脚本在lib/ms32doit.bat……而且写的非常糟糕……手动加了一些编译 flag以后终于是可以编译了……生成文件为miracl.lib (用起来不是很方便,于是改编译脚本为dll),MIRACL的识别准确率香蕉LuaJIT来说就没有那么高了……不是很清楚为什么,不过用的也不算太多,所以还好,这里可以点开第一个mirsys_basic函数,然后照着源码把miracl的结构体给撸出来(因为有各种ifndef之类的……所以没法直接看源码撸),这样之后会方便很多

这里我们注意到一点,MIRACL其实是有stack backtrace的,他的识别关键在于miracl结构体中trace这个结构(这也是为什么我们要照着mirsys_basic撸结构体的原因),找到这个以后我们MIRACL的识别就很简单了。stack trace主要是靠MR_IN(<number>)组成,每个主要函数都有自己的数字,所以只要对照这个就能找到大部分的函数了,这里以power为例子进行说明

红框中标出的即为该函数的MR_IN的stack trace number,下面我们直接grep 源码目录就可找到对应的定义

MIRACL几乎没有说明文档……所有东西都是靠我自己从源码里面翻出来的……dt,这里我还用到了cscope这个工具来在本地快速查找C和C++ project的函数定义很引用(有时候grep的结果太多了),使用方法很简单
find . -iname "*.c" -o -iname "*.cpp" -o -iname "*.hpp" -o -iname "*.h" > cscope.files (这里可以加任何你想要的文件后缀,只要find函数能找到就行)
cscope -bqk (建立索引)
cscope -d (避免重复建立索引)

个人感觉cscope只适合相对较小的project……如果是linux kernel那种……最好还是自己搭一个opengrok的服务器比较好
基本库函数标完……下面就进入正式的逆向工作……

一。des_cbc_decrypt

这个函数是des cbc的解密(加密?)函数,作者更改了很多的常量……使得我们不得不自己找一个des的库更改后使用。des的识别是靠了这串数组

以16进制数组搜索(搜 0xe,0x4,0xd,0x1,0x2,0xf,0xb,0x8,0x3,0xa,0x6,0xc)可找到 http://kodu.ut.ee/~lipmaa/teaching/crypto2000/des/des.C,查看后,发现是DES的sbox,由于des并不是所有操作都需要用到table,找到一个和所给des相似的库找了我很长时间(这里也是刚开始觉得常量不会改……),最终选用了这个https://gist.githubusercontent.com/eigenein/1275094/raw/62d1fee5e9d19df44fab6950ecbcd9c82b9b9805/pyDes.py

根据这个算法,对应的des的表改过来就好了(有个表只改了一个字节……实在是很坑爹)。des的key schedule用到的表也需要改(给的例子里面bits是按MSB encode的这个也需要改)。同时des_decrypt里面roundkey是从最后一轮开始的……(也就是为什么我说他是decrypt),但cbc的算法明显是block cipher的加密算法……这里还要翻一下iv。同时des_decrypt里面对调Ininital permutation和final permutation,调整后des的代码(3DES我懒得删了)
#############################################################################
#                               Documentation                               #
#############################################################################

# Author:   Todd Whiteman
# Date:     16th March, 2009
# Verion:   2.0.0
# License:  Public Domain - free to do as you wish
# Homepage: http://twhiteman.netfirms.com/des.html
#
# This is a pure python implementation of the DES encryption algorithm.
# It's pure python to avoid portability issues, since most DES
# implementations are programmed in C (for performance reasons).
#
# Triple DES class is also implemented, utilising the DES base. Triple DES
# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
#
# See the README.txt that should come with this python module for the
# implementation methods used.
#
# Thanks to:
#  * David Broadwell for ideas, comments and suggestions.
#  * Mario Wolff for pointing out and debugging some triple des CBC errors.
#  * Santiago Palladino for providing the PKCS5 padding technique.
#  * Shaya for correcting the PAD_PKCS5 triple des CBC errors.
#
"""A pure python implementation of the DES and TRIPLE DES encryption algorithms.

Class initialization
--------------------
pyDes.des(key, [mode], [IV], [pad], [padmode])
pyDes.triple_des(key, [mode], [IV], [pad], [padmode])

key     -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
           for Triple DES
mode    -> Optional argument for encryption type, can be either
           pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining)
IV      -> Optional Initial Value bytes, must be supplied if using CBC mode.
           Length must be 8 bytes.
pad     -> Optional argument, set the pad character (PAD_NORMAL) to use during
           all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5)
           to use during all encrypt/decrpt operations done with this instance.

I recommend to use PAD_PKCS5 padding, as then you never need to worry about any
padding issues, as the padding can be removed unambiguously upon decrypting
data that was encrypted using PAD_PKCS5 padmode.

Common methods
--------------
encrypt(data, [pad], [padmode])
decrypt(data, [pad], [padmode])

data    -> Bytes to be encrypted/decrypted
pad     -> Optional argument. Only when using padmode of PAD_NORMAL. For
           encryption, adds this characters to the end of the data block when
           data is not a multiple of 8 bytes. For decryption, will remove the
           trailing characters that match this pad character from the last 8
           bytes of the unencrypted data block.
padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL
           or PAD_PKCS5). Defaults to PAD_NORMAL.


Example
-------
from pyDes import *

data = "Please encrypt my data"
k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
# For Python3, you'll need to use bytes, i.e.:
#   data = b"Please encrypt my data"
#   k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
d = k.encrypt(data)
print "Encrypted: %r" % d
print "Decrypted: %r" % k.decrypt(d)
assert k.decrypt(d, padmode=PAD_PKCS5) == data


See the module source (pyDes.py) for more examples of use.
You can also run the pyDes.py file without and arguments to see a simple test.

Note: This code was not written for high-end systems needing a fast
      implementation, but rather a handy portable solution with small usage.

"""

import sys

# _pythonMajorVersion is used to handle Python2 and Python3 differences.
_pythonMajorVersion = sys.version_info[0]

# Modes of crypting / cyphering
ECB =   0
CBC =   1

# Modes of padding
PAD_NORMAL = 1
PAD_PKCS5 = 2

# PAD_PKCS5: is a method that will unambiguously remove all padding
#            characters after decryption, when originally encrypted with
#            this padding mode.
# For a good description of the PKCS5 padding technique, see:
# http://www.faqs.org/rfcs/rfc1423.html

# The base class shared by des and triple des.
class _baseDes(object):
        def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
                if IV:
                        IV = self._guardAgainstUnicode(IV)
                if pad:
                        pad = self._guardAgainstUnicode(pad)
                self.block_size = 8
                # Sanity checking of arguments.
                if pad and padmode == PAD_PKCS5:
                        raise ValueError("Cannot use a pad character with PAD_PKCS5")
                if IV and len(IV) != self.block_size:
                        raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")

                # Set the passed in variables
                self._mode = mode
                self._iv = IV
                self._padding = pad
                self._padmode = padmode

        def getKey(self):
                """getKey() -> bytes"""
                return self.__key

        def setKey(self, key):
                """Will set the crypting key for this object."""
                key = self._guardAgainstUnicode(key)
                self.__key = key

        def getMode(self):
                """getMode() -> pyDes.ECB or pyDes.CBC"""
                return self._mode

        def setMode(self, mode):
                """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
                self._mode = mode

        def getPadding(self):
                """getPadding() -> bytes of length 1. Padding character."""
                return self._padding

        def setPadding(self, pad):
                """setPadding() -> bytes of length 1. Padding character."""
                if pad is not None:
                        pad = self._guardAgainstUnicode(pad)
                self._padding = pad

        def getPadMode(self):
                """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
                return self._padmode

        def setPadMode(self, mode):
                """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
                self._padmode = mode

        def getIV(self):
                """getIV() -> bytes"""
                return self._iv

        def setIV(self, IV):
                """Will set the Initial Value, used in conjunction with CBC mode"""
                if not IV or len(IV) != self.block_size:
                        raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
                IV = self._guardAgainstUnicode(IV)
                self._iv = IV

        def _padData(self, data, pad, padmode):
                # Pad data depending on the mode
                if padmode is None:
                        # Get the default padding mode.
                        padmode = self.getPadMode()
                if pad and padmode == PAD_PKCS5:
                        raise ValueError("Cannot use a pad character with PAD_PKCS5")

                if padmode == PAD_NORMAL:
                        if len(data) % self.block_size == 0:
                                # No padding required.
                                return data

                        if not pad:
                                # Get the default padding.
                                pad = self.getPadding()
                        if not pad:
                                raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.")
                        data += (self.block_size - (len(data) % self.block_size)) * pad

                elif padmode == PAD_PKCS5:
                        pad_len = 8 - (len(data) % self.block_size)
                        if _pythonMajorVersion < 3:
                                data += pad_len * chr(pad_len)
                        else:
                                data += bytes([pad_len] * pad_len)

                return data

        def _unpadData(self, data, pad, padmode):
                # Unpad data depending on the mode.
                if not data:
                        return data
                if pad and padmode == PAD_PKCS5:
                        raise ValueError("Cannot use a pad character with PAD_PKCS5")
                if padmode is None:
                        # Get the default padding mode.
                        padmode = self.getPadMode()

                if padmode == PAD_NORMAL:
                        if not pad:
                                # Get the default padding.
                                pad = self.getPadding()
                        if pad:
                                data = data[:-self.block_size] + \
                                       data[-self.block_size:].rstrip(pad)

                elif padmode == PAD_PKCS5:
                        if _pythonMajorVersion < 3:
                                pad_len = ord(data[-1])
                        else:
                                pad_len = data[-1]
                        data = data[:-pad_len]

                return data

        def _guardAgainstUnicode(self, data):
                # Only accept byte strings or ascii unicode values, otherwise
                # there is no way to correctly decode the data into bytes.
                if _pythonMajorVersion < 3:
                        if isinstance(data, unicode):
                                raise ValueError("pyDes can only work with bytes, not Unicode strings.")
                else:
                        if isinstance(data, str):
                                # Only accept ascii unicode values.
                                try:
                                        return data.encode('ascii')
                                except UnicodeEncodeError:
                                        pass
                                raise ValueError("pyDes can only work with encoded strings, not Unicode.")
                return data

#############################################################################
#                                   DES                                     #
#############################################################################
class des(_baseDes):
        """DES encryption/decrytpion class

        Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.

        pyDes.des(key,[mode], [IV])

        key  -> Bytes containing the encryption key, must be exactly 8 bytes
        mode -> Optional argument for encryption type, can be either pyDes.ECB
                (Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
        IV   -> Optional Initial Value bytes, must be supplied if using CBC mode.
                Must be 8 bytes in length.
        pad  -> Optional argument, set the pad character (PAD_NORMAL) to use
                during all encrypt/decrpt operations done with this instance.
        padmode -> Optional argument, set the padding mode (PAD_NORMAL or
                PAD_PKCS5) to use during all encrypt/decrpt operations done
                with this instance.
        """


        # Permutation and translation tables for DES
        __pc1 = [56, 48, 40, 32, 24, 16,  8,
                  0, 57, 49, 41, 33, 25, 17,
                  9,  1, 58, 50, 42, 34, 26,
                 18, 10,  2, 59, 51, 43, 35,
                 62, 54, 46, 38, 30, 22, 14,
                  6, 61, 53, 45, 37, 29, 21,
                 13,  5, 60, 52, 44, 36, 28,
                 20, 12,  4, 27, 19, 11,  3
        ]

        # number left rotations of pc1
        # changed!
        __left_rotations = [
            0x17, 0xa, 2, 5, 9, 2, 3, 2, 3, 2, 5, 7, 2, 9, 2, 7
        ]

        # permuted choice key (table 2)
        # changed!
        __pc2 = [
                13, 16, 10, 23,  0,  4,
                 2, 27, 14,  5, 20,  9,
                22, 18, 11,  3, 25,  7,
                15,  6, 26, 19, 12,  1,
                40, 51, 30, 36, 46, 54,
                29, 39, 50, 44, 32, 46,
                43, 48, 38, 55, 33, 52,
                45, 41, 49, 35, 28, 31
        ]

        # initial permutation IP
        __ip = [57, 49, 41, 33, 25, 17, 9,  1,
                59, 51, 43, 35, 27, 19, 11, 3,
                61, 53, 45, 37, 29, 21, 13, 5,
                63, 55, 47, 39, 31, 23, 15, 7,
                56, 48, 40, 32, 24, 16, 8,  0,
                58, 50, 42, 34, 26, 18, 10, 2,
                60, 52, 44, 36, 28, 20, 12, 4,
                62, 54, 46, 38, 30, 22, 14, 6
        ]

        # Expansion table for turning 32 bit blocks into 48 bits
        __expansion_table = [
                0,  0,  1,  2,  3,  3,
                 4,  4,  5,  6,  7,  7,
                 8,  8,  9, 10, 11, 11,
                12, 12, 13, 14, 15, 15,
                16, 16, 17, 18, 19, 19,
                20, 20, 21, 22, 23, 23,
                24, 24, 25, 26, 27, 27,
                28, 28, 29, 30, 31, 31
        ]

        # The (in)famous S-boxes
        __sbox = [
                # S1
                [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
                 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
                 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
                 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],

                # S2
                [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
                 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
                 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
                 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],

                # S3
                [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
                 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
                 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
                 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],

                # S4
                [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
                 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
                 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
                 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],

                # S5
                [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
                 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
                 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
                 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],

                # S6
                [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
                 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
                 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
                 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],

                # S7
                [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
                 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
                 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
                 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],

                # S8
                [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
                 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
                 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
                 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
        ]


        # 32-bit permutation function P used on the output of the S-boxes
        __p = [
                15, 6, 19, 20, 28, 11,
                27, 16, 0, 14, 22, 25,
                4, 17, 30, 9, 1, 7,
                23,13, 31, 26, 2, 8,
                18, 12, 29, 5, 21, 10,
                3, 24
        ]

        # final permutation IP^-1
        __fp = [
                39,  7, 47, 15, 55, 23, 63, 31,
                38,  6, 46, 14, 54, 22, 62, 30,
                37,  5, 45, 13, 53, 21, 61, 29,
                36,  4, 44, 12, 52, 20, 60, 28,
                35,  3, 43, 11, 51, 19, 59, 27,
                34,  2, 42, 10, 50, 18, 58, 26,
                33,  1, 41,  9, 49, 17, 57, 25,
                32,  0, 40,  8, 48, 16, 56, 24
        ]

        # Type of crypting being done
        ENCRYPT =       0x00
        DECRYPT =       0x01

        # Initialisation
        def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
                # Sanity checking of arguments.
                if len(key) != 8:
                        raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
                _baseDes.__init__(self, mode, IV, pad, padmode)
                self.key_size = 8

                self.L = []
                self.R = []
                self.Kn = [ [0] * 48 ] * 16     # 16 48-bit keys (K1 - K16)
                self.final = []

                self.setKey(key)

        def setKey(self, key):
                """Will set the crypting key for this object. Must be 8 bytes."""
                _baseDes.setKey(self, key)
                self.__create_sub_keys()

        def __String_to_BitList(self, data):
                """Turn the string data, into a list of bits (1, 0)'s"""
                if _pythonMajorVersion < 3:
                        # Turn the strings into integers. Python 3 uses a bytes
                        # class, which already has this behaviour.
                        data = [ord(c) for c in data]
                l = len(data) * 8
                result = [0] * l
                pos = 0
                for ch in data:
                        i = 0
                        while i <= 7:
                                result[pos] = (ch >> i) & 1
                                pos += 1
                                i += 1

                return result

        def __BitList_to_String(self, data):
                """Turn the list of bits -> data, into a string"""
                result = []
                pos = 0
                c = 0
                while pos < len(data):
                        c += data[pos] << (pos % 8)
                        if (pos % 8) == 7:
                                result.append(c)
                                c = 0
                        pos += 1

                if _pythonMajorVersion < 3:
                        return ''.join([ chr(c) for c in result ])
                else:
                        return bytes(result)

        def __permutate(self, table, block):
                """Permutate this block with the specified table"""
                return list(map(lambda x: block[x], table))

        # Transform the secret key, so that it is ready for data processing
        # Create the 16 subkeys, K[1] - K[16]
        def __create_sub_keys(self):
                """Create the 16 subkeys K[1] to K[16] from the given key"""
                key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey()))
                i = 0
                # Split into Left and Right sections
                self.L = key[:28]
                self.R = key[28:]
                while i < 16:
                        j = 0
                        # Perform circular left shifts
                        while j < des.__left_rotations[i]:
                                self.L.append(self.L[0])
                                del self.L[0]

                                self.R.append(self.R[0])
                                del self.R[0]

                                j += 1

                        # Create one of the 16 subkeys through pc2 permutation
                        self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R)
                        i += 1

        # Main part of the encryption algorithm, the number cruncher :)
        def __des_crypt(self, block, crypt_type):
                """Crypt the block of data through DES bit-manipulation"""
                block = self.__permutate(des.__fp, block)
                self.L = block[:32]
                self.R = block[32:]

                # Encryption starts from Kn[1] through to Kn[16]
                if crypt_type == des.ENCRYPT:
                        iteration = 0
                        iteration_adjustment = 1
                # Decryption starts from Kn[16] down to Kn[1]
                else:
                        iteration = 15
                        iteration_adjustment = -1

                i = 0
                while i < 16:
                        # Make a copy of R[i-1], this will later become L[i]
                        tempR = self.R[:]

                        # Permutate R[i - 1] to start creating R[i]
                        self.R = self.__permutate(des.__expansion_table, self.R)

                        # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here
                        self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration]))
                        B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]]
                        # Optimization: Replaced below commented code with above
                        #j = 0
                        #B = []
                        #while j < len(self.R):
                        #       self.R[j] = self.R[j] ^ self.Kn[iteration][j]
                        #       j += 1
                        #       if j % 6 == 0:
                        #               B.append(self.R[j-6:j])

                        # Permutate B[1] to B[8] using the S-Boxes
                        j = 0
                        Bn = [0] * 32
                        pos = 0
                        while j < 8:
                                # Work out the offsets
                                m = (B[j][0] << 1) + B[j][5]
                                n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]

                                # Find the permutation value
                                v = des.__sbox[j][(m << 4) + n]
                                #print hex(j * 48 + (m << 4 + n))

                                # Turn value into bits, add it to result: Bn
                                Bn[pos] = (v & 8) >> 3
                                Bn[pos + 1] = (v & 4) >> 2
                                Bn[pos + 2] = (v & 2) >> 1
                                Bn[pos + 3] = v & 1

                                pos += 4
                                j += 1

                        # Permutate the concatination of B[1] to B[8] (Bn)
                        self.R = self.__permutate(des.__p, Bn)

                        # Xor with L[i - 1]
                        self.R = list(map(lambda x, y: x ^ y, self.R, self.L))
                        # Optimization: This now replaces the below commented code
                        #j = 0
                        #while j < len(self.R):
                        #       self.R[j] = self.R[j] ^ self.L[j]
                        #       j += 1

                        # L[i] becomes R[i - 1]
                        self.L = tempR

                        i += 1
                        iteration += iteration_adjustment

                # Final permutation of R[16]L[16]
                self.final = self.__permutate(des.__ip, self.R + self.L)
                return self.final


        # Data to be encrypted/decrypted
        def crypt(self, data, crypt_type):
                """Crypt the data in blocks, running it through des_crypt()"""

                # Error check the data
                if not data:
                        return ''
                if len(data) % self.block_size != 0:
                        if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks
                                raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
                        if not self.getPadding():
                                raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
                        else:
                                data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
                        # print "Len of data: %f" % (len(data) / self.block_size)

                if self.getMode() == CBC:
                        if self.getIV():
                                iv = self.__String_to_BitList(self.getIV())
                        else:
                                raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")

                # Split the data into blocks, crypting each one seperately
                i = 0
                dict = {}
                result = []
                #cached = 0
                #lines = 0
                while i < len(data):
                        # Test code for caching encryption results
                        #lines += 1
                        #if dict.has_key(data[i:i+8]):
                                #print "Cached result for: %s" % data[i:i+8]
                        #       cached += 1
                        #       result.append(dict[data[i:i+8]])
                        #       i += 8
                        #       continue

                        block = self.__String_to_BitList(data[i:i+8])

                        # Xor with IV if using CBC mode
                        if self.getMode() == CBC:
                                if crypt_type == des.ENCRYPT:
                                        block = list(map(lambda x, y: x ^ y, block, iv))
                                        #j = 0
                                        #while j < len(block):
                                        #       block[j] = block[j] ^ iv[j]
                                        #       j += 1

                                processed_block = self.__des_crypt(block, crypt_type)

                                if crypt_type == des.DECRYPT:
                                        processed_block = list(map(lambda x, y: x ^ y, processed_block, iv))
                                        #j = 0
                                        #while j < len(processed_block):
                                        #       processed_block[j] = processed_block[j] ^ iv[j]
                                        #       j += 1
                                        iv = block
                                else:
                                        iv = processed_block
                        else:
                                processed_block = self.__des_crypt(block, crypt_type)


                        # Add the resulting crypted block to our list
                        #d = self.__BitList_to_String(processed_block)
                        #result.append(d)
                        result.append(self.__BitList_to_String(processed_block))
                        #dict[data[i:i+8]] = d
                        i += 8

                # print "Lines: %d, cached: %d" % (lines, cached)

                # Return the full crypted string
                if _pythonMajorVersion < 3:
                        return ''.join(result)
                else:
                        return bytes.fromhex('').join(result)

        def encrypt(self, data, pad=None, padmode=None):
                """encrypt(data, [pad], [padmode]) -> bytes

                data : Bytes to be encrypted
                pad  : Optional argument for encryption padding. Must only be one byte
                padmode : Optional argument for overriding the padding mode.

                The data must be a multiple of 8 bytes and will be encrypted
                with the already specified key. Data does not have to be a
                multiple of 8 bytes if the padding character is supplied, or
                the padmode is set to PAD_PKCS5, as bytes will then added to
                ensure the be padded data is a multiple of 8 bytes.
                """
                data = self._guardAgainstUnicode(data)
                if pad is not None:
                        pad = self._guardAgainstUnicode(pad)
                data = self._padData(data, pad, padmode)
                return self.crypt(data, des.ENCRYPT)

        def decrypt(self, data, pad=None, padmode=None):
                """decrypt(data, [pad], [padmode]) -> bytes

                data : Bytes to be encrypted
                pad  : Optional argument for decryption padding. Must only be one byte
                padmode : Optional argument for overriding the padding mode.

                The data must be a multiple of 8 bytes and will be decrypted
                with the already specified key. In PAD_NORMAL mode, if the
                optional padding character is supplied, then the un-encrypted
                data will have the padding characters removed from the end of
                the bytes. This pad removal only occurs on the last 8 bytes of
                the data (last data block). In PAD_PKCS5 mode, the special
                padding end markers will be removed from the data after decrypting.
                """
                data = self._guardAgainstUnicode(data)
                if pad is not None:
                        pad = self._guardAgainstUnicode(pad)
                data = self.crypt(data, des.DECRYPT)
                return self._unpadData(data, pad, padmode)



#############################################################################
#                               Triple DES                                  #
#############################################################################
class triple_des(_baseDes):
        """Triple DES encryption/decrytpion class

        This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
        the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
        Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.

        pyDes.des(key, [mode], [IV])

        key  -> Bytes containing the encryption key, must be either 16 or
                24 bytes long
        mode -> Optional argument for encryption type, can be either pyDes.ECB
                (Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
        IV   -> Optional Initial Value bytes, must be supplied if using CBC mode.
                Must be 8 bytes in length.
        pad  -> Optional argument, set the pad character (PAD_NORMAL) to use
                during all encrypt/decrpt operations done with this instance.
        padmode -> Optional argument, set the padding mode (PAD_NORMAL or
                PAD_PKCS5) to use during all encrypt/decrpt operations done
                with this instance.
        """
        def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
                _baseDes.__init__(self, mode, IV, pad, padmode)
                self.setKey(key)

        def setKey(self, key):
                """Will set the crypting key for this object. Either 16 or 24 bytes long."""
                self.key_size = 24  # Use DES-EDE3 mode
                if len(key) != self.key_size:
                        if len(key) == 16: # Use DES-EDE2 mode
                                self.key_size = 16
                        else:
                                raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
                if self.getMode() == CBC:
                        if not self.getIV():
                                # Use the first 8 bytes of the key
                                self._iv = key[:self.block_size]
                        if len(self.getIV()) != self.block_size:
                                raise ValueError("Invalid IV, must be 8 bytes in length")
                self.__key1 = des(key[:8], self._mode, self._iv,
                                  self._padding, self._padmode)
                self.__key2 = des(key[8:16], self._mode, self._iv,
                                  self._padding, self._padmode)
                if self.key_size == 16:
                        self.__key3 = self.__key1
                else:
                        self.__key3 = des(key[16:], self._mode, self._iv,
                                          self._padding, self._padmode)
                _baseDes.setKey(self, key)

        # Override setter methods to work on all 3 keys.

        def setMode(self, mode):
                """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
                _baseDes.setMode(self, mode)
                for key in (self.__key1, self.__key2, self.__key3):
                        key.setMode(mode)

        def setPadding(self, pad):
                """setPadding() -> bytes of length 1. Padding character."""
                _baseDes.setPadding(self, pad)
                for key in (self.__key1, self.__key2, self.__key3):
                        key.setPadding(pad)

        def setPadMode(self, mode):
                """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
                _baseDes.setPadMode(self, mode)
                for key in (self.__key1, self.__key2, self.__key3):
                        key.setPadMode(mode)

        def setIV(self, IV):
                """Will set the Initial Value, used in conjunction with CBC mode"""
                _baseDes.setIV(self, IV)
                for key in (self.__key1, self.__key2, self.__key3):
                        key.setIV(IV)

        def encrypt(self, data, pad=None, padmode=None):
                """encrypt(data, [pad], [padmode]) -> bytes

                data : bytes to be encrypted
                pad  : Optional argument for encryption padding. Must only be one byte
                padmode : Optional argument for overriding the padding mode.

                The data must be a multiple of 8 bytes and will be encrypted
                with the already specified key. Data does not have to be a
                multiple of 8 bytes if the padding character is supplied, or
                the padmode is set to PAD_PKCS5, as bytes will then added to
                ensure the be padded data is a multiple of 8 bytes.
                """
                ENCRYPT = des.ENCRYPT
                DECRYPT = des.DECRYPT
                data = self._guardAgainstUnicode(data)
                if pad is not None:
                        pad = self._guardAgainstUnicode(pad)
                # Pad the data accordingly.
                data = self._padData(data, pad, padmode)
                if self.getMode() == CBC:
                        self.__key1.setIV(self.getIV())
                        self.__key2.setIV(self.getIV())
                        self.__key3.setIV(self.getIV())
                        i = 0
                        result = []
                        while i < len(data):
                                block = self.__key1.crypt(data[i:i+8], ENCRYPT)
                                block = self.__key2.crypt(block, DECRYPT)
                                block = self.__key3.crypt(block, ENCRYPT)
                                self.__key1.setIV(block)
                                self.__key2.setIV(block)
                                self.__key3.setIV(block)
                                result.append(block)
                                i += 8
                        if _pythonMajorVersion < 3:
                                return ''.join(result)
                        else:
                                return bytes.fromhex('').join(result)
                else:
                        data = self.__key1.crypt(data, ENCRYPT)
                        data = self.__key2.crypt(data, DECRYPT)
                        return self.__key3.crypt(data, ENCRYPT)

        def decrypt(self, data, pad=None, padmode=None):
                """decrypt(data, [pad], [padmode]) -> bytes

                data : bytes to be encrypted
                pad  : Optional argument for decryption padding. Must only be one byte
                padmode : Optional argument for overriding the padding mode.

                The data must be a multiple of 8 bytes and will be decrypted
                with the already specified key. In PAD_NORMAL mode, if the
                optional padding character is supplied, then the un-encrypted
                data will have the padding characters removed from the end of
                the bytes. This pad removal only occurs on the last 8 bytes of
                the data (last data block). In PAD_PKCS5 mode, the special
                padding end markers will be removed from the data after
                decrypting, no pad character is required for PAD_PKCS5.
                """
                ENCRYPT = des.ENCRYPT
                DECRYPT = des.DECRYPT
                data = self._guardAgainstUnicode(data)
                if pad is not None:
                        pad = self._guardAgainstUnicode(pad)
                if self.getMode() == CBC:
                        self.__key1.setIV(self.getIV())
                        self.__key2.setIV(self.getIV())
                        self.__key3.setIV(self.getIV())
                        i = 0
                        result = []
                        while i < len(data):
                                iv = data[i:i+8]
                                block = self.__key3.crypt(iv,    DECRYPT)
                                block = self.__key2.crypt(block, ENCRYPT)
                                block = self.__key1.crypt(block, DECRYPT)
                                self.__key1.setIV(iv)
                                self.__key2.setIV(iv)
                                self.__key3.setIV(iv)
                                result.append(block)
                                i += 8
                        if _pythonMajorVersion < 3:
                                data = ''.join(result)
                        else:
                                data = bytes.fromhex('').join(result)
                else:
                        data = self.__key3.crypt(data, DECRYPT)
                        data = self.__key2.crypt(data, ENCRYPT)
                        data = self.__key1.crypt(data, DECRYPT)
                return self._unpadData(data, pad, padmode)
CBC那里库函数好像有点问题……于是直接调用的ECB模式,然后自己写的CBC,如下,同时可写出逆运算。des_cbc_decrypt之后的hex encode也是先编码低四位,后编码高四位,所以需要更改一下变成16进制的算法
import pyDes
from pwn import xor
from gmpy2 import isqrt_rem

def hexc(inp):
    inp = inp.encode("hex")
    return "".join(inp[i + 1] + inp[i] for i in range(0, len(inp), 2))

def ihexc(inp):
    inp = format(inp, 'x')
    inp = '0' * (len(inp) % 2) + inp
    return "".join(inp[i + 1] + inp[i] for i in range(0, len(inp), 2)).decode("hex")

def compute(inp):
    des = pyDes.des("*2017*10")
    rest = len(inp) % 8
    if rest:
        inp += "\x00" * (8 - rest)
    inp = [inp[i:i+8] for i in range(0, len(inp), 8)]
    iv = "\x00" * 8
    out = [iv]
    for i in range(len(inp)):
        out.append(des.decrypt(xor(inp[i], out[i][::-1])))
    return hexc("".join(out[1:]))

def inv_compute(inp):
    des = pyDes.des("*2017*10")
    inp = ihexc(inp)
    ap = len(inp) % 8
    if ap:
        inp = "\x00" * (8 - ap) + inp
    inp = [inp[i:i+8] for i in range(0, len(inp), 8)]
    iv = "\x00" * 8
    out = []
    for i in range(len(inp)):
        out.append(xor(des.encrypt(inp[i]), iv[::-1]))
        iv = inp[i]
    return "".join(out).rstrip("\x00") 
DES 这部分花的时间大概最长……因为C++的STL也不是很好调试……所以只能一轮一轮的对……看是不是改对了……

二. LuaJIT
我刚开始没有直接看MIRACL,因为感觉太烦了……(看见了类似傅里叶变换的东西),所以从最后的lua开始继续逆向,lua字节码和0x5A异或了,这里直接idapython dump出来异或一下就好了,然后得到的是LuaJIT的bytecode(注意不是lua),字节码的enum定义我没有找到……所以主要用了这两个工具。由于之前从来没研究过lua……所以还是花了不少时间的

看起来好像大家都在用的一个反编译器,可得到源码,缺点:由于是JIT的缘故,所以得不到在函数中使用全局变量引用,导致某些地方看起来很诡异……
而且好像必须要在同一个目录下将文件命名为test.lua才可以执行……(貌似可以改AutoIt脚本,不过懒……)

得到的反编译代码大概长这个德行……搜索xyByInt,可以找到这个http://blog.csdn.net/xianyun2009/article/details/44796823,貌似是lua的某个大整数实现
经过精简后,得到的代码大概是这样的

function show(a)  
    print(get(a))  
end  
function get(a)  
    s = {a[#a]}  
    for i=#a-1, 1, -1 do  
        table.insert(s, string.format("%04d", a[i]))  
    end  
    return table.concat(s, "")  
end  


function create(s)  
    if s["xyBitInt"] == true then return s end  
    n, t, a = math.floor(#s/4), 1, {}  
    a["xyBitInt"] = true  
    if #s%4 ~= 0 then a[n + 1], t = tonumber(string.sub(s, 1, #s%4), 10), #s%4 + 1 end  
    for i = n, 1, -1 do a[i], t= tonumber(string.sub(s, t, t + 3), 10), t + 4 end  
    return a  
end 


function add(a,b)
	local var_3_2 = create(a)
	var_3_3 = create(b)
	c = create("0")
	t =  0 
	local var_3_1 = var_3_3
	local var_3_0 = var_3_2
	for var_3_5 =  1 , math.max(c, var_3_5), 1  do --location 0022, loop ends at 0048-1
		if not unknown2 then
		--jump to 0028 (if previous if statement is false)
		--location 0028
		if not unknown3 then
		--jump to 0033 (if previous if statement is false)
		--location 0033
		t =  (  t +  0   +  0  )
		local var_3_7 = t % uget_3_0
		local var_3_8 = math.floor( t / uget_3_0 )
		t = var_3_8
		c[var_3_5] = var_3_7
	end --location 0047, loops back to 0023-1
	if t ~= 0 then
		--jump to 0068 (if previous if statement is false)
		repeat
		var_3_4 = t % uget_3_0
		local var_3_5 = math.floor( t / uget_3_0 )
		t = var_3_5
		c[ c +  1 ] = var_3_4
		--jump to 0048 (if previous if statement is false)
		--location 0068
	until false or (previous if statement is true) --location 0068
return c
end


function bsub(a, b)
    a, b, c, t = create(a), create(b), create("0"), 0  
    for i = 1, #a do  
        c[i] = a[i] - t - (b[i] or 0)  
        if c[i] < 0 then t, c[i] = 1, c[i] + mod  else t = 0 end  
    end  
    return c  
end


function by(a, b)  
    a, b, c, t = create(a), create(b), create("0"), 0  
    for i = 1, #a do  
        for j = 1, #b do  
            t = t + (c[i + j - 1] or 0) + a[i] * b[j]  
            c[i + j - 1], t = t%mod, math.floor(t / mod)  
        end  
        if t ~= 0 then c[i + #b], t = t + (c[i + #b] or 0), 0 end  
    end  
    return c  
end  


function myst(xut)
	c = add(xut, "1001")
	d =  0 
	for i =  1 , 100 , 1  do --location 0011, loop ends at 0028-1
		for j =  1 , 100 , 1  do --location 0015, loop ends at 0022-1
			c = add(c, "1001")
		end --location 0021, loops back to 0016-1
		c = add(c, "10101")
	end --location 0027, loops back to 0012-1
	c = by(c, "983751509373")
	for i =  1 , 13 , 1  do --location 0036, loop ends at 0073-1
		c = bsub(c, "1023")
		for j =  1 , 14 , 1  do --location 0045, loop ends at 0072-1
			c = bsub(c, "1203")
			for k =  1 , 15 , 1  do --location 0054, loop ends at 0071-1
				c = bsub(c, "1230")
				for l =  1 , 16 , 1  do --location 0063, loop ends at 0070-1
					c = bsub(c, "1231")
					--until false or (previous if statement is true) --location 0068
				end --location 0069, loops back to 0064-1
			end --location 0070, loops back to 0055-1
		end --location 0071, loops back to 0046-1
	end --location 0072, loops back to 0037-1
	c = add(c, "1")
	c = by(c, "2")
	e = get(c)
	lene = string.len(e)
	leny = string.len(uget_6_0)
	if lene == leny then
	--jump to 0129 (if previous if statement is false)
		i =  1 
		d =  1 
	if i <= lene then
	--jump to 0129 (if previous if statement is false)
	repeat
	var_6_0 = string.byte(e, i)
	local var_6_1 = string.byte(uget_6_0, i)
	if var_6_0 ~= var_6_1 then
	--jump to 0125 (if previous if statement is false)
	d =  0 
	--jump to 0129 (if previous if statement is false)
	i =  i +  1 
	--jump to 0105 (if previous if statement is false)
	until false or (previous if statement is true) --location 0129
	return d
end


function add(INPUT_VAR_0_)
return  INPUT_VAR_0_ + INPUT_VAR_1_ 
end


function sub(INPUT_VAR_0_)
return  INPUT_VAR_0_ - INPUT_VAR_1_ 
end


function someFunc9()
local var_9_0 = 10000 --var_9_0 NUMBER-NUMBER
local var_9_1 = "1574592838300862641516215149137548264158058079230003764126382984039489925466995870724568174393389905601620735902909057604303543552180706761904" --strings longer than 40 characters get cut
end 
还有一些不重要的部分没有化简,不过可以大概看出lua的逻辑了,执行多次运算后,将其化为2进制的字符串和var_9_1做比较。这里刚开始判断错了add,以为是sub,所以写出来的代码不对……然后手动的去更改luaJIT的字节码,在get那里直接return,然后用Lua的C API写了个小的wrapper来测试
#include <stdio.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

/* Convenience stuff */
static void close_state(lua_State **L) { lua_close(*L); }
#define cleanup(x) __attribute__((cleanup(x)))
#define auto_lclose cleanup(close_state) 

int main(int argc, char *argv[])
{
    /* Create VM state */
    auto_lclose lua_State *L = luaL_newstate();
    if (!L)
        return 1;
    luaL_openlibs(L); /* Open standard libraries */

    /* Load config file */
    if (argc > 1) {
        luaL_loadfile(L, argv[1]); /* (1) */
        //luaJIT_setmode(L, 0, 0);
        int ret = lua_pcall(L, 0, 0, 0);
        if (ret != 0) {
            fprintf(stderr, "%s\n", lua_tostring(L, -1));
            return 1;
        }
    }
    lua_pushstring(L, argv[2]);
    lua_setfield(L, -10002, "xut");
    lua_getfield(L, -10002, "myst");
    lua_pcall(L, 0, 1, 0);
    printf("%s\n", lua_tolstring(L, -1, 0));
    lua_settop(L, 0); /* (4) */
    return 0;
} 
这里简单说一下patch luaJIT字节码的过程,不知道字节码的enum很头疼……于是上网搜索的途中找到了这个有用的工具https://github.com/franko/luajit-lang-toolkit, 其中run.lua的-bx功能可以查看lua字节码对应的16进制字节是什么……让更改字节码变得简单了许多,效果大概是这个样子


如果要增加一个kgc的话,需要更改的地方有kgc的长度和这段proto的总长度,其中proto总长度是U128LE编码,然后大概学习了一下怎么写luaJIT字节码……后来发现自己把add看成了sub以后解决问题就没啥用了……总的来说lua字节码还是挺难调试的……不知道有没有好的方法……之后可以考虑写个hook的工具之类的……

第一次逆向luaJIT,感觉还是挺有趣的,学到了不少东西,luaJIT代码逆向出来后的逆运算如下
def lua(target):
    target = int(target) / 2 - 1
    for i in range(13):
        for j in range(14):
            for k in range(15):
                for l in range(16):
                    target += 1231
                target += 1230
            target += 1203
        target += 1023

    target /= 983751509373
    for i in range(100):
        target -= 10101
        for j in range(100):
            target -= 1001
    target -= 1001
    return target 
可以看出并不是很困难……大概是当时比较困……所以出了各种差错……

三. MIRACL

攻破了luaJIT和des以后,就只剩下我们最后的MIRACL了……首先我们来看看他都做了什么
首先把我们des_cbc_decrypt之后的结果16进制化以后变成了一个mirvar的大整数,然后乘了个173,之后做了fft_mult(使用傅里叶变换进行快速大数乘法)1817,以后做了某个计算(其实是除法,这个之后我们说怎么识别出来的)

看起来很简单对不对……结果怎么算怎么不对……fft_mult那里的输出和输入永远对不上……(输入的变换也不是线性的……所以只能逆向了)于是考虑可能fft_mult被更改过了……查看fft_mult
之前都一样……最后居然多出了两行!



然后通过olly根据输入输出猜测,得到第一个是平方的算法,第二个根据bindiff得到是decr(减法,不过内置了第二个减数为1001)

于是fft_mult的实际算法是 (inp * 1817)^2 - 1001……嗯……怪不得怎么都算不对

最后还差divide那个函数,那个函数长这样……

嗯……看了一眼就不想看了……遂祭出olly的连蒙带猜的的方法……输入了几组数据,观察输出
输入:1
输出:713

输入:2
输出:436

输入:3
输出:159
嗯……这个好像第二个反过来再除以输入就是317啊……验证了一下以后还真是

至此,我们已将所有程序逆向完毕……完整脚本如下
import pyDes
from pwn import xor
from gmpy2 import isqrt_rem

def hexc(inp):
    inp = inp.encode("hex")
    return "".join(inp[i + 1] + inp[i] for i in range(0, len(inp), 2))

def ihexc(inp):
    inp = format(inp, 'x')
    inp = '0' * (len(inp) % 2) + inp
    return "".join(inp[i + 1] + inp[i] for i in range(0, len(inp), 2)).decode("hex")

def compute(inp):
    des = pyDes.des("*2017*10")
    rest = len(inp) % 8
    if rest:
        inp += "\x00" * (8 - rest)
    inp = [inp[i:i+8] for i in range(0, len(inp), 8)]
    iv = "\x00" * 8
    out = [iv]
    for i in range(len(inp)):
        out.append(des.decrypt(xor(inp[i], out[i][::-1])))
    return hexc("".join(out[1:]))

def inv_compute(inp):
    des = pyDes.des("*2017*10")
    inp = ihexc(inp)
    ap = len(inp) % 8
    if ap:
        inp = "\x00" * (8 - ap) + inp
    inp = [inp[i:i+8] for i in range(0, len(inp), 8)]
    iv = "\x00" * 8
    out = []
    for i in range(len(inp)):
        out.append(xor(des.encrypt(inp[i]), iv[::-1]))
        iv = inp[i]
    return "".join(out).rstrip("\x00")

def lua(target):
    target = int(target) / 2 - 1
    for i in range(13):
        for j in range(14):
            for k in range(15):
                for l in range(16):
                    target += 1231
                target += 1230
            target += 1203
        target += 1023

    target /= 983751509373
    for i in range(100):
        target -= 10101
        for j in range(100):
            target -= 1001
    target -= 1001
    return target

assert(compute("1122334455667788").upper() == "BEC20560FEB6FCEFF93B5F94B3CF259D")
assert(compute("1234567890abcdef").upper() == "C7BF2111CD9A94A30E31F260D9BF9432")


n1 = 0xad
n2 = 0x719
target = "1574592838300862641516215149137548264158058079230003764126382984039489925466995870724568174393389905601620735902909057604303543552180706761904"
target = lua(target)
target = int(str(target)[::-1])
assert(target % 317 == 0)
target /= 317
target += 1001
res, rem = isqrt_rem(target)
assert(int(rem) == 0)
prod = (n2 * n1)
assert(res % prod == 0)
res /= prod
print inv_compute(res)

"""
inp = "0011223344556677"
num1 = int(compute(inp), 16)
num1 *= n1
print hex(num1 * n2)
"""

总结:这题说难也不难……就是很繁琐……而且考察的知识点比较杂……附上我的IDA6.95 idb和解题用的全部代码……感兴趣的人可以看一眼……


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞2
打赏
分享
最新回复 (12)
雪    币: 41
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
小k不小 2017-11-5 13:18
2
0
跟楼主吸点经验,好详细
雪    币: 19584
活跃值: (60093)
能力值: (RANK:125 )
在线值:
发帖
回帖
粉丝
Editor 2017-11-5 13:55
3
0
厉害了我的哥!
雪    币: 930
活跃值: (1288)
能力值: ( LV15,RANK:750 )
在线值:
发帖
回帖
粉丝
iweizime 7 2017-11-5 14:03
4
0
厉害
雪    币: 22979
活跃值: (3337)
能力值: (RANK:648 )
在线值:
发帖
回帖
粉丝
KevinsBobo 8 2017-11-5 14:17
5
0
楼主的精神和经验值得学习!
雪    币: 223
活跃值: (27)
能力值: ( LV10,RANK:165 )
在线值:
发帖
回帖
粉丝
sliphades 3 2017-11-5 15:04
6
0
真心服!预祝夺魁!
雪    币: 2584
活跃值: (835)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
loudy 10 2017-11-5 15:38
7
0
真的分析很到位,我自己写分析文档还没这么详细。。
雪    币: 204
活跃值: (906)
能力值: (RANK:1324 )
在线值:
发帖
回帖
粉丝
mratlatsn 10 2017-11-5 17:01
8
0
楼主功力太深,只能膜拜。
雪    币: 5676
活跃值: (1303)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 15 2017-11-5 19:05
9
0
只能膜,太强
雪    币: 44
活跃值: (55)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
actor刘 2017-11-6 00:46
10
0
真的太强了
雪    币: 6818
活跃值: (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
聖blue 2017-11-6 21:34
11
0
不错!
雪    币: 870
活跃值: (2264)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
s1ber 2018-7-22 23:28
12
0
写的很好
雪    币: 207
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小小白wp 2018-12-11 10:35
13
0
老哥太骚了
游客
登录 | 注册 方可回帖
返回