16진수 바이트 값:

16진수 바이트 값:

내가 가지고 있는 파일 내에서 특정 바이트 시퀀스가 ​​몇 번이나 발생하는지 계산하고 싶습니다. 예를 들어 \0xdeadbeef실행 파일 내에서 해당 숫자가 몇 번 나오는지 알고 싶습니다 . 지금은 grep을 사용하여 해당 작업을 수행하고 있습니다.

#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file

(내 CPU가 리틀 엔디안이기 때문에 바이트가 역순으로 기록됩니다.)

그러나 내 접근 방식에는 두 가지 문제가 있습니다.

  • 이러한 \Xnn이스케이프 시퀀스는 물고기 껍질에서만 작동합니다.
  • grep은 실제로 내 매직 넘버가 포함된 줄 수를 계산합니다. 패턴이 같은 줄에 두 번 나타나면 한 번만 계산됩니다.

이러한 문제를 해결할 수 있는 방법이 있나요? 이 하나의 라이너를 Bash 셸에서 실행하고 파일 내에서 패턴이 발생하는 횟수를 정확하게 계산하려면 어떻게 해야 합니까?

답변1

이것은 요청된 한 줄짜리 솔루션입니다("프로세스 대체"가 있는 최근 쉘의 경우).

grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l

"프로세스 대체"를 사용할 수 없으면 <(…)grep을 필터로 사용하십시오.

hexdump -v -e '/1 "%02x "' infile.bin  | grep -o "ef be ad de" | wc -l

아래는 솔루션의 각 부분에 대한 자세한 설명입니다.

16진수 바이트 값:

첫 번째 문제는 해결하기 쉽습니다.

\Xnn 이스케이프 시퀀스는 피쉬 셸에서만 작동합니다.

위쪽을 X아래쪽으로 변경 x하고 printf를 사용합니다(대부분의 쉘에 대해).

$ printf -- '\xef\xbe\xad\xde'

또는 다음을 사용하십시오:

$ /usr/bin/printf -- '\xef\xbe\xad\xde'

'\x' 표현을 구현하지 않기로 선택한 쉘의 경우.

물론 16진수를 8진수로 변환하면 (거의) 모든 쉘에서 작동합니다.

$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'

여기서 "$sh"는 (합리적인) 쉘입니다. 하지만 이를 정확하게 인용하는 것은 매우 어렵습니다.

바이너리 파일.

가장 강력한 솔루션은 파일과 바이트 시퀀스(둘 다)를 (new line) 0x0A또는 (null byte) 와 같은 홀수 문자 값과 관련된 문제가 없는 일부 인코딩으로 변환하는 것입니다 0x00. 둘 다 "텍스트 파일"을 처리하도록 설계되고 조정된 도구를 사용하여 올바르게 관리하기가 매우 어렵습니다.

base64와 같은 변환은 유효한 것처럼 보일 수 있지만 모든 입력 바이트가 mod 24(비트) 위치의 첫 번째, 두 번째 또는 세 번째 바이트인지에 따라 최대 3개의 출력 표현을 가질 수 있다는 문제를 제시합니다.

$ echo "abc" | base64
YWJjCg==

$ echo "-abc" | base64
LWFiYwo=

$ echo "--abc" | base64
LS1hYmMK

$ echo "---abc" | base64        # Note that YWJj repeats.
LS0tYWJjCg==

16진수 변환.

이것이 바로 가장 강력한 변환이 단순한 HEX 표현처럼 각 바이트 경계에서 시작되는 변환이어야 하는 이유입니다.
다음 도구 중 하나를 사용하여 파일의 16진수 표현이 포함된 파일을 얻을 수 있습니다.

$ od -vAn -tx1 infile.bin | tr -d '\n'   > infile.hex
$ hexdump -v -e '/1 "%02x "' infile.bin  > infile.hex
$ xxd -c1 -p infile.bin | tr '\n' ' '    > infile.hex

이 경우 검색할 바이트 시퀀스는 이미 16진수입니다.
:

$ var="ef be ad de"

하지만 변형될 수도 있습니다. 왕복 hex-bin-hex의 예는 다음과 같습니다.

$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de

검색 문자열은 이진 표현에서 설정될 수 있습니다. od, hexdump 또는 xxd 위에 제시된 세 가지 옵션은 모두 동일합니다. 일치 항목이 바이트 경계에 있는지 확인하려면 공백을 포함해야 합니다(니블 시프트는 허용되지 않음).

