当前位置: 首页 > 知识库问答 >
问题:

如何在bash中存储一个32位的小端有符号整数?

李和裕
2023-03-14

(*在下面的帖子中,所有的IP、端口和密码都已更改。很抱歉这篇文章的格式,编辑似乎不喜欢新行。)

问题:如何将整数存储为带符号的32位小尾数?

背景:我试图使用RCon连接到bash中的minecraft服务器。到目前为止,服务器显示正在接收连接,但我无法正确格式化数据包。我可以使用mcrcon连接到服务器并在wireshark中查看数据包,但是当我尝试使用bash脚本时,数据包长度、requestid和类型值看起来是错误的。

以下是我的一些来源,故障排除数据和我的代码,这可能有助于回答这个问题。

资料来源:https://wiki.vg/RCON

实施:https://developer.valvesoftware.com/wiki/Source_RCON_Protocol

服务器控制台:

[22:24:09警告]:跟不上!服务器超载了吗?运行3190ms或落后63次

[22:24:23 INFO]: Rcon连接from:/164.256.8.10

[22:24:34 WARN]:跟不上!服务器超载了吗?运行9961毫秒或199滴答落后

[22:24:55警告]:跟不上!服务器超载了吗?跑2006ms或落后40秒

[22:25:12信息]:Rcon连接自:/164.256.8.10

.

代码

#!/bin/bash

# Length    int     Length of remainder of packet
# Request ID    int     Client-generated ID
# Type  int     3 for login, 2 to run a command, 0 for a multi-packet response
# Payload   byte[]  ASCII text
# 2-byte pad    byte, byte  Two null bytes 

# Connection details
RCON_HEADER=$(echo -e "\xff\xff\xff\xff")
HOST="192.168.0.173"
PORT=12345  
LENGTH=0 # Length of packet
REQUESTID=$RANDOM
PASSWORD="$1"
RES=0
COM=2
AUTH=3
NULL="\0"
COMMAND=${@:2}
echo "command: $COMMAND"



## Packet Format as per docs
#Packet Size in Bytes
#Request ID any int
#Type as above
#Body null terminated ascii string
#Empty string null terminated

