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 이스케이프 시퀀스는 Fish 쉘에서만 유효합니다.

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"는 (합리적인) 쉘입니다. 하지만 이를 정확하게 인용하는 것은 매우 어렵습니다.

바이너리 파일.

0x0A가장 신뢰할 수 있는 해결책은 파일과 바이트 시퀀스(둘 다)를 (new line) 또는 (null byte)와 같은 홀수 문자 값에 문제가 없는 일부 인코딩으로 변환하는 것입니다 0x00. "텍스트 파일"을 처리하도록 설계되고 적용된 도구를 사용하여 적절하게 관리하는 것은 매우 어렵습니다.

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

$ echo "abc" | base64
YWJjCg==

$ echo "-abc" | base64
LWFiYwo=

$ echo "--abc" | base64
LS1hYmMK

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

16진수 변환.

이것이 바로 가장 강력한 변환이 단순한 16진수 표현처럼 모든 바이트 경계에서 시작되는 변환이어야 하는 이유입니다.
다음 도구 중 하나를 사용하여 파일을 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"

하지만 변형도 가능합니다. 왕복 16진수-2진수-16진수의 예는 다음과 같습니다.

$ 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

grepGNU -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

입력 파일을 바이너리 파일로 처리합니다(개행이나 인코딩된 번역 없음, 참조).페렌) 그런 다음 인쇄하지 않고 입력 파일을 반복하여 카운터를 증가시킵니다(또는 무엇이든 참조).페레).

답변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=="".

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

관련 정보