文字列を出力するプログラムを楽に難読化(?)する方法

センスは感じられないがBrainfuckを使えばとりあえずぱっと見何やってるかわからない. 配列(とポインタ)さえ扱えればどんな言語でもできるはず. 1時間もかからない.

  1. 入力した文字列を出力するBrainfuckコードを出力するプログラムをインターネット上で見つける.
  2. Brainfuckコードを得る.
  3. 好きな言語に翻訳
  4. 整形
  5. 完成.

完成した例がこちら. 1文字目で張り切りすぎてバランスが悪い. 「み」と「と」だけを使おうと努力はした.

#define Mi while
#define To putchar
char mi[3+10];main(to){
            *(mi+--to)
            +=01+3;Mi(
            *(mi+to++)
            +=01+3,*(mi                 +to--)-=3,10,
            *mi)to;To(                  ++*(mi+to+++
            01));Mi(*(                  mi+to++)-=3,
            ++*(mi+to--                 ),*(mi+01));
*(mi+to+++01)+=3;To(*(mi+to));Mi(*(mi+to++)-=3,++*(mi+to--),
*(mi+to));*(mi+to+++01)-=01+3;To(--*(mi+to));*(mi+to)-=3;Mi
(--        *(mi+to++),                  *(mi+to--)+=
01+        3,*(mi+to))                  ;To(01+*(mi+
to++       +01));Mi(*(                  mi+to++)-=01
+3         ,++*(mi+to                   --),*(mi+to
));        *(mi+to++                    +01)+=3;To
(*(mi+to));*(mi+to                      )+=01+01;
 Mi(*(mi+to++)-=                        3,*(mi+
   to--)+=01+                           01,*(
     mi+to)                             );To
                                        (*


     (mi+to                        +++01
       ));*(                    mi+to
        )+=3;               To(*(mi
         +to));          *(mi+to)
          +=-3+     10;To((*
          (mi+to  ))++);Mi
           (--*(mi+to++)
         ,*(mi+to--
       )+=3,*
     (mi+to
     ));To
     (*(mi
     +to++
      +01));
        *(mi+to)
           -=01+01;Mi(*(mi+to++)-=3,
                 ++*(mi+to--),*(



    mi+to));--*(mi+to+++01);To(*(mi+to));
                               *(mi+to)
                 +=3+3;        Mi(--
                  *(mi+        to
                  ++),*        (mi
                  +to--
                  )+=01
                 +01,*
                (mi+to
                ));++
               *(mi+
               to++
              +01
              );


               To((
               *(mi
               +to)
               )++);
               Mi(*
               (mi+
               to++
               )-=3
               ,*(mi
               +to--
               )-=01
               -3+01
               -3,*(
               mi+to
               ));++
               *(mi+
               to++
               +01)


        ;To(*
        (mi+
        to));
        *(mi
        +to)
        +=01+
        10;To
        ((*(
        mi+to
        ))--);*(mi
        +to)-=01+3;To
        ((*(     mi+to)
        )++);      Mi(*(mi
        +to++         )-=3,*
        (mi+to          --)-=
        01-3+             01-3,
        *(mi+              to));
        --*(
        mi+to
        +++01
        );To(
        *(mi+
        to++)


                          );To(*(
                          mi+ to)
                           +10);}

無題

133565, 39302, 33134, 38327, 32709, 79486, 141338, 29224, 67390, 33156, 135192, 144337, 154547, 21212, 5610, 105208, 21821, 139964, 115775, 30189, 159181, 130547, 125287, 74453, 55876, 133762, 80912, 6067, 79578, 27557, 129153, 138025, 114769, 154497, 140840, 159900, 126286, 42271, 66379, 68012, 36996, 66001, 118864, 48626, 102740, 159973, 8286, 123763, 131668, 23535, 81349, 106040, 147948, 159900, 49912, 146801, 156242, 58531, 35122, 130937, 27382, 65529, 152765, 155236, 132217, 28150, 81048, 74596, 150708, 159973, 21732, 88284, 142556, 18318, 22208, 114975, 67522, 694, 78986, 82151, 23030, 38561, 40287, 61505, 19801, 37301, 79072, 124490, 29043, 10878, 80203, 83226, 30154, 151910, 148669, 18713, 82050, 14420, 87625, 159020, 19723, 25978, 61403, 72963, 158603, 119404, 44367, 154893, 58283, 76993, 103941, 154031, 83569, 32324, 136716, 24366, 70680, 28614, 84925, 149951, 15188, 82340, 74612, 135269, 44609, 68240, 66945, 146670, 28010, 18276, 139655
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import requests
from bs4 import BeautifulSoup
from collections import defaultdict
from random import choice

