【问题标题】:bitwise XOR a string in Bash在 Bash 中按位异或一个字符串
【发布时间】:2011-02-26 22:58:21
【问题描述】:

我正在尝试在 Bash 脚本中完成一项工作。我有一个字符串,我想用我的密钥进行异或。

#!/bin/sh
PATH=/bin:/usr/bin:/sbin:/usr/sbin export PATH

teststring="abcdefghijklmnopqr"

现在我如何异或测试字符串的值并使用 bash 将其存储在变量中?

任何帮助将不胜感激。

基本上我试图复制以下 VB 脚本的结果:

Function XOREncryption(CodeKey, DataIn)

Dim lonDataPtr
Dim strDataOut
Dim temp
Dim tempstring
Dim intXOrValue1
Dim intXOrValue2


For lonDataPtr = 1 To Len(DataIn) Step 1
    'The first value to be XOr-ed comes from the data to be encrypted
    intXOrValue1 = Asc(Mid(DataIn, lonDataPtr, 1))
    'The second value comes from the code key
    intXOrValue2 = Asc(Mid(CodeKey, ((lonDataPtr Mod Len(CodeKey)) + 1), 1))

    temp = (intXOrValue1 Xor intXOrValue2)
    tempstring = Hex(temp)
    If Len(tempstring) = 1 Then tempstring = "0" & tempstring

    strDataOut = strDataOut + tempstring
Next
XOREncryption = strDataOut
End Function

