JavaScript Obfuscation Tersine Mühendislik: Pratik Deobfuscation Rehberi
JavaScript obfuscation, hedefin frontend bundle'ında gizlenen iş mantığı ile senin aranda duran ilk engeldir. Bu rehber onu nasıl sökeceğini gösteriyor — basit eval(unescape(...)) sarmalayıcısından AST cerrahisi gerektiren çok aşamalı JScrambler çıktısına kadar.
Bu bir araç listesi yazısı değil. Aşağıdaki her teknik neden işe yaradığını ve ne zaman kullanılacağını içeriyor.
Hedefler Neden Obfuscate Ediyor?
En yaygın sebepler:
- SPA bundle'larına gömülü API key ve endpoint'leri gizlemek
- Ücretli içerik mantığını korumak (DRM, lisans kontrolleri)
- Production bundle'larda kalan admin veya debug route'ları saklamak
- Scraping açısından hassas ürünlerde anti-otomasyon
- Dolandırıcılık tespiti katmanlarındaki parmak izi mantığını korumak
Pentest açısından her biri bekleyen bir bulgu kategorisi.
Önce Obfuscation Türünü Tanı
Araç seçmeden önce ne ile karşı karşıya olduğunu belirle. Yanlış obfuscator'a yanlış araç uygulamak saatleri mahveder.
Pattern 1 — String Array Rotation (obfuscator.io varsayılanı)
var _0x3a2b = ['log', 'Merhaba', 'Dünya'];
var _0x1f4c = function(_0x3a2b, _0x1f4c) { /* rotation */ };
(function(_0x3a2b, _0x1f4c) { /* shuffle */ })(_0x3a2b, 0x1b3);
İşaret: Hex değişken adları (_0x...), dosyanın başında büyük dizi, kendini çağıran shuffle fonksiyonu. obfuscator.io ve pek çok ticari aracın çıktısı.
Pattern 2 — Control Flow Flattening
var _0xabc = '3|1|4|0|2'.split('|'), _0xi = 0x0;
while (true) {
switch (_0xabc[_0xi++]) {
case '0': doThing0(); continue;
case '1': doThing1(); continue;
}
break;
}
İşaret: String güdümlü dağıtımlı büyük while(true) + switch bloğu. Her fonksiyon gövdesi durum makinesiyle değiştirilmiş.
Pattern 3 — Dead Code Injection
Meşru mantık, yüzlerce erişilemeyen dal ve anlamsız atama arasına gömülmüş. Tespit sinyali: görünen işlevselliğe kıyasla aşırı büyük dosya boyutu.
Pattern 4 — eval / Function Constructor zincirleri
eval(function(p,a,c,k,e,d){...}('...', 62, ...))
Klasik [p,a,c,k,e,r] (packer.exe) veya özel eval zincirleri. Eski numara, ama legacy PHP üretimi JS ve bazı WordPress eklentilerinde hâlâ yaşıyor.
Pattern 5 — Webpack + Terser (obfuscation değil, minified)
Obfuscate edilmemiş — sadece küçültülmüş. webcrack bunu tamamen çözer. AST cerrahisine zaman harcama.
Adım 1 — Önce Source Map Kontrol Et
Herhangi bir şey yapmadan önce:
# Bundle'da inline source map yorumu var mı?
grep "sourceMappingURL" app.bundle.js
# .map dosyası herkese açık var mı?
curl -I https://hedef.com/static/js/main.chunk.js.map
# Yaygın konumlar
curl https://hedef.com/static/js/2.abc123.chunk.js.map
curl https://hedef.com/_next/static/chunks/pages/_app-abc.js.map
.map dosyasından 200 alırsan: oyun bitti, orijinal kaynak kodun var. İndir:
npx source-map-explorer app.chunk.js app.chunk.js.map
Ya da manuel çıkarım için:
node -e "
const m = JSON.parse(require('fs').readFileSync('app.chunk.js.map'));
m.sources.forEach((s,i) => {
const path = require('path');
const outPath = 'recovered/' + s.replace(/\.\.\//g,'_/');
require('fs').mkdirSync(path.dirname(outPath), {recursive:true});
require('fs').writeFileSync(outPath, m.sourcesContent[i] || '');
});
"
Production'da açık kalan source map'ler bir bulgudur: Bilgi İfşası — Kaynak Kod Kurtarma.
Adım 2 — Hızlı Otomatik Geçiş
Dosyaya manuel dokunmadan önce bunları çalıştır:
webcrack (webpack için en iyi)
npx webcrack app.bundle.js -o ./recovered/
Webpack bundle parçalama, chunk yeniden birleştirme, değişken yeniden adlandırma. Terser/webpack çıktısında çok iyi çalışır.
synchrony (obfuscator.io çıktısı için en iyi)
npx synchrony deobfuscate app.obfuscated.js
obfuscator.io string array rotation deseni için özel olarak yapılmış. Diziyi çözer, tüm referansları değiştirir, değişkenleri okunabilir adlarla yeniden adlandırır.
prettier ile okunabilirlik
npx prettier --parser babel app.obfuscated.js > app.pretty.js
Adım 3 — eval Zincirlerini Dinamik Olarak Çözme
Obfuscator, çalışma zamanında string'leri çözmek için iç içe eval() çağrıları kullandığında statik analiz çöker. Güvenilir yaklaşım: çalışma zamanında eval'i ele geçir.
Chrome DevTools snippet yaklaşımı
DevTools (F12) aç → Sources → Snippets → Yeni snippet:
// Tüm eval çağrılarını yakala ve çözülmüş içeriği logla
const origEval = window.eval;
window.eval = function(code) {
console.log('=== EVAL YAKALANDI ===');
console.log(code.substring(0, 500));
// İsteğe bağlı: copy(code) ile tam string'i panoya gönder
return origEval.call(this, code);
};
// Function constructor'ı da yakala
const origFunction = Function;
window.Function = function(...args) {
console.log('=== Function() YAKALANDI ===');
console.log(args);
return origFunction(...args);
};
Snippet'i çalıştır, sonra sayfayı yenile. Dinamik olarak üretilen tüm kodlar çalışmadan önce console'da görünür.
String çözme fonksiyonlarını yakala
Çoğu obfuscator'ın tüm çağrıların yönlendirildiği tek bir merkezi çözme fonksiyonu vardır. Bul ve patch'le:
// Çözme fonksiyonunu belirledikten sonra (örn. _0x1f4c)
const orig = _0x1f4c;
window._0x1f4c = function(a, b) {
const result = orig(a, b);
console.log(`decode(${a}, ${b}) = "${result}"`);
return result;
};
Console'da çalıştır. Artık her obfuscate edilmiş string referansı sayfa çalışırken düz metin eşdeğerini logluyor.
Adım 4 — AST Tabanlı Cerrahi (Control Flow Flattening için)
Kod yapısının kendisi tahrip edildiğinde (yalnızca string kodlama değil), AST manipülasyonu gerekir. Doğru araç: Babel.
Kurulum
npm install -g @babel/core @babel/parser @babel/traverse @babel/generator
Önce string dizisini çöz
// deobfuscate-strings.mjs
import parser from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';
import fs from 'fs';
const code = fs.readFileSync('app.obfuscated.js', 'utf8');
const ast = parser.parse(code);
let stringArray = [];
traverse.default(ast, {
VariableDeclarator(path) {
if (
path.node.id.name.startsWith('_0x') &&
path.node.init?.type === 'ArrayExpression'
) {
stringArray = path.node.init.elements.map(e => e.value);
}
}
});
// Tüm dizi erişim çağrılarını string değerleriyle değiştir
traverse.default(ast, {
CallExpression(path) {
if (path.node.callee.name?.startsWith('_0x') &&
path.node.arguments.length === 1) {
const idx = path.node.arguments[0].value;
if (typeof stringArray[idx] === 'string') {
path.replaceWithSourceString(JSON.stringify(stringArray[idx]));
}
}
}
});
const output = generate.default(ast);
fs.writeFileSync('adim1-stringler-cozuldu.js', output.code);
console.log('Tamamlandı. String dizisi çözüldü.');
AST analizi için astexplorer.net
Obfuscate edilmiş kodu astexplorer.net adresine yapıştır, ayrıştırıcıyı @babel/parser olarak ayarla. Dönüştürme fonksiyonlarını inline yazabilir ve çıktının gerçek zamanlı güncellendiğini görebilirsin. Tam dönüşümü script haline getirmeden önce node şekillerini anlamak için vazgeçilmez.
Adım 5 — Kurtarılan Kodda Sır Avı
Okunabilir hale geldikten sonra ne aranır:
API key'leri ve token'lar
grep -E "(api[_-]?key|apikey|api[_-]?secret|access[_-]?token|bearer|Authorization)" \
recovered/ -r -i --include="*.js"
Hardcoded admin route'lar
grep -E '"/admin|"/internal|"/debug|"/v[0-9]/admin' recovered/ -r --include="*.js"
AWS/GCP/Azure kimlik bilgileri
grep -E "(AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35})" \
recovered/ -r --include="*.js"
Next.js SPA için gerçek vaka deseni
curl https://hedef.com/_next/static/chunks/pages/index-abc.js.map→ 200 → bitti- Source map yoksa: tüm chunk'ları indir (
_next/static/chunks/*.js) - Her chunk'ta
webcrackçalıştır — mantığın ~%70'ini okunabilir kılar - Kalan obfuscate edilmiş chunk'lar için: her chunk'ta string dizisini bul →
synchronyçalıştır _next/static/chunks/framework-*.jsdosyalarını atla — polyfill'lerpages/veapp/chunk'larına odaklan — gerçek route mantığı orada
SSS
Pentest sırasında JavaScript deobfuscation yasal mı?
Evet. Test yetkisi verilen bir hedeften indirilen JavaScript'i okumak herhangi bir yargı bölgesinde yasadışı değil. Bu, HTML kaynağını okumakla aynı şey. Obfuscation'ın yasal koruması yok.
Otomatik araçlar her JS'yi tam olarak deobfuscate edebilir mi?
Hayır. Yüksek düzeyde özelleştirilmiş obfuscator'lar (JScrambler Enterprise, özel araçlar) manuel AST cerrahisi gerektirir. Otomatik araçlar yaygın desenleri (obfuscator.io, packer, webpack) güvenilir biçimde çözer.
.map dosyasının herkese açık olması her zaman bulgu mudur?
Açık kaynak projeler kasıtlı olarak source map yayınlar. Kapalı kaynaklı ticari bir hedef için ifşa olan source map her zaman bilgi ifşası olarak raporlanabilir.
Obfuscator müdahaleyi tespit eden self-defending kod kullanıyorsa ne yapılır?
Self-defending kod genellikle Function.prototype.toString veya hash kontrolleri aracılığıyla kendi kaynağında bütünlük denetimleri çalıştırır. Herhangi bir kod çalışmadan önce DevTools snippet düzeyinde bunları geçersiz kıl:
const orig = Function.prototype.toString;
Function.prototype.toString = function() {
return orig.call(this).replace(/\s+/g, ' ');
};
Güvenlik Doğrulaması
Bu riski kendi sisteminizde test ettirdiniz mi?
Eresus Security; sızma testi, AI ajan güvenliği ve kırmızı takım operasyonlarıyla gerçek istismar kanıtı üretir.
Pilot test talep etİlgili Araştırmalar
Frontend'de Unutulan Sırlar: Hackerlar JavaScript Dosyalarından Neleri Çalıyor?
React, Angular, Vue gibi modern framework'lerde derlenen (build) istemci taraflı JavaScript dosyalarında unutulan API anahtarları, şifreler ve AWS...
Web SecurityWeb Pentest Kapsamı Nasıl Çıkarılır?
Web uygulama pentest kapsamını asset sayısı yerine riskli iş akışları, roller, veriler ve entegrasyonlar üzerinden nasıl planlayacağınızı anlatıyoruz.