如何识别对该字符串执行的编码

逆向工程 加密 编码 字符串
2021-07-09 05:24:10

我有一个旧的应用程序,它以明文形式存储用户名和密码,但对我来说是由一种未知的编码算法编码的,它看起来像 base64,但我认为它不是,任何超过 22 或 23 的单词都会被截断,但不幸的是有一些改变最后一个字符的奇怪例子

for example:

Input: ABCDEFGHIJKLMNOPQRSTUV
Output: //DeparF3/jFcmVGBf5LcmNHYqJBmw

Input: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Output: //DeparF3/jFcmVGBf5LcmNHYqJBmw

Input: ABCDEFGHIJKLMNOPQRSTUV1
Output: //DeparF3/jFcmVGBf5LcmNHYqJBm2

Input: ABCDEFGHIJKLMNOPQRSTUV123456
Output: //DeparF3/jFcmVGBf5LcmNHYqJBm2

Input: 1234567890123456789012
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w

Input: 12345678901234567890123
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/2

Input: 123456789012345678901234
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/2

Input: 1234567890123456789012a
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z

Input: 1234567890123456789012ab
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z

Input: 1234567890123456789012c
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z

Input: 1234567890123456789012F
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/x

Input: 1234567890123456789012V
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w

Input: 1A2B3C4D5E6F7G8H9I0J1K
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhg

Input: 1A2B3C4D5E6F7G8H9I0J1K2
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhm

Input: 1A2B3C4D5E6F7G8H9I0J1K3
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhm

Input: 1A2B3C4D5E6F7G8H9I0J1KL
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhh

Input: 1A2B3C4D5E6F7G8H9I0J1KM
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhh

Input: A1B2C3D4E5F6G7H8I9J0KL
Output: /4Pf06yw3ITJDWg8D4dMGnsse8ZfgQ

Input: A1B2C3D4E5F6G7H8I9J0KLM
Output: /4Pf06yw3ITJDWg8D4dMGnsse8ZfgR

Input: A1B2C3D4E5F6G7H8I9J0KL7
Output: /4Pf06yw3ITJDWg8D4dMGnsse8ZfgW

Input: abcABC123/+321++1GWZVV
Output: 39D+oK3AqYK/FwU5eoEvCQNSZqxCmw

Input: abcABC123/+321++1GWZVV1
Output: 39D+oK3AqYK/FwU5eoEvCQNSZqxCm2

Input: lkjsd2093jljsdLJSDl12A
Output: 0tn3kouxqIm/UkJgO9RIaGFRXccmjA

Input: lkjsd2093jljsdLJSDl123
Output: 0tn3kouxqIm/UkJgO9RIaGFRXccm/g

Input: lkjsd2093jljsdLJSDl1234
Output: 0tn3kouxqIm/UkJgO9RIaGFRXccm/m

Input: aaaaaaaaaaaaaaaaaaaaaa
Output: 39P8gI7i+dHtWU9rKdFlQ1N0UJd1rA

Input: aaaaaaaaaaaaaaaaaaaaaaa
Output: 39P8gI7i+dHtWU9rKdFlQ1N0UJd1rD

Input: aaaaaaaaaaaaaaaaaaaaaa1
Output: 39P8gI7i+dHtWU9rKdFlQ1N0UJd1rG

Input: aaaaaaaaaaaaaaaaaaaaaaF
Output: 39P8gI7i+dHtWU9rKdFlQ1N0UJd1rB

Input: 1111111111111111111111
Output: j4Os0N6yqYG9CR87eYE1EwMkAMcl/A

Input: 11111111111111111111111
Output: j4Os0N6yqYG9CR87eYE1EwMkAMcl/G

Input: a
Output: 3w==

Input: c
Output: 3Q==

Input: ab
Output: 39A=

Input: abc
Output: 39D+

Input: abcd
Output: 39D+hQ==

Input: abcde
Output: 39D+hYo=

Input: abcdef
Output: 39D+hYrl

Input: abcdefg
Output: 39D+hYrl/w==

Input: abcdefgh
Output: 39D+hYrl/9g=

Input: 123
Output: j4Cu

Input: 1234
Output: j4Cu1Q==

Input: 12345
Output: j4Cu1do=

Input: 123456
Output: j4Cu1dq1

任何的想法?谢谢!

2个回答

OK,编码如下:

  1. 字节异或输入字符串 [0xbe,0xb2,0x9d,0xe1,0xef,0x83,0x98,0xb0,0x8c,0x38,0x2e,0x0a,0x48,0xb0,0x04,0x22,0x32,0x10x10x4c ,0x51]
  2. 将其编码为 base64
  3. 取 base 64 编码字符串的前 30 个字节并将其写下来,这将是结果。

实际上,密钥的最后一个字节并未作为整体使用(仅使用 msb 半字节),您可以插入范围 [0x50-0x5f] 而不是 0x51 中的任何内容。

正如您所说的那样,输出被截断,因此您只能提取编码字符串的前 22 个字母。

以下 python 代码对提供的输出进行解码并检查算法:

