나는 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 main
gdb
$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바이트 덤프에는 $eip
90, 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
와 시스템의 바이트 순서에 따라 모두 동일한 비트 패턴에서 다른 숫자가 표시됩니다.