def main():
    from argparse import ArgumentParser
    parser = ArgumentParser()
    parser.add_argument('-d', '--decrypt', action='store_true')
    parser.add_argument('-u', '--aozora-url', nargs='?', type=str, default='')
    parser.add_argument('-i', '--aozora-file', nargs='?', type=str, default='')
    parser.add_argument('file', nargs=None, type=str)
    args = parser.parse_args()

    if bool(args.aozora_url) == bool(args.aozora_file):
        sys.exit(1)
    soup = None
    ENCODING = 'sjis'
    if args.aozora_url:
        req = requests.get(args.aozora_url)
        req.raise_for_status()
        soup = BeautifulSoup(req.content, 'lxml', from_encoding=ENCODING)
    else:
        with open(args.aozora_file, 'rb') as f:
            soup = BeautifulSoup(f.read(), 'lxml', from_encoding=ENCODING)
    texts = []
    for node in soup.find_all('div', class_='main_text'):
        for tag in node.find_all(['rp', 'rt']):
            tag.decompose()
        for tag in node.find_all('span', class_='notes'):
            tag.decompose()
        texts.append(node.get_text().replace('\n', '').replace(' ', ''))
    text = ''.join(texts)
    input_text = None
    with open(args.file) as f:
        input_text = f.read()
    if args.decrypt:
        print(''.join(text[int(e.strip())] for e in input_text.split(',')))
    else:
        table = defaultdict(list)
        for i, ch in enumerate(text):
            table[ch].append(i)
        print(', '.join(str(choice(table[ch])) for ch in input_text if ch in table))

if __name__ == '__main__':
    main()

SKB

C言語のmain関数の仮引数について

最近のC言語の規格では, main関数の仮引数は基本的に

int main(void)

int main(int argc, char **argv)

のどちらか(またはこれらと同等のもの)にしなければならない(C99, C11).

のだが, 今日ネット上で

int main(int argc, char **argv, char **envp)

というのを見た. ので調べた.

envpは広く利用されているが, 移植性に問題がある(C99, C11). envpにはargvと同じ形式で環境変数が記憶される. 検索してヒットしたページによると, どうやらUnix系OSでは伝統的に使われていたらしい.

現代的なプログラムで環境変数を取得する場合は標準ライブラリのgetenvを利用するべきである. POSIXシステムならば,

#include <unistd.h>
extern char **environ;

とすることで, envpと同等のグローバル変数environが利用できる.

このリンクの付け方はあまりよろしくない気がする.

あと,なぜ今まで知らなかったのかよくわからないが, argv[argc] == NULLは保証されている.

某ディストリビューションでPythonパッケージを扱う

はじめに

venvを紹介するだけの記事なので知ってる人は帰ってよし.

三行で

$ sudo apt install python3-venv
$ python3 -m venv ${PATH_TO_VIRT_ENV}
$ source ${PATH_TO_VIRT_ENV}/bin/activate

プログラミング言語のパッケージ管理システムは微妙

基本的にディストリビューションのパッケージ管理システムを使うべきだと思う. もし作るなら管理者権限の必要ないものにするべき.

某ディストロの問題

某ディストロのレポジトリはPythonのパッケージがあまり充実していない. しかし, pipでパッケージをインストールするとシステムのプログラムがうまく動作しなくなってしまう. そこ(解決がめんどくさいの)でvenvで仮想環境を作り自由にパッケージを使えるようにする.