$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de

바이너리 파일이 다음과 같은 경우:

$ cat infile.bin | xxd
00000000: 5468 6973 2069 7320 efbe adde 2061 2074  This is .... a t
00000010: 6573 7420 0aef bead de0a 6f66 2069 6e70  est ......of inp
00000020: 7574 200a dead beef 0a66 726f 6d20 6120  ut ......from a 
00000030: 6269 0a6e 6172 7920 6669 6c65 2e0a 3131  bi.nary file..11
00000040: 3232 3131 3232 3131 3232 3131 3232 3131  2211221122112211
00000050: 3232 3131 3232 3131 3232 3131 3232 3131  2211221122112211
00000060: 3232 0a

그런 다음 간단한 grep 검색을 통해 일치하는 시퀀스 목록이 제공됩니다.

$ grep -o "$a" infile.hex | wc -l
2

한 줄?

이 모든 작업은 한 줄로 수행될 수 있습니다:

$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l

11221122예를 들어, 동일한 파일에서 검색하려면 다음 두 단계가 필요합니다.

$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4

일치 항목을 "확인"하려면 다음을 수행하세요.

$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
3131323231313232
3131323231313232
3131323231313232
3131323231313232

$ grep "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')

… 0a3131323231313232313132323131323231313232313132323131323231313232313132320a


버퍼링

grep이 파일 전체를 버퍼링하고, 파일이 크면 컴퓨터에 큰 부하를 줄 우려가 있습니다. 이를 위해 버퍼링되지 않은 sed 솔루션을 사용할 수 있습니다.

a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin  | 
    sed -ue 's/\('"$a"'\)/\n\1\n/g' | 
        sed -n '/^'"$a"'$/p' |
            wc -l

첫 번째 sed는 버퍼링되지 않으며( -u) 일치하는 문자열마다 스트림에 두 개의 개행 문자를 삽입하는 데에만 사용됩니다. 두 번째는 sed(짧은) 일치하는 줄만 인쇄합니다. wc -l은 일치하는 줄을 계산합니다.

이렇게 하면 일부 짧은 줄만 버퍼링됩니다. 두 번째 sed에서 일치하는 문자열입니다. 이는 사용되는 리소스가 상당히 낮아야 합니다.

또는 이해하기가 다소 복잡하지만 하나의 sed에서는 동일한 아이디어입니다.

a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin  |
    sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
        wc -l

답변2

GNU grep-P(perl-regexp) 플래그 사용

LC_ALL=C grep -oaP '\xef\xbe\xad\xde' file | wc -l

LC_ALL=Cgrep이는 바이트 시퀀스를 문자로 해석하려고 시도하는 다중 바이트 로케일의 문제를 방지하는 것입니다 .

-agrep이진 파일을 텍스트 파일과 동일하게 처리합니다( 일치하는 항목이 하나 이상 있는지 여부만 인쇄하는 일반적인 동작 대신 ).

답변3

PERLIO=:raw perl -nE '$c++ while m/\xef\xbe\xad\xde/g; END{say $c}' file

입력 파일을 바이너리로 처리합니다(줄바꿈이나 인코딩에 대한 번역 없음, 참조).펄런) 그런 다음 주어진 16진수(또는 어떤 형식이든)와 일치하는 모든 항목에 대한 카운터를 증가시키면서 인쇄하지 않는 입력 파일을 반복합니다.펄레).

답변4

GNU를 사용하면 awk다음을 수행할 수 있습니다.

LC_ALL=C awk -v 'RS=\xef\xbe\xad\xde' 'END{print NR - (NR && RT == "")}'

바이트 중 하나라도 ERE 연산자인 경우 (를 사용하여) 이스케이프해야 합니다 \\. 는 0x2e또는 로 .입력해야 합니다 . 그 외에는 0과 0xa를 포함한 임의의 바이트 값으로 작동해야 합니다.\\.\\\x2e

NR-1몇 가지 특수한 경우가 있다고 해서 그렇게 간단하지는 않습니다 .

  • 입력이 비어 있으면 NR은 0이고 NR-1은 -1을 제공합니다.
  • 입력이 레코드 구분 기호로 끝나면 그 이후에는 빈 레코드가 생성되지 않습니다. 우리는 이를 테스트합니다 RT=="".

또한 최악의 경우(파일에 검색어가 포함되어 있지 않은 경우)에는 파일 전체가 메모리에 로드됩니다.

관련 정보