build_packet()
{
    local TYPE="$1";
    $([ "$TYPE" == "$AUTH" ]) && local BODY="$PASSWORD" || local BODY=$COMMAND;
    local DATA="$REQUESTID$TYPE$BODY";    
    local LENGTH=${#DATA};
    local PACKET="$LENGTH$DATA";    
    echo $PACKET;
}



send()
{
    #local PACKET="$1"
    echo "sending: $PACKET"
    printf "$PACKET%s\0%s\0" >&5 &
}

read ()
{
    LENGTH="$1"
    RETURN=`dd bs=$1 count=1 <&5 2> /dev/null`
}


echo "trying to open socket"
# try to connect
if ! exec 5<> /dev/tcp/$HOST/$PORT; then
  echo "`basename $0`: unable to connect to $HOST:$PORT"
  exit 1
fi
echo "socket is open"


PACKET=$(build_packet $AUTH $PASSWORD);
echo "Command: $COMMAND"
echo "Packet: $PACKET"

send $PACKET

read 7
echo "RETURN: $RETURN"


PACKET=$(build_packet $COM $COMMAND);
echo "Command: $COMMAND"
echo "Packet: $PACKET"


send $PACKET

read 7
echo "RETURN: $RETURN"

.

引用代码:https://blog.chris007.de/using-bash-for-network-socket-operation/

共有2个答案

郎正初
2023-03-14

有一种bash方法,可以通过一些build_pkt函数创建二进制数据包。

但由于不能将空字符(\0)存储为bash字符串,因此必须使用printf来开发字符串:

declare -i REQUESTID=1

build_pkt() {
    local pkt='' len
    case $1 in
        AUTH ) local TYPE=3 BODY="$PASSWORD" ;;
        CMD ) local TYPE=2 BODY="${@:2}" ;;
        * ) return 1;;
    esac
    addtopkt() {
        local i v c
        printf -v v "%08x" $1
        for i in {3..0..-1};do
            printf -v c %03o 0x${v:2*i:2}
            pkt+=\\$c
        done
    }
    len=$(( ${#BODY} >0 ? ${#BODY}+10 : 10 ))
    addtopkt $len
    REQUESTID+=1
    addtopkt $REQUESTID
    addtopkt $TYPE
    pkt+="$BODY\0\0"
    printf "$pkt"
}

由于我版本的bash无法识别/dev/tcp/,因此我使用netcat(1 fork to background task)来执行以下任务:

TMPFIFO=/tmp/.mcrcon-$$
mkfifo $TMPFIFO
exec 8> >(exec stdbuf -i 0 -o 0 nc $HOST $PORT >$TMPFIFO 2>&1)
exec 9<$TMPFIFO
rm $TMPFIFO

然后一个快速的脏脚本来转储答案:

readrawanswer() {
     local foo LANG=C
     while IFS= read -u 9 -t .01 -r foo ;do
         printf "%q\n" "$foo"
     done
}
readrawanswer

build_pkt AUTH >&8
readrawanswer

我现在准备发送help请求到我的spiget(minecraft)servr:

build_pkt CMD "help" >&8
readrawanswer

在我的主机上,这将打印:

$'\272\001\'\302\247e--------- \302\247fHelp: Index (1/10) \302\247e--------------------'
$'\302\2477Use /help [n] to get page n of help.'
$'\302\2476Aliases: \302\247fLists command aliases'
$'\302\2476Bukkit: \302\247fAll commands for Bukkit'
$'\302\2476Minecraft: \302\247fAll commands for Minecraft'
$'\302\2476/advancement: \302\247fA Mojang provided command.'
$'\302\2476/ban: \302\247fA Mojang provided command.'
$'\302\2476/ban-ip: \302\247fA Mojang provided command.'
$'\302\2476/banlist: \302\247fA Mojang provided command.'
$'\302\2476/bossbar: \302\247fA Mojang provided command.'

那么现在,在第二页,我可以:

build_pkt >&8 CMD "help 2" && readrawanswer

$'\307\001)\302\247e--------- \302\247fHelp: Index (2/10) \302\247e--------------------'
$'\302\2476/clear: \302\247fA Mojang provided command.'
$'\302\2476/clone: \302\247fA Mojang provided command.'
$'\302\2476/data: \302\247fA Mojang provided command.'
$'\302\2476/datapack: \302\247fA Mojang provided command.'
$'\302\2476/debug: \302\247fA Mojang provided command.'
$'\302\2476/defaultgamemode: \302\247fA Mojang provided command.'
$'\302\2476/deop: \302\247fA Mojang provided command.'
$'\302\2476/difficulty: \302\247fA Mojang provided command.'
$'\302\2476/effect: \302\247fA Mojang provided command.'

最后,整个help列表,包含从110的页面:

for i in {1..10};do
    build_pkt >&8 CMD "help $i" && readrawanswer
done |
    sed 's/^.*[0-9]\/\([a-z:-]\+\): .*$/\1/p;d' |
    xargs |
    fold -s

在我的主机上,这将呈现:

advancement ban ban-ip banlist bossbar clear clone data datapack debug 
defaultgamemode deop difficulty effect enchant execute experience fill 
forceload function gamemode gamerule give help kick kill list locate loot me 
minecraft:help minecraft:reload msg op pardon pardon-ip particle playsound 
plugins publish recipe reload replaceitem restart save-all save-off save-on say 
schedule scoreboard seed setblock setidletimeout setworldspawn spawnpoint 
spigot spreadplayers stop stopsound summon tag team teammsg teleport tell 
tellraw time timings title tm tp tps trigger version w weather whitelist 
worldborder xp

一旦mcrcmd准备就绪:

mcrcmd() { 
    build_pkt CMD "$@" 1>&8 && readrawanswer
}

创建一个$mcrcmds数组,保存所有可用的命令:

mcrcmds=($(
    for i in {1..10};do
        build_pkt >&8 CMD "help $i" && readrawanswer
    done |
    sed 's/^.*[0-9]\/\([a-z:-]\+\): .*$/\1/p;d'
))

然后:

complete -W "${mcrcmds[*]}" mcrcmd 

现在,mcrcmd是一个带有bash completion的函数;-)

我们登录到一个bash会话,连接到minecraft服务器,准备通过发送命令和检索答案与他们交互。

感谢链接:

源RCON协议

让关闭nc

exec 8>&- ; exec 9<&-

但是,请考虑在Perl,Python中这样做,如果不是C

朱通
2023-03-14

解决方案是按如下方式进行十六进制编码和解码:

build_packet()
{
    local TYPE="$1"
    local BODY=""
    $([ "$TYPE" == "$AUTH" ]) && BODY="$PASSWORD" || BODY="$COMMAND"

    # Add null chars "\0" and hex encode to preserve them "xxd -p".
    # David C Rankin also mentioned, you could possibly printf them in using flags.
    local DATA=`echo -ne "$ID\0\0\0\0$TYPE\0\0\0$BODY\0\0" | xxd -p | tr -d '\n'`

    local LEN="${#DATA}"
    LEN=$((LEN / 2))
    LEN="$(asc $LEN)"  
    LEN=`echo -ne "$LEN\0\0\0" | xxd -p | tr -d '\n'`
    local PACKET="$LEN$DATA"    
    echo $PACKET
}

注:#(asc)是一个将LEN转换为ascii字符的自定义函数。我不知道为什么数据包长度必须是ascii字符,但类型(也是整数)却不是。也许其他人可以回答这个问题。

在传输时,十六进制被解码为“echo-ne”$PACKET | xxd-r-p

 类似资料:
  • 问题内容: 由于Redis尝试将字符串解析为64位带符号整数,因此存储32位带符号整数而不是基数10个整数字符串的二进制表示是个好主意吗? 在我们的系统中,我们列出了许多32位带符号整数ID。 问题答案: 在内部,Redis以最有效的方式存储字符串。将整数强制为基数10的字符串实际上会占用更多的内存。 这是Redis存储字符串的方式- 小于10000的整数存储在共享内存池中,并且没有任何内存开销。

  • 我的数字类型是有符号二的补码整数。 在内存寄存器%rdi/edi/di中,我有0xFFFFFFFF。在%rsi/esi/si中,我有0x8000000。 如何正确添加这些? 我认为答案是: 由于我添加的是完整的32位寄存器,因此可以添加完整的0xFFFFFFFF和0x8000000。 我把一个有符号整数加到一个有符号整数上,它们都是32位的。我实际上是在加-2147483648和0x8000000

  • 如果我的问题令人困惑,我很抱歉,但这是我想做的例子, 假设我有一个二进制的无符号long int=1265985549,我可以把它写成010010110010110100000001101 现在我想把这个32位二进制数分成4位,像这样,分别处理这4位 0100 1011 0111 0101 0110 1000 0000 1101 任何帮助都将不胜感激。

  • GCC和Clang似乎对有符号整数和无符号整数之间的加法有不同的解释,这取决于它们的大小。为什么会这样?所有编译器和平台上的转换是否一致? 举个例子: 结果: 在这两种情况下,我们得到了-1,但其中一个被解释为无符号整数和下溢。我本以为两者都会以同样的方式转化。 那么,为什么编译器会以如此不同的方式转换它们,这保证了一致性吗?我用G11.1.0和Clang12.0测试了这个。以及Arch Linu

  • 问题内容: 哪种Java数据类型可以存储较大的数值,例如9999999999? 问题答案: 您的具体示例可以存储在(或必要时)中。 如果在任何时候都需要更大的数字,则可以尝试 (如果是整数)或(如果是十进制)

  • 问题内容: 假设我有这个号码。我如何将其称为无符号变量?像C一样 问题答案: 假设 : 您会想到2的补码表示法;和, 通过您 的意思是 32位无符号整数, 那么您只需将其添加为负值即可。 例如,将此应用于-1: 假设1表示您希望将-1视为一个1位的实字符串,而假设2表示您希望其中的32个。 但是,除了你,没人可以说你隐藏的假设是什么。例如,如果考虑到1的补码表示形式,则需要应用前缀运算符。Pyth