やり方

(記憶で書いていて実際に動作を確認していないので注意)

仮想環境を~/Persistent/user_env作成する (user_venvがこの仮想環境の名前になる). シェルはBashと仮定する.

$ echo $SHELL
/bin/bash
$ echo $VENV_PATH
/(省略)/Persistent/user_env
  • pipとvenvをインストール.
$ sudo apt install python3-pip python3-venv
  • 仮想環境を作成.
$ python3 -m vev ${VENV_PATH}
  • 仮想環境をアクティベートする. プロンプトに仮想環境名が表示される.
$ source ${VENV_PATH}/bin/activate
(user_env) $ 
  • 後は好きなように環境構築
(user_env) $ pip install pyjokes
(user_env) $ pyjoke
A programmer walks into a bar and orders 1.38 root beers. The bartender informs her it's a root beer float. She says 'Make it a double!'
  • 仮想環境をディアクティベートすると元の環境に戻ってくる.
(user_env) $ deactivate
$ pyjoke
bash: pyjoke: command not found

はい.

Internet Kill Switch by Firewall (OpenVPN + iptables)

タイトルの通り. このPythonプログラムはopenvpnの設定ファイルの

remote <hostname> <port>

remote <ip address> <port>

に書き換える.

また, 書き換えたVPNサーバのIPアドレスへのアクセスを許可するiptablesのOUTPUTチェインの追加ルールを./rules.txtに保存する. iptables-restoreで読み込める形にしてるので, このルールを追加しその他のルールを適切に設定すれば, ファイアウォールキルスイッチを実現できる.

プログラム

使用は自己責任で

何をやってるかわからない人は実行しないほうがよいです.

Use at your own risk

Don't execute it unless you understand what it does.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import socket
import ipaddress

def main():
    from argparse import ArgumentParser
    parser = ArgumentParser()
    parser.add_argument('-d', '--dry-run', action='store_true')
    args = parser.parse_args()

    path = '/etc/openvpn/client/'
    filenames = [f for f in os.listdir(path)
                 if os.path.isfile(path + f) and (f.endswith('.conf') or f.endswith('.ovpn'))]

    targets = []
    for filename in filenames:
        lines = None
        with open(path + filename, 'r') as f:
            lines = [line.strip() for line in f.readlines()]
        found = False
        port = 0
        addr = ''
        proto = ''
        for i, line in enumerate(lines):
            if not line:
                continue
            linesplit = line.split()
            if linesplit[0] == 'remote':
                port = linesplit[2]
                try:
                    addr = socket.gethostbyname(linesplit[1])
                    ipaddress.ip_address(addr)
                except:
                    print('cannot get ip address of {} in {}'.format(linesplit[1], filename))
                    break
                found = True
                lines[i] = 'remote {} {}'.format(addr, port)
            elif linesplit[0] == 'proto':
                proto = linesplit[1]
        if found:
            if not args.dry_run:
                with open(path + filename, 'w') as f:
                    f.writelines('{}\n'.format('\n'.join(lines)))
            targets.append({
                'addr': addr,
                'port': port,
                'proto': proto,
                'comment': 'VPN {}'.format(' '.join(filename.rsplit('.', 1)[0].split('_'))),
            })

    rules = []
    # iptables rules
    for target in targets:
        rules.append('# {}'.format(target['comment']))
        rules.append('-A OUTPUT -p {} -m {} -d {}/32 --dport {} -j ACCEPT'.format(target['proto'], target['proto'], target['addr'], target['port']))

    f = None
    if not args.dry_run:
        try:
            f = open('rules.txt', 'w')
        except OSError:
            print('failed to create a rule file.')
            print('write to stdout')
    try:
        print(*rules, sep='\n', file=f)
    except IOError:
        print('failed to write to the rule file')
    if f is not None and not args.dry_run:
        f.close()

if __name__ == '__main__':
    main()

おまけ

動作確認はしていないので注意. 統合する気はありません.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys

def main():
    with open('./rules.txt') as f:
        vpn_rules = f.readlines()
    fw = '/etc/iptables/iptables.rules'
    with open(fw) as f:
        rules = f.readlines()
    start = -1
    end = -1
    for i, rule in enumerate(rules):
        if rule.startswith('## BEGIN VPN'):
            start = i
        elif rule.startswith('## END VPN'):
            end = i
            break
    if start >= end or end == -1:
        sys.exit(1)
    rules = rules[:start + 1] + vpn_rules + rules[end:]                                                

    print(*rules, sep='')

    yn = input('write to file?[y/N]: ')
    if yn == 'y':
        with open(fw, 'w') as f:
            f.writelines(rules)

if __name__ == '__main__':
    main()

安全でないストリーム暗号ARCFOUR

はじめに

Wikipediaを見てみたらARCFOURのアルゴリズムが単純だったので実装してみた. 一応言っておくが, 暗号アルゴリズムの実装は素人がやるものではない.

参考記事: https://en.wikipedia.org/wiki/RC4

記事に書いてある通りに実装した.

ソースコード

/* This program is in the public domain */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdint.h>

#define MAX_KEY_LEN 256
#define S_LEN 256

uint8_t key[MAX_KEY_LEN + 1];
uint8_t S[S_LEN];

void swap(uint8_t *a, uint8_t *b)
{
    uint8_t t = *a;
    *a = *b;
    *b = t;
}

char *readline(char *s, size_t size)
{
    if (fgets(s, size, stdin) == NULL)
        return NULL;
    if (strchr(s, '\n') != NULL)
        s[strlen(s) - 1] = '\0';
    else
        while (getchar() != '\n')
            ;
    return s;
}

/* key-scheduling algorithm */
void KSA(size_t key_len)
{
    size_t i, j;
    for (i = 0; i < S_LEN; i++)
        S[i] = i;
    j = 0;
    for (i = 0; i < S_LEN; i++) {
        j = (j + S[i] + key[i % key_len]) % S_LEN;
        swap(&S[i], &S[j]);
    }
}

/* pseudo-random generation algorithm (PRGA) */
uint8_t PRGA(void)
{
    /* static variables! */
    static size_t i, j;
    i = (i + 1) % S_LEN;
    j = (j + S[i]) % S_LEN;
    swap(&S[i], &S[j]);
    return S[(S[i] + S[j]) % S_LEN];
}

int main(int argc, char **argv)
{
    FILE *fr, *fw;
    uint8_t buf;
    size_t key_len;

    if (argc != 3) {
        fprintf(stderr, "specify files\n");
        return 1;
    }
    if (!readline((char *)key, MAX_KEY_LEN + 1)) {
        fprintf(stderr, "failed to read key");
        return 1;
    }
    key_len = strlen((char *)key);
    KSA(key_len);
    if ((fr = fopen(argv[1], "rb")) == NULL) {
        fprintf(stderr, "failed to open file");
        return 1;
    }
    if ((fw = fopen(argv[2], "wb")) == NULL) {
        fprintf(stderr, "failed to open file");
        fclose(fr);
        return 1;
    }
    for (;;) {
        fread(&buf, sizeof(buf), 1, fr);
        if (feof(fr))
            break;
        if (ferror(fr)) {
            fprintf(stderr, "failed to read\n");
            break;
        }
        buf ^= PRGA();
        if (fwrite(&buf, sizeof(buf), 1, fw) < 1) {
            fprintf(stderr, "failed to write\n");
            break;
        }
    }
    fclose(fr);
    fclose(fw);
    return 0;
}

動作テスト

テキストファイルtest.txtを暗号化する. 暗号化すると文字化けするので16進数で表示する.

