使用DC命令简单加密字符串

By gyakkun


上一篇捣腾路由的先鸽着, 写点有意思的。

打开一个bash, 敲dc -e 11533144122526433426838417720292223103708995620728433930P就可以找到我的联系方式了, 很神奇吧(笑)。

这里面有点小故事, 实现方法也不难。

今天U2例行签到, 见到了一个失效的头像URL, 顺手看了下主域名, 发现有点面善atr.me。视奸了一会儿之后终于确定就是bgm网友AstroProfundis的博客… 好吧并不意外, 毕竟华语核心观众的圈子相当小。

看了一会儿博客,找到了atr菊苣进入阿里的伯乐骨头, 又视奸了一下, 发现他的联系方式很有意思

联系方式:
gtalk,执行以下代码可得:

dc -e 561966095130892579929141297432895687762711112970P

具体是什么大家敲一敲就知道了… 但这样的加密方式很炫酷好吗!比base64之流高到不知哪里去了(笑)

这个DC命令什么来头? 一查才知道是上古unix工具之一, 本职是计算逆波兰表达式, 又衍生出一系列的语法, 和三剑客sed/grep/awk一样有着自成一套的控制指令, 说是一种脚本也不为过。

dc -e接受一个dc script作为输入, 结尾的P是输出栈顶元素的意思, 输出的结果可能是字符串或是数字。

//man dc

P Command

Otherwise it is a number, and the integer  portion  of its  absolute value is printed out as a "base (UCHAR_MAX+1)" byte stream.
Strings

All registers and the stack can hold strings, and dc always knows whether any given object is a string or a number.

也就是说, dc维护着一个栈, 当通过P输出栈顶的时候, 栈顶元素可以被当成8位字节流输出。

打开ASCII表, 尝试dc -e 81P, 输出果然是”A”。

接下来要做的事情便是把字符串转成字节流, 表示成二进制串, 再用二进制转十进制将其转成长整数。

举个栗子:

gyak -> g(0110 0111)

​ y(0111 1001)

​ a(0110 0001)

​ k(0110 1011)

-> 0110 0111 0111 1001 0110 0001 0110 1011 (bin)

-> 1736008043 (dec)

敲进终端试试

gyakkun@gyakkun-vm:~$ dc -e 1736008043P
gyakgyakkun@gyakkun-vm:~$

由于没有换行符, 所以gyakgyakkun@gyakkun-vm:~$粘在了一起。

所以本质上, 我们需要做的是把字符串从内存中取出来, 然后用十进制表示。字符串->字节流->二进制数->十进制数, 难点在于字节流到二进制字符串的转换, 以及超长的二进制转十进制。

一个朴素的字符串元素转01字符串函数string2Bin如下(javascript):

function string2Bin(str) {
  var result = [];
  for (var i = 0; i < str.length; i++) {
    // toString(Base), 二进制
    result.push(str.charCodeAt(i).toString(2));
  }
  return result;
}

返回一个数组, 存储的是高有效位置存在低地址(小端序)的01字符串, 和正常的认知习惯一致(小端序万岁(笑))

> string2Bin("gyak")
> (4) ["1100111", "1111001", "1100001", "1101011"]

发现并没有补齐8位, 取一个补齐函数

function pad(num, n) {
  var len = num.toString().length;
  while(len < n) {
    num = "0" + num;
    len++;
  }
  return num;
}

加在for循环后面, 并简单reduce即得到整合的01串

function string2Bin(str) {
  var result = [];
  var strArr = [];
  for (var i = 0; i < str.length; i++) {
    result.push(str.charCodeAt(i).toString(2));
  }
  result.forEach(function(curVal, idx, arr){
    var num = parseInt(curVal);
    strArr.push(pad(num,8))
  })
  var final = strArr.reduce(function(x,y){
    return x+y;
  })
  return final;
}

至于超长的二进制转十进制… 原生js应该是无力了, 找了段11年前的代码 (https://bbs.pediy.com/thread-56002.htm)

#include 
#include 
#define MAX_LEN 10000

typedef struct{
    int len;
    char value[MAX_LEN];
} TBigInt, *pBigInt;

void AddBit(pBigInt s,char BitValue){    //加一位, 0或者1。
    int i;
    for (i=0;ilen;i++)
        s->value[i]*=2;
    s->value[0]+=BitValue;
    for (i=0;ilen;i++){
        s->value[i+1]+=s->value[i]/10;
        s->value[i]%=10;
    }
    if (s->value[s->len]) s->len++;
}

void OutputBigInt(pBigInt s){           //输出大整数类型
    int i;
    if (s->len==0){
        puts("0");
    } else {
        for (i=s->len-1;i>=0;i--)
            putchar(s->value[i]+'0');
        putchar('\n');
    }
}

int main(){
    static char buf[1000000];
    static TBigInt BigInt;
    while (scanf("%s",buf)!=EOF){
        memset(&BigInt,0,sizeof BigInt);
        int i;
        for (i=0;buf[i];i++)
            AddBit(&BigInt,buf[i]=='1'?1:0);
        OutputBigInt(&BigInt);
    }
    return 0;
}

现在大功告成了。最后给一个python一行解决方案 [允悲]

dc -e 119478888636517259055612461521120705176651269182846473956513706195861203318266031963052663857590454411930259941938858428322656289858423108577680485174083166511734620065184747508449486602P