# Rozpoznawanie zaciemnionego kodu PHP

> eval, base64_decode i inne: po czym poznać zaciemniony kod PHP, jak bezpiecznie go szukać i jak odróżnić uzasadnione enkodery od złośliwego kodu.

Źródło: https://phpsos.de/pl/blog/verschleierter-php-code/  
Stan: 25.10.2026  
Dostawca: PHP SOS, Pomoc awaryjna PHP i bezpieczeństwo serwerów. Natychmiastowa pomoc przez WhatsApp: https://wa.me/491736494914?text=Dzie%C5%84%20dobry%2C%20PHP%20SOS%2C%20potrzebujemy%20pomocy%20z%20naszym%20serwerem%20PHP.%20Firma%3A%20%E2%80%A6%20Strona%20internetowa%3A%20%E2%80%A6

**Krótko:** Zaciemniony kod PHP to kod celowo uczyniony nieczytelnym, zwykle przez kodowanie i kompresję, rozwiązywane dopiero w czasie działania. Typowe wzorce rozpoznawcze to `eval` razem z `base64_decode`, `gzinflate` lub `str_rot13`, bardzo długie wiersze, ciągi szesnastkowe oraz nazwy funkcji składane z części. Proszę szukać nieszkodliwymi poleceniami grep, nigdy nie wykonywać kodu i porównywać znaleziska z oryginalnym pakietem.

## Dlaczego atakujący ukrywają swój kod

Kto umieszcza na serwerze webshell lub tylną furtkę, chce, aby długo pozostała niewykryta. Czytelny kod rzuciłby się w oczy każdemu administratorowi, który otworzy plik, a każdy skaner by go znalazł. Dlatego atakujący pakują swój kod: jest kodowany, kompresowany i dopiero przy wywołaniu składany na nowo i wykonywany.

