C# ile ALTCHA Nasıl Çözülür ve Token Değiştirilmeden Geri Gönderilir

C# ile ALTCHA çözmek için okunacak hiçbir şey yok. ALTCHA bir tanıma yöntemi değil, iş kanıtı (proof of work) yöntemidir: site bir challenge dağıtır ve istemcinin bu challenge’ı sağlayan sayıyı kaba kuvvetle bulması gerekir. Ortada görüntü, ses ya da tahmin yoktur; bu da çözümü deterministik ve hızlı kılar. Ya cevabı bulur ya da challenge bozuktur veya süresi çoktan geçmiştir. CapSkip, ALTCHA desteğini 1.2.6 sürümünde ekledi ve .NET SDK bunu, sayfa URL’si ile challenge’ı alan tek bir metot olarak sunuyor. İnsanları gerçekten zorlayan kısım ise sonrasında olanlar: token, çözücünün döndürdüğü hâliyle birebir aynı şekilde forma girmelidir.
Neye ihtiyacınız var
- Bir Windows makinesinde çalışan CapSkip 1.2.6 veya üzeri. ALTCHA desteği bu sürümle geldi.
- CapSkip .NET paketi. Bu paket .NET Standard 2.0’ı hedefler; yani .NET Framework 4.6.1 ve üzeri, .NET Core 2.0 ve üzeri ve .NET 6 ile sonrası desteklenir.
- Widget’ın bulunduğu sayfanın URL’si ve o widget’ın challenge’ını çektiği endpoint.
- Çözücü için bir adres. Local modu 127.0.0.1 üzerinden yalnızca o cihaza yanıt verir; Server modu ise ağ adresinizi veya genel IP’nizi dinler, böylece başka bir makine ona ulaşabilir. Hangisini istediğinizi 4. adım anlatıyor; ikisi de şurada bulunur: bağlantı ayarları.
# dotnet add package CapSkip dotnet add package CapSkip
1. adım: ALTCHA widget’ının çağırdığı endpoint’i bulun
Geri kalan her şey bu tek değere bağlı, bu yüzden önce onu alın. DevTools’u açın, Network sekmesine geçin ve widget’ın bulunduğu sayfayı yeniden yükleyin. Widget, challenge’ı için genellikle içinde altcha geçen bir yola istek gönderir. Çözücüye verdiğiniz şey bu istek URL’sidir; o endpoint’in döndürdüğü JSON ise challenge belgesinin kendisidir ve URL yerine bu belgeyi de verebilirsiniz.
Challenge’ı adlandıran özniteliği tahmin etmeye çalışmayın, çünkü bu öznitelik widget nesilleri arasında değişti. Sayfa kaynağını okuyun.
| Widget nesli | Challenge’ı adlandıran öznitelik |
|---|---|
| v1 ve v2 | Bir endpoint için challengeurl, satır içi challenge için ayrı bir challengejson özniteliği |
| v3 ve sonrası | challenge; aynı öznitelik hem bir URL hem de challenge verisini kabul eder |
Üç görüntüleme stili olan native, checkbox ve switch tamamen görseldir. Üçü de aynı payload’ı gönderir ve aradaki fark asla çözücüye ulaşmaz, bu yüzden hangisine baktığınızı çözmek zorunda değilsiniz. ALTCHA, widget özniteliklerini şurada ele alıyor: kendi entegrasyon dokümanları.
2. adım: çözüm çağrısı ve challenge vermenin iki yolu
Tek metot, iki argüman: önce sayfa URL’si, ardından challenge’ı taşıyan bir seçenekler sözlüğü. Ona endpoint’i verin, CapSkip challenge’ı sizin için çeker.
// dotnet add package CapSkip
using CapSkip;
var solver = new CapSkipClient(host: "127.0.0.1", port: 8080);
// CapSkip fetches the challenge, then brute-forces the counter.
var result = await solver.AltchaAsync(
"https://example.com/signup",
new Dictionary<string, object?>
{
["challenge_url"] = "https://example.com/altcha/challenge",
});
Console.WriteLine(result.Token); // base64 payload for the form field
Console.WriteLine(result.Number); // the counter that satisfied itSonuçtaki iki alan yalnızca ALTCHA içindir. Token, formun beklediği base64 payload’ıdır; Number ise challenge’ı çözen sayaçtır. Code özelliği Token ile aynı dizeyi taşır, yani ikisi de işe yarar; ancak Token, gireceği alanın adını taşıdığı için çağrı yerinde daha iyi okunur. GeeTest alanları ve Turnstile user agent değeri burada null kalır.
Number’ı loglamaya değer. Payload’ları farklı olsa da bu değer her iki ALTCHA nesli için de raporlanır: legacy bir token sayacı en üst düzeyde taşır, proof-of-work v2 token’ı ise taşımaz ve sayacı bir solution nesnesinin içinde tutar. CapSkip bu değeri kendi API yanıtında solution nesnesinden okur ve bu yanıt her iki nesli de aynı şekilde raporlar.
Bunun yerine challenge belgesini vermek
Kodunuz challenge’ı zaten çektiyse belgeyi verin; o zaman hiç ağ isteği yapılmaz. Sayfayı hâlihazırda scrape ediyorsanız bu daha hızlı yoldur ve challenge bir endpoint’ten gelmek yerine HTML içine gömülü geliyorsa kullanmanız gereken yol budur.
// No fetch happens: the document is already here.
var result = await solver.AltchaAsync(
"https://example.com/signup",
new Dictionary<string, object?>
{
["challenge_json"] = new Dictionary<string, object>
{
["algorithm"] = "SHA-256",
["challenge"] = "YOUR_CHALLENGE_HASH",
["salt"] = "YOUR_SALT",
["signature"] = "YOUR_SIGNATURE",
["maxnumber"] = 1000000,
},
});Bu seçenek bir sözlük alır ve sözlüğü sizin için serileştirir; elinizde hazır bir JSON dizesi varsa onu da verebilirsiniz. Hem endpoint’i hem belgeyi göndermek serbesttir ve satır içi belge kazanır, çünkü çekme işlemi yalnızca sizin az önce verdiğiniz şeyi yeniden almak olurdu. İki yol yük altında da farklı davranır: süresi çoktan geçmiş bir satır içi challenge, boşuna hash alınmak yerine doğrudan reddedilir; buna karşılık bir endpoint, iş kuyrukta beklerken ilk challenge öldüyse çözücünün yeni bir challenge çekmesine izin verir.
Çözücünün kapsadığı algoritmalar
Aynı metot her iki nesli de karşılar. Legacy şema SHA-1, SHA-256, SHA-384 ve SHA-512 ile desteklenir; proof-of-work v2 ise PBKDF2 ve iteratif SHA ile desteklenir. PBKDF2, ALTCHA’nın kendisinin önerdiği varsayılandır, dolayısıyla bu kapsam yayındaki sitelerin büyük çoğunluğunu içerir.
Argon2id ve scrypt istisnadır ve denenmek yerine reddedilirler: bunlardan birini kullanan bir görev, yaklaşık saniyenin üçte biri içinde ERROR_CAPTCHA_UNSOLVABLE ile geri döner ve asla yeniden denenmez. Bu bilinçli bir tercih. Bellek yoğun bir fonksiyonu yeniden denemek düzeltmez, bu yüzden hemen başarısız olmak meşgul görünmekten iyidir. ALTCHA söz konusu olduğunda bu sonuç, okunamayan bir görseli değil algoritmayı işaret eder ve hata kodu ayrıca şurada ele alınıyor: kendine ait bir rehber.
3. adım: token’ı süresi geçmeden, hiç değiştirmeden geri gönderin
Widget, payload’ını altcha adlı bir form alanında gönderir; dolayısıyla token’ınız da oraya gider. Sessiz sedasız bozulan adım budur.
// Send it exactly as it came back: no trimming,
// no re-encoding, no reordering.
var body = new FormUrlEncodedContent(new Dictionary<string, string>
{
["email"] = "[email protected]",
["altcha"] = result.Token!,
});
var response = await http.PostAsync("https://example.com/signup", body);Token, alanları sunucunun kendi HMAC imzasıyla korunan bir JSON belgesinin base64 hâlidir. Her değişiklik onu geçersiz kılar; bu yüzden düzeltme ya da toparlama gibi görünen her şey gönderimi bozar: boşlukları kırpmak, çözüp yeniden kodlamak veya JSON’ı anahtarları farklı sırada olacak şekilde yeniden oluşturmak. Bazı entegrasyonlar payload’ı form alanı yerine bir JSON gövde alanından okur, bu yüzden sayfanın kendi gönderiminin ne yolladığını kontrol edip aynısını yapın.
Bu adımın bozulmasının diğer yolu zamanlamadır. Challenge pencereleri kısadır ve bazı siteler bu pencereleri iki dakika içinde kapatır; süre dolduğunda site, cevabı tam olarak yanlış cevap gibi görünen yalın bir doğrulama hatasıyla reddeder. Hatanın içinde bu ikisinden hangisinin yaşandığını söyleyen hiçbir şey yoktur. Üç alışkanlık bunu önler: challenge’ı uzun bir çalışmanın başında değil, çözümden hemen önce çekin; token’ı onu çözen aynı iş biriminde gönderin; ve bir kişi formu doldururken elinizde token tutmayın.
Burada sizi sınırlayan şey istemcinin kendi yoklama zaman aşımları değil, çünkü ikisi de o iki dakikalık pencereden çok daha uzun. ALTCHA bir tarayıcı oturumu değil CPU işidir; bu yüzden daha uzun olan reCAPTCHA’nınki yerine varsayılan yoklama zaman aşımıyla çalışır.
| Yapıcı seçeneği | Varsayılan | Neyi kapsar |
|---|---|---|
| defaultTimeout | 120 saniye | ALTCHA ve görsel CAPTCHA yoklaması |
| recaptchaTimeout | 300 saniye | reCAPTCHA, Turnstile ve GeeTest yoklaması |
| pollingInterval | En fazla 5 saniye | Yoklama 0,25 saniyeden başlar ve kademeli olarak bu değere kadar çıkar |
4. adım: çözücünün nerede çalıştığı ve bunun hangi bağlantı modunu gerektirdiği
Yukarıdaki örnekler 127.0.0.1 kullanıyor, çünkü kodunuz ile çözücü aynı makinede olduğunda doğru adres budur. Çözücüyü çağıran kod başka bir yerde çalışmaya başladığı anda, örneğin bir konteynerde, bir derleme ajanında, bir VPS’te veya yönetilen bir sunucuda, loopback artık çözücüyü göstermez ve ilk çözüm bir NetworkException fırlatır.
CapSkip’i Server moduna alın; o zaman bunun yerine ağ adresinizi veya genel IP’nizi dinler, böylece bu ortamların hepsi API üzerinden ona ulaşabilir. İnternet üzerinden gidiyorsanız statik bir genel IP ve yalnızca beklediğiniz adreslere izin veren bir güvenlik duvarı kuralı önerilir. Server modu yalnızca çözücünün nerede dinlediğini değiştirir, başka hiçbir şeyi değiştirmez: donanım yine sizin donanımınızdır ve kullanım yine sınırsızdır. Tek bir derlemenin her iki yerde de çalışması için host değerini bir ortam değişkeninden okuyun. İstemci CAPSKIP_HOST değerini kendi başına okumaz; bu yüzden, aşağıdaki tam örnekte olduğu gibi, onu yapıcıya geçirin.
| C# kodunun çalıştığı yer | Hangi bağlantı modu |
|---|---|
| CapSkip makinesinde, bir IDE’de veya konsol uygulamasında | Local modu. 127.0.0.1 gerçekten doğru |
| Aynı ağdaki başka bir makinede | Server modu, o makinenin özel adresinde |
| Bir konteyner sunucusunda, VPS’te veya yönetilen bir platformda | Statik bir genel IP ve bir güvenlik duvarı kuralı ile Server modu |
Proxy’lerle ilgili ALTCHA’ya özgü bir not. Burada proxy desteklenir, ancak yalnızca challenge çekme işlemi için kullanılır. Yönlendirilecek bir tarayıcı oturumu olmadığından, iş kanıtının kendisine hiçbir etkisi yoktur.
Tam çalışan örnek
// dotnet add package CapSkip
using CapSkip;
var http = new HttpClient();
var solver = new CapSkipClient(
host: Environment.GetEnvironmentVariable("CAPSKIP_HOST") ?? "127.0.0.1",
port: 8080);
try
{
var result = await solver.AltchaAsync(
"https://example.com/signup",
new Dictionary<string, object?>
{
["challenge_url"] = "https://example.com/altcha/challenge",
});
// Submit here, while the challenge is still fresh.
var body = new FormUrlEncodedContent(new Dictionary<string, string>
{
["email"] = "[email protected]",
["altcha"] = result.Token!,
});
var response = await http.PostAsync("https://example.com/signup", body);
Console.WriteLine($"{(int)response.StatusCode} after counter {result.Number}");
}
catch (ApiException ex)
{
// ERROR_CAPTCHA_UNSOLVABLE here means Argon2id or scrypt.
Console.WriteLine($"refused: {ex.Message}");
}
catch (CapSkip.TimeoutException)
{
Console.WriteLine("gave up waiting; defaultTimeout is 120 seconds");
}Diğer türler aynı biçimdedir, yalnızca metot değişir. RecaptchaAsync bir sitekey ve bir sayfa URL’si alır, TurnstileAsync ve GeetestAsync aynı şekilde çalışır, görsel çözme ise base64 ile yapılan bir çağrıdır. Metotların tam listesi şurada: C# CAPTCHA çözücü sayfası.
Bir sitekey’den fazlasını gerektiren tek tür, challenge sayfasındaki Turnstile’dır. Ek değerleri şurada ele alınıyor: C# challenge sayfası rehberi.
Sık görülen hatalar ve anlamları
| Gördüğünüz | Neden | Düzeltme |
|---|---|---|
| Siteden gelen yalın bir doğrulama hatası, token ise sorunsuz görünüyor | Form gönderilmeden önce challenge’ın süresi doldu | Çekme, çözme ve gönderme işlemlerini tek bir iş biriminde yapın |
| Yaklaşık saniyenin üçte biri içinde, bir ApiException içinde ERROR_CAPTCHA_UNSOLVABLE | Challenge, Argon2id veya scrypt kullanıyor | Yeniden denenecek bir şey yok. Bu ikisi tasarım gereği reddedilir |
| Çağrıda bir ValidationException | İki challenge seçeneğinden hiçbiri verilmedi ya da ALTCHA’nın kabul etmediği bir seçenek verildi | Challenge endpoint’ini ya da challenge belgesini verin ve geri kalan her şeyi çıkarın |
| İlk çözümde bir NetworkException | CapSkip çalışmıyor ya da host ve port yanlış | CapSkip’i başlatın, ardından Local modunda mı yoksa Server modunda mı olması gerektiğini kontrol edin |
| Sonuçtaki Token özelliği null görünüyor | Token yalnızca ALTCHA için doldurulur | AltchaAsync’i çağırın. Bir ALTCHA sonucunda Code özelliği aynı dizeyi tutar |
| Derleme, belirsiz bir TimeoutException yüzünden başarısız oluyor | O kısa adı hem CapSkip hem System tanımlıyor | CapSkip.TimeoutException’ı tam olarak yazın veya CapSkipError’ı yakalayın |
| Loglarınızda çözüldüğü görünen bir token’ı form reddediyor | Bir şey payload’ı yeniden kodladı, kırptı veya sırasını değiştirdi | Dizeyi hiç dokunmadan, olduğu gibi geçirin |
FAQ
C# ile ALTCHA çözmek için tarayıcı gerekir mi?
Hayır ve işin işe yarar kısmı da bu. ALTCHA bakılacak bir şey değil, bir hash problemi verir; dolayısıyla iş tamamen CPU üzerinde yapılır ve milisaniyeler içinde biter. WebDriver, headless Chrome veya user agent gerekmez. HttpClient kullanan bir konsol uygulaması yeterlidir; bu da tarayıcı sürmenin zahmetli olacağı bir worker service, bir kuyruk tüketicisi ya da bir derleme adımının içinde sorunsuz çalıştığı anlamına gelir.
Barındırılan bir platformdaki .NET uygulaması çözücüye erişebilir mi?
Evet. Bağlantı ayarlarından CapSkip’i Server moduna alın; böylece loopback yerine bir ağ adresini dinler. Ardından CAPSKIP_HOST’u o adrese yönlendirin. Bir konteyner sunucusu, bir VPS, bir CI ajanı veya yönetilen bir uygulama servisi; hepsi aynı şekilde, aynı HTTP API üzerinden bağlanır. Rota internetten geçiyorsa statik bir genel IP kullanın ve bunu bir güvenlik duvarı kuralıyla sınırlayın. Bu durumların hepsinde çözücü sizin sahip olduğunuz donanımda kalır, dolayısıyla lisans veya çözüm sayısı açısından hiçbir şey değişmez.
Endpoint’i mi challenge belgesini mi vermeliyim?
Belge elinizde yoksa endpoint’i verin. Seçenek sözlüğündeki tek bir girdidir, size bir istek kazandırır ve iş kuyrukta beklerken challenge bayatlarsa çözücü yenisini kendisi çeker. Belgeyi ise şu durumlarda verin: kazıyıcınız belgeyi sayfadan zaten okuduysa, challenge bir endpoint’ten sunulmak yerine HTML içine gömülüyse ya da challenge’ı çekmek sizin kodunuzda olan ama çözücüde olmayan çerezleri veya başlıkları gerektiriyorsa. Bu son durumda proxy seçeneğini bilmeye değer, çünkü ALTCHA’da bu seçenek yalnızca çekme işlemi için geçerlidir.
Sayacım neden her seferinde farklı bir sayı oluyor?
Çünkü o sayı sitenin bir özelliği değil, o belirli challenge’ın cevabıdır. Her challenge kendi salt değerini taşır, bu yüzden onu sağlayan sayı her verilişte değişir ve challenge’ın izin verdiği maxnumber değerine kadar herhangi bir yere denk gelebilir. Büyük bir sayaç yalnızca daha fazla hash gerektiğini gösterir; bu da birkaç milisaniye fazladan süre olarak görünür, başka hiçbir anlamı yoktur. Sayaç, işin gerçekten yapıldığının kanıtı olarak loglarda işe yarar; önbelleğe alınacak bir şey olarak ise işe yaramaz.
Kısa özet
Challenge endpoint’ini widget’tan okuyun, sayfa URL’si ile birlikte tek ALTCHA metoduna verin ve token’ı hiç dokunmadan altcha adlı alana geri gönderin. Çekme, çözme ve gönderme işlemlerini aynı blokta tutun, çünkü challenge penceresi iki dakika içinde kapanabilir ve süresi geçmiş bir challenge tam olarak yanlış cevap gibi görünür. ERROR_CAPTCHA_UNSOLVABLE hatasını yalnızca Argon2id ve scrypt’ten bekleyin; bu ikisi denenmez bile, doğrudan reddedilir. Çağıran kod, çözücüyle aynı makineyi paylaşmayı bıraktığı anda Server moduna geçin.
- Challenge’ın ne olduğu ve bu türün nasıl çalıştığı: ALTCHA çözücü sayfası.
- .NET paketinin sunduğu diğer tüm metotlar: C# ve .NET çözücü sayfası.
Yeniden denemeyi nasıl tasarladığınızı değiştiren son bir nokta. Bir yerel captcha çözücü iş kanıtını zaten sahip olduğunuz bir makinede hesapladığı için, süresi geçmiş bir challenge’ı yeniden denemek size kendi CPU’nuzdan yalnızca birkaç milisaniyeye mal olur, başka hiçbir şeye mal olmaz; bu yüzden bayat bir challenge’ı kurtarmaya çalışmak yerine yeni bir challenge çekmeyi rahatça göze alabilirsiniz.
