programing

GDB 파손된 스택프레임 - 디버깅 방법

procenter 2022. 8. 28. 21:50
반응형

GDB 파손된 스택프레임 - 디버깅 방법

다음과 같은 스택 트레이스가 있습니다.이것으로 디버깅에 도움이 되는 것을 알 수 있을까요?

Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0  0x00000002 in ?? ()
#1  0x00000001 in ?? ()
#2  0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) 

어디서부터 요?Segmentation fault스택 트레이스는 그다지 유용하지 않습니다.

메모: 코드를 게시하면 SO 전문가가 답을 알려 줍니다.SO의 안내를 받아 직접 답을 찾고 싶기 때문에 여기에 코드를 게시하지 않습니다.죄송합니다.

이러한 가짜 주소(0x00000002 등)는 실제로는 PC 값이지 SP 값이 아닙니다.이러한 종류의 SEGV를 취득했을 경우, 가짜(매우 작은) PC 주소를 사용하는 경우는, 99%가 가짜 함수 포인터를 사용한 호출에 의한 것입니다.C++의 가상 콜은 함수 포인터를 통해 구현되므로 가상 콜에 관한 모든 문제는 동일한 방법으로 나타날 수 있습니다.

간접 콜 명령은 콜 후 PC를 스택에 푸시하고 PC를 목표값(이 경우 bogus)으로 설정합니다.따라서 이 경우 수동으로 PC를 스택에서 분리하여 쉽게 취소할 수 있습니다.32비트 x86 코드에서는 다음 작업을 수행합니다.

(gdb) set $pc = *(void **)$esp
(gdb) set $esp = $esp + 4

필요한 64비트 x86 코드

(gdb) set $pc = *(void **)$rsp
(gdb) set $rsp = $rsp + 8

이제 '어느 정도', '어느 정도', ' 정도'를 할 수 있을 예요.bt암호가 실제로 어디에 있는지 알아낼 수 있습니다.

나머지 1%의 경우 일반적으로 스택에 저장된 배열이 오버플로우되어 스택을 덮어쓰게 됩니다.이 경우 valgrind와 같은 도구를 사용하여 상황을 보다 명확하게 파악할 수 있습니다.

상황이 꽤 간단하다면 크리스 도드의 대답이 최선이다.NULL 포인터를 통과한 것 같습니다.

그러나 프로그램이 충돌하기 전에 발, 무릎, 목 및 눈에 스스로 총을 쐈을 수 있습니다. 스택을 오버와이드하거나 프레임 포인터를 흐트러뜨리거나 기타 해가 될 수 있습니다.만약 그렇다면, 해시를 푸는 것은 감자와 고기를 보여주지 않을 것이다.

보다 효율적인 해결책은 디버거에서 프로그램을 실행하고 프로그램이 크래시 될 때까지 기능을 단계적으로 수행하는 것입니다.크래시 함수가 특정되면 다시 시작하고 해당 함수에 들어가 어떤 함수가 호출하여 크래시의 원인이 되는지 판단합니다.문제의 코드 행을 찾을 때까지 반복합니다.75%의 경우 수정이 명확해집니다.

나머지 25%의 상황에서 소위 말하는 위반 코드 라인은 속임수입니다.이전에 많은 회선을 설정(아마도 수천 회선)한 조건에 반응합니다.이 경우 최선의 코스는 많은 요소에 따라 달라집니다.대부분 코드에 대한 이해와 코드에 대한 경험입니다.

  • printf는 중요한 변수에 따라 필요한 Aha!로 이어집니다.
  • 입력이 다른 테스트 조건을 변경하면 디버깅보다 더 많은 통찰력을 얻을 수 있습니다.
  • 아마도 두 번째 눈은 당신의 가정을 확인하거나 간과된 증거를 모으도록 강요할 것이다.
  • 가끔은 저녁을 먹으러 가서 수집된 증거에 대해 생각하는 것만이 필요할 때가 있다.

행운을 빕니다.

스택 포인터가 유효하다고 가정하면...

백트레이스에서는 SEGV가 어디에서 발생하는지 정확하게 알 수 없는 경우가 있습니다.처음 2개의 스택프레임은 완전히 덮어쓰기 되어 있다고 생각합니다.0xbffff284는 유효한 주소 같지만 다음 두 주소는 올바르지 않습니다.스택에 대해 자세히 알아보려면 다음을 시도해 보십시오.

gdb$ x/32ga $rsp

또는 바리안트(32를 다른 번호로 바꿉니다)그러면 주소(a)로 포맷된 자이언트(g) 크기의 스택포인터부터 몇 개의 단어(32)가 출력됩니다.형식에 대한 자세한 내용을 보려면 '도움말 x'를 입력하십시오.

이 경우 일부 sentinel 'printf'로 코드를 설정하는 것도 나쁘지 않을 수 있습니다.

다른 레지스터 중 하나에 스택포인터가 캐시되어 있는지 확인합니다.거기서 스택을 취득할 수 있을 가능성이 있습니다.또, 이것이 짜넣어져 있는 경우는, 스택이 특정의 주소로 정의되는 경우가 많습니다.그것을 사용하면, 때때로 좋은 스택을 얻을 수 있습니다.이 모든 것은 하이퍼스페이스에 뛰어들었을 때 프로그램이 메모리 전체에 토해내지 않았다고 가정합니다.

스택 덮어쓰기일 경우 이 값은 프로그램에서 인식할 수 있는 값과 일치할 수 있습니다.

예를 들어, 나는 단지 스택을 보고 있는 나를 발견했다.

(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x000000000000342d in ?? ()
#2  0x0000000000000000 in ?? ()

★★★★★★★★★★★★★★★★★」0x342d 했을 때 되었습니다.어플리케이션로그에 grep 했을 때 노드 ID로 판명되었습니다.이를 통해 스택 덮어쓰기가 발생했을 가능성이 있는 후보 사이트를 즉시 좁히는 데 도움이 되었습니다.

재밌다...여기 오래된 C앱에서도 똑같은 일이 일어났어요.상위 2개의 스택트레이스 값 포인터(16진수)는 포트 밖에서 읽히는 데이터 바이트였습니다.익숙해서 우연히 하나 발견했어요.

언급URL : https://stackoverflow.com/questions/9809810/gdb-corrupted-stack-frame-how-to-debug

반응형