Kolejna technika wywoływania wstrzykniętego kodu, czyli przechwytywanie PLT, jest podobna do przechwytywania GOT. Podobnie jak GOT, przechwytywanie PLT pozwala na wstawienie zamiennika dla istniejącej funkcji bibliotecznej. Jedyną różnicą jest to, że zamiast zmieniać adres funkcji przechowywany we wpisie GOT używanym przez element zastępczy PLT, zmienia się sam element zastępczy PLT. Ponieważ technika ta wiąże się ze zmianą sekcji PLT, która jest sekcją kodu, nie nadaje się ona do modyfikowania zachowania pliku binarnego w czasie wykonywania. Listing pokazuje, jak wykorzystać technikę przechwytywania PLT.
Listing : Calling injected code by hijacking a PLT entry
(1) $ cp /bin/ls ls.plt
(2) $ ./elfinject ls.plt hello-got.bin „.injected” 0x800000 -1
$ objdump -M intel -d ls.plt
…
(3) 0000000000402800 <fwrite_unlocked@plt>:
402800: (4)ff 25 9a ba 21 00 jmp QWORD PTR [rip+0x21ba9a] # 61e2a0 <_fini@@Base+0x20a644>
402806: 68 51 00 00 00 push 0x51
40280b: e9 d0 fa ff ff jmp 4022e0 <_init@@Base+0x28>
…
(5) $ hexedit ls.plt
$ objdump -M intel -d ls.plt
…
(6) 0000000000402800 <fwrite_unlocked@plt>:
402800: e9 73 e6 3f 00 jmp 800e78 <_end@@Base+0x1e1b10>
402805: 00 68 51 add BYTE PTR [rax+0x51],ch
402808: 00 00 add BYTE PTR [rax],al
40280a: 00 e9 add cl,ch
40280c: d0 fa sar dl,1
40280e: ff (bad)
40280f: ff .byte 0xff
…
(7)Ð $ ./ls.plt
hello world!
hello world!
hello world!
hello world!
hello world!
…
Tak jak poprzednio, zacznij od utworzenia kopii pliku binarnego ls (1) i wstrzyknięcia do niego nowego kodu (2). Zauważ, że ten przykład używa tego samego kodu, co w technice przejęcia GOT. Podobnie jak w przykładzie przejęcia GOT, zastąpisz wywołanie biblioteki fwrite_unlocked funkcją „hello world”. Używając objdump, spójrz na wpis PLT dla fwrite_unlocked (3). Ale tym razem nie interesuje Cię adres wpisu GOT używanego przez stub PLT. Zamiast tego spójrz na kodowanie binarne pierwszej instrukcji stubu PLT. Jak pokazuje objdump, kodowanie to ff259aba2100 (4), co odpowiada instrukcji pośredniej jmp z przesunięciem względem rejestru rip. Możesz przejąć wpis PLT, nadpisując tę instrukcję inną, która skacze bezpośrednio do wstrzykniętego kodu. Następnie, używając hexedit, wyszukaj sekwencję bajtów ff259aba2100 odpowiadającą pierwszej instrukcji szczątkowej instrukcji PLT (5). Po jej znalezieniu zastąp ją ciągiem e973e63f00, który jest kodowaniem dla bezpośredniego jmp na adres 0x800e78, gdzie znajduje się wstrzykiwany kod. Pierwszy bajt, e9, ciągu zastępującego, to kod operacji dla bezpośredniego jmp, a kolejne 4 bajty to przesunięcie wstrzykiwanego kodu względem samej instrukcji jmp. Po zakończeniu modyfikacji ponownie zdezasembluj instrukcję PLT, używając objdump do weryfikacji zmian (6). Jak widać, pierwsza zdezasemblowana instrukcja wpisu fwrite_unlocked instrukcji PLT teraz odczytuje jmp 800e78: bezpośredni skok do wstrzykiwanego kodu. Następnie deasembler pokazuje kilka błędnych instrukcji wynikających z pozostałych bajtów oryginalnego wpisu PLT, których nie nadpisałeś. Błędne instrukcje nie stanowią problemu, ponieważ pierwsza instrukcja jest jedyną, która i tak zostanie wykonana. Teraz sprawdźmy, czy modyfikacje zadziałały. Po uruchomieniu zmodyfikowanego pliku binarnego ls zobaczysz, że komunikat „hello world” jest wyświetlany przy każdym wywołaniu funkcji fwrite_unlocked (7), zgodnie z oczekiwaniami, dając taki sam rezultat, jak w przypadku przejęcia kontroli nad GOT.