"H"/72/0x48이 실행 파일에서 두 번째로 흔한 바이트인 이유는 무엇입니까?

"H"/72/0x48이 실행 파일에서 두 번째로 흔한 바이트인 이유는 무엇입니까?

(이 질문이 72점이라면 투표하지 마세요!)

나는 이것을 실행했습니다 :

cat /usr/bin/* |
  perl -ne 'map {$a{$_}++} split//; END{print map { "$a{$_}\t$_\n" } keys %a}' |
  grep --text . | sort -n | plotpipe --log y {1}

그리고 이것을 얻었습니다 :

바이트 값의 발생 횟수

(대수적인 y축을 사용하더라도 여전히 기하급수적으로 보입니다! 위쪽과 아래쪽 사이의 거리가 100배 이상 더 큽니다.)

숫자를 살펴보세요:

:
31919597        ^H
32983719        ^B
33943030        ^O
39130281        \213
39893389        $
52237360        \211
53229196        ^A
76884442        \377
100776756       H
746405320       ^@

^@(NUL)이 실행 파일에서 가장 일반적인 바이트라는 것은 놀라운 일이 아닙니다. \377 (255) 및 ^A (1)도 나에게 직관적으로 이해됩니다.

그러나 "H"(72)가 실행 파일에서 두 번째로 흔한 바이트인 이유는 무엇입니까? 255와 1보다 더 흔한 바이트입니까?

배경

Perl 스크립트의 경우 Perl 스크립트에서 최소 공통 바이트를 찾아야 합니다. 놀랍게도 Perl 스크립트를 단순히 greping하는 대신 모든 바이너리에 대해 명령을 실행했습니다. 나는 NUL, 1, 255와 같은 몇 바이트가 눈에 띄기를 기대했지만 "H"는 확실히 그렇지 않았습니다.

이 그래프의 입력은 각 바이트 수(정렬됨)입니다. y축은 개수를 나타내고 x축은 줄 번호(1바이트는 256개의 다른 값만 가질 수 있으므로 1~256)를 나타냅니다. y축은 로그 스케일이므로 그 차이는 지수보다 큽니다.

답변1

그럴 것이다64비트 피연산자 크기 접두사amd64 기계어 코드 명령어.

amd64 실행 파일에서만 발생한다는 것을 알 수 있습니다.

/bin/*비교한다면http://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_9.1-1_arm64.deb, http://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_9.1-1_amd64.deb그리고 http://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_9.1-1_i386.deb, 다음 내용이 표시됩니다.

$ for f (coreutils_9.1-1_*.deb) bsdtar xOf $f da\* | bsdtar xO ./bin/\* | xxd -p -c1 | sort | uniq -c | sort -rn | head -n 5 | grep -H --label="${${f:r}##*_}" .
amd64: 692417 00
amd64: 145689 ff
amd64:  81911 48
amd64:  48006 89
amd64:  45331 0f
arm64:1409826 00
arm64:  70391 ff
arm64:  67915 03
arm64:  49380 20
arm64:  41655 40
i386: 515346 00
i386: 171643 ff
i386:  78361 0e
i386:  69317 24
i386:  50497 83

0x48(72, 'H')는 amd64의 상위 3개에만 있습니다.

ls내 amd64 데비안 시스템 에서 :

$ xxd -p -c1 =ls | sort | uniq -c | sort -rn | head -n 5
  39187 00
   7827 ff
   5565 48
   4181 20
   3393 0f

이 실행 파일의 코드를 분해하면 지침에서 많은 수의 0x48 바이트를 찾을 수 있습니다.

$ objdump -d =ls | grep -cw 48
5353

대부분은 첫 번째 위치에 있습니다.

$ objdump -d =ls | grep -wm10 48
    4000:       48 83 ec 08             sub    $0x8,%rsp
    4004:       48 8b 05 ad ff 01 00    mov    0x1ffad(%rip),%rax        # 23fb8 <__gmon_start__@Base>
    400b:       48 85 c0                test   %rax,%rax
    4012:       48 83 c4 08             add    $0x8,%rsp
    44b6:       68 48 00 00 00          push   $0x48
    4751:       48 89 f3                mov    %rsi,%rbx
    4754:       48 83 ec 68             sub    $0x68,%rsp
    4758:       48 8b 3e                mov    (%rsi),%rdi
    475b:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
    4764:       48 89 44 24 58          mov    %rax,0x58(%rsp)
$ objdump -d =ls | grep -Pc '^\s*[\da-f]+:\s+48'
5113

~에 따르면http://ref.x86asm.net/geek.html#x48, 0x48은64비트 피연산자 크기 REX.W기본 피연산자 대신 64비트 피연산자에 대한 연산을 지정하는 Opcode 접두사입니다.

$ objdump -d =ls | pcregrep -o1 -o2 '^\s*[\da-f]+:\s+(48 .. ).*?\t(\S+)' | sort | uniq -c  | sort -rn | head
   1512 48 89 mov
   1040 48 8b mov
    630 48 8d lea
    372 48 85 test
    326 48 83 add
    198 48 39 cmp
    158 48 83 sub
     79 48 01 add
     72 48 83 cmp
     69 48 c7 movq

모든 명령어는 64비트 피연산자에서 수행됩니다.

관련 정보