gdb를 사용하여 메모리를 확인하는 것이 혼란스러우신가요?

gdb를 사용하여 메모리를 확인하는 것이 혼란스러우신가요?

나는 Hacking, The Art of Exploitation을 읽기 시작했고 메모리 검사에 관한 몇 가지 사항에 대해 혼란스러워했습니다.

메인을 분해하면 개별 조립설명서가 있는 메모리 전체의 출력이 나오죠? 각 명령어가 반드시 메모리 주소를 1씩 늘릴 필요는 없으므로 끝에 <main+1>과 <main +3>을 추가할 수 있습니다. 그래서 명령은이 스크린샷프로그램의 처음 20개 명령어를 표시합니다. 각 메시지에는 몇 바이트의 정보가 포함되어 있습니까?

이제 혼란스럽습니다. ir eip를 사용할 때 이 레지스터의 위치는 0x8048384입니다(그래서 main의 첫 번째 명령으로 저장됩니까?).스크린샷에. 옆에 있는 값이 저장하는 값인데 여기서 알고 싶은데 0x00fc45c7을 저장하는데 위 출력에는 이 명령어가 없나요? 나는 그것이 프로그램의 다음 명령을 포함하는 메모리를 가리켜야 한다고 생각합니다.

이제 가장 큰 혼란이 옵니다. $eip가 저장된 메모리를 관찰하고 동시에 여러 셀을 관찰할 수 있습니다.이 스크린샷. 그런데 x/2x를 사용한 스크린샷을 보면 메모리에 두 개의 값이 저장되어 있는데, 둘 다 크기가 4바이트라는 것을 알 수 있죠? 그런 다음 x/12를 사용하면 갑자기 0x8048384에 4개의 단어가 있고 다른 4개의 단어 0x8048394가 있습니까?

사용하는 단위에 따라 메모리 주소에 저장된 값이 어떻게 다른지 이해가 안되는 것 같습니다. 또한 각 메모리 주소에는 1바이트의 정보만 포함되어야 한다고 생각했습니다.

질문에 대한 설명이 필요하면 게시해 주세요. 영어는 제 모국어가 아니며 제 설명이 맞는지 잘 모르겠습니다.

미리 도움을 주셔서 감사합니다.

답변1

각 메시지에는 몇 바이트의 정보가 포함되어 있습니까?

1바이트부터 여러 바이트까지 명령어 push %ebp에는 고정된 길이가 없습니다. 예를 들어, 명령의 출력은 objdump -d a.out명령의 길이를 더 명확하게 만듭니다(그리고 명령이 무엇인지 표시하므로 유용할 수 있습니다).

08048400 <main>:
 8048400:       55                      push   %ebp
 8048401:       89 e5                   mov    %esp,%ebp
 8048403:       83 ec 08                sub    $0x8,%esp
 8048406:       90                      nop
 8048407:       c7 45 fc 00 00 00 00    movl   $0x0,0xfffffffc(%ebp)
 804840e:       89 f6                   mov    %esi,%esi
 8048410:       83 7d fc 09             cmpl   $0x9,0xfffffffc(%ebp)

이제 혼란스럽습니다. ir eip를 사용하면 이 레지스터의 위치를 ​​알 수 있습니다...

(gdb) disassemble main
Dump of assembler code for function main:
0x8048400 <main>:       push   %ebp
0x8048401 <main+1>:     mov    %esp,%ebp
0x8048403 <main+3>:     sub    $0x8,%esp
0x8048406 <main+6>:     nop
0x8048407 <main+7>:     movl   $0x0,0xfffffffc(%ebp)
0x804840e <main+14>:    mov    %esi,%esi
0x8048410 <main+16>:    cmpl   $0x9,0xfffffffc(%ebp)
...
(gdb) i r eip
eip            0x8048406        0x8048406

내 버전에서는 프로그램이 eip프로그램을 중지하는 명령을 가리킵니다 nop.b maingdb

$eip이제 가장 큰 혼란이 옵니다. 메모리가 어디에 저장되어 있는지 확인할 수 있습니다.

(gdb) x/10b $eip
0x8048406 <main+6>:     0x90    0xc7    0x45    0xfc    0x00    0x00    0x00    0x00
0x804840e <main+14>:    0x89    0xf6