import base64
# our inputs, input and output correspondingly
pairs = [
    ("ABCDEFGHIJKLMNOPQRSTUV","//DeparF3/jFcmVGBf5LcmNHYqJBmw"),
    ("ABCDEFGHIJKLMNOPQRSTUVWXYZ","//DeparF3/jFcmVGBf5LcmNHYqJBmw"),
    ("ABCDEFGHIJKLMNOPQRSTUV1","//DeparF3/jFcmVGBf5LcmNHYqJBm2"),
    ("ABCDEFGHIJKLMNOPQRSTUV123456","//DeparF3/jFcmVGBf5LcmNHYqJBm2"),
    ("1234567890123456789012","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w"),
    ("12345678901234567890123","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/2"),
    ("123456789012345678901234","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/2"),
    ("1234567890123456789012a","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z"),
    ("1234567890123456789012ab","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z"),
    ("1234567890123456789012c","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z"),
    ("1234567890123456789012F","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/x"),
    ("1234567890123456789012V","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w"),
    ("1A2B3C4D5E6F7G8H9I0J1K","j/Ovo9zArPS5fRhMf/c8agtcAbwlhg"),
    ("1A2B3C4D5E6F7G8H9I0J1K2","j/Ovo9zArPS5fRhMf/c8agtcAbwlhm"),
    ("1A2B3C4D5E6F7G8H9I0J1K3","j/Ovo9zArPS5fRhMf/c8agtcAbwlhm"),
    ("1A2B3C4D5E6F7G8H9I0J1KL","j/Ovo9zArPS5fRhMf/c8agtcAbwlhh"),
    ("1A2B3C4D5E6F7G8H9I0J1KM","j/Ovo9zArPS5fRhMf/c8agtcAbwlhh"),
    ("A1B2C3D4E5F6G7H8I9J0KL","/4Pf06yw3ITJDWg8D4dMGnsse8ZfgQ"),
    ("A1B2C3D4E5F6G7H8I9J0KLM","/4Pf06yw3ITJDWg8D4dMGnsse8ZfgR"),
    ("A1B2C3D4E5F6G7H8I9J0KL7","/4Pf06yw3ITJDWg8D4dMGnsse8ZfgW"),
    ("abcABC123/+321++1GWZVV","39D+oK3AqYK/FwU5eoEvCQNSZqxCmw"),
    ("abcABC123/+321++1GWZVV1","39D+oK3AqYK/FwU5eoEvCQNSZqxCm2"),
    ("lkjsd2093jljsdLJSDl12A","0tn3kouxqIm/UkJgO9RIaGFRXccmjA"),
    ("lkjsd2093jljsdLJSDl123","0tn3kouxqIm/UkJgO9RIaGFRXccm/g"),
    ("lkjsd2093jljsdLJSDl1234","0tn3kouxqIm/UkJgO9RIaGFRXccm/m"),
    ("aaaaaaaaaaaaaaaaaaaaaa","39P8gI7i+dHtWU9rKdFlQ1N0UJd1rA"),
    ("aaaaaaaaaaaaaaaaaaaaaaa","39P8gI7i+dHtWU9rKdFlQ1N0UJd1rD"),
    ("aaaaaaaaaaaaaaaaaaaaaa1","39P8gI7i+dHtWU9rKdFlQ1N0UJd1rG"),
    ("aaaaaaaaaaaaaaaaaaaaaaF","39P8gI7i+dHtWU9rKdFlQ1N0UJd1rB"),
    ("1111111111111111111111","j4Os0N6yqYG9CR87eYE1EwMkAMcl/A"),
    ("11111111111111111111111","j4Os0N6yqYG9CR87eYE1EwMkAMcl/G"),
    ("a","3w=="),
    ("c","3Q=="),
    ("ab","39A="),
    ("abc","39D+"),
    ("abcd","39D+hQ=="),
    ("abcde","39D+hYo="),
    ("abcdef","39D+hYrl"),
    ("abcdefg","39D+hYrl/w=="),
    ("abcdefgh","39D+hYrl/9g="),
    ("123","j4Cu"),
    ("1234","j4Cu1Q=="),
    ("12345","j4Cu1do="),
    ("123456","j4Cu1dq1"),
]
# the xoring "key"
key = [0xbe,0xb2,0x9d,0xe1,0xef,0x83,0x98,0xb0,0x8c,0x38,0x2e,0x0a,0x48,0xb0,0x04,0x22,0x32,0x15,0x31,0xf6,0x14,0xcd,0x51]

def do_xor(a, b):
    res = ""
    for i in range(len(a)):
        res += chr(ord(a[i]) ^ b[i % len(b)])
    return res

for (inp, output) in pairs:
    #adding universal padding for a case of bad length
    try:
        decoded = base64.b64decode(output)
    except:
        decoded = base64.b64decode(output + "===")

    xored = do_xor(decoded, key)
    encoded = base64.b64encode(do_xor(inp, key))[:30]
    print "Input    --> ", inp
    print "Output   --> ", output
    print "Encoded  --> ", encoded
    print "restored --> ", xored
    if xored == inp:
        print "result: exact match"
    elif inp.find(xored) != -1:
        print "result: truncated, but still good"
    else:
        print "result: FAILURE"
    if encoded==output:
        print "encode: result OK"
    else:
        print "encode: FAILURE"
function crypt(b) {
    var raw = atob(b)
    var key = [0xbe,0xb2,0x9d,0xe1,0xef,0x83,0x98,0xb0,0x8c,0x38,0x2e,0x0a,0x48,0xb0,0x04,0x22,0x32,0x15,0x31,0xf6,0x14,0xcd]
    var ret = []
    for (var i in raw) ret[i] = raw.charCodeAt(i) ^ key[i%key.length]
    return String.fromCharCode.apply(null, ret)
}
// example
console.log(crypt('//DeparF3/jFcmVGBf5LcmNHYqJBmw'))
console.log(crypt('//DeparF3/jFcmVGBf5LcmNHYqJBm2'))
console.log(crypt('j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w'))

结果