Cara Memecahkan ALTCHA di C# dan Mengirim Token Kembali Tanpa Perubahan

Untuk memecahkan ALTCHA di C#, tidak ada yang perlu dibaca. ALTCHA adalah proof of work, bukan pengenalan: situs memberikan sebuah challenge dan klien harus mencari angka yang memenuhinya secara brute-force. Tidak ada gambar, tidak ada audio dan tidak ada tebakan yang terlibat, sehingga pemecahan bersifat deterministik dan cepat. Hasilnya hanya dua: menemukan jawabannya, atau challenge tersebut cacat atau sudah kedaluwarsa sebelumnya. CapSkip menambahkan ALTCHA pada versi 1.2.6, dan SDK .NET menyediakannya sebagai satu metode yang menerima URL halaman plus challenge. Bagian yang sebenarnya sering menjebak orang adalah apa yang terjadi setelahnya: token harus dikirim kembali ke dalam form persis seperti yang dikembalikan pemecah CAPTCHA.
Apa yang Anda butuhkan
- CapSkip 1.2.6 atau lebih baru yang berjalan di komputer Windows. Dukungan ALTCHA hadir pada rilis tersebut.
- Paket CapSkip .NET, yang menargetkan .NET Standard 2.0, jadi .NET Framework 4.6.1 ke atas, .NET Core 2.0 ke atas, dan .NET 6 serta yang lebih baru.
- URL halaman tempat widget berada, dan endpoint tempat widget itu mengambil challenge-nya.
- Sebuah alamat untuk pemecah CAPTCHA. Mode Local menjawab di 127.0.0.1 hanya untuk perangkat itu; mode Server mendengarkan di alamat jaringan atau IP publik Anda sehingga mesin lain dapat menjangkaunya. Langkah 4 membahas mana yang Anda butuhkan, dan keduanya ada di pengaturan koneksi.
# dotnet add package CapSkip dotnet add package CapSkip
Langkah 1: temukan endpoint yang dipanggil widget ALTCHA
Semua hal lain bergantung pada satu nilai ini, jadi dapatkan nilai itu lebih dulu. Buka DevTools, masuk ke tab Network dan muat ulang halaman tempat widget berada. Widget mengirim satu permintaan untuk challenge-nya, biasanya ke path yang mengandung altcha. URL permintaan itulah yang Anda berikan ke pemecah CAPTCHA, dan JSON yang dikembalikan endpoint itu adalah dokumen challenge itu sendiri, yang bisa Anda berikan sebagai ganti URL-nya.
Jangan menebak atribut yang menamainya, karena atribut itu berubah antar generasi widget. Baca kode sumber halaman.
| Generasi widget | Atribut yang menamai challenge |
|---|---|
| v1 dan v2 | challengeurl untuk endpoint, dengan atribut challengejson terpisah untuk challenge inline |
| v3 dan setelahnya | challenge, dan atribut yang sama itu menerima URL atau data challenge |
Ketiga gaya tampilan, native, checkbox dan switch, murni bersifat visual. Semuanya mengirim payload yang sama dan perbedaannya tidak pernah sampai ke pemecah CAPTCHA, jadi Anda tidak perlu mencari tahu yang mana yang sedang Anda lihat. ALTCHA membahas atribut widget di dokumentasi integrasinya sendiri.
Langkah 2: pemanggilan pemecahan, dan dua cara menyediakan challenge
Satu metode, dua argumen: URL halaman, lalu sebuah dictionary opsi yang membawa challenge. Beri dia endpoint dan CapSkip akan mengambil challenge untuk Anda.
// 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 itDua field pada hasil hanya ada untuk ALTCHA. Token adalah payload base64 yang diminta form, dan Number adalah counter yang memecahkan challenge. Properti Code membawa string yang sama dengan Token, jadi keduanya bisa dipakai, tetapi Token dinamai sesuai field tujuannya dan lebih mudah dibaca di titik pemanggilan. Field GeeTest dan user agent Turnstile tetap null di sini.
Number layak dicatat di log. Nilai ini dilaporkan untuk kedua generasi ALTCHA meskipun payload keduanya berbeda: token legacy membawa counter di level teratas, sedangkan token proof-of-work v2 tidak, melainkan menyimpannya di dalam objek solution. CapSkip membacanya dari objek solution pada respons API miliknya sendiri, yang melaporkan kedua generasi dengan cara yang sama.
Memberikan dokumen challenge sebagai gantinya
Jika kode Anda sudah mengambil challenge, berikan dokumennya dan tidak ada permintaan jaringan sama sekali. Ini jalur yang lebih cepat ketika Anda memang sudah melakukan scraping pada halaman itu, dan jalur yang harus dipakai ketika challenge datang tertanam di dalam HTML bukan dari sebuah endpoint.
// 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,
},
});Opsi itu menerima dictionary, yang diserialisasi untuk Anda, atau string JSON jika Anda sudah memilikinya. Mengirim endpoint dan dokumen sekaligus diperbolehkan, dan dokumen inline yang menang, karena pengambilan hanya akan memperoleh ulang apa yang baru saja Anda berikan. Kedua jalur juga berperilaku berbeda saat beban tinggi: challenge inline yang sudah kedaluwarsa langsung ditolak alih-alih di-hash sia-sia, sedangkan endpoint memungkinkan pemecah CAPTCHA mengambil challenge baru jika yang pertama mati saat pekerjaan menunggu di antrean.
Algoritma apa saja yang dicakup pemecah CAPTCHA
Metode yang sama menangani kedua generasi. Skema legacy dicakup dengan SHA-1, SHA-256, SHA-384 dan SHA-512, dan proof-of-work v2 dicakup dengan PBKDF2 dan SHA iteratif. PBKDF2 adalah default yang direkomendasikan ALTCHA sendiri, jadi itu mencakup sebagian besar situs yang aktif.
Argon2id dan scrypt adalah pengecualian, dan keduanya ditolak alih-alih dicoba: tugas yang memakai salah satunya kembali dalam sekitar sepertiga detik dengan ERROR_CAPTCHA_UNSOLVABLE dan tidak pernah dicoba ulang. Itu memang disengaja. Fungsi memory-hard bukan sesuatu yang bisa diperbaiki dengan percobaan ulang, jadi gagal seketika lebih baik daripada terlihat sibuk. Untuk ALTCHA hasil itu menunjuk ke algoritma dan bukan ke gambar yang tidak terbaca, dan kode error itu punya panduan tersendiri.
Langkah 3: kirim token kembali tanpa perubahan, sebelum kedaluwarsa
Widget mengirim payload-nya di field form bernama altcha, jadi di situlah token Anda ditempatkan. Inilah langkah yang gagal tanpa memberi tanda.
// 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 adalah base64 dari dokumen JSON yang field-nya dicakup oleh signature HMAC milik server itu sendiri. Modifikasi apa pun membuatnya tidak valid, jadi apa pun yang tampak seperti merapikan akan merusak pengiriman: memangkas spasi, mendekode dan mengodekan ulang, atau membangun ulang JSON dengan urutan key yang berbeda. Sebagian integrasi membaca payload dari field body JSON bukan dari field form, jadi periksa apa yang dikirim oleh submit halaman itu sendiri dan tiru itu.
Cara lain langkah ini gagal adalah soal waktu. Jendela challenge itu singkat dan sebagian situs menutupnya dalam waktu kurang dari dua menit, dan ketika satu challenge kedaluwarsa situs menolak jawabannya dengan kegagalan verifikasi biasa yang tampak persis seperti jawaban salah. Tidak ada apa pun dalam error itu yang memberi tahu Anda mana dari keduanya yang terjadi. Tiga kebiasaan menghindarinya: ambil challenge tepat sebelum memecahkannya, bukan di awal proses yang panjang; kirim token dalam unit kerja yang sama dengan yang memecahkannya; dan jangan pernah menahan token sementara seseorang mengisi form.
Timeout polling milik klien sendiri bukan yang membatasi Anda di sini, karena keduanya jauh lebih panjang daripada jendela dua menit itu. ALTCHA adalah pekerjaan CPU dan bukan sesi browser, jadi ia berjalan pada timeout polling default dan bukan timeout reCAPTCHA yang lebih panjang.
| Opsi constructor | Bawaan | Apa yang dicakupnya |
|---|---|---|
| defaultTimeout | 120 detik | Polling ALTCHA dan CAPTCHA gambar |
| recaptchaTimeout | 300 detik | Polling reCAPTCHA, Turnstile dan GeeTest |
| pollingInterval | Maksimum 5 detik | Polling dimulai pada 0,25 detik dan melambat hingga nilai ini |
Langkah 4: di mana pemecah CAPTCHA berjalan, dan mode koneksi apa yang dibutuhkan
Contoh di atas memakai 127.0.0.1 karena itu benar ketika kode Anda dan pemecah CAPTCHA berada di satu mesin yang sama. Begitu kode yang memanggil pemecah CAPTCHA berjalan di tempat lain, seperti container, build agent, VPS atau managed host, loopback tidak lagi menunjuk ke pemecah CAPTCHA, dan pemecahan pertama akan melempar NetworkException.
Alihkan CapSkip ke mode Server dan ia akan mendengarkan di alamat jaringan atau IP publik Anda, sehingga semua lingkungan itu dapat menjangkaunya melalui API. IP publik statis disarankan jika Anda melewati internet, dengan aturan firewall yang hanya mengizinkan alamat yang Anda harapkan. Mode Server hanya mengubah tempat pemecah CAPTCHA mendengarkan dan tidak ada yang lain: perangkatnya tetap milik Anda, dan tetap tanpa kuota. Baca host dari variabel lingkungan agar satu build bekerja di kedua tempat. Klien tidak membaca CAPSKIP_HOST dengan sendirinya, jadi teruskan nilainya ke konstruktor, seperti yang dilakukan contoh lengkap di bawah.
| Di mana kode C# berjalan | Mode koneksi yang mana |
|---|---|
| Di mesin CapSkip, dalam IDE atau aplikasi konsol | Mode Local. 127.0.0.1 memang benar |
| Di mesin lain pada jaringan yang sama | Mode Server, di alamat privat mesin tersebut |
| Di container host, VPS atau platform terkelola | Pakai mode Server dengan IP publik statis dan sebuah aturan firewall |
Satu catatan khusus ALTCHA soal proxy. Proxy didukung di sini, tetapi hanya dipakai untuk pengambilan challenge. Tidak ada sesi browser yang perlu dirutekan, jadi proxy tidak berpengaruh pada proof of work itu sendiri.
Contoh lengkap yang berfungsi
// 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");
}Tipe lain memiliki bentuk yang sama dengan metode yang berbeda. RecaptchaAsync menerima sitekey dan URL halaman, TurnstileAsync dan GeetestAsync bekerja dengan cara yang sama, dan pemecahan gambar adalah pemanggilan base64. Daftar lengkap metodenya ada di halaman pemecah CAPTCHA C#.
Turnstile halaman challenge adalah satu-satunya tipe yang membutuhkan lebih dari sitekey. Nilai tambahannya dibahas di panduan halaman challenge C#.
Kesalahan umum dan artinya
| Apa yang Anda lihat | Penyebab | Perbaiki |
|---|---|---|
| Kegagalan verifikasi biasa dari situs, dengan token yang tampak baik-baik saja | Challenge kedaluwarsa sebelum form dikirim | Ambil, pecahkan dan kirim dalam satu unit kerja |
| ERROR_CAPTCHA_UNSOLVABLE di dalam ApiException, dalam sekitar sepertiga detik | Challenge memakai Argon2id atau scrypt | Tidak ada yang perlu dicoba ulang. Keduanya ditolak secara desain |
| ValidationException pada pemanggilan | Tidak ada opsi challenge yang diberikan, atau ada opsi yang diberikan tetapi tidak diterima ALTCHA | Berikan endpoint challenge atau dokumen challenge, dan buang yang lainnya |
| NetworkException pada pemecahan pertama | CapSkip tidak berjalan, atau host dan port-nya salah | Jalankan CapSkip, lalu periksa apakah ia harus berada di mode Local atau mode Server |
| Properti Token pada hasil terbaca null | Token hanya terisi untuk ALTCHA | Panggil AltchaAsync. Pada hasil ALTCHA, properti Code menyimpan string yang sama |
| Build gagal karena TimeoutException yang ambigu | CapSkip dan System sama-sama mendefinisikan nama pendek itu | Tulis CapSkip.TimeoutException secara lengkap, atau tangkap CapSkipError |
| Form menolak token yang menurut log Anda berhasil dipecahkan | Ada sesuatu yang mengodekan ulang, memangkas atau mengubah urutan payload | Teruskan string itu apa adanya, tanpa disentuh |
FAQ
Apakah memecahkan ALTCHA di C# membutuhkan browser?
Tidak, dan justru itu bagian yang berguna. ALTCHA memberikan masalah hashing, bukan sesuatu untuk dilihat, jadi pekerjaannya hanya CPU dan selesai dalam hitungan milidetik. Anda tidak butuh WebDriver, tidak butuh Chrome headless dan tidak butuh user agent. Aplikasi konsol dengan HttpClient sudah cukup, yang juga berarti ia berjalan nyaman di dalam worker service, queue consumer atau build step, tempat menjalankan browser justru merepotkan.
Bisakah aplikasi .NET di platform hosting menjangkau pemecah CAPTCHA?
Bisa. Alihkan CapSkip ke mode Server di pengaturan koneksi agar ia mendengarkan di alamat jaringan, bukan di loopback, lalu arahkan CAPSKIP_HOST ke alamat itu. Container host, VPS, CI agent atau layanan aplikasi terkelola semuanya terhubung dengan cara yang sama, melalui API HTTP yang sama. Gunakan IP publik statis jika rutenya melewati internet, dan batasi dengan aturan firewall. Pemecah CAPTCHA tetap berada di perangkat milik Anda dalam setiap kasus itu, jadi tidak ada yang berubah soal lisensi atau jumlah pemecahan.
Sebaiknya saya memberikan endpoint atau dokumen challenge?
Berikan endpoint kecuali Anda sudah memiliki dokumennya. Itu hanya satu entri dalam dictionary opsi, menghemat satu permintaan, dan jika challenge menjadi basi saat pekerjaan masih di antrean, pemecah CAPTCHA akan mengambil yang baru dengan sendirinya. Berikan dokumennya ketika scraper Anda sudah membacanya dari halaman, ketika challenge tertanam di HTML bukan disajikan dari sebuah endpoint, atau ketika mengambilnya membutuhkan cookie atau header yang dimiliki kode Anda tetapi tidak dimiliki pemecah CAPTCHA. Pada kasus terakhir itu, opsi proxy layak diketahui, karena untuk ALTCHA opsi itu berlaku pada pengambilan dan hanya pada pengambilan.
Mengapa counter saya berbeda angkanya setiap kali?
Karena angka itu adalah jawaban untuk challenge tertentu itu, bukan properti dari situsnya. Setiap challenge membawa salt-nya sendiri, jadi angka yang memenuhinya berubah pada setiap penerbitan, dan angkanya bisa jatuh di mana saja sampai batas maxnumber yang diizinkan challenge. Counter yang besar hanya berarti dibutuhkan lebih banyak hashing, yang muncul sebagai tambahan beberapa milidetik dan tidak lebih. Angka itu berguna di log sebagai bukti bahwa pekerjaan benar-benar dilakukan, dan tidak berguna untuk di-cache.
Versi singkatnya
Baca endpoint challenge dari widget, berikan ke satu metode ALTCHA bersama URL halaman, dan kirim token kembali ke field bernama altcha tanpa menyentuhnya. Jaga pengambilan, pemecahan dan pengiriman dalam blok yang sama, karena jendela challenge bisa tertutup dalam waktu kurang dari dua menit dan challenge yang kedaluwarsa tampak persis seperti jawaban yang salah. Harapkan ERROR_CAPTCHA_UNSOLVABLE hanya dari Argon2id dan scrypt, yang langsung ditolak alih-alih dicoba. Beralihlah ke mode Server begitu kode pemanggil tidak lagi berbagi mesin dengan pemecah CAPTCHA.
- Apa itu challenge dan bagaimana tipe ini bekerja: halaman pemecah ALTCHA.
- Setiap metode lain yang disediakan paket .NET: halaman pemecah C# dan .NET.
Satu hal terakhir yang mengubah cara Anda merancang percobaan ulang. Karena sebuah pemecah captcha lokal menghitung proof of work di mesin yang sudah Anda miliki, mencoba ulang challenge yang kedaluwarsa hanya memakan beberapa milidetik CPU Anda sendiri dan tidak lebih, jadi Anda leluasa mengambil challenge baru daripada merawat challenge yang sudah basi.