Dla PHP2Shell nie ma potwierdzonych wskaźników kompromitacji (Indicators of Compromise). Zaciemniony kod nie jest cechą tej luki, lecz ogólnym znakiem, że ktoś wprowadził kod, niezależnie od drogi. Jak ogólnie znajdować i usuwać webshelle, opisuje [Rozpoznawanie i usuwanie webshella](https://phpsos.de/pl/ratgeber/webshell-erkennen/). Ten artykuł koncentruje się na pytaniu, po czym poznać zaciemniony kod.

## eval, base64 i inne: typowe elementy składowe

Zaciemnianie opiera się niemal zawsze na tej samej zasadzie: nieczytelny blok danych jest przez jedną lub kilka funkcji przekształcany z powrotem w kod, który następnie jest wykonywany. Poniższe elementy powtarzają się przy tym stale.

| Element | Przykłady | Dlaczego zwraca uwagę |
|---|---|---|
| Wykonanie | `eval`, `assert` z ciągiem znaków (do PHP 7.x), `create_function` (do PHP 7.x) | wykonuje tekst jako kod |
| Dekodowanie | `base64_decode`, `str_rot13`, `hex2bin`, `strrev`, `convert_uudecode` | czyni dane znów czytelnymi |
| Dekompresja | `gzinflate`, `gzuncompress`, `gzdecode` | rozpakowuje osadzony kod |
| Składanie | łańcuchy `chr()`, łączenie krótkich fragmentów, sekwencje `\x` | ukrywa nazwy funkcji |
| Wywołanie dynamiczne | zmienne jako nazwa funkcji, `call_user_func`, `array_map` z ciągiem znaków | wywołanie nigdzie nie pojawia się jawnym tekstem |
| Dostęp do systemu | `system`, `exec`, `shell_exec`, `passthru`, `proc_open`, `popen` | polecenia na serwerze |

Według podręcznika PHP `assert()` od PHP 8.0 nie traktuje już ciągów znaków jako kodu, a `create_function()` usunięto w PHP 8.0. Na serwerach ze starszymi wersjami PHP oba pozostają istotnymi hasłami wyszukiwania.

Pojedynczy element rzadko jest podejrzany, na przykład `base64_decode` wiele aplikacji używa zasadnie. Podejrzana staje się kombinacja: dekodowanie, którego wynik wpływa bezpośrednio do wykonania.

## Bezpieczne wyszukiwanie: nieszkodliwe polecenia grep

Poniższe polecenia przeszukują pliki wyłącznie jako tekst. Nie wykonują żadnego kodu PHP. Ścieżkę proszę dostosować.

```bash
# Wykonanie połączone z dekodowaniem lub dekompresją
grep -rlE "(eval|assert)\s*\(\s*(base64_decode|gzinflate|gzuncompress|str_rot13|hex2bin)" /var/www --include="*.php*"

# Bardzo długie wiersze (często spakowany kod)
grep -rlE ".{2000,}" /var/www --include="*.php*"

# Wiele sekwencji szesnastkowych lub łańcuchów chr() w jednym wierszu
grep -rlE "(\\\\x[0-9a-fA-F]{2}){20,}" /var/www --include="*.php*"
grep -rlE "(chr\s*\(\s*[0-9]+\s*\)\s*\.\s*){8,}" /var/www --include="*.php*"

# Wywołanie funkcji przez zmienną bezpośrednio po przypisaniu z ciągów znaków
grep -rnE "\\\$[a-zA-Z_]+\s*=\s*['\"][a-z_]{4,}['\"]\s*;\s*\\\$[a-zA-Z_]+\s*\(" /var/www --include="*.php*" | head -50
```

Trafienia proszę najpierw sprawdzić przez `ls -l` i znacznik czasu, a potem otwierać wyłącznie jako tekst, na przykład przez `less`. Podejrzanych plików nigdy nie wolno wywoływać przez przeglądarkę, ponieważ serwer by je wykonał.

## Gdzie zaciemniony kod zwykle siedzi

Zaciemniony kod rzadko stoi w osobnym, rzucającym się w oczy pliku. Częściej jest wstawiany do istniejących plików, na samym początku lub na samym końcu, często po wielu pustych wierszach, aby przy otwarciu nie rzucał się od razu w oczy. Popularne są pliki ładowane przy każdym wywołaniu: centralny `index.php`, pliki konfiguracyjne i pliki funkcji motywu.

Szczególnie niepozorna droga prowadzi przez konfigurację. Ustawieniem `auto_prepend_file` PHP ładuje przed każdym skryptem dodatkowy plik. Jeśli stoi w `.user.ini` lub `.htaccess` założonym przez atakującego, jego kod działa przy każdym wywołaniu strony, choć nie zmieniono ani jednego pliku aplikacji. Dołączany plik chętnie nosi nieszkodliwe rozszerzenie, takie jak `.ico` lub `.png`.

```bash
# Czy przed lub po każdym skrypcie ładowany jest dodatkowy kod?
grep -rnE "auto_(prepend|append)_file" /var/www --include=".user.ini" --include=".htaccess" --include="*.conf" 2>/dev/null

# Kod PHP w plikach z rozszerzeniem obrazu lub ikony
grep -rlE "<\?php" /var/www --include="*.ico" --include="*.png" --include="*.jpg" --include="*.gif" 2>/dev/null
```

## Odróżnianie uzasadnionego zaciemniania od złośliwego kodu

Nie każdy nieczytelny kod jest złośliwy. Często zdarzają się trzy przypadki:

1. **Komercyjne enkodery:** Niektórzy producenci wtyczek i oprogramowania chronią swój kod narzędziami takimi jak ionCube czy SourceGuardian. Pliki są nieczytelne, ale zwykle odsyłają do potrzebnego loadera. Proszę je porównać z oryginalnym pakietem.
2. **Kod zminimalizowany lub generowany:** Pamięci podręczne i silniki szablonów tworzą długie wiersze bez złamań, jednak bez wykonywania zdekodowanego tekstu.
3. **Osadzone dane:** Obrazy lub czcionki jako ciąg Base64 w pliku PHP są nietypowe, ale niegroźne, dopóki wynik nie jest wykonywany.

Najwiarygodniejszym testem jest porównanie ze źródłem: jeśli plik pochodzi niezmieniony z pakietu producenta, jest uzasadniony. Jeśli nie leży w żadnym oryginalnym pakiecie lub od niego odbiega, należy go zbadać.

> **Ważne:**
> Proszę nie próbować odszyfrowywać zaciemnionego kodu na serwerze produkcyjnym, na przykład zastępując `eval` wyjściem i wywołując plik. Już jeden błąd przy tym wykona złośliwy kod. Analiza należy do odizolowanego środowiska.

## Granice wyszukiwania według wzorców

Atakujący znają popularne polecenia wyszukiwania. Dzielą nazwy funkcji, składają je w czasie działania albo wynoszą właściwy kod do bazy danych, plików obrazów lub wpisów konfiguracyjnych. Wyszukiwanie według wzorców znajduje więc dużo, ale nie wszystko. Dlaczego także skanery napotykają tu granice, wyjaśnia [Skanery złośliwego oprogramowania dla stron: co znajdują, a czego nie](https://phpsos.de/pl/blog/grenzen-von-malware-scannern/).

Znalezisko rzadko jest ponadto jedyne. Atakujący używają w kilku plikach często tego samego elementu, na przykład rzucającej się w oczy nazwy zmiennej lub charakterystycznego ciągu znaków na początku zakodowanego bloku. Po potwierdzonym znalezisku proszę więc celowo szukać właśnie tego ciągu w całym katalogu WWW, także w innych stronach na tym samym serwerze. W ten sposób znajdą Państwo rodzeństwo, którego nie uchwyciło żadne z ogólnych wyszukiwań.

Wyszukiwanie proszę uzupełnić o porównanie z plikami oryginalnymi i spojrzenie do bazy danych, zob. [Sprawdzanie bazy danych pod kątem wprowadzonego kodu](https://phpsos.de/pl/blog/datenbank-auf-schadcode-pruefen/). Prewencyjnie można zablokować niepotrzebne funkcje systemowe, jak opisano w poradniku [Prawidłowe użycie disable_functions](https://phpsos.de/pl/ratgeber/disable-functions/).

## Jak konkretnie pomaga PHP SOS?

- **Szukać:** Przeszukujemy pliki i bazę danych rozszerzonymi wzorcami i porównujemy z oryginalnymi pakietami.
- **Analizować:** Badamy podejrzany kod w odizolowanym środowisku, nie narażając Państwa systemu.
- **Oczyścić:** W ramach [usuwania złośliwego oprogramowania](https://phpsos.de/pl/leistungen/malware-entfernung/) usuwamy wszystkie składniki i zamykamy drogę włamania.
- **Zabezpieczyć:** Ograniczamy funkcje PHP i uprawnienia zapisu oraz monitorujemy nowe lub zmienione pliki.


## Częste pytania

**Czy eval z base64_decode to zawsze złośliwy kod?**  
Nie zawsze, ale bardzo często. Uzasadniony kod prawie nie potrzebuje tej kombinacji. W plikach, które nie należą do Państwa aplikacji ani znanej wtyczki, jest to silny sygnał ostrzegawczy.

**Czy mogę zdekodować zaciemniony kod, żeby go przeczytać?**  
Tylko bez jego wykonywania i nie na serwerze produkcyjnym. Już przypadkowe wykonanie może wyrządzić szkodę. W razie wątpliwości analizę proszę zostawić specjalistom, którzy pracują w odizolowanych środowiskach.

**Dlaczego mój skaner złośliwego oprogramowania nie znajduje tego kodu?**  
Skanery szukają znanych wzorców. Atakujący stale zmieniają swój kod, dzielą nazwy funkcji lub składają je w czasie działania. Pusty wynik skanowania nie jest więc dowodem czystego serwera.

**A co z komercyjnie szyfrowanymi wtyczkami?**  
Niektórzy producenci chronią swój kod enkoderami takimi jak ionCube czy SourceGuardian. Takie pliki są nieczytelne, ale uzasadnione, jeśli pochodzą od producenta. Proszę je porównać z oryginalnym pakietem.

**Czy disable_functions pomaga przeciw zaciemnionemu kodowi?**  
Częściowo. Funkcje takie jak exec czy system można zablokować, eval natomiast nie, ponieważ jest konstrukcją języka, a nie funkcją. Blokada utrudnia atakującym następny krok, ale nie zapobiega każdemu złośliwemu kodowi.

## Źródła

- [Podręcznik PHP: assert()](https://www.php.net/manual/de/function.assert.php)
- [Podręcznik PHP: create_function()](https://www.php.net/manual/de/function.create-function.php)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