$ hexdump test.txt                                                                
0000000 83e3 e390 bc83 83e3 e381 a383 83e3 e3ab                                                         
0000010 a983 82e3 e3a4 9083 83e3 e3bc ae81 9ce6                                                         
0000020 e388 8e83 bee7 e58e 8e85 81e3 e3a7 9981                                                         
0000030 80e3 0a82 81e3 e3bf a881 83e3 e3a9 b882                                                         
0000040 81e3 e8ae b3a9 b4e7 e3b0 af81 81e3 e393                                                         
0000050 a181 82e3 ef89 81bc 86e2 6892 7474 3a70                                                         
0000060 2f2f 7774 6670 6a2e 2f70 694d 6f74 7354                                                         
0000070 6b75 6e69 0a6f 4d44 81e3 e5af 8b80 88e5                                                         
0000080 e3a5 a781 81e3 e3af aa81 81e3 408f 696e                                                         
0000090 696a 6173 6a6e 5f69 7061 e370 be81 81e3                                                         
00000a0 e3a7 8a81 a1e9 e398 8481 87e8 e3b4 9781                                                         
00000b0 81e3 e3be 9981 80e3 0a82 83e3 e395 a182                                                         
00000c0 83e3 e3b3 a282 83e3 e3bc 8883 81e3 20af                                                         
00000d0 e323 bf81 81e3 e3a8 8281 83e3 e3bc a881                                                         
00000e0 e320 a781 99e7 e8ba 8ba6 81e3 e397 be81                                                         
00000f0 81e3 ef99 81bc 000a
00000f7

これをキーVirtual YouTuberで暗号化してtest.txt.encに保存.

$ ./arc4 test.txt test.txt.enc
Virtual YouTuber
$ hexdump test.txt.enc
0000000 ee83 d240 22d7 820f afa8 e854 2757 303f
0000010 bfed 3ebb f9a3 b055 29da b054 e975 17c2
0000020 1211 d87e 97b4 bae8 a525 3d65 7256 05e6
0000030 138a 697d c071 a7a5 e715 75be 49de 8e4c
0000040 bf07 bfff b0a4 cc27 8498 0dbd 77ea 67b1
0000050 670a 2daa a470 ab84 251f 8129 9885 41c2
0000060 afd8 57e4 2552 39ff 18db 327e 0745 399f
0000070 5c34 b6e2 c6d2 93ef c14b d111 da67 b225
0000080 fb26 fc24 d760 15fa 71e7 dce4 b55f 6d71
0000090 3355 874c e3ef 105b 423d ca88 4d9b 58ec
00000a0 f162 ef70 510a 8faa 6ef5 d474 0a03 1598
00000b0 4a35 7574 494e 2117 b50c 9898 7e58 1a4a
00000c0 93b2 4c73 4116 2b17 b2ab 4290 9d2b 107f
00000d0 1f7e d8be e6ef 0bfe 2012 f740 4595 8a8d
00000e0 ff29 84a3 a444 7c0e fbae 2515 f988 2ce9
00000f0 dcd7 7249 e9ec 002a
00000f7

test.txt.encを復号してtest.txt.enc.decに保存.

$ ./arc4 test.txt.enc test.txt.enc.dec                                            
Virtual YouTuber
$ hexdump test.txt.enc.dec                                                       
0000000 83e3 e390 bc83 83e3 e381 a383 83e3 e3ab
0000010 a983 82e3 e3a4 9083 83e3 e3bc ae81 9ce6
0000020 e388 8e83 bee7 e58e 8e85 81e3 e3a7 9981
0000030 80e3 0a82 81e3 e3bf a881 83e3 e3a9 b882
0000040 81e3 e8ae b3a9 b4e7 e3b0 af81 81e3 e393
0000050 a181 82e3 ef89 81bc 86e2 6892 7474 3a70
0000060 2f2f 7774 6670 6a2e 2f70 694d 6f74 7354
0000070 6b75 6e69 0a6f 4d44 81e3 e5af 8b80 88e5
0000080 e3a5 a781 81e3 e3af aa81 81e3 408f 696e
0000090 696a 6173 6a6e 5f69 7061 e370 be81 81e3
00000a0 e3a7 8a81 a1e9 e398 8481 87e8 e3b4 9781
00000b0 81e3 e3be 9981 80e3 0a82 83e3 e395 a182
00000c0 83e3 e3b3 a282 83e3 e3bc 8883 81e3 20af
00000d0 e323 bf81 81e3 e3a8 8281 83e3 e3bc a881
00000e0 e320 a781 99e7 e8ba 8ba6 81e3 e397 be81
00000f0 81e3 ef99 81bc 000a
00000f7

