dd를 사용하여 바이너리 패치

dd를 사용하여 바이너리 패치

나는 이 문장(아래)을 여러 번 읽었습니다. 가장 최근에는여기dd, 패치 사용 방법에 대해 혼란스러워 했습니다.아무것컴파일러는 말할 것도 없고:

30년 전 제가 학교에서 사용했던 Unix 시스템은 RAM과 디스크 공간이 매우 제한되어 있었습니다. 특히, /usr/tmp파일 시스템이 매우 작기 때문에 누군가 큰 프로그램을 컴파일하려고 할 때 문제가 발생할 수 있습니다. 물론 학생들은 어쨌든 "대형 프로그램"을 작성해서는 안 됩니다. 대형 프로그램은 일반적으로 "어딘가"에서 복사된 소스 코드입니다. 우리 중 많은 사람들 /usr/bin/cc/home/<myname>/cc, 및dd대체할 바이너리 /tmp패치 용/usr/tmp, 어느 것이 더 큰지. 물론 이는 문제를 더욱 악화시킬 뿐입니다. 당시에는 이러한 복사본이 차지하는 디스크 공간이 매우 중요했으며 지금 /tmp은 꽉 차서 다른 사용자가 파일을 편집할 수 없게 만드는 경우가 많습니다. 무슨 일이 일어나고 있는지 발견한 후 시스템 관리자는 chmod go-r /bin/* /usr/bin/*"수정"을 수행하고 C 컴파일러의 모든 복사본을 제거했습니다.

(강조는 내 것)

매뉴얼 페이지에는 dd패치에 대한 언급이 없으며 어차피 용도 변경이 불가능하다고 가정합니다.

바이너리가 실제로 패치될 수 있나요 dd? 이것이 역사적 의미가 있나요?

답변1

해 보자. 이것은 간단한 C 프로그램입니다:

#include <stdio.h>
int main(int argc, char **argv) {
    puts("/usr/tmp");
}

우리는 그것을 다음과 같이 구축할 것입니다 test:

$ cc -o test test.c

실행하면 "/usr/tmp"가 인쇄됩니다.

/usr/tmp바이너리에서 " "가 어디에 있는지 알아 보겠습니다 .

$ strings -t d test | grep /usr/tmp
1460 /usr/tmp

-t d 10진수로 인쇄 오프셋찾은 각 문자열에 대해 파일에 추가합니다.

/tmp\0이제 " "만 포함하는 임시 파일을 만들어 보겠습니다 .

$ printf "/tmp\x00" > tmp

이제 바이너리가 있으므로 변경하려는 문자열이 어디에 있는지 알고 있으며 대체 문자열이 포함된 파일이 있습니다.

이제 다음을 사용할 수 있습니다 dd.

$ dd if=tmp of=test obs=1 seek=1460 conv=notrunc

tmp(" " 파일) 에서 데이터를 읽고 /tmp\0, 1바이트의 출력 블록 크기를 사용하여 이진 파일에 쓰고, 아무것도 쓰기 전에 이전에 찾은 오프셋으로 점프하고, 완료되면 지웁니다. 파일은 잘리지 않습니다.

패치된 실행 파일을 실행할 수 있습니다:

$ ./test
/tmp

프로그램에 의해 인쇄된 문자열 리터럴은 변경되어 이제 " /tmp\0tmp\0"를 포함하지만 문자열 함수는 첫 번째 널 바이트를 보는 즉시 중지됩니다. 이 패치를 사용하면 문자열을 더 짧게 만들거나 같은 길이로 만들 수 있지만 더 이상 만들 수는 없지만 이러한 목적에는 충분합니다.

그래서 우리는 물건을 패치하는 데 사용할 수 있을 뿐만 아니라 dd이미 해냈습니다.

답변2

"패치된 바이너리"의 의미에 따라 다릅니다.

dd가끔 변경 바이너리를 사용합니다. 물론 에는 그러한 기능이 없지만 dd파일을 열고 특정 오프셋에서 내용을 읽고 쓸 수 있으므로 내용을 쓸 위치를 알고 있다면 짜잔, 그게 바로 패치입니다.

예를 들어 일부 PNG 데이터가 포함된 이진 파일이 있습니다. binwalk오프셋 찾기를 사용하여 dd추출하고(보통 binwalk에서도 항목을 추출하지만 복사본에 문제가 있음) 편집을 사용하여 gimp편집된 파일의 크기가 원본보다 작거나 같은지 확인합니다(오프셋 변경은 수행할 수 있는 작업이 아닙니다). 쉽게))을 dd사용하여 변경된 이미지를 다시 제자리에 놓습니다.

$ binwalk thebinary
[…]
4194643    0x400153     PNG image, 800 x 160, 8-bit/color RGB, non-interlaced
[…]
$ dd if=nickel bs=1 skip=4194641 count=2 conv=swab | od -i
21869 # file size in this case - depends on the binary format
$ dd if=thebinary bs=1 skip=4194643 count=21869 of=theimage.png
$ gimp theimage.png
$ pngcrush myimage.png myimage.crush.png
# make sure myimage.crush.png is smaller than the original
$ dd if=myimage.crush.png of=thebinary bs=1 seek=4194643 conv=notrunc

때로는 바이너리 파일의 문자열(예: 경로 또는 변수 이름)을 바꾸고 싶을 때도 있습니다. 를 사용하여 이 작업을 수행할 수도 있지만 를 dd사용하는 것이 더 간단합니다 sed. 교체된 문자열이 원래 문자열과 동일한 길이인지 확인하면 오프셋이 변경되지 않습니다.

sed -e s@/the/old/save/path@/the/new/save/path@ -i thebinary

또는 0바이트를 추가하는 @MichaelHomer의 예를 선택하세요.

sed -e 's@/usr/tmp@/tmp\x00tmp@' -i test

물론, 실제로 작동하는지 나중에 확인해야 합니다.

답변3

예, dd를 사용하여 바이너리를 패치하는 것이 가능합니다.

stdin의 데이터로 파일을 패치하려면 다음을 수행하십시오.

dd of=file.bin bs=1 count=2 conv=notrunc

그런 다음 패치할 (텍스트) 데이터를 입력합니다. 위의 내용은 2자 이상을 입력하라는 메시지가 표시되는 경우에도 파일 시작 부분부터 2바이트만 패치합니다.
파일의 중간 부분을 패치하려면 seek파일 데이터의 위치(주소)를 제공하여 패치를 시작합니다. 예를 들어.

dd of=file.out bs=1 count=2 seek=2 conv=notrunc

또는

echo "<address: data>" | xxd -r - file.out

인쇄할 수 없는 문자를 패치하는 경우 패치할 데이터가 포함된 바이너리 임시 파일을 만듭니다. 예를 들어.

dd if=file.in of=file.out bs=1 count=2 seek=2 conv=notrunc

또는

xxd -r hexoffsets.in file.out

또는 표준 입력에 오프셋이 있는 16진수 데이터를 제공하세요.

echo "<address: hex-data>" | xxd -r - file.out

정의:
패치는 hexdump를 바이너리로 변환하는 것을 의미합니다(xxd 맨페이지에서).

우분투 16.04.7에서 테스트되었습니다.

관련 정보