eval ve new Function ile VM Obfuscation
VM obfuscation işleminin dinamik kod oluşturmayı (doğrudan eval ve Function yapıcısı) nasıl ele aldığı, neyin bayt koduna çevrilip neyin atlandığı, obfuscator aracının ürettiği uyarılar ve çalışma zamanındaki ReferenceError hatalarının nasıl teşhis edileceği.
Bu neden önemli
VM obfuscation, fonksiyon gövdelerini çalışma zamanındaki bir yorumlayıcı üzerinden yürütülen bayt koduna derler. Çevreleyen
kapsamdaki tanımlayıcılar da yeniden adlandırılır. Her iki dönüşüm de çalışma zamanında bir dizeden oluşturulan kodla kötü etkileşir:
eval(s), new Function(...s) ve Function(...s). Çalışma zamanında oluşturulan kod, obfuscator aracının yeniden adlandırdığı bir
tanımlayıcıya başvurursa, üretilen fonksiyon ilk kez çalıştığında Uncaught ReferenceError: <renamed-name> is not defined hatası alırsınız.
Obfuscator her kalıbı farklı şekilde ele alır. Aşağıdaki tablo kısa özettir; sayfanın geri kalanı her satırı açıklar.
Obfuscator ne yapar: bir bakışta
| Kaynak kodunuzdaki kalıp | Ne olur |
|---|---|
eval('literal string') (gövde bir dize değişmezi) | Doğru çalışır. Bu çağrıyı içeren fonksiyon - ve içinde tanımlanan her fonksiyon - VM bayt koduna çevrilmez (doğrudan eval çevreleyen yerel değişkenleri okur; VM ise bir fonksiyon bayt koduna derlendikten sonra bu değişkenleri korumaz). |
eval(dynamicExpression) | Çalışma zamanında ReferenceError ile çökebilir. Bu çağrıyı içeren fonksiyon ve içinde tanımlanan her fonksiyon da VM bayt koduna çevrilmez. |
(0, eval)(s) / window.eval(s) (dolaylı) | Bu çağrıyı içeren fonksiyon normal şekilde VM bayt koduna çevrilir. Dolaylı eval global kapsamda çalışır ve çevreleyen yerel değişkenleri göremez; bu nedenle yeniden adlandırılmış yerel değişkenler onu bozamaz. Değerlendirilen kod yeniden adlandırılmış veya kaldırılmış bir global değişkene başvurursa yine de hata verebilir. |
new Function('a', 'b', 'return a + b') (tüm argümanlar dize değişmezi) | Doğru çalışır. Bu çağrıyı içeren fonksiyon normal şekilde VM bayt koduna çevrilir. |
new Function(dynamicBody) / Function(dynamicBody) | Çalışma zamanında ReferenceError ile çökebilir. Bu çağrıyı içeren fonksiyon ve içinde tanımlanan her fonksiyon da VM bayt koduna çevrilmez. |
Derleyici ayrıca eval?.(code) ifadesini ihtiyatlı biçimde tespit eder ve doğrudan eval gibi ele alır. JavaScript bu
isteğe bağlı çağrı biçimini dolaylı eval olarak tanımlar; derleyicinin tespiti bu dil davranışını değiştirmez.
Bu kalıpların yalnızca bazıları ölümcül olmayan uyarılar üretir ve yalnızca çağrı bir fonksiyonun içindeyse: dinamik bir
eval, new Function veya Function çağrısı hem DynamicCodeRenameRisk hem de VMDynamicCodeSkipped bildirir, statik bir
eval('...') yalnızca VMDynamicCodeSkipped bildirir, dolaylı eval ve tamamen statik bir new Function ise hiçbir şey bildirmez. Bir
dosyanın en üst düzeyinde, herhangi bir fonksiyonun dışındaki dinamik bir çağrı hiçbir zaman bildirilmez. Uyarı türleri ve bir CI kod parçası için aşağıdaki
Sorunu çalışma zamanından önce tespit etme bölümüne bakın.
Atlama IIFE'ler boyunca yayılır. En üst düzeydeki bir IIFE paketinizin tamamını sarmalıyorsa ve içindeki herhangi bir fonksiyon
dinamik eval veya new Function kullanıyorsa, IIFE'nin tamamı VM bayt koduna çevrilmekten muaf tutulur. Etki alanını sınırlayan
IIFE'yi açma kalıbı için Doğrudan eval Davranışı sayfasına bakın.
Statik ve dinamik neden farklı ele alınır
eval(s), çağrıldığı fonksiyonun yerel değişkenlerini okuyabilir ve yazabilir. s bir dize değişmezi olduğunda
obfuscator gövdeyi obfuscation sırasında ayrıştırabilir ve tanımlayıcıları çevreleyen kodla tutarlı biçimde yeniden adlandırabilir.
s dinamik bir ifade olduğunda ayrıştırma yalnızca çalışma zamanında gerçekleşir; o noktada tanımlayıcılar zaten yeniden
adlandırılmıştır, dolayısıyla çalışma zamanında oluşturulan kaynak artık var olmayan eski adlara başvurur.
new Function(s) ve dolaylı eval farklı çalışır: gövde her zaman global kapsamda çalışır; yalnızca global
değişkenlere erişebilir, çağrının çevresindeki yerel değişkenlere asla erişemez. Yeniden adlandırılmış yerel değişkenlere bağımlı olamazlar, ancak
yeniden adlandırılmış veya kaldırılmış global değişkenlere başvururlarsa ya da gövdeyi yeniden adlandırılmış bir tanımlayıcıyı birleştirerek
oluşturursanız (örn. iç yapısı obfuscator tarafından yeniden yazılmış bir fonksiyonun func.toString() çıktısı aracılığıyla) yine de hata verebilirler.
new Function('a', 'b', 'return a + b') gibi yalnızca kendi parametrelerini kullanan statik bir gövdenin böyle bir
bağımlılığı yoktur: gövde, yeniden adlandırıcının hiç incelemediği düz bir dizedir. Obfuscator çağrıyı olduğu yerde bırakır ve
çevreleyen fonksiyon VM bayt koduna çevrilmeye uygun olmaya devam eder. Yalnızca eval söz dizimini değiştirmek rastgele
dinamik kodu güvenli hale getirmez; uyarıları inceleyin ve son paketi test edin.
Çalışma zamanında gördüğünüz hata
Yaygın belirti, dinamik olarak oluşturulan fonksiyon ilk kez çalıştığında ortaya çıkan bir ReferenceError hatasıdır:
Buradaki TU, obfuscator aracının paketin IIFE kapsamında oluşturduğu yeniden adlandırılmış bir tanımlayıcıdır. Dinamik eval /
Function yapıcısı çağrısı ona başvuran bir gövdeyi değerlendirir, ancak gövde TU değişkeninin tanımsız olduğu bir kapsamda çalışır.
Sorunu çalışma zamanından önce tespit etme
Obfuscator, bu kalıpların bazılarını yayınlamadan önce CI'da yakalayabilmeniz için API üzerinden ölümcül olmayan uyarılar üretir. İki uyarı türü ilgilidir ve ikisi de yalnızca bir fonksiyonun içindeki çağrılar için bildirilir:
DynamicCodeRenameRisk- bir fonksiyon çalışma zamanında bir dizeden kod oluşturur: doğrudan birevalveya gövdesi statik olmayan birnew Function/Functionçağrısı ya da bir<script>veya Worker içine enjekte edilenfn.toString(). BirReferenceErrorhatasını öngören uyarı budur.VMDynamicCodeSkipped- bir fonksiyon doğrudan birevalveya dinamik birnew Function/Functionçağrısı içerdiği için o fonksiyon ve içinde tanımlanan her fonksiyon için VM bayt koduna çevirme atlanmıştır. Güvenli, statik bireval('literal')için de tetiklenir; bu nedenle bir çalışma zamanı çökmesine değil, kaybedilen VM korumasına işaret eder. Fonksiyon adını (varsa) ve atlamayı tetikleyen yapıyı içerir.
javascript-obfuscator npm paketi uyarıları sonucunda sunmaz; bu nedenle bir CI kapısı onları API
yanıtından okur: result ve chunk_end mesajları bir warnings dizisi taşır. Aşağıdaki örnek,
API Referansı sayfasındaki akış okuyucusu olan readObfuscationResponse() fonksiyonunu kullanır ve derlemeyi yalnızca
DynamicCodeRenameRisk durumunda başarısız kılar; bir fonksiyonda VM korumasının kaybedilmesi de sürümü engellemeliyse filtreye
VMDynamicCodeSkipped ekleyin.
Bir uyarı türü derlemenizde bekleniyorsa ve onu CI'da filtrelemek yerine kaynağında susturmayı tercih ediyorsanız,
warnings seçeneği (v7.8.0+) hangi uyarıların üretileceğini denetler: 'none' her şeyi bastırır, { VMDynamicCodeSkipped: false }
gibi tür bazında bir eşleme ise diğerlerini korurken yalnızca bir türü susturur.
Geçici çözümler
Dolaylı eval'e (
(0, eval)(s)) geçinYalnızca
evaldurumu için işe yarar. Dolaylı eval global kapsamda çalışır; bu nedenle çevreleyen yerel değişkenleri göremez ve aynı nedenle yeniden adlandırılmış yerel değişkenlere de başvuramaz. Çağrıyı içeren fonksiyon VM bayt koduna çevrilmeye devam eder. Değerlendirilen kod yine de obfuscator aracının yeniden adlandırdığı global değişkenlere bağımlı olmamalıdır.Gövdeyi tamamen statik yapın
new Functioniçin gövdeyi enterpolasyon içermeyen tek bir dize değişmezi / şablon değişmezi olarak ifade edebiliyorsanız, çağrı hiçbir yeniden adlandırma riski taşımaz ve çevresindeki fonksiyon yine bayt koduna çevrilir.new Function('a', 'b', 'return a + b')sorunsuzdur;new Function('return ' + expr)değildir.Çağrıyı kendi en üst düzey fonksiyonuna taşıyın ve IIFE'yi açın
Her en üst düzey fonksiyon bağımsız olarak denetlenir. Dinamik kod çağrısını kendi en üst düzey fonksiyonuna taşımak, atlamanın paketinizin tamamını saran en üst düzey bir IIFE boyunca yayılması yerine yalnızca o fonksiyonun VM bayt koduna çevrilmemesi anlamına gelir.
vmTargetFunctionsMode: 'comment'moduna geçinKatılım tabanlı mod: yalnızca
/* javascript-obfuscator:vm */ile işaretlenmiş fonksiyonlar bayt koduna çevrilir. Dinamik kod çağrısını içeren fonksiyonu işaretlemeyin, geri kalanları işaretleyin. Bu yorumları obfuscation adımına kadar koruyun. Bkz. Fonksiyonları Hedefleme.Atlamayı
vmForceCompileDynamicCode: trueile geçersiz kılın (v6.14.0+)Son çare olarak kullanılacak bir kaçış yolu. Etkinleştirildiğinde obfuscator çevreleyen fonksiyonu yine de bayt koduna çevirir ve
VMDynamicCodeSkippeduyarısını bastırır. Kapsam veya yeniden adlandırılmış tanımlayıcı bağımlılıklarını onaramaz: yalnızca çalışma zamanında oluşturulan gövdenin obfuscator aracının yeniden adlandırdığı hiçbir tanımlayıcıya başvurmadığını garanti edebildiğinizde kullanın; aksi halde temiz bir obfuscation karşılığında çalışma zamanında birReferenceErroralırsınız. CI'ın bu uyarıya göre engellemeye devam edebilmesi içinDynamicCodeRenameRiskyine de üretilir. Panelde bu, VM bölümündeki Geçersiz Kılmalar grubunun altındaki "Force Compile Dynamic Code" anahtarıdır.
İlgili sayfalar
- Doğrudan eval Davranışı - atlamanın etki alanını sınırlamak için IIFE'yi açma kalıbı.
- Fonksiyonları Hedefleme - fonksiyon düzeyinde katılmak veya hariç tutmak için
vmTargetFunctionsModekullanımı.