もとに戻った.

$ sha256sum test.txt test.txt.enc.dec
0e288309792d64d4b14af3db9e847de9e0c32526f2f42b9254580f86648fc3e6  test.txt
0e288309792d64d4b14af3db9e847de9e0c32526f2f42b9254580f86648fc3e6  test.txt.enc.dec

おまけ

Rustの練習で書いてみた. まったく感覚がわからない.

use std::env;
use std::fs::File;
use std::io::{self, BufReader, BufWriter, Read, Write};
use std::process;

const MAX_KEY_LEN: usize = 256;
const S_LEN: usize = 256;

struct Arc4 {
    s: [u8; S_LEN],
    i: usize,
    j: usize,
}

// key-scheduling algorithm
fn ksa(key: &[u8]) -> Arc4 {
    let mut a = Arc4 {
        s: [0; S_LEN],
        i: 0,
        j: 0,
    };
    for i in 0..S_LEN {
        a.s[i] = i as u8;
    }
    let mut j: usize = 0;
    let key_len = key.len();
    for i in 0..S_LEN {
        j = (j + usize::from(a.s[i]) + usize::from(key[i % key_len])) % S_LEN;
        a.s.swap(i, j);
    }
    a
}

// pseudo-random generation algorithm (PRGA)
fn prga(a: &mut Arc4) -> u8 {
    a.i = (a.i + 1) % S_LEN;
    a.j = (a.j + usize::from(a.s[a.i])) % S_LEN;
    a.s.swap(a.i, a.j);
    a.s[(usize::from(a.s[a.i]) + usize::from(a.s[a.j])) % S_LEN]
}

fn main() {
    if env::args().len() != 3 {
        println!("specify files");
        process::exit(1);
    }
    let mut key_input = String::new();
    io::stdin().read_line(&mut key_input).expect("failed to read input from stdin");
    let key_input_trimmed = key_input.trim();
    match key_input_trimmed.len() {
        1...MAX_KEY_LEN => {},
        _ => {
            println!("the key length must be 1-{} bytes (8 - {} bits)", MAX_KEY_LEN, MAX_KEY_LEN * 8);
            process::exit(1);
        }
    }
    let mut a = ksa(key_input_trimmed.as_bytes());
    let args: Vec<String> = env::args().skip(1).collect();
    let fr = File::open(&args[0]).expect("failed to open a file");
    let mut reader = BufReader::new(fr);
    let fw = File::create(&args[1]).expect("failed to create a file");
    let mut writer = BufWriter::new(fw);
    let mut buf = vec![];
    reader.read_to_end(&mut buf).expect("failed to read a file");
    for b in buf.iter_mut() {
        *b ^= prga(&mut a);
    }
    writer.write_all(&buf).expect("failed to write to a file");
}

TweetDeckのサムネイルを小さいサイズの画像にするブックマークレット

通信速度制限を喰らっているときにTweetDeckの画像抽出カラムの流速が速いととてもつらいのでそれを若干緩和させるブックマークレットを書いた. JavaScriptをまともに触るのは初めてなので苦戦. Web系まったくわからん.

Content Security PolicyのアレでこれをFirefoxで実行するにはabout:configからsecurity.csp.enablefalseにする必要がある. 常用する場合は*monkeyとかを使えばよさそう.

javascript:(()=>Array.from(document.getElementsByClassName('chirp-container')).forEach(e=>(new MutationObserver((r,o)=>r.forEach(s=>Array.from(s.addedNodes).forEach(n=>Array.from(n.getElementsByTagName('a')).filter(a=>a.hasAttribute('rel')&&a.getAttribute('rel')==='mediaPreview').forEach(a=>a.style.backgroundImage=a.style.backgroundImage.replace(/name=.*$/,'name=tiny")')))))).observe(e,{childList:true})))();