Mctrain's Blog

What I learned in IT, as well as thought about life

Base64编码

| Comments

这两天看Jiang团队对AnserverBot的一篇详细的分析中提到AnserverBot中对很多数据(比如C&C server的url等)进行了Base64编码,于是,我对这种编码进行了了解,并根据他们实现的python解码脚本,写了一个ruby的解码脚本。

Base64

以下资料基本上来自于wiki

Base64用于对字符串进行编码,并将该编码通过网络传输出去,源于MIME内容(如邮件)的编码,用于防止内容在网络传输中被篡改。

接下来会通过一个示例进行说明:

首先我们会有一张Base64 index table:

Base64 index table

该表记录了0-63(即000000-111111)对应的ASCII字符。

然后我们有一段需要翻译的字符串,一段从Thomas Hobbes Leviathan的引用:

Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.

如果将每一个字母的ASCII码作为一个8-bit的二进制数展开的话会得到一个二进制串,比如我们Man的三个字母Man的ASCII码分别为7797110,展开二进制并连起来既得到010011010110000101101110。然后我们将这个3x8的bit分成4x6,并将每6个bit组成的数字记下来,然后通过查询前面的那张Base64 index table即可得到四个字符TWFu,也就是Man的Base64表达形式。如下图所示:

Base64 example

当然这张Base64 index table可以采用任意的字母数字顺序,而不用按照这种顺序,只要解码的时候用同一张index table就可以了。

还有一点要注意的是如果字符的数目不是3的倍数该怎么办?也就是说最后一个block也有可能是1个字符或者2个字符。这个时候就需要在后面再加0来进行补齐了,然后再用Base64的规则进行转换,在编码后的base64文本后加上一个或两个=号,代表补足的字节数。也就是说,当最后剩余1个字符时,最后一个6位的base64字节块有四位是0值,最后附加上两个等号;如果最后剩余2个字符时,最后一个6位的base64字节块有两位是0值,最后附加一个等号。

Base64 decode by Ruby

在ruby中可以通过requite 'base64'来很方便地对Base64字符串进行解码,比如在AnserverBot中,采用了STvJjktoVFZ9f0PGlicqy3xK7zH8ruXdn5WwDRIeb1UmEgOhYs2NpLC4QBa6AM+/_index table进行编码,于是ruby程序如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# decrypt str encoded by base64
# ARGV[0] translate command (0 for string, 1 for file)
# ARGV[1] translate string or file
# ARGV[2] my_base64chars if exist


require 'base64'

BEGIN { puts "Hello!\n" }

cmd = ARGV[0].to_i

if ARGV.size < 2
  puts "Usage: ruby #{$0} [0|1] [str|file] [base64chars]"
  exit
end

if cmd == 0
  s = ARGV[1]
elsif cmd == 1
  f = ARGV[1]
  s = File.read(f)
end

puts "\ntranslate string is:\n#{s}"

std_base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

if (ARGV[2] != nil) and (ARGV[2].size >= 65)
  my_base64chars = ARGV[2]
else
  my_base64chars  = "STvJjktoVFZ9f0PGlicqy3xK7zH8ruXdn5WwDRIeb1UmEgOhYs2NpLC4QBa6AM+/_"
end

puts "\ntranslate str according to #{my_base64chars}"

s = s.tr(my_base64chars, std_base64chars)

data = Base64.decode64(s)
puts "\ntranslated str is #{data}\n"

END { puts "\nBye!" }

其中s.tr(my_base64chars, std_base64chars)是将自定义的index table和标准index table进行下转换,将字符串中的字符用标准index table中对应的字符进行替换,然后再通过Base64.decode64(s)进行解码。

整个过程还是很简单的。

Comments