체뚱로그
[Linux] GDB를 활용하여 쉘 실행하기 본문
GDB 사용법을 활용하여, 아래 C 코드를 컴파일한 실행 파일이 GDB 상에서 쉘을 실행하도록 실행 흐름을 바꾸는 과정에 대해 정리해보았다.
#include <stdio.h>
void dont_call(void) {
printf("Good job~!\n");
}
void should_call(char *str) {
printf("%s\n", str);
}
int main(int argc, char **argv) {
void (*func)(char *);
func = should_call;
func("no way\n");
return 0;
}
1. 디버깅할 소스 코드 생성
1) 실행 파일 생성
vim debug.c
#include <stdio.h>
void dont_call(void)
{
printf("Good job~!\n");
}
void should_call(char *str)
{
printf("%s\n", str);
}
int main(int argc, char **argv)
{
void (*func)(char *);
func = should_call;
func("no way\n");
return 0;
}
2) 컴파일
gcc debug.c -o debug -g
"-o" 옵션은 컴파일된 바이너리 파일 이름을 지정한다.
2. GDB를 사용하여 쉘 실행
1) GDB를 사용하여 debug 파일 실행
gdb -q ./debug # debug라는 이름을 가진 바이너리 파일을 gdb로 실행
b main # main 함수에 breakpoint 걸기
r # 프로그램 실행(run)
2) func을 system 함수로 변경
n # 다음 코드 func = should_call; 호출
print func # func 변수 값 출력
set var func = system # func을 system 함수로 변경
print func # 변경된 func 변수 값 확인
p system # 위의 변경된 func 변수 값의 주소가 system 함수의 주소와 동일한지 확인
*. 명령어 n
'set var func = system' 호출 후에 'func = should_call;'이 호출되면,
'func'이 'should_call'로 덮어씌워지므로,
명령어 n을 통해 다음 코드인 'func = should_call;' 부분을 먼저 실행해야 한다.
3) main 함수 디버깅
disas main # main 함수를 디버깅하여 어셈블리어 출력
%rdi: 함수의 첫 번째 인자 전달
%rdx: 함수의 두 번째 인자 전달
%rax: 반환하는 값 저장
mov: 값 복사 명령어
call: 함수 호출
lea: 주소 연산 수행 및 결과 주소를 레지스터에 저장
*. 아래 명령어를 통해 %rdx 레지스터의 값을 %rdi 레지스터로 복사하기 때문에, %rdx 또는 %rdi 둘 중 어떤 레지스터 주소로 바꾸어도 상관 없음
mov %rdx,%rdi
(1) 명령어를 한 줄씩 실행하여 레지스터 문자열 변경
x/s $rdi # $rdi 레지스터의 문자열 출력
x/s $rdx # $rdx 레지스터의 문자열 출력
ni # 다음 명령어 실행
(2) call *%rax 부분에 breakpoint를 걸어서 실행
4) "bin/sh" 주소 찾기
find &system, +99999999, "/bin/sh"
# system 함수의 주소에서 메모리 검색하여 "/bin/sh" 문자열 주소 찾아 출력
5) 문자열 주소 변경
$rdi와 $rdx는 현재 문자열 “no way\n”로 지정되어 있는데, 해당 문자열의 주소를 위에서 찾은 “bin/sh” 주소로 변경하여 문자열이 변경되도록 함
set $rdi = 0x7ffff7dd8698 # $rdi의 주소를 “bin/sh” 주소로 변경, $rdx도 가능
c # 프로그램 계속 실행
# system("/bin/sh")가 실행됨
ps # 쉘 명령어 ps(프로세스 정보 표시)를 통해 /bin/sh가 잘 동작하는지 테스트
exit # 쉘 나가기
3. 궁금한 점
system("/bin/sh") 코드 자체를 소스파일 안의 main에 넣어서 실행해도 되는건가?
⇒ 된다. 애초에 system("/bin/sh") 이 코드가 소스파일의 메인 함수 안에 있으면 실행했을 때 자동으로 실행된다.
아예 set $rdx = "/bin/sh" 이렇게 문자열 자체를 바꿔서 실행해도 되나?
⇒ 된다. 앞에서 set var func = system으로 바꾸었기 때문에 현재 func에는 system 함수를 호출하는 것으로 바뀌어 있고, 원래 문자열대로라면 system(”no way\n”); 이런 방식인데, 이 부분의 문자열을 “/bin/sh”로 바꾸게 되면, system(”/bin/sh”)가 되기 때문에 쉘이 실행된다.
교수님께서는 첫번째 인자를 전달하는 %rdi 레지스터의 주소를 바꾸라고 했는데, %rdx 레지스터 주소를 바꿔서 실행해도 될까?
⇒ 해본 결과 되는 것 같다. 단 call 전에 breakpoint를 걸면 두 레지스터 모두 문자열이 동일하게 변경되어 있지만, 한 줄씩 호출할 경우에는 각자 레지스터에 들어있는 문자열이 잘 바뀌어 있는지 확인해야 한다.
'Language > Linux' 카테고리의 다른 글
[Linux] Issue - apt intall 오류 (0) | 2023.11.15 |
---|---|
[Linux] Issue - osboxes.org 비밀번호 오류 (0) | 2023.11.15 |
[Linux] Issue - VMware NumLock Key 반복 실행 이슈 (0) | 2023.11.15 |
[Linux] 리눅스에 SSH 서버 설치 (0) | 2023.11.15 |