위의 출력을 기억해 보세요 objdump.

 8048406:       90                      nop
 8048407:       c7 45 fc 00 00 00 00    movl   $0x0,0xfffffffc(%ebp)
 804840e:       89 f6                   mov    %esi,%esi

10바이트 덤프에는 $eip90, c7 45 fc 00 00 00 00, 89 f6 등 다음 세 가지 명령어가 표시됩니다.

사용하는 단위에 따라 메모리 주소에 저장된 값이 어떻게 다른지 이해가 안되는 것 같습니다. 또한 각 메모리 주소에는 1바이트의 정보만 포함되어야 한다고 생각했습니다.

gdb다양한 너비 또는 논리 단위로 확인할 수 있습니다. x/3i예를 들어 다음 세 가지 명령을 표시한다고 말할 수 있습니다 .

(gdb) x/3i $eip
0x8048406 <main+6>:     nop
0x8048407 <main+7>:     movl   $0x0,0xfffffffc(%ebp)
0x804840e <main+14>:    mov    %esi,%esi

사용할 너비의 최선의 선택은 상황에 따라 달라집니다. 8비트 시스템에서는 바이트를 확인해야 합니다. 64비트 시스템의 메모리 주소가 g점보인지 8바이트인지 확인해야 합니다. 선택 영역의 너비를 변경하면 gdb표시되는 숫자가 변경될 수 있습니다. 너비가 다르면 비트 패턴이 달라져 숫자도 달라질 수 있기 때문입니다. 또한 CPU의 엔디안은 상황을 복잡하게 만들 수 있습니다.

고려하다:

#include <stdio.h>
char *pointer = "test";
int main(void) {
        printf("%s\n", pointer);
}

검사할 때 다른 너비를 선택하면 pointer다른 비트 패턴에서 다른 숫자를 얻게 됩니다.

(gdb) p pointer
$1 = 0x8048488 "test"
(gdb) x/4c pointer
0x8048488 <_IO_stdin_used+4>:   116 't' 101 'e' 115 's' 116 't'
(gdb) x/4t pointer
0x8048488 <_IO_stdin_used+4>:   01110100        01100101        01110011        01110100
(gdb) x/t (int *)pointer
0x8048488 <_IO_stdin_used+4>:   01110100011100110110010101110100

또 다른 문제는 Intel 시스템이 리틀 엔디안이라는 것입니다(Intel에서는 이를 문제로 간주하지 않을 수도 있습니다). 그래서 자세히 살펴보면 이러한 복잡성으로 인해 비트 패턴이 x/4t약간 다릅니다 . 빅 엔디안 모드로 들어가도록 x/t (int *)지시할 수 있습니다 .gdb

(gdb) set endian big
The target is assumed to be big endian
(gdb) x/t (int *)pointer
0x88840408:     Cannot access memory at address 0x88840408

하지만 이제 메모리 주소를 거꾸로 입력해야 합니다! 이 htonl 함수 호출은 32비트 리틀엔디안 값을 빅엔디안 값으로 변환합니다.

$ cfu 'printf("%d\n", htonl(0x88840408))'
134513800

그런 다음 gdb해당 주소를 시도해 볼 수 있습니다.

(gdb) x/t (int *)134513800
0x8048488 <_IO_stdin_used+4>:   01110100011001010111001101110100

이제 4자리 비트 패턴이 빅엔디안 형식과 일치합니다. 이러한 결과를 표에 표시하는 것이 도움이 될 수 있습니다.

            --> big endian reads this way
              t        e        s        t
x/4t (char)   01110100 01100101 01110011 01110100
big endian    01110100 01100101 01110011 01110100
            <-- yaw siht sdaer naidne elttil dna
              t        s        e        t
little endian 01110100 01110011 01100101 01110100

test문자열의 비트 패턴을 표시할 수 있는 세 가지 방법(여러 가지 방법 중 하나)이 있습니다 gdb. 입력을 분할하도록 지정한 비트 수 gdb와 시스템의 바이트 순서에 따라 모두 동일한 비트 패턴에서 다른 숫자가 표시됩니다.

관련 정보