【问题讨论】:

  • 不就是teststring="abcdefghijklmnopqr" ^ key吗?
  • 也许如果有某种方法可以从 bash 脚本传递 perl 中的 teststring 的值并在那里异或。
  • 这听起来像是您试图在 bash 中进行某种密码混淆或加密。 Perl 或 python 可能是更好的语言选择。
  • 似乎无法全部停留在 bash 中。所以也许你应该坚持使用 perl,或者使用比语言解释器更轻量级的东西;例如可以使用 od 和 sed 例如:echo $(( $(echo -n "c" |od -td1 |sed -e 's/^[0-9]\+ *//g;') ^ 1 ));把你的char而不是“c”......如果你把它作为一个函数更好;然后你可以按字符串拆分 char 并应用 xor (也许 xor char by char 不是你想要的......;如果你需要 xorring 两个相同长度的字符串,你可以拆分两者并“构建”结果;否则字符串 ^ 整数...什么意思?
  • 这一切都可以在 BASH 中完成。请参阅下面的答案。

标签: perl bash scripting shell


【解决方案1】:

BASH 中的按位异或要求两个操作数都是数字。由于在 bash 中没有获取字符序数 (ASCII) 值的内置方法,因此您需要使用 Perl 来获取该值。

编辑:如下所述,ord 仅适用于字符串的第一个字符。

let a=`perl -e 'print ord $_ for split //, $ARGV[0]' string`^123; echo $a

当然,一旦你在 Perl 中,你也可以在那里做所有事情:

let a=`perl -e '$ordinal .= ord $_ for split //, $ARGV[0]; print $ordinal ^ $ARGV[1]' string 123`

编辑:事实证明,您可以使用 printf 在 BASH 中获取字符串的序数值。只需在字符串前面加上 '

printf "%d" "'string"

因此,仅在 BASH 中:

let a=$(printf "%d" "'string")^123; echo $a

【讨论】:

  • 我在 bash 中有类似的东西,但它的输出显示为 0... username=whoami test=perl -e 'print ord $strUrl ^ key' echo ${test} 我有什么错误吗?
  • 您需要将perl 命令包装在反引号或$() 中。此外,$strUrl 是未定义的,key 是一个裸词。
  • 这是正确的做法吗?我得到一个两位数的数字作为输出,但我期待一个长数字。 key=secret strUrl=cdcdcdc strUrl=$(($(printf "%d" "'strUrl")^$(printf "%d" "'key")));echo $strUrl
  • ord 对单个字符进行操作,因此ord $ARGV[0] 返回$ARGV[0]第一个字符 的序数值
  • 你得到十进制格式的结果。使用unpack("N", pack("B32", substr("0" x 32 . $decimal, -32))) 将其转换为二进制
【解决方案2】:

如果你决定使用 Perl 单线,这就是我想出的

perl -e '@a=split("", $ARGV[0]); @b=split("", $ARGV[1]); print unpack "H2", chr(ord(shift @a)^ord(shift @b)) while @a; print "\n"' aab aaa

Perl 6 中的 zip 函数会做得更好...

【讨论】:

    【解决方案3】:

    these hints 的帮助下,我快速编写了这个脚本来完成佩德罗的回答:

    #!/bin/bash
    
    function ascii2dec
    {
      RES=""
      for i in `echo $1 | sed "s/./& /g"`
      do 
        RES="$RES `printf \"%d\" \"'$i\"`"
      done 
      echo $RES
    }
    
    function dec2ascii
    {
      RES=""
      for i in $*
      do 
        RES="$RES`printf \\\\$(printf '%03o' $i)`"
      done 
      echo $RES
    }
    
    function xor
    {
      KEY=$1
      shift
      RES=""
      for i in $*
      do
        RES="$RES $(($i ^$KEY))"
      done
    
      echo $RES
    }
    
    
    KEY=127
    TESTSTRING="abcdefghijklmnopqr"
    
    echo "Original String: $TESTSTRING"
    STR_DATA=`ascii2dec "$TESTSTRING"`
    echo "Original String Data: $STR_DATA"
    XORED_DATA=`xor $KEY $STR_DATA`
    echo "XOR-ed Data: $XORED_DATA"
    RESTORED_DATA=`xor $KEY $XORED_DATA`
    echo "Restored Data: $RESTORED_DATA"
    RESTORED_STR=`dec2ascii $RESTORED_DATA`
    echo "Restored String: $RESTORED_STR"
    

    结果:

    iMac:Desktop fer$ bash test.sh
    Original String: abcdefghijklmnopqr
    Original String Data: 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 
    XOR-ed Data: 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13
    Restored Data: 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 
    Restored String: abcdefghijklmnopqr
    

    【讨论】:

    • 这是一个只有一个字节的密钥的异或运算。
    • 这只能异或 ASCII 字符串。不适用于 unicode。
    • 反引号已折旧。请用命令替换结构 $() 替换它们。
    【解决方案4】:

    在busybox中工作(粘贴无法接收两个流),也使关键重复

    #!/bin/sh
    numitems() { i=0;while read ITEM; do i=$(( $i + 1 )) ; done; echo $i; }
    starmap() { while read ITEM; do $1 $ITEM; done; }
    ord() { printf '%d\n' "'$1"; }
    chr() { printf \\$(printf '%03o' $1); }
    split_chars() { echo -n "$1" | sed 's/./&\n/g'; }
    xor() { echo $(($1 ^ $2)); }
    map_ord() { split_chars "$1" | starmap ord; }
    encrypt()
    {
    KEY=$1;STR=$2;
    while [ ${#KEY} -lt ${#STR} ]; do KEY="$KEY$KEY"; done; #make key longer then str
    [ -e /tmp/paste_col1 ] && rm -rf /tmp/paste_col1
    [ -e /tmp/paste_col1t ] && rm -rf /tmp/paste_col1t
    [ -e /tmp/paste_col2 ] && rm -rf /tmp/paste_col2
    map_ord "$KEY">/tmp/paste_col1t
    map_ord "$STR">/tmp/paste_col2
    head -n `wc -l /tmp/paste_col2 |sed -r 's|^([0-9]+)\s.*|\1|'` /tmp/paste_col1t>/tmp/paste_col1 #trim lines
    [ -e /tmp/paste_col1t ] && rm -rf /tmp/paste_col1t
    paste /tmp/paste_col1 /tmp/paste_col2 | starmap xor | starmap chr
    [ -e /tmp/paste_col1 ] && rm -rf /tmp/paste_col1
    [ -e /tmp/paste_col2 ] && rm -rf /tmp/paste_col2
    echo
    }
    KEY="12345678"
    TESTSTRING="abcdefghasdfasdfasfdas"
    encrypt "$KEY" "$TESTSTRING"
    ENC="`encrypt \"$KEY\" \"$TESTSTRING\"`"
    encrypt "$KEY" "$ENC" # we should get $TESTSTRING again
    

    【讨论】:

    • 输出可以是任何字节值,chr 产生控制字符不是一个好主意。这就是为什么问题中的代码使用Hex 作为输出。
    【解决方案5】:

    另一个答案

    function xenc {
      local data=$1 key=$2
      local _data _key ndata nkey count i _res
      _data=($(eval printf "'%d '" $(printf "%s" "$data" | sed -e '$!N;${s/./"'"'"'&" /g;s/""/\\&/g}')))
      _key=($(eval printf "'%d '" $(printf "%s" "$key" | sed '$!N;${s/./"'"'"'&" /g;s/""/\\&/g}')))
      ndata=${#_data[@]} nkey=${#_key[@]}
      (( count = ndata < nkey ? nkey : ndata ))
      for ((i = 0; i < count; i++)); do
        (( _res[i] = ${_data[i]:-0} ^ ${_key[i%nkey]:-0} ))
      done
      printf "\\\\\\%o" "${_res[@]}"  | xargs printf
    }
    res=$(xenc abcdefghijklmnopqrstuvwxyz FLqFb8LU0TY)
    xenc "$res" FLqFb8LU0TY
    

    【讨论】:

    • 您可以使用循环 data=abcdef; for ((i=0;i&lt;${#data};i++)); do printf '%d ' "'${data:i:1}"; done 来避免使用 eval 和调用外部命令 sed 的复杂构造。
    • 由于很可能使用 xor 发出控制字符,因此将编码结果显示为十六进制数字(作为问题请求)是合理的。
    • @JosEduSol 实际上,这是对所提出问题的合理解决方案:一个 bash 脚本对两个字符串(作为 ascii 值)进行异或运算。为此,将printf "\\\\\\%o" "${_res[@]}" | xargs printf 更改为更简单的printf '%02x' "${_res[@]}"
    【解决方案6】:

    仅将函数转换为 bash 的最相似的转换是:
    (# 表示评论):

    # Function XOREncryption(CodeKey, DataIn)
    XOREncryption(){
        local CodeKey=$1
        local DataIn=$2
        # Dim lonDataPtr strDataOut temp tempstring intXOrValue1 intXOrValue2
        local lonDataPtr strDataOut temp tempstring intXOrValue1 intXOrValue2
    
        # For lonDataPtr = 1 To Len(DataIn) Step 1
        for (( lonDataPtr=0; lonDataPtr < ${#DataIn}; lonDataPtr++ )); do
            #The first value to be XOr-ed comes from the data to be encrypted
        # intXOrValue1 = Asc(Mid(DataIn, lonDataPtr, 1))
        intXOrValue1=$( toAsc "${DataIn:$lonDataPtr:1}" )
        echo "val1=$intXOrValue1 and=<${DataIn:$lonDataPtr:1}> and $(toAsc "${DataIn:$lonDataPtr:1}")"
            #The second value comes from the code key
        echo "Ptr=$lonDataPtr Mod=<$(( lonDataPtr % ${#CodeKey} ))>"
        # intXOrValue2 = Asc(Mid(CodeKey, ((lonDataPtr Mod Len(CodeKey)) + 1), 1))
        intXOrValue2=$( toAsc "${CodeKey:$(( lonDataPtr % ${#CodeKey} )):1}" )
        echo "val1=$intXOrValue1 val2=<${CodeKey:$(( lonDataPtr % ${#CodeKey} )):1}> and |$intXOrValue2|"
        # temp = (intXOrValue1 Xor intXOrValue2)
        temp=$(( intXOrValue1 ^ intXOrValue2 ))
        echo "temp=$temp"
            # tempstring = Hex(temp)
        tempstring=$(printf '%02x' "$temp")
        echo "tempstring=$tempstring"
        # strDataOut = strDataOut + tempstring
            strDataOut+=$tempstring
        echo
        # Next
        done
        # XOREncryption = strDataOut
        printf '%s\n' "$strDataOut"
    # End Function
    }
    

    移除 cmets,并清理代码:

    #!/bin/bash
    
    Asc() { printf '%d' "'$1"; }
    
    XOREncryption(){
        local key=$1 DataIn=$2
        local ptr DataOut val1 val2
    
        for (( ptr=0; ptr < ${#DataIn}; ptr++ )); do
            val1=$( Asc "${DataIn:$ptr:1}" )
            val2=$( Asc "${key:$(( ptr % ${#key} )):1}" )
            DataOut+=$(printf '%02x' "$(( val1 ^ val2 ))")
        done
        printf '%s\n' "$DataOut"
    }
    
    CodeKey="$1"
    teststring="$2"
    XOREncryption "$CodeKey" "$teststring"
    

    运行它:

    $ ./script.sh "123456789" "abcdefghijklmnopqr"
    5050505050505050505b595f595b5947494b
    

    【讨论】:

      猜你喜欢
      • 2012-03-03
      • 2013-02-06
      • 1970-01-01
      • 2021-07-23
      • 2016-06-19
      • 2023-01-18
      • 1970-01-01
      • 2010-10-02
      • 2013-08-31
      相关资源
      最近更新 更多