해결 방법 1 : C (Mac OS X x86_64), 109 바이트
golf_sol1.c의 소스
main[]={142510920,2336753547,3505849471,284148040,2370322315,2314740852,1351437506,1208291319,914962059,195};
위의 프로그램은 __DATA 세그먼트에 대한 실행 액세스 권한으로 컴파일해야합니다.
clang golf_sol1.c -o golf_sol1 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
그런 다음 프로그램을 실행하려면 다음을 실행하십시오.
./golf_sol1 $(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
결과 :
불행히도 Valgrind는 시스템 호출에서 할당 된 메모리를 감시하지 않으므로 누출을 감지 할 수 없습니다.
그러나 vmmap을 보면 할당 된 메모리의 큰 덩어리 (MALLOC 메타 데이터)를 볼 수 있습니다.
VIRTUAL REGION
REGION TYPE SIZE COUNT (non-coalesced)
=========== ======= =======
Kernel Alloc Once 4K 2
MALLOC guard page 16K 4
MALLOC metadata 16.2M 7
MALLOC_SMALL 8192K 2 see MALLOC ZONE table below
MALLOC_TINY 1024K 2 see MALLOC ZONE table below
STACK GUARD 56.0M 2
Stack 8192K 3
VM_ALLOCATE (reserved) 520K 3 reserved VM address space (unallocated)
__DATA 684K 42
__LINKEDIT 70.8M 4
__TEXT 5960K 44
shared memory 8K 3
=========== ======= =======
TOTAL 167.0M 106
TOTAL, minus reserved VM space 166.5M 106
설명
개선 된 솔루션으로 넘어 가기 전에 실제로 여기서 무슨 일이 일어나고 있는지 설명해야한다고 생각합니다.
이 주요 함수는 C의 누락 된 형식 선언을 남용합니다 (따라서 문자를 쓰지 않아도 int로 기본 설정됩니다). 링커는라는 심볼을 찾을 수 있는지 여부에만 관심을 갖습니다.main
는 호출 할 . 그래서 여기서 우리는 main을 배열 코드로 초기화 할 int 배열로 만들고 있습니다. 이 때문에 main은 __TEXT 세그먼트에 추가되지 않고 __DATA 세그먼트에 추가되므로 실행 가능한 __DATA 세그먼트로 프로그램을 컴파일해야합니다.
main에있는 쉘 코드는 다음과 같습니다.
movq 8(%rsi), %rdi
movl (%rdi), %eax
movq 4(%rdi), %rdi
notl %eax
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
이 작업은 syscall 함수를 호출하여 메모리 페이지를 할당하는 것입니다 (syscall mach_vm_allocate는 내부적으로 사용함). RAX는 0x100000a (원하는 함수를 알려주는 syscall을 알려줘야 함) 여야하지만 RDI는 할당 대상 (이 경우 mach_task_self ()이 되길 원함)을 유지하지만 RSI는 새로 작성된 메모리에 포인터를 쓸 주소를 보유해야합니다. (따라서 스택의 섹션을 가리키고 있습니다.) RDX는 할당 크기를 유지합니다 (우리는 바이트를 절약하기 위해 RAX 또는 0x100000a로 전달합니다) .R10은 플래그를 보유합니다 (우리는 할 수 있음을 나타냅니다) 어디서나 할당 할 수 있습니다.
이제 RAX와 RDI가 어디에서 가치를 얻는 지 분명하지 않습니다. RAX는 0x100000a 여야하고 RDI는 mach_task_self ()가 반환하는 값이어야합니다. 운 좋게도 mach_task_self ()는 실제로 매번 동일한 메모리 주소에있는 변수 (mach_task_self_)의 매크로입니다 (다시 부팅 할 때 변경해야 함). 내 특정 인스턴스에서 mach_task_self_는 0x00007fff7d578244에 있습니다. 따라서 지침을 줄이기 위해 argv 에서이 데이터를 대신 전달합니다. 이런 식으로 프로그램을 실행하는 이유$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
첫 번째 주장. 문자열은 두 값을 합한 것입니다. 여기서 RAX 값 (0x100000a)은 32 비트이며 여기에 1의 보수가 적용되었습니다 (따라서 null 바이트는 없습니다. 원본을 얻을 수있는 값은 아닙니다). 다음 값은 RDI (0x00007fff7d578244)는 왼쪽에 2 개의 여분의 정크 바이트가 추가되어 왼쪽으로 이동되었습니다 (널 바이트를 제외하기 위해 다시 오른쪽으로 이동하여 원래로 되돌립니다).
syscall 후에 우리는 새로 할당 된 메모리에 쓰고 있습니다. 그 이유는 mach_vm_allocate (또는이 syscall)를 사용하여 할당 된 메모리는 실제로 VM 페이지이며 자동으로 메모리에 페이징되지 않기 때문입니다. 오히려 데이터가 기록 될 때까지 예약 된 다음 해당 페이지가 메모리에 매핑됩니다. 예약 된 경우에만 요구 사항을 충족하는지 확실하지 않았습니다.
다음 솔루션에서는 쉘 코드에 널 바이트가 없다는 점을 이용하므로 프로그램 코드 외부로 이동하여 크기를 줄일 수 있습니다.
해결 방법 2 : C (Mac OS X x86_64), 44 바이트
golf_sol2.c의 소스
main[]={141986632,10937,1032669184,2,42227};
위의 프로그램은 __DATA 세그먼트에 대한 실행 액세스 권한으로 컴파일해야합니다.
clang golf_sol2.c -o golf_sol2 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
그런 다음 프로그램을 실행하려면 다음을 실행하십시오.
./golf_sol2 $(ruby -e 'puts "\xb8\xf5\xff\xff\xfe\xf7\xd0\x48\xbf\xff\xff\x44\x82\x57\x7d\xff\x7f\x48\xc1\xef\x10\x8b\x3f\x48\x8d\x74\x24\xf8\x89\xc2\x4c\x8d\x50\xf7\x0f\x05\x48\x8b\x36\x89\x36\xc3"')
동일한 크기의 할당을 수행하므로 결과는 이전과 동일해야합니다.
설명
누출 코드 조각을 프로그램 외부로 옮겼다는 점을 제외하면 솔루션 1과 거의 동일한 개념을 따릅니다.
main에있는 쉘 코드는 이제 다음과 같습니다.
movq 8(%rsi), %rsi
movl $42, %ecx
leaq 2(%rip), %rdi
rep movsb (%rsi), (%rdi)
이것은 기본적으로 argv에 전달한 쉘 코드를이 코드 다음에 복사합니다 (복사 한 후에는 삽입 된 쉘 코드를 실행합니다). 우리에게 유리한 점은 __DATA 세그먼트는 최소한 페이지 크기이므로 코드가 크지 않아도 "안전하게"더 많이 쓸 수 있다는 것입니다. 단점은 여기서 이상적인 솔루션입니다. 복사본이 필요하지 않습니다. 대신 argv에서 직접 쉘 코드를 호출하고 실행합니다. 그러나 불행히도이 메모리에는 실행 권한이 없습니다. 이 메모리의 권한을 변경할 수 있지만 단순히 복사하는 것보다 더 많은 코드가 필요합니다. 다른 전략은 외부 프로그램에서 권한을 변경하는 것입니다 (하지만 나중에 더 자세히 설명).
argv에 전달하는 쉘 코드는 다음과 같습니다.
movl $0xfefffff5, %eax
notl %eax
movq $0x7fff7d578244ffff, %rdi
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
이것은 이전 코드와 거의 동일하지만 EAX 및 RDI 값을 직접 포함한다는 점만 다릅니다.
가능한 해결 방법 1 : C (Mac OS X x86_64), 11 바이트
프로그램을 외부에서 수정한다는 아이디어는 누출 프로그램을 외부 프로그램으로 옮길 수있는 솔루션을 제공합니다. 실제 프로그램 (제출)이 단지 더미 프로그램 인 경우, 누수 프로그램은 대상 프로그램에 약간의 메모리를 할당합니다. 이제 이것이이 도전에 대한 규칙에 해당되는지 확실하지 않지만 그럼에도 불구하고 공유합니다.
따라서 타겟 프로그램을 챌린지 프로그램으로 설정 한 외부 프로그램에서 mach_vm_allocate를 사용하는 경우, 챌린지 프로그램은 다음과 같은 라인 만 있으면됩니다.
main=65259;
그 쉘 코드가 단순히 짧은 점프 (무한 점프 / 루프)이므로 프로그램은 열린 상태로 유지되며 외부 프로그램에서 참조 할 수 있습니다.
가능한 해결책 2 : C (Mac OS X x86_64), 8 바이트
나는 valgrind 출력을 볼 때 재미있게도 적어도 valgrind에 따르면 dyld가 메모리를 누출하는 것을 보았습니다. 따라서 모든 프로그램이 실제로 일부 메모리를 누출하고 있습니다. 이 경우 우리는 실제로 아무것도하지 않는 (단순히 종료) 프로그램을 만들 수 있으며 실제로 메모리가 누출됩니다.
출처:
main(){}
==55263== LEAK SUMMARY:
==55263== definitely lost: 696 bytes in 17 blocks
==55263== indirectly lost: 17,722 bytes in 128 blocks
==55263== possibly lost: 0 bytes in 0 blocks
==55263== still reachable: 0 bytes in 0 blocks
==55263== suppressed: 16,316 bytes in 272 blocks