CTFSHOW: 36D杯 misc ez-qrcode


前言

本题为:CTFSHOW的36D杯 misc ez-qrcode
和攻防世界的qr-easy解法基本一模一样。

1.第一眼

在这里插入图片描述

用ps稍微处理一下:

在这里插入图片描述

此二维码的大小为 29x29,版本V的大小为N × NN = 17 + 4V,所以这是版本 3。

2.格式信息

在这里插入图片描述

图中两处红色方框,表示二维码的格式信息,前面7位,后面8位。

后面8位的格式信息为:11100111,前7位我们不清楚,所以我们的信息字符串为:???????11100111

所有格式信息字符串的列表

ECC 级别掩码图案类型信息比特
L0111011111000100
L1111001011110011
L2111110110101010
L3111100010011101
L4110011000101111
L5110001100011000
L6110110001000001
L7110100101110110
M0101010000010010
M1101000100100101
M2101111001111100
M3101101101001011
M4100010111111001
M5100000011001110
M6100111110010111
M7100101010100000
Q0011010101011111
Q1011000001101000
Q2011111100110001
Q3011101000000110
Q4010010010110100
Q5010000110000011
Q6010111011011010
Q7010101111101101
H0001011010001001
H1001001110111110
H2001110011100111
H3001100111010000
H4000011101100010
H5000001001010101
H6000110100001100
H7000100000111011

根据二维的信息字符串???????11100111再结合所有格式信息字符串的列表或者上图表格。

很容易得到这个二维码的ECC级别为H和掩码模式为2。

3.去掉掩码

掩码号如果下面的公式对于给定的行/列坐标为真,则切换该坐标处的位
0(行 + 列) mod 2 == 0
1(行) mod 2 == 0
2(列) mod 3 == 0
3(行 + 列) mod 3 == 0
4(floor(行 / 2) + floor(列 / 3)) mod 2 == 0
5((行 * 列) mod 2) + ((行 * 列) mod 3) == 0
6( ((行 * 列) mod 2) + ((行 * 列) mod 3) ) mod 2 == 0
7( ((行 + 列) mod 2) + ((行 * 列) mod 3) ) mod 2 == 0

根据QR Mask Patterns Explained,也就是上面的表格,2 号掩码有公式(列) mod 3 == 0。注意列号是从0开始的,所以我们要切换坐标为0,3,6,9, … ,27的列的位。

二维码也有固定的图案,所以我们只需要切换数据部分的位即可。查看数据区和位顺序。

在这里插入图片描述
(通过wikipedia.org)

所以,原始的 D1-D26 是:

D1 = 00010100
D14 = 10100010
D2 = 00110011
D15 = 00010001
D3 = 00110011
D16 = 01110110
D4 = 11000110
D17 = 01010111
D5 = 00010110
D18 = 01000100
D6 = 11011101
D19 = 00101100
D7 = 00011111
D20 = 01011110
D8 = 00011100
D21 = 00000010
D9 = 00100001
D22 = 01100110
D10 = 00110110
D23 = 01010111
D11 = 00010110
D24 = 11010000
D12 = 11100110
D25 = 11101101
D13 = 10110011
D26 = 01000100

去掉掩码后,(列) mod 3 == 0

D1 = 01000001
D14 = 11110111
D2 = 01100110
D15 = 01000100
D3 = 01100110
D16 = 01110110
D4 = 11000110
D17 = 01010111
D5 = 00010110
D18 = 01000100
D6 = 01110111
D19 = 10000110
D7 = 10110101
D20 = 11110111
D8 = 01000110
D21 = 01010111
D9 = 01110100
D22 = 00110110
D10 = 00110110
D23 = 01010111
D11 = 00010110
D24 = 11010000
D12 = 11100110
D25 = 11101100
D13 = 11100110
D26 = 00010001

4.数据解码

解码有模式指示符

  • 0001:数字模式(每 3 位 10 位)
  • 0010:字母数字模式(每 2 个字符 11 位)
  • 0100:字节模式(每个字符 8 位)
  • 1000:汉字模式(每个字符 13 位)
  • 0111: ECI 模式

字符计数指示符跟在模式指示符之后。

  • 版本 1-9
    • 数字模式:10 位
    • 字母数字模式:9位
    • 字节模式:8位
    • 汉字模式:8位
  • 版本 10–26
    • 数字模式:12位
    • 字母数字模式:11 位
    • 字节模式:16位
    • 汉字模式:10位
  • 版本 27–40
    • 数字模式:14位
    • 字母数字模式:13 位
    • 字节模式:16位
    • 汉字模式:12位

查看每种模式的编码过程:

让我们从上面的数据 D1-D26 开始:

data = '01000001' \
       '01100110' \
       '01100110' \
       '11000110' \
       '00010110' \
       '01110111' \
       '10110101' \
       '01000110' \
       '01110100' \
       '00110110' \
       '00010110' \
       '11100110' \
       '11100110' \
       '11110111' \
       '01000100' \
       '01110110' \
       '01010111' \
       '01000100' \
       '10000110' \
       '11110111' \
       '01010111' \
       '00110110' \
       '01010111' \
       '11010000' \
       '11101100' \
       '00010001'
alphanumeric = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'.chars

def read(str, size)
  str.slice!(0, size)
end

def kanji(num)
  if num >= 0x1740
    (0xC140 + num / 0xC0 * 0x100 + num % 0xC0)
      .chr(Encoding::Shift_JIS).encode(Encoding::UTF_8)
  else
    (0x8140 + num / 0xC0 * 0x100 + num % 0xC0)
      .chr(Encoding::Shift_JIS).encode(Encoding::UTF_8)
  end
end

loop do
  case mode = read(data, 4)
  when '0010' # Alphanumeric
    count = read(data, 9).to_i(2)
    (count / 2).times do
      chunk = read(data, 11).to_i(2)
      print alphanumeric[chunk / 45] + alphanumeric[chunk % 45]
    end
    print alphanumeric[read(data, 11).to_i(2)] if count.odd?
  when '0100' # Byte
    count = read(data, 8).to_i(2)
    count.times do
      print read(data, 8).to_i(2).chr
    end
  when '1000' # Kanji
    count = read(data, 8).to_i(2)
    count.times do
      print kanji(read(data, 13).to_i(2))
    end
  when '0000' # Terminate
    break
  else
    fail "Unhandled mode #{mode}"
  end
end

Ruby在线网站运行即可得到

flag{TgCannotGetHouse}


Python代码同样也没有问题:

# D1-D26:
data = ['01000001', '01100110', '01100110', '11000110', '00010110', '01110111', '10110101', '01000110', '01110100', '00110110', '00010110', '11100110', '11100110', '11110111', '01000100', '01110110', '01010111', '01000100', '10000110', '11110111', '01010111', '00110110', '01010111', '11010000', '11101100', '00010001']

data = "".join(data)
data = data[4:]

for i in range(0, len(data), 8):
	print(chr(int(data[i:i+8], 2)), end="")

运行效果如下:

在这里插入图片描述

flag{TgCannotGetHouse}

拜拜!

5.参考网站:

https://yous.be/2014/12/07/seccon-ctf-2014-qr-easy-write-up/

https://www.zhihu.com/question/65253283

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值