├── .gitattributes ├── .gitignore ├── CloudflareSolverRe.sln ├── LICENSE ├── README.md ├── appveyor.yml ├── sample └── CloudflareSolverRe.Sample │ ├── ClearanceHandlerSample.cs │ ├── CloudflareSolverRe.Sample.csproj │ ├── CloudflareSolverSample.cs │ ├── IntegrationSample.cs │ └── Program.cs ├── src └── CloudflareSolverRe │ ├── ClearanceHandler.cs │ ├── CloudflareDetector.cs │ ├── CloudflareHandler.cs │ ├── CloudflareSolver.cs │ ├── CloudflareSolverRe.csproj │ ├── Constants │ ├── Errors.cs │ ├── General.cs │ ├── HttpHeaderValues.cs │ ├── HttpHeaders.cs │ └── UserAgents.cs │ ├── Exceptions │ └── CloudflareClearanceException.cs │ ├── Extensions │ ├── CookieContainerExtensions.cs │ ├── CookieExtensions.cs │ ├── HttpClientExtensions.cs │ ├── HttpMessageHandlerExtensions.cs │ ├── IEnumerableExtensions.cs │ └── UriExtensions.cs │ ├── Solvers │ ├── CaptchaChallengeSolver.cs │ ├── ChallengeSolver.cs │ └── JsChallengeSolver.cs │ ├── Types │ ├── Captcha │ │ ├── CaptchaChallenge.cs │ │ ├── CaptchaChallengeSolution.cs │ │ └── CaptchaSolveResult.cs │ ├── CloudflareProtection.cs │ ├── DetectResult.cs │ ├── ICloudflareSolver.cs │ ├── Javascript │ │ ├── JsChallenge.cs │ │ ├── JsChallengeSolution.cs │ │ └── JsForm.cs │ ├── SessionCookies.cs │ └── SolveResult.cs │ ├── Utilities │ ├── SemaphoreLocker.cs │ └── Utils.cs │ └── build.bat └── test └── CloudflareSolverRe.Tests ├── ClearanceHandlerTests.cs ├── CloudflareSolverRe.Tests.csproj ├── CloudflareSolverTests.cs ├── IntegrationTests.cs ├── JsChallengeTests.cs ├── Settings.cs └── resources └── js_challenge.html /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # Benchmark Results 50 | BenchmarkDotNet.Artifacts/ 51 | 52 | # .NET Core 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_h.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *_wpftmp.csproj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # Visual Studio Trace Files 110 | *.e2e 111 | 112 | # TFS 2012 Local Workspace 113 | $tf/ 114 | 115 | # Guidance Automation Toolkit 116 | *.gpState 117 | 118 | # ReSharper is a .NET coding add-in 119 | _ReSharper*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | 123 | # JustCode is a .NET coding add-in 124 | .JustCode 125 | 126 | # TeamCity is a build add-in 127 | _TeamCity* 128 | 129 | # DotCover is a Code Coverage Tool 130 | *.dotCover 131 | 132 | # AxoCover is a Code Coverage Tool 133 | .axoCover/* 134 | !.axoCover/settings.json 135 | 136 | # Visual Studio code coverage results 137 | *.coverage 138 | *.coveragexml 139 | 140 | # NCrunch 141 | _NCrunch_* 142 | .*crunch*.local.xml 143 | nCrunchTemp_* 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | # Note: Comment the next line if you want to checkin your web deploy settings, 172 | # but database connection strings (with potential passwords) will be unencrypted 173 | *.pubxml 174 | *.publishproj 175 | 176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 177 | # checkin your Azure Web App publish settings, but sensitive information contained 178 | # in these scripts will be unencrypted 179 | PublishScripts/ 180 | 181 | # NuGet Packages 182 | *.nupkg 183 | # The packages folder can be ignored because of Package Restore 184 | **/[Pp]ackages/* 185 | # except build/, which is used as an MSBuild target. 186 | !**/[Pp]ackages/build/ 187 | # Uncomment if necessary however generally it will be regenerated when needed 188 | #!**/[Pp]ackages/repositories.config 189 | # NuGet v3's project.json files produces more ignorable files 190 | *.nuget.props 191 | *.nuget.targets 192 | 193 | # Microsoft Azure Build Output 194 | csx/ 195 | *.build.csdef 196 | 197 | # Microsoft Azure Emulator 198 | ecf/ 199 | rcf/ 200 | 201 | # Windows Store app package directories and files 202 | AppPackages/ 203 | BundleArtifacts/ 204 | Package.StoreAssociation.xml 205 | _pkginfo.txt 206 | *.appx 207 | 208 | # Visual Studio cache files 209 | # files ending in .cache can be ignored 210 | *.[Cc]ache 211 | # but keep track of directories ending in .cache 212 | !*.[Cc]ache/ 213 | 214 | # Others 215 | ClientBin/ 216 | ~$* 217 | *~ 218 | *.dbmdl 219 | *.dbproj.schemaview 220 | *.jfm 221 | *.pfx 222 | *.publishsettings 223 | orleans.codegen.cs 224 | 225 | # Including strong name files can present a security risk 226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 227 | #*.snk 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | ServiceFabricBackup/ 244 | *.rptproj.bak 245 | 246 | # SQL Server files 247 | *.mdf 248 | *.ldf 249 | *.ndf 250 | 251 | # Business Intelligence projects 252 | *.rdl.data 253 | *.bim.layout 254 | *.bim_*.settings 255 | *.rptproj.rsuser 256 | 257 | # Microsoft Fakes 258 | FakesAssemblies/ 259 | 260 | # GhostDoc plugin setting file 261 | *.GhostDoc.xml 262 | 263 | # Node.js Tools for Visual Studio 264 | .ntvs_analysis.dat 265 | node_modules/ 266 | 267 | # Visual Studio 6 build log 268 | *.plg 269 | 270 | # Visual Studio 6 workspace options file 271 | *.opt 272 | 273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 274 | *.vbw 275 | 276 | # Visual Studio LightSwitch build output 277 | **/*.HTMLClient/GeneratedArtifacts 278 | **/*.DesktopClient/GeneratedArtifacts 279 | **/*.DesktopClient/ModelManifest.xml 280 | **/*.Server/GeneratedArtifacts 281 | **/*.Server/ModelManifest.xml 282 | _Pvt_Extensions 283 | 284 | # Paket dependency manager 285 | .paket/paket.exe 286 | paket-files/ 287 | 288 | # FAKE - F# Make 289 | .fake/ 290 | 291 | # JetBrains Rider 292 | .idea/ 293 | *.sln.iml 294 | 295 | # CodeRush personal settings 296 | .cr/personal 297 | 298 | # Python Tools for Visual Studio (PTVS) 299 | __pycache__/ 300 | *.pyc 301 | 302 | # Cake - Uncomment if you are using it 303 | # tools/** 304 | # !tools/packages.config 305 | 306 | # Tabs Studio 307 | *.tss 308 | 309 | # Telerik's JustMock configuration file 310 | *.jmconfig 311 | 312 | # BizTalk build output 313 | *.btp.cs 314 | *.btm.cs 315 | *.odx.cs 316 | *.xsd.cs 317 | 318 | # OpenCover UI analysis results 319 | OpenCover/ 320 | 321 | # Azure Stream Analytics local run output 322 | ASALocalRun/ 323 | 324 | # MSBuild Binary and Structured Log 325 | *.binlog 326 | 327 | # NVidia Nsight GPU debugger configuration file 328 | *.nvuser 329 | 330 | # MFractors (Xamarin productivity tool) working folder 331 | .mfractor/ 332 | 333 | # Local History for Visual Studio 334 | .localhistory/ 335 | -------------------------------------------------------------------------------- /CloudflareSolverRe.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudflareSolverRe", "src\CloudflareSolverRe\CloudflareSolverRe.csproj", "{E541E27A-8D55-4E2F-AC7D-DCA0DCDAC220}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudflareSolverRe.Sample", "sample\CloudflareSolverRe.Sample\CloudflareSolverRe.Sample.csproj", "{F44FEFA6-B85B-4C05-AD34-836DF6BF63B9}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudflareSolverRe.Tests", "test\CloudflareSolverRe.Tests\CloudflareSolverRe.Tests.csproj", "{89A9D8CB-01BA-43CA-83AE-2D760088154C}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {E541E27A-8D55-4E2F-AC7D-DCA0DCDAC220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {E541E27A-8D55-4E2F-AC7D-DCA0DCDAC220}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {E541E27A-8D55-4E2F-AC7D-DCA0DCDAC220}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {E541E27A-8D55-4E2F-AC7D-DCA0DCDAC220}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {F44FEFA6-B85B-4C05-AD34-836DF6BF63B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {F44FEFA6-B85B-4C05-AD34-836DF6BF63B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {F44FEFA6-B85B-4C05-AD34-836DF6BF63B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {F44FEFA6-B85B-4C05-AD34-836DF6BF63B9}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {89A9D8CB-01BA-43CA-83AE-2D760088154C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {89A9D8CB-01BA-43CA-83AE-2D760088154C}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {89A9D8CB-01BA-43CA-83AE-2D760088154C}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {89A9D8CB-01BA-43CA-83AE-2D760088154C}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {9327E4CA-EFBC-4D17-85AB-80D58BD9EF88} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kamil Monicz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CloudflareSolverRe 2 | ================== 3 | [![AppVeyor](https://img.shields.io/appveyor/ci/RyuzakiH/CloudflareSolverRe/master.svg?maxAge=60)](https://ci.appveyor.com/project/RyuzakiH/CloudflareSolverRe) 4 | [![NuGet](https://img.shields.io/nuget/v/CloudflareSolverRe.svg?maxAge=60)](https://www.nuget.org/packages/CloudflareSolverRe) 5 | [![NuGet](https://img.shields.io/nuget/v/CloudflareSolverRe.Captcha.svg?maxAge=60)](https://www.nuget.org/packages/CloudflareSolverRe.Captcha) 6 | 7 | Cloudflare Javascript & reCaptcha challenge (I'm Under Attack Mode or IUAM) solving/bypass .NET Standard library. 8 | 9 | _Reawakening of [CloudflareSolver](https://www.nuget.org/packages/CloudflareSolver) (removed) adding the capabilities ([DelegatingHandler](https://msdn.microsoft.com/en-us/library/system.net.http.delegatinghandler(v=vs.110).aspx)) of [CloudFlareUtilities](https://github.com/elcattivo/CloudFlareUtilities) (not working)._ 10 | 11 | This can be useful if you wish to scrape or crawl a website protected with Cloudflare. Cloudflare's IUAM page currently just checks if the client supports JavaScript or requires solving captcha challenge. Fortunately, this library supports both. 12 | 13 | For reference, this is the default message Cloudflare uses for these sorts of pages: 14 | 15 | ``` 16 | Checking your browser before accessing website.com. 17 | 18 | This process is automatic. Your browser will redirect to your requested content shortly. 19 | 20 | Please allow up to 5 seconds... 21 | ``` 22 | 23 | The first visit to any site with Cloudflare IUAM enabled will sleep for 4 to 5 seconds until the challenge is solved, though no delay will occur after the first request. 24 | 25 | # Installation 26 | Full-Featured library: 27 | 28 | `PM> Install-Package CloudflareSolverRe.Captcha` 29 | 30 | Or get just javascript challenge solver without the captcha features: 31 | 32 | `PM> Install-Package CloudflareSolverRe` 33 | 34 | # Dependencies 35 | - No dependencies (no javaScript interpreter required) 36 | - [.NET Standard 1.1](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard1.1.md) 37 | 38 | In case you need to use captcha solvers: 39 | #### CloudflareSolverRe.Captcha 40 | - [2CaptchaAPI](https://www.nuget.org/packages/2CaptchaAPI/) 41 | - [AntiCaptchaAPI](https://www.nuget.org/packages/AntiCaptchaAPI/) 42 | 43 | If you want to use another captcha provider, see [How to implement a captcha provider?](#implement-a-captcha-provider) 44 | 45 | # Issues 46 | Cloudflare regularly modifies their IUAM protection challenge and improves their bot detection capabilities. 47 | 48 | If you notice that the anti-bot page has changed, or if library suddenly stops working, please create a GitHub issue so that I can update the code accordingly. 49 | 50 | Before submitting an issue, just be sure that you have the latest version of the library. 51 | 52 | # Usage 53 | 54 | - ### ClearanceHandler 55 | 56 | A [DelegatingHandler](https://docs.microsoft.com/en-us/dotnet/api/system.net.http.delegatinghandler?view=netstandard-1.1) that 57 | handles the challenge solution automatically. 58 | 59 | > A type for HTTP handlers that delegate the processing of HTTP response messages to another handler, called the inner handler. 60 | 61 | It checks on every request if the clearance is required or not, if required, it solves the challenge in background then returns the response. 62 | 63 | Websites not using Cloudflare will be treated normally. You don't need to configure or call anything further, and you can effectively treat all websites as if they're not protected with anything. 64 | 65 | ```csharp 66 | var target = new Uri("https://uam.hitmehard.fun/HIT"); 67 | 68 | var handler = new ClearanceHandler 69 | { 70 | MaxTries = 3, 71 | ClearanceDelay = 3000 72 | }; 73 | 74 | var client = new HttpClient(handler); 75 | 76 | var content = client.GetStringAsync(target).Result; 77 | Console.WriteLine(content); 78 | ``` 79 | 80 | 81 | - ### CloudflareSolver 82 | The internal challenge solver, that's what happens inside the ClearanceHandler, you can use it directly. 83 | 84 | Use it when you already have a HttpClient and you want to solve the challenge manually for some specific website so you can scrape it freely. 85 | 86 | ```csharp 87 | var target = new Uri("https://uam.hitmehard.fun/HIT"); 88 | 89 | var cf = new CloudflareSolver 90 | { 91 | MaxTries = 3, 92 | ClearanceDelay = 3000 93 | }; 94 | 95 | var handler = new HttpClientHandler(); 96 | var client = new HttpClient(handler); 97 | 98 | var result = cf.Solve(client, handler, target).Result; 99 | 100 | if (!result.Success) 101 | { 102 | Console.WriteLine($"[Failed] Details: {result.FailReason}"); 103 | return; 104 | } 105 | 106 | // Once the protection has been bypassed we can use that HttpClient to send the requests as usual 107 | var content = client.GetStringAsync(target).Result; 108 | Console.WriteLine($"Server response: {content}"); 109 | ``` 110 | 111 | **Full Samples [Here](https://github.com/RyuzakiH/CloudflareSolverRe/tree/master/sample/CloudflareSolverRe.Sample)** 112 | 113 | # Options 114 | ### Message Handlers 115 | To use a message handler ([HttpClientHandler](https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler), [SocketsHttpHandler](https://docs.microsoft.com/en-us/dotnet/api/system.net.http.socketshttphandler), etc.) with ClearanceHandler, provide it as an inner handler. 116 | 117 | To provide an inner handler, there are two ways 118 | 119 | 1. By setting the InnerHandler property of the ClearanceHandler: 120 | 121 | ```csharp 122 | var handler = new ClearanceHandler 123 | { 124 | InnerHandler = new HttpClientHandler 125 | { 126 | CookieContainer = cookieContainer, 127 | AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, 128 | Proxy = proxy 129 | }, 130 | MaxTries = 5, 131 | ClearanceDelay = 3000 132 | }; 133 | ``` 134 | 135 | 2. By passing the inner handler to the constructor of the ClearanceHandler. 136 | 137 | ```csharp 138 | var httpHandler = new HttpClientHandler 139 | { 140 | CookieContainer = cookieContainer, 141 | AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, 142 | Proxy = proxy 143 | }; 144 | 145 | var handler = new ClearanceHandler(httpHandler) 146 | { 147 | MaxTries = 5, 148 | ClearanceDelay = 3000 149 | }; 150 | ``` 151 | 152 | ### Maximum Tries 153 | The maximum number of challenge solving tries. Most of the time the minimum tries required are 2 or 3, so default value is 3. 154 | If you would like to override this number to make sure it always succeed, change MaxTries property. 155 | 156 | ```csharp 157 | var handler = new ClearanceHandler 158 | { 159 | MaxTries = 10 160 | }; 161 | ``` 162 | ```csharp 163 | var cf = new CloudflareSolver 164 | { 165 | MaxTries = 10 166 | }; 167 | ``` 168 | 169 | Cloudflare challenges are not always javascript challenges, it may be reCaptcha challenges and this library also provides a way to solve these challenges using captcha solvers (2captcha, anti-captcha, etc.). 170 | 171 | So, there's MaxCaptchaTries property to set the max number of captcha challenge solving tries (default is 1). 172 | 173 | ```csharp 174 | handler.MaxCaptchaTries = 2; 175 | ``` 176 | ```csharp 177 | cf.MaxCaptchaTries = 2; 178 | ``` 179 | 180 | ### Delays 181 | Normally, when a browser is faced with a Cloudflare IUAM challenge page, Cloudflare requires the browser to wait 4 seconds (default delay) before submitting the challenge answer. If you would like to override this delay, change ClearanceDelay property. 182 | 183 | ```csharp 184 | var handler = new ClearanceHandler 185 | { 186 | ClearanceDelay = 5000 187 | }; 188 | ``` 189 | ```csharp 190 | var cf = new CloudflareSolver 191 | { 192 | ClearanceDelay = 5000 193 | }; 194 | ``` 195 | 196 | ### User-Agent 197 | **User-Agent must be the same as the one used to solve the challenge, otherwise Cloudflare will flag you as a bot.** 198 | 199 | You can set the user-agent of ClearanceHandler once and it will be used along with the handler. 200 | All requests made using that handler will have this user-agent, cannot be changed even if you set the user-agent header explicitly. 201 | ```csharp 202 | var handler = new ClearanceHandler( 203 | userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"); 204 | ``` 205 | If you didn't set it, a random user-agent will be generated, also cannot be changed even if you set the user-agent header explicitly. 206 | 207 | Also, you can set the user-agent of CloudflareSolver once and it will be used to solve every challenge. 208 | But unlike the ClearanceHandler, this user-agent can be changed to a specific value or set to random. 209 | ```csharp 210 | var cf = new CloudflareSolver( 211 | userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"); 212 | ``` 213 | To use a random user-agent set randomUserAgent to true when calling Solve method. 214 | ```csharp 215 | var result = await cf.Solve(client, handler, target, randomUserAgent: true); 216 | ``` 217 | To use a specific user-agent set userAgent. 218 | ```csharp 219 | var result = await cf.Solve(client, handler, target, userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"); 220 | ``` 221 | 222 | 223 | # Integration 224 | It's easy to integrate CloudflareSolverRe with other applications and tools. Cloudflare uses two cookies as tokens: 225 | 226 | 1. [\__cfuid](https://support.cloudflare.com/hc/en-us/articles/200170156-What-does-the-Cloudflare-cfduid-cookie-do-) 227 | > The \__cfduid cookie is used to identify individual clients behind a shared IP address and apply security settings on a per-client basis. 228 | 229 | 2. [cf_clearance](https://blog.cloudflare.com/cloudflare-supports-privacy-pass/) 230 | > Clearance cookies are like authentication cookies, but instead of being tied to an identity, they are tied to the fact that you solved a challenge sometime in the past. 231 | 232 | To bypass the challenge page, simply include both of these cookies (with the appropriate user-agent) in all HTTP requests you make. 233 | 234 | **To retrieve working cookies and a user-agent for a specific website:** 235 | 236 | ```csharp 237 | var target = new Uri("https://uam.hitmehard.fun/HIT"); 238 | 239 | var cf = new CloudflareSolver 240 | { 241 | MaxTries = 3, 242 | ClearanceDelay = 3000 243 | }; 244 | 245 | var result = await cf.Solve(target); 246 | ``` 247 | 248 | Can be used with a proxy: 249 | ```csharp 250 | IWebProxy proxy = new WebProxy("51.83.15.1:8080"); 251 | 252 | var result = await cf.Solve(target, proxy); 253 | ``` 254 | 255 | User-agent can be set to 256 | 1. random: 257 | ```csharp 258 | var result = await cf.Solve(target, randomUserAgent: true); 259 | ``` 260 | 2. specific value 261 | ```csharp 262 | var result = await cf.Solve(target, userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"); 263 | ``` 264 | 265 | CancellationToken can be passed to control solving operation cancelling: 266 | ```csharp 267 | var cancellationToken = new CancellationTokenSource(); 268 | 269 | var result = await cf.Solve(target, cancellationToken: cancellationToken.Token); 270 | ``` 271 | 272 | Returns [SolveResult](https://github.com/RyuzakiH/CloudflareSolverRe/blob/master/src/CloudflareSolverRe/Types/SolveResult.cs) struct: 273 | ```csharp 274 | public struct SolveResult 275 | { 276 | public bool Success; // indicates that challenge solve process succeeded 277 | public string FailReason // indicates clearance fail reason, if failed 278 | public DetectResult DetectResult; // indicates the protection type found (js, captcha, banned, unknown, no-protection) 279 | public string UserAgent; // the user-agent used to solve the challenge (must be used during the session) 280 | public SessionCookies Cookies; // the two tokens/cookies to use during the session indicating that we passed the challenge. 281 | } 282 | ``` 283 | 284 | Session cookies can be retrieved as 285 | ```csharp 286 | var cookiesHeaderValue = result.Cookies.AsHeaderString(); // __cfduid=anyvalue;cf_clearance=anyvalue; 287 | var cookieCollection = result.Cookies.AsCookieCollection(); 288 | var cookieContainer = result.Cookies.AsCookieContainer(); 289 | ``` 290 | 291 | Remember, you must always use the same user-agent when retrieving or using these cookies. 292 | 293 | ### WebClient 294 | Here is an example of integrating CloudflareSolver with WebClient. As you can see, all you have to do is pass the cookies and user-agent to the webclient headers. 295 | 296 | ```csharp 297 | var target = new Uri("https://uam.hitmehard.fun/HIT"); 298 | 299 | var cf = new CloudflareSolver 300 | { 301 | MaxTries = 3, 302 | ClearanceDelay = 3000 303 | }; 304 | 305 | var result = cf.Solve(target).Result; 306 | 307 | if (!result.Success) 308 | { 309 | Console.WriteLine($"[Failed] Details: {result.FailReason}"); 310 | return; 311 | } 312 | 313 | var client = new WebClient(); 314 | client.Headers.Add(HttpRequestHeader.Cookie, result.Cookies.AsHeaderString()); 315 | client.Headers.Add(HttpRequestHeader.UserAgent, result.UserAgent); 316 | 317 | var content = client.DownloadString(target); 318 | Console.WriteLine($"Server response: {content}"); 319 | ``` 320 | 321 | ### HttpWebRequest 322 | Here is an example of integrating CloudflareSolver with HttpWebRequest. As you can see, all you have to do is pass the cookies and user-agent to the HttpWebRequest headers. 323 | 324 | ```csharp 325 | var target = new Uri("https://uam.hitmehard.fun/HIT"); 326 | 327 | var cf = new CloudflareSolver 328 | { 329 | MaxTries = 3, 330 | ClearanceDelay = 3000 331 | }; 332 | 333 | var result = cf.Solve(target).Result; 334 | 335 | if (!result.Success) 336 | { 337 | Console.WriteLine($"[Failed] Details: {result.FailReason}"); 338 | return; 339 | } 340 | 341 | var request = (HttpWebRequest)WebRequest.Create(target); 342 | request.Headers.Add(HttpRequestHeader.Cookie, result.Cookies.AsHeaderString()); 343 | request.Headers.Add(HttpRequestHeader.UserAgent, result.UserAgent); 344 | 345 | var response = (HttpWebResponse)request.GetResponse(); 346 | var content = new StreamReader(response.GetResponseStream()).ReadToEnd(); 347 | Console.WriteLine($"Server response: {content}"); 348 | ``` 349 | 350 | 351 | # Captcha 352 | To use captcha solving capabilities, you can install [CloudflareSolverRe.Captcha](https://www.nuget.org/packages/CloudflareSolverRe.Captcha) package which supports the following captcha providers. 353 | 354 | - [Anti-Captcha](https://anti-captcha.com) 355 | - [2captcha](https://2captcha.com) 356 | 357 | _If you want to use another captcha solver, see [How to implement a captcha provider?](#implement-a-captcha-provider)_ 358 | 359 | - ### ClearanceHandler 360 | 361 | ```csharp 362 | var handler = new ClearanceHandler(new TwoCaptchaProvider("YOUR_API_KEY")) 363 | { 364 | MaxTries = 3, 365 | MaxCaptchaTries = 2 366 | }; 367 | ``` 368 | 369 | - ### CloudflareSolver 370 | 371 | ```csharp 372 | var cf = new CloudflareSolver(new TwoCaptchaProvider("YOUR_API_KEY")) 373 | { 374 | MaxTries = 3, 375 | MaxCaptchaTries = 1 376 | }; 377 | ``` 378 | 379 | 380 | # Implement a Captcha Provider 381 | Implement [ICaptchaProvider](https://github.com/RyuzakiH/CloudflareSolverRe/blob/master/src/CloudflareSolverRe/Types/Captcha/ICaptchaProvider.cs) interface. 382 | 383 | ```csharp 384 | public interface ICaptchaProvider 385 | { 386 | string Name { get; } 387 | 388 | Task SolveCaptcha(string siteKey, string webUrl); 389 | } 390 | ``` 391 | 392 | Example [AntiCaptchaProvider](https://github.com/RyuzakiH/CloudflareSolverRe/blob/master/src/CloudflareSolverRe.Captcha/AntiCaptchaProvider.cs) 393 | 394 | ```csharp 395 | public class AntiCaptchaProvider : ICaptchaProvider 396 | { 397 | public string Name { get; } = "AntiCaptcha"; 398 | 399 | private readonly AntiCaptcha antiCaptcha; 400 | 401 | public AntiCaptchaProvider(string apiKey) => antiCaptcha = new AntiCaptcha(apiKey); 402 | 403 | public async Task SolveCaptcha(string siteKey, string webUrl) 404 | { 405 | var result = await antiCaptcha.SolveReCaptchaV2(siteKey, webUrl); 406 | 407 | return new CaptchaSolveResult 408 | { 409 | Success = result.Success, 410 | Response = result.Response, 411 | }; 412 | } 413 | } 414 | ``` 415 | 416 | # Tested Sites 417 | - [uam.hitmehard.fun](https://uam.hitmehard.fun/HIT) 418 | - [hdmovie8.com](https://hdmovie8.com) 419 | - [japscan.to](https://www.japscan.to) 420 | - [spacetorrent.cloud](https://www.spacetorrent.cloud) 421 | - [codepen.io](https://codepen.io) (captcha challenges only - js challenges not allowed) 422 | - [temp-mail](https://temp-mail.org) (not always using cloudflare) 423 | - [hidemy.name](https://hidemy.name/en/proxy-list/) 424 | - [speed.cd](https://speed.cd) 425 | - [gktorrent.biz](https://www.gktorrent.biz/) 426 | - [humanweb.fr](https://www.humanweb.fr/) 427 | - [steamdb.info](https://steamdb.info/) 428 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: Visual Studio 2017 3 | before_build: 4 | - dotnet restore 5 | build_script: 6 | - dotnet build /verbosity:quiet "CloudflareSolverRe.sln" 7 | test_script: 8 | - dotnet test --no-build .\test\CloudflareSolverRe.Tests\CloudflareSolverRe.Tests.csproj 9 | -------------------------------------------------------------------------------- /sample/CloudflareSolverRe.Sample/ClearanceHandlerSample.cs: -------------------------------------------------------------------------------- 1 | using CaptchaSharp; 2 | using CaptchaSharp.Services; 3 | using CloudflareSolverRe; 4 | using System; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | 8 | namespace CloudflareSolverRe.Sample 9 | { 10 | public class ClearanceHandlerSample 11 | { 12 | private static readonly Uri target = new Uri("https://uam.hitmehard.fun/HIT"); 13 | 14 | public async static Task Sample() 15 | { 16 | var handler = new ClearanceHandler 17 | { 18 | MaxTries = 3, 19 | ClearanceDelay = 3000 20 | }; 21 | 22 | var client = new HttpClient(handler); 23 | 24 | var content = await client.GetStringAsync(target); 25 | Console.WriteLine(content); 26 | } 27 | 28 | public async static void Sample_2Captcha() 29 | { 30 | var handler = new ClearanceHandler(new TwoCaptchaService("YOUR_API_KEY")) 31 | { 32 | MaxTries = 5, 33 | MaxCaptchaTries = 2 34 | }; 35 | 36 | var client = new HttpClient(handler); 37 | 38 | // You can use the HttpClient to send requests as usual, any challenge will be solved automatically 39 | var content = await client.GetStringAsync(target); 40 | Console.WriteLine(content); 41 | } 42 | 43 | public async static Task Sample_AntiCaptcha() 44 | { 45 | var handler = new ClearanceHandler(new AntiCaptchaService("YOUR_API_KEY")) 46 | { 47 | MaxTries = 5, 48 | MaxCaptchaTries = 2 49 | }; 50 | 51 | var client = new HttpClient(handler); 52 | 53 | // You can use the HttpClient to send requests as usual, any challenge will be solved automatically 54 | var content = await client.GetStringAsync(target); 55 | Console.WriteLine(content); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /sample/CloudflareSolverRe.Sample/CloudflareSolverRe.Sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | CloudflareSolverRe.Sample 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /sample/CloudflareSolverRe.Sample/CloudflareSolverSample.cs: -------------------------------------------------------------------------------- 1 | using CaptchaSharp; 2 | using CaptchaSharp.Services; 3 | using System; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | 7 | namespace CloudflareSolverRe.Sample 8 | { 9 | public class CloudflareSolverSample 10 | { 11 | private static readonly Uri target = new Uri("https://uam.hitmehard.fun/HIT"); 12 | 13 | public static async Task Sample() 14 | { 15 | var cf = new CloudflareSolver 16 | { 17 | MaxTries = 3, 18 | ClearanceDelay = 3000 19 | }; 20 | 21 | var handler = new HttpClientHandler(); 22 | var client = new HttpClient(handler); 23 | 24 | var result = await cf.Solve(client, handler, target); 25 | 26 | if (!result.Success) 27 | { 28 | Console.WriteLine($"[Failed] Details: {result.FailReason}"); 29 | return; 30 | } 31 | 32 | // Once the protection has been bypassed we can use that HttpClient to send the requests as usual 33 | var content = await client.GetStringAsync(target); 34 | Console.WriteLine($"Server response: {content}"); 35 | } 36 | 37 | public async static Task Sample_2Captcha() 38 | { 39 | var cf = new CloudflareSolver(new TwoCaptchaService("YOUR_API_KEY")) 40 | { 41 | MaxTries = 5, 42 | MaxCaptchaTries = 2 43 | }; 44 | 45 | var handler = new HttpClientHandler(); 46 | var client = new HttpClient(handler); 47 | 48 | var result = await cf.Solve(client, handler, target); 49 | 50 | if (!result.Success) 51 | { 52 | Console.WriteLine($"[Failed] Details: {result.FailReason}"); 53 | return; 54 | } 55 | 56 | // Once the protection has been bypassed we can use that httpClient to send the requests as usual 57 | var content = await client.GetStringAsync(target); 58 | Console.WriteLine($"Server response: {content}"); 59 | } 60 | 61 | public async static Task Sample_AntiCaptcha() 62 | { 63 | var cf = new CloudflareSolver(new AntiCaptchaService("YOUR_API_KEY")) 64 | { 65 | MaxTries = 5, 66 | MaxCaptchaTries = 2 67 | }; 68 | 69 | var handler = new HttpClientHandler(); 70 | var client = new HttpClient(handler); 71 | 72 | var result = await cf.Solve(client, handler, target); 73 | 74 | if (!result.Success) 75 | { 76 | Console.WriteLine($"[Failed] Details: {result.FailReason}"); 77 | return; 78 | } 79 | 80 | // Once the protection has been bypassed we can use that httpClient to send the requests as usual 81 | var content = await client.GetStringAsync(target); 82 | Console.WriteLine($"Server response: {content}"); 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /sample/CloudflareSolverRe.Sample/IntegrationSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Threading.Tasks; 5 | 6 | namespace CloudflareSolverRe.Sample 7 | { 8 | public class IntegrationSample 9 | { 10 | private static readonly Uri target = new Uri("https://uam.hitmehard.fun/HIT"); 11 | 12 | public static async Task WebClientSample() 13 | { 14 | var cf = new CloudflareSolver 15 | { 16 | MaxTries = 3, 17 | ClearanceDelay = 3000 18 | }; 19 | 20 | var result = await cf.Solve(target); 21 | 22 | if (!result.Success) 23 | { 24 | Console.WriteLine($"[Failed] Details: {result.FailReason}"); 25 | return; 26 | } 27 | 28 | // Add session cookies, user-agent and proxy (if used) to the WebClient headers 29 | var client = new WebClient(); 30 | client.Headers.Add(HttpRequestHeader.Cookie, result.Cookies.AsHeaderString()); 31 | client.Headers.Add(HttpRequestHeader.UserAgent, result.UserAgent); 32 | 33 | // Once the protection has been bypassed we can use that WebClient to send the requests as usual 34 | var content = await client.DownloadStringTaskAsync(target); 35 | Console.WriteLine($"Server response: {content}"); 36 | } 37 | 38 | public static async Task HttpWebRequestSample() 39 | { 40 | var cf = new CloudflareSolver 41 | { 42 | MaxTries = 3, 43 | ClearanceDelay = 3000 44 | }; 45 | 46 | var result = await cf.Solve(target); 47 | 48 | if (!result.Success) 49 | { 50 | Console.WriteLine($"[Failed] Details: {result.FailReason}"); 51 | return; 52 | } 53 | 54 | // Add session cookies, user-agent and proxy (if used) to the HttpWebRequest headers 55 | var request = (HttpWebRequest)WebRequest.Create(target); 56 | request.Headers.Add(HttpRequestHeader.Cookie, result.Cookies.AsHeaderString()); 57 | request.Headers.Add(HttpRequestHeader.UserAgent, result.UserAgent); 58 | 59 | // Once the protection has been bypassed we can use that HttpWebRequest to send the requests as usual 60 | var response = (HttpWebResponse)await request.GetResponseAsync(); 61 | var content = await new StreamReader(response.GetResponseStream()).ReadToEndAsync(); 62 | Console.WriteLine($"Server response: {content}"); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /sample/CloudflareSolverRe.Sample/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace CloudflareSolverRe.Sample 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | ClearanceHandlerSample.Sample().Wait(); 10 | 11 | Task.Delay(5000).Wait(); 12 | 13 | CloudflareSolverSample.Sample().Wait(); 14 | 15 | Task.Delay(5000).Wait(); 16 | 17 | IntegrationSample.WebClientSample().Wait(); 18 | 19 | Task.Delay(5000).Wait(); 20 | 21 | IntegrationSample.HttpWebRequestSample().Wait(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/ClearanceHandler.cs: -------------------------------------------------------------------------------- 1 | using CaptchaSharp; 2 | using CloudflareSolverRe.Constants; 3 | using CloudflareSolverRe.Exceptions; 4 | using CloudflareSolverRe.Extensions; 5 | using CloudflareSolverRe.Types; 6 | using CloudflareSolverRe.Utilities; 7 | using System.Net; 8 | using System.Net.Http; 9 | using System.Runtime.InteropServices; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace CloudflareSolverRe 14 | { 15 | /// 16 | /// A HTTP handler that transparently manages Cloudflare's protection bypass. 17 | /// 18 | public class ClearanceHandler : DelegatingHandler, ICloudflareSolver 19 | { 20 | private readonly CookieContainer _cookies; 21 | private readonly HttpClient _client; 22 | private readonly HttpClientHandler _handler; 23 | private readonly CloudflareSolver _cloudflareSolver; 24 | private readonly string userAgent; 25 | 26 | /// 27 | /// Gets or sets the number of clearance retries, if clearance fails. 28 | /// 29 | /// A negative value causes an infinite amount of retries. 30 | public int MaxTries 31 | { 32 | get => _cloudflareSolver.MaxTries; 33 | set => _cloudflareSolver.MaxTries = value; 34 | } 35 | 36 | /// 37 | /// Gets or sets the max number of captcha clearance tries. 38 | /// 39 | public int MaxCaptchaTries 40 | { 41 | get => _cloudflareSolver.MaxCaptchaTries; 42 | set => _cloudflareSolver.MaxCaptchaTries = value; 43 | } 44 | 45 | /// 46 | /// Gets or sets the number of milliseconds to wait before sending the clearance request. 47 | /// 48 | /// 49 | /// Negative value or zero means to wait the delay time required by the challenge (like a browser). 50 | /// 51 | public int ClearanceDelay 52 | { 53 | get => _cloudflareSolver.ClearanceDelay; 54 | set => _cloudflareSolver.ClearanceDelay = value; 55 | } 56 | 57 | private HttpClientHandler HttpClientHandler => InnerHandler.GetMostInnerHandler() as HttpClientHandler; 58 | 59 | 60 | /// 61 | /// Creates a new instance of the class with a as inner handler. 62 | /// 63 | /// The user-agent which will be used accross this session (null means random user-agent). 64 | public ClearanceHandler([Optional]string userAgent) : this(new HttpClientHandler(), userAgent) { } 65 | 66 | /// 67 | /// Creates a new instance of the class with a specific inner handler. 68 | /// 69 | /// The inner handler which is responsible for processing the HTTP response messages. 70 | /// The user-agent which will be used accross this session (null means random user-agent). 71 | public ClearanceHandler(HttpMessageHandler innerHandler, [Optional]string userAgent) : this(innerHandler, null, userAgent) { } 72 | 73 | /// 74 | /// Creates a new instance of the class with a captcha provider. 75 | /// 76 | /// The captcha provider which is responsible for solving captcha challenges. 77 | /// The user-agent which will be used accross this session (null means random user-agent). 78 | public ClearanceHandler(CaptchaService captchaProvider, [Optional]string userAgent) : this(new HttpClientHandler(), captchaProvider, userAgent) { } 79 | 80 | /// 81 | /// Creates a new instance of the class with a specific inner handler and a captcha provider. 82 | /// 83 | /// The inner handler which is responsible for processing the HTTP response messages. 84 | /// The captcha provider which is responsible for solving captcha challenges. 85 | /// The user-agent which will be used accross this session (null means random user-agent). 86 | public ClearanceHandler(HttpMessageHandler innerHandler, CaptchaService captchaProvider, [Optional]string userAgent) : base(innerHandler) 87 | { 88 | _client = new HttpClient(_handler = new HttpClientHandler 89 | { 90 | AllowAutoRedirect = false, 91 | AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, 92 | CookieContainer = _cookies = new CookieContainer() 93 | }); 94 | 95 | this.userAgent = userAgent ?? Utils.GetGenerateRandomUserAgent(); 96 | 97 | _cloudflareSolver = new CloudflareSolver(captchaProvider, this.userAgent); 98 | } 99 | 100 | 101 | /// 102 | /// Sends an HTTP request to the inner handler to send to the server as an asynchronous operation. 103 | /// 104 | /// The HTTP request message to send to the server. 105 | /// A cancellation token to cancel operation. 106 | /// The task object representing the asynchronous operation. 107 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 108 | { 109 | var sessionCookies = SessionCookies.FromCookieContainer(HttpClientHandler.CookieContainer, request.RequestUri); 110 | 111 | var response = await SendRequestAsync(request, cancellationToken); 112 | 113 | var result = default(SolveResult); 114 | 115 | if (CloudflareDetector.IsClearanceRequired(response)) 116 | result = await GetClearance(request, cancellationToken); 117 | 118 | if (result.Success) 119 | { 120 | response = await SendRequestAsync(request, cancellationToken); 121 | InjectSetCookieHeader(response, sessionCookies); 122 | } 123 | 124 | if (!result.Success && CloudflareDetector.IsClearanceRequired(response)) 125 | throw new CloudflareClearanceException(MaxTries); 126 | 127 | return response; 128 | } 129 | 130 | private void EnsureHeaders(HttpRequestMessage request) 131 | { 132 | if (!request.Headers.UserAgent.ToString().Equals(userAgent)) 133 | { 134 | request.Headers.UserAgent.Clear(); 135 | request.Headers.Add(HttpHeaders.UserAgent, userAgent); 136 | } 137 | } 138 | 139 | private void InjectCookies(HttpRequestMessage request) 140 | { 141 | var sessionCookies = SessionCookies.FromCookieContainer(_cookies, request.RequestUri); 142 | 143 | if (!sessionCookies.Valid) 144 | return; 145 | 146 | if (HttpClientHandler.UseCookies) 147 | { 148 | HttpClientHandler.CookieContainer.Add(request.RequestUri, sessionCookies.Cfduid); 149 | HttpClientHandler.CookieContainer.Add(request.RequestUri, sessionCookies.Cf_Clearance); 150 | } 151 | else 152 | { 153 | request.Headers.Add(HttpHeaders.Cookie, sessionCookies.Cfduid.ToHeaderValue()); 154 | request.Headers.Add(HttpHeaders.Cookie, sessionCookies.Cf_Clearance.ToHeaderValue()); 155 | } 156 | } 157 | 158 | private async Task SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) 159 | { 160 | EnsureHeaders(request); 161 | InjectCookies(request); 162 | 163 | return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); 164 | } 165 | 166 | private async Task GetClearance(HttpRequestMessage request, CancellationToken cancellationToken) => 167 | await _cloudflareSolver.Solve(_client, _handler, request.RequestUri, cancellationToken: cancellationToken); 168 | 169 | private void InjectSetCookieHeader(HttpResponseMessage response, SessionCookies oldSessionCookies) 170 | { 171 | var newSessionCookies = SessionCookies.FromCookieContainer(HttpClientHandler.CookieContainer, response.RequestMessage.RequestUri); 172 | 173 | if (oldSessionCookies.Equals(newSessionCookies)) 174 | return; 175 | 176 | // inject set-cookie headers in case the cookies changed 177 | if (newSessionCookies.Cfduid != null && newSessionCookies.Cfduid != oldSessionCookies.Cfduid) 178 | { 179 | response.Headers.Add(HttpHeaders.SetCookie, newSessionCookies.Cfduid.ToHeaderValue()); 180 | } 181 | if (newSessionCookies.Cf_Clearance != null && newSessionCookies.Cf_Clearance != oldSessionCookies.Cf_Clearance) 182 | { 183 | response.Headers.Add(HttpHeaders.SetCookie, newSessionCookies.Cf_Clearance.ToHeaderValue()); 184 | } 185 | } 186 | 187 | 188 | /// 189 | /// Releases the unmanaged resources used by the , and optionally disposes of the managed resources. 190 | /// 191 | /// to release both managed and unmanaged resources; to releases only unmanaged resources. 192 | protected override void Dispose(bool disposing) 193 | { 194 | if (disposing) 195 | _client.Dispose(); 196 | 197 | base.Dispose(disposing); 198 | } 199 | 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/CloudflareDetector.cs: -------------------------------------------------------------------------------- 1 | using CloudflareSolverRe.Constants; 2 | using CloudflareSolverRe.Extensions; 3 | using CloudflareSolverRe.Types; 4 | using CloudflareSolverRe.Utilities; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Net.Http.Headers; 11 | using System.Threading.Tasks; 12 | 13 | namespace CloudflareSolverRe 14 | { 15 | public class CloudflareDetector 16 | { 17 | private static readonly IEnumerable CloudflareServerNames = new[] { "cloudflare", "cloudflare-nginx" }; 18 | 19 | //private static readonly SemaphoreLocker _locker = new SemaphoreLocker(); 20 | 21 | /// 22 | /// Checks if the response declaring that the website is protected by cloudflare. 23 | /// 24 | /// The HttpResponseMessage to check. 25 | public static bool IsCloudflareProtected(HttpResponseMessage response) => 26 | response.Headers.Server 27 | .Any(i => i.Product != null 28 | && CloudflareServerNames.Any(s => string.Compare(s, i.Product.Name, StringComparison.OrdinalIgnoreCase).Equals(0))); 29 | 30 | /// 31 | /// Checks if the response status code is a cloudflare error status code. 32 | /// 33 | /// The HttpResponseMessage to check. 34 | public static bool IsErrorStatusCode(HttpResponseMessage response) => 35 | response.StatusCode.Equals(HttpStatusCode.ServiceUnavailable) || response.StatusCode.Equals(HttpStatusCode.Forbidden); 36 | 37 | /// 38 | /// Checks if cloudflare clearance required. 39 | /// 40 | /// The HttpResponseMessage to check. 41 | public static bool IsClearanceRequired(HttpResponseMessage response) => 42 | IsErrorStatusCode(response) && IsCloudflareProtected(response); 43 | 44 | 45 | /// 46 | /// Detects cloudflare protection type for a specific website. 47 | /// 48 | /// HttpClient to use in detection process. 49 | /// HttpClientHandler of the HttpClient. 50 | /// The uri of the website. 51 | /// Https is required. 52 | public static async Task Detect(HttpClient httpClient, HttpClientHandler httpClientHandler, Uri targetUri, bool requireHttps = false) 53 | { 54 | DetectResult result = default(DetectResult); 55 | 56 | //await _locker.LockAsync(async () => 57 | //{ 58 | var cloudflareHandler = new CloudflareHandler(httpClientHandler); 59 | 60 | result = await Detect(httpClient, cloudflareHandler, targetUri, requireHttps); 61 | 62 | //cloudflareHandler.Dispose(); 63 | //}); 64 | 65 | return result; 66 | } 67 | 68 | /// 69 | /// Detects cloudflare protection type for a specific website. 70 | /// 71 | /// The HttpClient to use in detection process (if its handler is not the cloudflareHandler). 72 | /// The CloudflareHandler of the HttpClient. 73 | /// The uri of the website. 74 | /// Https is required. 75 | internal static async Task Detect(HttpClient httpClient, CloudflareHandler cloudflareHandler, Uri targetUri, bool requireHttps = false) 76 | { 77 | var _httpClient = httpClient.Clone(cloudflareHandler, false); 78 | 79 | if (!requireHttps) 80 | targetUri = targetUri.ForceHttp(); 81 | 82 | var detectResult = await Detect(_httpClient, targetUri); 83 | 84 | if (detectResult.Protection.Equals(CloudflareProtection.Unknown) && !detectResult.SupportsHttp) 85 | { 86 | targetUri = targetUri.ForceHttps(); 87 | detectResult = await Detect(_httpClient, targetUri); 88 | } 89 | 90 | _httpClient.Dispose(); 91 | 92 | return detectResult; 93 | } 94 | 95 | /// 96 | /// Detects cloudflare protection type for a specific website. 97 | /// 98 | /// The HttpClient to use in detection process (must have handler). 99 | /// The uri of the website. 100 | /// Https is required. 101 | internal static async Task Detect(HttpClient httpClient, Uri targetUri, bool requireHttps = false) 102 | { 103 | DetectResult detectResult = default(DetectResult); 104 | 105 | //await _locker.LockAsync(async () => 106 | //{ 107 | if (!requireHttps) 108 | targetUri = targetUri.ForceHttp(); 109 | 110 | detectResult = await Detect(httpClient, targetUri); 111 | 112 | if (detectResult.Protection.Equals(CloudflareProtection.Unknown) && !detectResult.SupportsHttp) 113 | { 114 | targetUri = targetUri.ForceHttps(); 115 | detectResult = await Detect(httpClient, targetUri); 116 | detectResult.SupportsHttp = false; 117 | } 118 | //}); 119 | 120 | return detectResult; 121 | } 122 | 123 | private static async Task Detect(HttpClient httpClient, Uri targetUri) 124 | { 125 | var request = new HttpRequestMessage(HttpMethod.Get, targetUri); 126 | var response = await httpClient.SendAsync(request); 127 | 128 | return await Detect(response); 129 | } 130 | 131 | /// 132 | /// Detects cloudflare protection type for a specific HttpResponseMessage. 133 | /// 134 | /// The HttpResponseMessage to check. 135 | public static async Task Detect(HttpResponseMessage response) 136 | { 137 | var html = await response.Content.ReadAsStringAsync(); 138 | 139 | if (response.StatusCode.Equals(HttpStatusCode.ServiceUnavailable) && html.Contains("jschl_answer")) 140 | { 141 | return new DetectResult 142 | { 143 | Protection = CloudflareProtection.JavaScript, 144 | Html = html, 145 | SupportsHttp = true 146 | }; 147 | } 148 | 149 | if (response.StatusCode.Equals(HttpStatusCode.Forbidden)) 150 | { 151 | if (html.Contains("hcaptcha")) 152 | { 153 | return new DetectResult 154 | { 155 | Protection = CloudflareProtection.Captcha, 156 | Html = html, 157 | SupportsHttp = true 158 | }; 159 | } 160 | 161 | if (html.Contains("Access denied")) 162 | { 163 | return new DetectResult 164 | { 165 | Protection = CloudflareProtection.Banned, 166 | Html = html, 167 | }; 168 | } 169 | } 170 | 171 | if (response.StatusCode.Equals(HttpStatusCode.MovedPermanently) && response.Headers.Location != null && response.Headers.Location.Scheme.Equals(General.UriSchemeHttps)) 172 | { 173 | return new DetectResult 174 | { 175 | Protection = CloudflareProtection.Unknown, 176 | SupportsHttp = false 177 | }; 178 | } 179 | 180 | if (!IsCloudflareProtected(response) || response.Headers.Contains("CF-RAY") && response.IsSuccessStatusCode) 181 | { 182 | return new DetectResult 183 | { 184 | Protection = CloudflareProtection.NoProtection, 185 | SupportsHttp = true 186 | }; 187 | } 188 | 189 | return new DetectResult 190 | { 191 | Protection = CloudflareProtection.Unknown, 192 | }; 193 | } 194 | 195 | } 196 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/CloudflareHandler.cs: -------------------------------------------------------------------------------- 1 | using CloudflareSolverRe.Constants; 2 | using CloudflareSolverRe.Extensions; 3 | using CloudflareSolverRe.Utilities; 4 | using System; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Runtime.InteropServices; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace CloudflareSolverRe 13 | { 14 | internal class CloudflareHandler : DelegatingHandler 15 | { 16 | private readonly string userAgent; 17 | 18 | internal HttpClientHandler HttpClientHandler => InnerHandler.GetMostInnerHandler() as HttpClientHandler; 19 | 20 | 21 | /// 22 | /// Creates a new instance of the class with a as inner handler. 23 | /// 24 | /// The user-agent which will be used accross this session. 25 | public CloudflareHandler([Optional]string userAgent) : this(new HttpClientHandler(), userAgent) { } 26 | 27 | /// 28 | /// Creates a new instance of the class with a specific inner handler. 29 | /// 30 | /// The inner handler which is responsible for processing the HTTP response messages. 31 | /// The user-agent which will be used accross this session. 32 | public CloudflareHandler(HttpMessageHandler innerHandler, [Optional]string userAgent) : base(innerHandler) 33 | { 34 | this.userAgent = userAgent ?? Utils.GetGenerateRandomUserAgent(); 35 | } 36 | 37 | 38 | /// 39 | /// Sends an HTTP request to the inner handler to send to the server as an asynchronous operation. 40 | /// 41 | /// The HTTP request message to send to the server. 42 | /// A cancellation token to cancel operation. 43 | /// The task object representing the asynchronous operation. 44 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 45 | { 46 | PrepareHttpHandler(); 47 | PrepareHttpHeaders(request); 48 | 49 | var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); 50 | 51 | GeneralizeCookies(request.RequestUri); 52 | 53 | return response; 54 | } 55 | 56 | private void PrepareHttpHandler() 57 | { 58 | try 59 | { 60 | HttpClientHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; 61 | } 62 | catch { } 63 | } 64 | 65 | private void PrepareHttpHeaders(HttpRequestMessage request) 66 | { 67 | if (request.Headers.Host == null) 68 | request.Headers.Host = request.RequestUri.Host; 69 | 70 | if (!request.Headers.UserAgent.ToString().Equals(userAgent)) 71 | { 72 | request.Headers.UserAgent.Clear(); 73 | request.Headers.UserAgent.ParseAdd(userAgent); 74 | } 75 | 76 | if (!request.Headers.Accept.Any()) 77 | request.Headers.TryAddWithoutValidation(HttpHeaders.Accept, HttpHeaderValues.HtmlXmlAll); 78 | 79 | if (!request.Headers.AcceptLanguage.Any()) 80 | request.Headers.TryAddWithoutValidation(HttpHeaders.AcceptLanguage, HttpHeaderValues.En_Us); 81 | 82 | if (!request.Headers.Connection.Any()) 83 | request.Headers.Connection.ParseAdd(HttpHeaderValues.KeepAlive); 84 | 85 | if (!request.Headers.Contains(HttpHeaders.UpgradeInsecureRequests)) 86 | request.Headers.Add(HttpHeaders.UpgradeInsecureRequests, "1"); 87 | } 88 | 89 | private void GeneralizeCookies(Uri requestUri) 90 | { 91 | if (requestUri.Scheme.Equals(General.UriSchemeHttp)) 92 | { 93 | var httpsRequestUri = new Uri($"{General.UriSchemeHttps}://{requestUri.Host}{requestUri.PathAndQuery}"); 94 | var httpsCookies = HttpClientHandler.CookieContainer.GetCookies(httpsRequestUri); 95 | foreach (Cookie cookie in httpsCookies) 96 | cookie.Secure = false; 97 | } 98 | 99 | var httpCookies = HttpClientHandler.CookieContainer.GetCookies(requestUri); 100 | foreach (Cookie cookie in httpCookies) 101 | cookie.Secure = false; 102 | } 103 | 104 | } 105 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/CloudflareSolver.cs: -------------------------------------------------------------------------------- 1 | using CaptchaSharp; 2 | using CloudflareSolverRe.Constants; 3 | using CloudflareSolverRe.Extensions; 4 | using CloudflareSolverRe.Solvers; 5 | using CloudflareSolverRe.Types; 6 | using CloudflareSolverRe.Utilities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Net; 10 | using System.Net.Http; 11 | using System.Runtime.InteropServices; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace CloudflareSolverRe 16 | { 17 | public class CloudflareSolver : ICloudflareSolver 18 | { 19 | private readonly SemaphoreLocker _locker = new SemaphoreLocker(); 20 | 21 | /// 22 | /// The default number of retries, if clearance fails. 23 | /// 24 | public static readonly int DefaultMaxTries = 3; 25 | 26 | /// 27 | /// The default number of captcha clearance tries. 28 | /// 29 | public static readonly int DefaultMaxCaptchaTries = 1; 30 | 31 | 32 | private readonly CaptchaService captchaProvider; 33 | private readonly string defaultUserAgent; 34 | 35 | private string userAgent; 36 | 37 | private HttpClient httpClient; 38 | private CloudflareHandler cloudflareHandler; 39 | private Uri siteUrl; 40 | private CancellationToken? cancellationToken; 41 | private List captchaDetectResults; 42 | 43 | /// 44 | /// Gets or sets the number of clearance retries, if clearance fails. 45 | /// 46 | /// A negative value causes an infinite amount of retries. 47 | public int MaxTries { get; set; } = DefaultMaxTries; 48 | 49 | /// 50 | /// Gets or sets the max number of captcha clearance tries. 51 | /// 52 | public int MaxCaptchaTries { get; set; } = DefaultMaxCaptchaTries; 53 | 54 | /// 55 | /// Gets or sets the number of milliseconds to wait before sending the clearance request. 56 | /// 57 | /// 58 | /// Negative value or zero means to wait the delay time required by the challenge (like a browser). 59 | /// 60 | public int ClearanceDelay { get; set; } 61 | 62 | private bool CaptchaSolvingEnabled => captchaProvider != null; 63 | 64 | 65 | public CloudflareSolver([Optional]string userAgent) : this(null, userAgent) { } 66 | 67 | public CloudflareSolver(CaptchaService captchaProvider, [Optional]string userAgent) 68 | { 69 | this.captchaProvider = captchaProvider; 70 | defaultUserAgent = userAgent ?? Utils.GetGenerateRandomUserAgent(); 71 | captchaDetectResults = new List(); 72 | } 73 | 74 | 75 | /// 76 | /// Solves cloudflare challenge protecting a specific website. 77 | /// 78 | /// Uri of the website. 79 | /// The user-agent which will be used to solve the challenge. 80 | /// Proxy to use while solving the challenge. 81 | /// CancellationToken to contol solving operation cancelling. 82 | public async Task Solve(Uri siteUrl, string userAgent, [Optional]IWebProxy proxy, [Optional]CancellationToken cancellationToken) 83 | { 84 | SolveResult result = default(SolveResult); 85 | 86 | await _locker.LockAsync(async () => 87 | { 88 | this.userAgent = userAgent; 89 | cloudflareHandler = new CloudflareHandler(userAgent); 90 | cloudflareHandler.HttpClientHandler.Proxy = proxy; 91 | httpClient = new HttpClient(cloudflareHandler); 92 | this.siteUrl = siteUrl; 93 | this.cancellationToken = cancellationToken; 94 | 95 | result = await Solve(); 96 | 97 | httpClient.Dispose(); 98 | captchaDetectResults.Clear(); 99 | }); 100 | 101 | return result; 102 | } 103 | 104 | /// 105 | /// Solves cloudflare challenge protecting a specific website. 106 | /// 107 | /// Uri of the website. 108 | /// Proxy to use while solving the challenge. 109 | /// CancellationToken to contol solving operation cancelling. 110 | /// Use a new random user-agent. 111 | public async Task Solve(Uri siteUrl, [Optional]IWebProxy proxy, [Optional]CancellationToken cancellationToken, bool randomUserAgent = false) 112 | { 113 | SolveResult result = default(SolveResult); 114 | 115 | await _locker.LockAsync(async () => 116 | { 117 | userAgent = randomUserAgent ? Utils.GetGenerateRandomUserAgent() : defaultUserAgent; 118 | cloudflareHandler = new CloudflareHandler(userAgent); 119 | cloudflareHandler.HttpClientHandler.Proxy = proxy; 120 | httpClient = new HttpClient(cloudflareHandler); 121 | this.siteUrl = siteUrl; 122 | this.cancellationToken = cancellationToken; 123 | 124 | result = await Solve(); 125 | 126 | httpClient.Dispose(); 127 | captchaDetectResults.Clear(); 128 | }); 129 | 130 | return result; 131 | } 132 | 133 | /// 134 | /// Solves cloudflare challenge protecting a specific website. 135 | /// 136 | /// HttpClient to use in challenge solving process. 137 | /// HttpMessageHandler of the HttpClient. 138 | /// Uri of the website. 139 | /// The user-agent which will be used to solve the challenge. 140 | /// CancellationToken to contol solving operation cancelling. 141 | public async Task Solve(HttpClient httpClient, HttpMessageHandler httpMessageHandler, Uri siteUrl, string userAgent, [Optional]CancellationToken cancellationToken) 142 | { 143 | SolveResult result = default(SolveResult); 144 | 145 | await _locker.LockAsync(async () => 146 | { 147 | this.userAgent = userAgent; 148 | var cloudflareHandler = new CloudflareHandler(httpMessageHandler, userAgent); 149 | result = await Solve(httpClient, cloudflareHandler, siteUrl, cancellationToken); 150 | 151 | if (result.UserAgent != null) 152 | { 153 | httpClient.DefaultRequestHeaders.UserAgent.Clear(); 154 | httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(result.UserAgent); 155 | } 156 | }); 157 | 158 | return result; 159 | } 160 | 161 | /// 162 | /// Solves cloudflare challenge protecting a specific website. 163 | /// 164 | /// HttpClient to use in challenge solving process. 165 | /// HttpClientHandler of the HttpClient. 166 | /// Uri of the website. 167 | /// The user-agent which will be used to solve the challenge. 168 | /// CancellationToken to contol solving operation cancelling. 169 | public async Task Solve(HttpClient httpClient, HttpClientHandler httpClientHandler, Uri siteUrl, string userAgent, [Optional]CancellationToken cancellationToken) 170 | { 171 | SolveResult result = default(SolveResult); 172 | 173 | await _locker.LockAsync(async () => 174 | { 175 | this.userAgent = userAgent; 176 | var cloudflareHandler = new CloudflareHandler(httpClientHandler, userAgent); 177 | result = await Solve(httpClient, cloudflareHandler, siteUrl, cancellationToken); 178 | 179 | if (result.UserAgent != null) 180 | { 181 | httpClient.DefaultRequestHeaders.UserAgent.Clear(); 182 | httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(result.UserAgent); 183 | } 184 | }); 185 | 186 | return result; 187 | } 188 | 189 | /// 190 | /// Solves cloudflare challenge protecting a specific website. 191 | /// 192 | /// HttpClient to use in challenge solving process. 193 | /// HttpMessageHandler of the HttpClient. 194 | /// Uri of the website. 195 | /// CancellationToken to contol solving operation cancelling. 196 | /// Use a new random user-agent. 197 | public async Task Solve(HttpClient httpClient, HttpMessageHandler httpMessageHandler, Uri siteUrl, [Optional]CancellationToken cancellationToken, bool randomUserAgent = false) 198 | { 199 | SolveResult result = default(SolveResult); 200 | 201 | await _locker.LockAsync(async () => 202 | { 203 | userAgent = randomUserAgent ? Utils.GetGenerateRandomUserAgent() : defaultUserAgent; 204 | var cloudflareHandler = new CloudflareHandler(httpMessageHandler, userAgent); 205 | result = await Solve(httpClient, cloudflareHandler, siteUrl, cancellationToken); 206 | 207 | if (result.UserAgent != null) 208 | { 209 | httpClient.DefaultRequestHeaders.UserAgent.Clear(); 210 | httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(result.UserAgent); 211 | } 212 | }); 213 | 214 | return result; 215 | } 216 | 217 | /// 218 | /// Solves cloudflare challenge protecting a specific website. 219 | /// 220 | /// HttpClient to use in challenge solving process. 221 | /// HttpClientHandler of the HttpClient. 222 | /// Uri of the website. 223 | /// CancellationToken to contol solving operation cancelling. 224 | /// Use a new random user-agent. 225 | public async Task Solve(HttpClient httpClient, HttpClientHandler httpClientHandler, Uri siteUrl, [Optional]CancellationToken cancellationToken, bool randomUserAgent = false) 226 | { 227 | SolveResult result = default(SolveResult); 228 | 229 | await _locker.LockAsync(async () => 230 | { 231 | userAgent = randomUserAgent ? Utils.GetGenerateRandomUserAgent() : defaultUserAgent; 232 | var cloudflareHandler = new CloudflareHandler(httpClientHandler, userAgent); 233 | result = await Solve(httpClient, cloudflareHandler, siteUrl, cancellationToken); 234 | 235 | if (result.UserAgent != null) 236 | { 237 | httpClient.DefaultRequestHeaders.UserAgent.Clear(); 238 | httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(result.UserAgent); 239 | } 240 | }); 241 | 242 | return result; 243 | } 244 | 245 | /// 246 | /// Solves cloudflare challenge protecting a specific website. 247 | /// 248 | /// HttpClient to use in challenge solving process. 249 | /// CloudflareHandler of the HttpClient 250 | /// Uri of the website. 251 | /// CancellationToken to contol solving operation cancelling. 252 | internal async Task Solve(HttpClient httpClient, CloudflareHandler cloudflareHandler, Uri siteUrl, [Optional]CancellationToken cancellationToken) 253 | { 254 | this.cloudflareHandler = cloudflareHandler; 255 | this.httpClient = httpClient.Clone(this.cloudflareHandler, false); 256 | this.siteUrl = siteUrl; 257 | this.cancellationToken = cancellationToken; 258 | 259 | var result = await Solve(); 260 | 261 | this.httpClient.Dispose(); 262 | captchaDetectResults.Clear(); 263 | 264 | return result; 265 | } 266 | 267 | private async Task Solve() 268 | { 269 | var result = await SolveWithJavascript(MaxTries - (CaptchaSolvingEnabled ? MaxCaptchaTries : 0)); 270 | 271 | if (!result.Success && CaptchaSolvingEnabled) 272 | result = await SolveWithCaptcha(); 273 | 274 | return result; 275 | } 276 | 277 | 278 | private async Task SolveWithJavascript(int tries) 279 | { 280 | var result = default(SolveResult); 281 | 282 | for (var i = 0; i < tries && !result.Success; i++) 283 | { 284 | if (cancellationToken.HasValue) 285 | cancellationToken.Value.ThrowIfCancellationRequested(); 286 | 287 | result = await SolveJavascriptChallenge(); 288 | } 289 | 290 | return result; 291 | } 292 | 293 | private async Task SolveWithCaptcha() 294 | { 295 | var result = default(SolveResult); 296 | 297 | for (int i = 0; i < MaxCaptchaTries && !result.Success; i++) 298 | { 299 | if (cancellationToken.HasValue) 300 | cancellationToken.Value.ThrowIfCancellationRequested(); 301 | 302 | var captchaDetectResult = captchaDetectResults.Count > i ? (DetectResult?)captchaDetectResults[i] : null; 303 | result = await SolveCaptchaChallenge(captchaDetectResult); 304 | } 305 | 306 | return result; 307 | } 308 | 309 | 310 | private async Task SolveJavascriptChallenge(DetectResult? jsDetectResult = null) 311 | { 312 | var result = default(SolveResult); 313 | 314 | if (!jsDetectResult.HasValue) 315 | { 316 | jsDetectResult = await CloudflareDetector.Detect(httpClient, siteUrl); 317 | siteUrl = ChangeUrlScheme(siteUrl, jsDetectResult.Value.SupportsHttp); 318 | } 319 | 320 | var exceptional = IsExceptionalDetectionResult(jsDetectResult.Value); 321 | if (exceptional.Item1) 322 | result = exceptional.Item2; 323 | else if (jsDetectResult.Value.Protection.Equals(CloudflareProtection.JavaScript)) 324 | result = await new JsChallengeSolver(httpClient, cloudflareHandler, siteUrl, jsDetectResult.Value, userAgent, ClearanceDelay) 325 | .Solve(); 326 | 327 | if (!result.Success && result.NewDetectResult.HasValue && result.NewDetectResult.Value.Protection.Equals(CloudflareProtection.Captcha)) 328 | captchaDetectResults.Add(result.NewDetectResult.Value); 329 | 330 | return result; 331 | } 332 | 333 | private async Task SolveCaptchaChallenge(DetectResult? captchaDetectResult = null) 334 | { 335 | var result = default(SolveResult); 336 | 337 | if (!CaptchaSolvingEnabled) 338 | return result; 339 | 340 | if (!captchaDetectResult.HasValue) 341 | { 342 | captchaDetectResult = await CloudflareDetector.Detect(httpClient, siteUrl); 343 | siteUrl = ChangeUrlScheme(siteUrl, captchaDetectResult.Value.SupportsHttp); 344 | } 345 | 346 | var exceptional = IsExceptionalDetectionResult(captchaDetectResult.Value); 347 | if (exceptional.Item1) 348 | result = exceptional.Item2; 349 | else if (captchaDetectResult.Value.Protection.Equals(CloudflareProtection.Captcha)) 350 | result = await new CaptchaChallengeSolver(httpClient, cloudflareHandler, siteUrl, captchaDetectResult.Value, userAgent, captchaProvider) 351 | .Solve(); 352 | else if (captchaDetectResult.Value.Protection.Equals(CloudflareProtection.JavaScript)) 353 | result = await SolveJavascriptChallenge(captchaDetectResult); 354 | 355 | return result; 356 | } 357 | 358 | 359 | private Tuple IsExceptionalDetectionResult(DetectResult detectResult) 360 | { 361 | var result = default(SolveResult); 362 | 363 | if (IsNotProtected(detectResult)) 364 | result = SolveResult.NoProtection; 365 | else if (IsBanned(detectResult)) 366 | result = SolveResult.Banned; 367 | else if (IsUnknown(detectResult)) 368 | result = SolveResult.Unknown; 369 | 370 | result.DetectResult = detectResult; 371 | 372 | var done = result.Success || !string.IsNullOrEmpty(result.FailReason); 373 | 374 | return Tuple.Create(done, result); 375 | } 376 | 377 | private bool IsNotProtected(DetectResult detectResult) => detectResult.Protection.Equals(CloudflareProtection.NoProtection); 378 | 379 | private bool IsBanned(DetectResult detectResult) => detectResult.Protection.Equals(CloudflareProtection.Banned); 380 | 381 | private bool IsUnknown(DetectResult detectResult) => detectResult.Protection.Equals(CloudflareProtection.Unknown); 382 | 383 | 384 | private Uri ChangeUrlScheme(Uri uri, bool supportsHttp) 385 | { 386 | if (!supportsHttp && uri.Scheme.Equals(General.UriSchemeHttp)) 387 | uri = uri.ForceHttps(); 388 | else if (supportsHttp && uri.Scheme.Equals(General.UriSchemeHttps)) 389 | uri = uri.ForceHttp(); 390 | 391 | return uri; 392 | } 393 | } 394 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/CloudflareSolverRe.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | CloudflareSolverRe 6 | CloudflareSolverRe 7 | 1.0.7 8 | Zaczero, RyuzakiH, Ngosang 9 | Cloudflare Javascript & hCaptcha challenge (I'm Under Attack Mode or IUAM) solving / bypass .NET Standard library. 10 | https://github.com/RyuzakiH/CloudflareSolverRe/blob/master/LICENSE 11 | https://github.com/RyuzakiH/CloudflareSolverRe 12 | 1.0.7.0 13 | 1.0.7.0 14 | cloudflare, solver, bypass, protection, solving, library, cloudflaresolver, delegatinghandler, hcaptcha, captcha, javascript, challenge, utilities 15 | true 16 | https://github.com/RyuzakiH/CloudflareSolverRe 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Constants/Errors.cs: -------------------------------------------------------------------------------- 1 | namespace CloudflareSolverRe.Constants 2 | { 3 | internal static class Errors 4 | { 5 | public const string ClearanceCookieNotFound = "Clearance cookie not found"; 6 | 7 | public const string CaptchaSolverRequired = "Captcha solver required"; 8 | 9 | public const string SomethingWrongHappened = "Something wrong happened"; 10 | 11 | public const string MissingCaptchaProvider = "Missing captcha provider"; 12 | 13 | public const string NoProtectionDetected = "No protection detected"; 14 | 15 | public const string IpAddressIsBanned = "IP address is banned"; 16 | 17 | public const string UnknownProtectionDetected = "Unknown protection detected"; 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Constants/General.cs: -------------------------------------------------------------------------------- 1 | namespace CloudflareSolverRe.Constants 2 | { 3 | internal static class General 4 | { 5 | public const string UriSchemeHttp = "http"; 6 | 7 | public const string UriSchemeHttps = "https"; 8 | 9 | public const string Javascript = "Javascript"; 10 | 11 | public const string Captcha = "Captcha"; 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Constants/HttpHeaderValues.cs: -------------------------------------------------------------------------------- 1 | namespace CloudflareSolverRe.Constants 2 | { 3 | internal static class HttpHeaderValues 4 | { 5 | public const string HtmlXmlAll = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; 6 | 7 | public const string En_Us = "en-US,en;q=0.5"; 8 | 9 | public const string KeepAlive = "keep-alive"; 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Constants/HttpHeaders.cs: -------------------------------------------------------------------------------- 1 | namespace CloudflareSolverRe.Constants 2 | { 3 | internal static class HttpHeaders 4 | { 5 | public const string Accept = "Accept"; 6 | 7 | public const string AcceptLanguage = "Accept-Language"; 8 | 9 | public const string UserAgent = "User-Agent"; 10 | 11 | public const string Cookie = "Cookie"; 12 | 13 | public const string SetCookie = "Set-Cookie"; 14 | 15 | public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests"; 16 | 17 | public const string DNT = "DNT"; 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Constants/UserAgents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace CloudflareSolverRe.Constants 6 | { 7 | internal static class UserAgents 8 | { 9 | public static readonly List UserAgentList = new List 10 | { 11 | Chrome59_Win7, 12 | Chrome59_Win8, 13 | Chrome59_Win81, 14 | Chrome59_Win10, 15 | Chrome60_Win7, 16 | Chrome60_Win8, 17 | Chrome60_Win81, 18 | Chrome60_Win10, 19 | Chrome61_Win7, 20 | Chrome61_Win8, 21 | Chrome61_Win81, 22 | Chrome61_Win10, 23 | Firefox66_Win7, 24 | Firefox66_Win8, 25 | Firefox66_Win81, 26 | Firefox66_Win10 27 | }; 28 | 29 | public const string Firefox66_Win7 = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"; 30 | public const string Firefox66_Win8 = "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"; 31 | public const string Firefox66_Win81 = "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"; 32 | public const string Firefox66_Win10 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"; 33 | 34 | public const string Chrome59_Win7 = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36"; 35 | public const string Chrome59_Win8 = "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36"; 36 | public const string Chrome59_Win81 = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36"; 37 | public const string Chrome59_Win10 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36"; 38 | 39 | public const string Chrome60_Win7 = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36"; 40 | public const string Chrome60_Win8 = "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36"; 41 | public const string Chrome60_Win81 = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36"; 42 | public const string Chrome60_Win10 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36"; 43 | 44 | public const string Chrome61_Win7 = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"; 45 | public const string Chrome61_Win8 = "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"; 46 | public const string Chrome61_Win81 = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"; 47 | public const string Chrome61_Win10 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"; 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Exceptions/CloudflareClearanceException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | 4 | namespace CloudflareSolverRe.Exceptions 5 | { 6 | /// 7 | /// The exception that is thrown if CloudFlare clearance failed after the declared number of attempts. 8 | /// 9 | public class CloudflareClearanceException : HttpRequestException 10 | { 11 | public CloudflareClearanceException(int attempts) : this(attempts, $"Clearance failed after {attempts} attempt(s).") { } 12 | 13 | public CloudflareClearanceException(int attempts, string message) : base(message) 14 | { 15 | Attempts = attempts; 16 | } 17 | 18 | public CloudflareClearanceException(int attempts, string message, Exception inner) : base(message, inner) 19 | { 20 | Attempts = attempts; 21 | } 22 | 23 | /// 24 | /// Returns the number of failed clearance attempts. 25 | /// 26 | public int Attempts { get; } 27 | } 28 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Extensions/CookieContainerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | 6 | namespace CloudflareSolverRe.Extensions 7 | { 8 | internal static class CookieContainerExtensions 9 | { 10 | public static IEnumerable GetCookiesByName(this CookieContainer container, Uri uri, params string[] names) => 11 | container.GetCookies(uri).Cast().Where(c => names.Contains(c.Name)).ToList(); 12 | 13 | public static Cookie GetCookie(this CookieContainer cookieContainer, Uri uri, string name) => 14 | cookieContainer.GetCookiesByName(uri, name).FirstOrDefault(); 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Extensions/CookieExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace CloudflareSolverRe.Extensions 4 | { 5 | internal static class CookieExtensions 6 | { 7 | public static string ToHeaderValue(this Cookie cookie) => $"{cookie.Name}={cookie.Value}"; 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Extensions/HttpClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace CloudflareSolverRe.Extensions 5 | { 6 | internal static class HttpClientExtensions 7 | { 8 | internal static HttpClient Clone(this HttpClient httpClient) 9 | { 10 | return httpClient.Clone(null, null); 11 | } 12 | 13 | internal static HttpClient Clone(this HttpClient httpClient, HttpMessageHandler handler) 14 | { 15 | return httpClient.Clone(handler, null); 16 | } 17 | 18 | internal static HttpClient Clone(this HttpClient httpClient, HttpMessageHandler handler, bool disposeHandler) 19 | { 20 | return httpClient.Clone(handler, disposeHandler, true); 21 | } 22 | 23 | private static HttpClient Clone(this HttpClient httpClient, HttpMessageHandler handler, bool? disposeHandler, [Optional]bool internal_) 24 | { 25 | HttpClient client; 26 | 27 | if (handler != null && disposeHandler.HasValue) 28 | client = new HttpClient(handler, disposeHandler.Value); 29 | else if (handler != null) 30 | client = new HttpClient(handler); 31 | else 32 | client = new HttpClient(); 33 | 34 | client.BaseAddress = httpClient.BaseAddress; 35 | client.MaxResponseContentBufferSize = httpClient.MaxResponseContentBufferSize; 36 | client.Timeout = httpClient.Timeout; 37 | 38 | foreach (var header in httpClient.DefaultRequestHeaders) 39 | client.DefaultRequestHeaders.Add(header.Key, header.Value); 40 | 41 | return client; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Extensions/HttpMessageHandlerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | 3 | namespace CloudflareSolverRe.Extensions 4 | { 5 | internal static class HttpMessageHandlerExtensions 6 | { 7 | public static HttpMessageHandler GetMostInnerHandler(this HttpMessageHandler self) 8 | { 9 | return self is DelegatingHandler handler 10 | ? handler.InnerHandler.GetMostInnerHandler() 11 | : self; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Extensions/IEnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace CloudflareSolverRe.Extensions 5 | { 6 | public static class IEnumerableExtensions 7 | { 8 | public static IEnumerable Prepend(this IEnumerable values, T value) 9 | { 10 | yield return value; 11 | foreach (T item in values) 12 | yield return item; 13 | } 14 | 15 | public static IEnumerable Append(this IEnumerable values, T value) 16 | { 17 | return values.Concat(new T[] { value }); 18 | } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Extensions/UriExtensions.cs: -------------------------------------------------------------------------------- 1 | using CloudflareSolverRe.Constants; 2 | using System; 3 | 4 | namespace CloudflareSolverRe.Extensions 5 | { 6 | internal static class UriExtensions 7 | { 8 | public static Uri ForceHttp(this Uri uri) 9 | { 10 | var newUri = new UriBuilder(uri); 11 | 12 | var hadDefaultPort = newUri.Uri.IsDefaultPort; 13 | newUri.Scheme = General.UriSchemeHttp; 14 | newUri.Port = hadDefaultPort ? -1 : newUri.Port; 15 | 16 | return newUri.Uri; 17 | } 18 | 19 | public static Uri ForceHttps(this Uri uri) 20 | { 21 | var newUri = new UriBuilder(uri); 22 | 23 | var hadDefaultPort = newUri.Uri.IsDefaultPort; 24 | newUri.Scheme = General.UriSchemeHttps; 25 | newUri.Port = hadDefaultPort ? -1 : newUri.Port; 26 | 27 | return newUri.Uri; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Solvers/CaptchaChallengeSolver.cs: -------------------------------------------------------------------------------- 1 | using CaptchaSharp; 2 | using CloudflareSolverRe.Constants; 3 | using CloudflareSolverRe.Types; 4 | using CloudflareSolverRe.Types.Captcha; 5 | using System; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Net.Http; 9 | using System.Threading.Tasks; 10 | 11 | namespace CloudflareSolverRe.Solvers 12 | { 13 | internal class CaptchaChallengeSolver : ChallengeSolver 14 | { 15 | private readonly CaptchaService captchaProvider; 16 | 17 | private bool CaptchaSolvingEnabled => captchaProvider != null; 18 | 19 | 20 | internal CaptchaChallengeSolver(HttpClient client, CloudflareHandler handler, Uri siteUrl, DetectResult detectResult, string userAgent, CaptchaService captchaProvider) 21 | : base(client, handler, siteUrl, detectResult, userAgent) 22 | { 23 | this.captchaProvider = captchaProvider; 24 | } 25 | 26 | internal CaptchaChallengeSolver(CloudflareHandler handler, Uri siteUrl, DetectResult detectResult, string userAgent, CaptchaService captchaProvider) 27 | : base(handler, siteUrl, detectResult, userAgent) 28 | { 29 | this.captchaProvider = captchaProvider; 30 | } 31 | 32 | 33 | internal new async Task Solve() 34 | { 35 | if (!CaptchaSolvingEnabled) 36 | { 37 | return new SolveResult 38 | { 39 | Success = false, 40 | FailReason = Errors.MissingCaptchaProvider, 41 | DetectResult = DetectResult, 42 | }; 43 | } 44 | 45 | var solve = await SolveChallenge(DetectResult.Html); 46 | return new SolveResult 47 | { 48 | Success = solve.Success, 49 | FailReason = solve.FailReason, 50 | DetectResult = DetectResult, 51 | }; 52 | } 53 | 54 | private async Task SolveChallenge(string html) 55 | { 56 | var challenge = CaptchaChallenge.Parse(html, SiteUrl); 57 | 58 | var result = await challenge.Solve(captchaProvider); 59 | 60 | if (!result.Success) 61 | return new SolveResult(false, LayerCaptcha, $"captcha provider error ({result.Response})", DetectResult); 62 | 63 | var solution = new CaptchaChallengeSolution(challenge, result.Response); 64 | 65 | return await SubmitCaptchaSolution(solution); 66 | } 67 | 68 | private async Task SubmitCaptchaSolution(CaptchaChallengeSolution solution) 69 | { 70 | var request = new HttpRequestMessage(HttpMethod.Post, new Uri(solution.ClearanceUrl)); 71 | request.Headers.Referrer = SiteUrl; 72 | request.Content = new FormUrlEncodedContent(solution.ClearanceBody); 73 | 74 | var response = await HttpClient.SendAsync(request); 75 | 76 | return GetSolveResult(response); 77 | } 78 | 79 | private SolveResult GetSolveResult(HttpResponseMessage submissionResponse) 80 | { 81 | var sessionCookies = SessionCookies.FromCookieContainer(CloudflareHandler.HttpClientHandler.CookieContainer, SiteUrl); 82 | 83 | if (submissionResponse.StatusCode.Equals(HttpStatusCode.Found)) 84 | { 85 | var success = submissionResponse.Headers.Contains(HttpHeaders.SetCookie) && 86 | submissionResponse.Headers.GetValues(HttpHeaders.SetCookie) 87 | .Any(cookieValue => cookieValue.Contains(SessionCookies.ClearanceCookieName)); 88 | 89 | return new SolveResult(success, LayerCaptcha, success ? null : Errors.ClearanceCookieNotFound, DetectResult, sessionCookies, UserAgent, submissionResponse); 90 | } 91 | else 92 | { 93 | return new SolveResult(false, LayerCaptcha, Errors.SomethingWrongHappened, DetectResult, sessionCookies, UserAgent, submissionResponse); //"invalid submit response" 94 | } 95 | } 96 | 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Solvers/ChallengeSolver.cs: -------------------------------------------------------------------------------- 1 | using CloudflareSolverRe.Types; 2 | using System; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | 6 | namespace CloudflareSolverRe.Solvers 7 | { 8 | internal class ChallengeSolver 9 | { 10 | protected const string LayerJavaScript = "JavaScript"; 11 | protected const string LayerCaptcha = "Captcha"; 12 | 13 | protected HttpClient HttpClient { get; } 14 | protected CloudflareHandler CloudflareHandler { get; } 15 | protected DetectResult DetectResult { get; private set; } 16 | protected string UserAgent { get; } 17 | protected Uri SiteUrl { get; } 18 | 19 | internal ChallengeSolver(HttpClient client, CloudflareHandler handler, Uri siteUrl, DetectResult detectResult, string userAgent) 20 | { 21 | HttpClient = client; 22 | CloudflareHandler = handler; 23 | SiteUrl = siteUrl; 24 | DetectResult = detectResult; 25 | UserAgent = userAgent; 26 | } 27 | 28 | internal ChallengeSolver(CloudflareHandler handler, Uri siteUrl, DetectResult detectResult, string userAgent) 29 | : this(new HttpClient(handler), handler, siteUrl, detectResult, userAgent) 30 | { 31 | } 32 | 33 | 34 | internal virtual Task Solve() 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | 39 | } 40 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Solvers/JsChallengeSolver.cs: -------------------------------------------------------------------------------- 1 | using CloudflareSolverRe.Constants; 2 | using CloudflareSolverRe.Types; 3 | using CloudflareSolverRe.Types.Javascript; 4 | using System; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Runtime.InteropServices; 9 | using System.Threading.Tasks; 10 | 11 | namespace CloudflareSolverRe.Solvers 12 | { 13 | internal class JsChallengeSolver : ChallengeSolver 14 | { 15 | /// 16 | /// Gets or sets the number of milliseconds to wait before sending the clearance request. 17 | /// 18 | /// 19 | /// Negative value or zero means to wait the delay time required by the challenge (like a browser). 20 | /// 21 | public int ClearanceDelay { get; set; } 22 | 23 | internal JsChallengeSolver(HttpClient client, CloudflareHandler handler, Uri siteUrl, DetectResult detectResult, string userAgent, [Optional]int? clearanceDelay) 24 | : base(client, handler, siteUrl, detectResult, userAgent) 25 | { 26 | if (clearanceDelay.HasValue) 27 | ClearanceDelay = clearanceDelay.Value; 28 | } 29 | 30 | internal JsChallengeSolver(CloudflareHandler handler, Uri siteUrl, DetectResult detectResult, string userAgent, [Optional]int? clearanceDelay) 31 | : base(handler, siteUrl, detectResult, userAgent) 32 | { 33 | if (clearanceDelay.HasValue) 34 | ClearanceDelay = clearanceDelay.Value; 35 | } 36 | 37 | 38 | internal new async Task Solve() 39 | { 40 | var solution = await SolveChallenge(DetectResult.Html); 41 | 42 | if (!solution.Success && solution.FailReason.Contains(General.Captcha)) 43 | { 44 | solution.NewDetectResult = new DetectResult 45 | { 46 | Protection = CloudflareProtection.Captcha, 47 | Html = await solution.Response.Content.ReadAsStringAsync(), 48 | SupportsHttp = DetectResult.SupportsHttp 49 | }; 50 | } 51 | 52 | return solution; 53 | } 54 | 55 | private async Task SolveChallenge(string html) 56 | { 57 | JsChallenge challenge; 58 | try 59 | { 60 | challenge = JsChallenge.Parse(html, SiteUrl); 61 | } 62 | catch (Exception) 63 | { 64 | // The exception can be caused by Im Under Attack Mode or a new challenge 65 | // If we throw the exception there are no more retries. In IUAM is better to wait a bit and retry. 66 | await Task.Delay(ClearanceDelay); 67 | return new SolveResult(false, LayerJavaScript, Errors.SomethingWrongHappened, DetectResult); 68 | } 69 | 70 | var jschlAnswer = challenge.Solve(); 71 | 72 | var solution = new JsChallengeSolution(SiteUrl, challenge.Form, jschlAnswer); 73 | 74 | await Task.Delay(ClearanceDelay <= 0 ? challenge.Delay : ClearanceDelay); 75 | 76 | return await SubmitJsSolution(solution); 77 | } 78 | 79 | private async Task SubmitJsSolution(JsChallengeSolution solution) 80 | { 81 | var request = new HttpRequestMessage(HttpMethod.Post, new Uri(solution.ClearanceUrl)); 82 | request.Headers.Referrer = SiteUrl; 83 | request.Content = new FormUrlEncodedContent(solution.ClearanceBody); 84 | 85 | var response = await HttpClient.SendAsync(request); 86 | 87 | return GetSolveResult(response); 88 | } 89 | 90 | private SolveResult GetSolveResult(HttpResponseMessage submissionResponse) 91 | { 92 | var sessionCookies = SessionCookies.FromCookieContainer(CloudflareHandler.HttpClientHandler.CookieContainer, SiteUrl); 93 | 94 | if (submissionResponse.StatusCode == HttpStatusCode.Found) 95 | { 96 | var success = submissionResponse.Headers.Contains(HttpHeaders.SetCookie) && 97 | submissionResponse.Headers.GetValues(HttpHeaders.SetCookie) 98 | .Any(cookieValue => cookieValue.Contains(SessionCookies.ClearanceCookieName)); 99 | 100 | return new SolveResult(success, LayerJavaScript, success ? null : Errors.ClearanceCookieNotFound, DetectResult, sessionCookies, UserAgent, submissionResponse); // "invalid submit response" 101 | } 102 | else if (submissionResponse.StatusCode == HttpStatusCode.Forbidden) 103 | { 104 | return new SolveResult(false, LayerCaptcha, Errors.CaptchaSolverRequired, DetectResult, sessionCookies, UserAgent, submissionResponse); 105 | } 106 | else 107 | { 108 | return new SolveResult(false, LayerJavaScript, Errors.SomethingWrongHappened, DetectResult, sessionCookies, UserAgent, submissionResponse); 109 | } 110 | } 111 | 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/Captcha/CaptchaChallenge.cs: -------------------------------------------------------------------------------- 1 | using CaptchaSharp; 2 | using System; 3 | using System.Text.RegularExpressions; 4 | using System.Threading.Tasks; 5 | 6 | namespace CloudflareSolverRe.Types.Captcha 7 | { 8 | public class CaptchaChallenge 9 | { 10 | private static readonly Regex CaptchaFormRegex = new Regex(@"\S+?)"".*?>.*?name=""r"" value=""(?[^""]*?)"".*?data-ray=""(?\S+)"".*?data-sitekey=""(?\S+)""", RegexOptions.Singleline/* | RegexOptions.Compiled*/); 11 | 12 | public string Action { get; set; } 13 | public string R { get; set; } 14 | public string DataRay { get; set; } 15 | public string SiteKey { get; set; } 16 | public Uri SiteUrl { get; set; } 17 | 18 | public static CaptchaChallenge Parse(string html, Uri siteUrl) 19 | { 20 | var formMatch = CaptchaFormRegex.Match(html); 21 | 22 | return new CaptchaChallenge 23 | { 24 | Action = formMatch.Groups["action"].Value, 25 | R = formMatch.Groups["r"].Value, 26 | DataRay = formMatch.Groups["dataRay"].Value, 27 | SiteKey = formMatch.Groups["siteKey"].Value, 28 | SiteUrl = siteUrl 29 | }; 30 | } 31 | 32 | public async Task Solve(CaptchaService captchaProvider) 33 | { 34 | try 35 | { 36 | var result = await captchaProvider.SolveHCaptchaAsync(SiteKey, SiteUrl.AbsoluteUri); 37 | 38 | return new CaptchaSolveResult 39 | { 40 | Response = result.Response, 41 | Success = true 42 | }; 43 | } 44 | catch 45 | { 46 | return new CaptchaSolveResult 47 | { 48 | Response = "", 49 | Success = false 50 | }; 51 | } 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/Captcha/CaptchaChallengeSolution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CloudflareSolverRe.Types.Captcha 5 | { 6 | /// 7 | /// Holds the information, which is required to pass the Cloudflare clearance. 8 | /// 9 | public class CaptchaChallengeSolution : IEquatable 10 | { 11 | public string ClearancePage { get; } 12 | public string HCaptchaResponse { get; } 13 | public string R { get; } 14 | public string Id { get; } 15 | 16 | public string ClearanceUrl => ClearancePage; 17 | 18 | public Dictionary ClearanceBody => new Dictionary 19 | { 20 | { "r", Uri.EscapeDataString(R) }, 21 | { "id", Uri.EscapeDataString(Id) }, 22 | { "h-captcha-response", Uri.EscapeDataString(HCaptchaResponse) }, 23 | { "g-recaptcha-response", Uri.EscapeDataString(HCaptchaResponse) }, 24 | { "cf_captcha_kind", "h" } 25 | }; 26 | 27 | public CaptchaChallengeSolution(string clearancePage, string s, string id, string hcaptchaResponse) 28 | { 29 | ClearancePage = clearancePage; 30 | R = s; 31 | Id = id; 32 | HCaptchaResponse = hcaptchaResponse; 33 | } 34 | 35 | public CaptchaChallengeSolution(CaptchaChallenge challenge, string hcaptchaResponse) 36 | { 37 | ClearancePage = $"{challenge.SiteUrl.Scheme}://{challenge.SiteUrl.Host}{challenge.Action}"; 38 | R = challenge.R; 39 | Id = ""; 40 | HCaptchaResponse = hcaptchaResponse; 41 | } 42 | 43 | public static bool operator ==(CaptchaChallengeSolution solution1, CaptchaChallengeSolution solution2) => 44 | (solution1 is null) ? (solution2 is null) : solution1.Equals(solution2); 45 | 46 | public static bool operator !=(CaptchaChallengeSolution solution1, CaptchaChallengeSolution solution2) => !(solution1 == solution2); 47 | 48 | public override bool Equals(object obj) => Equals(obj as CaptchaChallengeSolution); 49 | 50 | public bool Equals(CaptchaChallengeSolution other) => other != null && other.ClearanceUrl == ClearanceUrl; 51 | 52 | public override int GetHashCode() => ClearanceUrl.GetHashCode(); 53 | 54 | } 55 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/Captcha/CaptchaSolveResult.cs: -------------------------------------------------------------------------------- 1 | namespace CloudflareSolverRe.Types.Captcha 2 | { 3 | public struct CaptchaSolveResult 4 | { 5 | public bool Success; 6 | public string Response; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/CloudflareProtection.cs: -------------------------------------------------------------------------------- 1 | namespace CloudflareSolverRe.Types 2 | { 3 | public enum CloudflareProtection 4 | { 5 | NoProtection, 6 | JavaScript, 7 | Captcha, 8 | Banned, 9 | Unknown, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/DetectResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CloudflareSolverRe.Types 4 | { 5 | public struct DetectResult : IEquatable 6 | { 7 | public CloudflareProtection Protection { get; set; } 8 | public string Html { get; set; } 9 | public bool SupportsHttp { get; set; } 10 | 11 | public override string ToString() => Protection.ToString(); 12 | 13 | public static bool operator ==(DetectResult resultA, DetectResult resultB) => resultA.Equals(resultB); 14 | 15 | public static bool operator !=(DetectResult resultA, DetectResult resultB) => !(resultA == resultB); 16 | 17 | public override bool Equals(object obj) 18 | { 19 | var other = obj as DetectResult?; 20 | return other.HasValue && Equals(other.Value); 21 | } 22 | 23 | public bool Equals(DetectResult other) => other.Protection.Equals(Protection) && other.Html.Equals(Html); 24 | 25 | public override int GetHashCode() => Protection.GetHashCode(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/ICloudflareSolver.cs: -------------------------------------------------------------------------------- 1 | namespace CloudflareSolverRe.Types 2 | { 3 | internal interface ICloudflareSolver 4 | { 5 | //int MaxJavascriptTries { get; set; } 6 | int MaxTries { get; set; } 7 | int MaxCaptchaTries { get; set; } 8 | int ClearanceDelay { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/Javascript/JsChallenge.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using Jint; 4 | 5 | namespace CloudflareSolverRe.Types.Javascript 6 | { 7 | public class JsChallenge 8 | { 9 | private static readonly Regex JsCodeRegex = new Regex(@"setTimeout\s*\([^{]*{(?.+\.submit\s*\(\s*\)\s*;)\s*}\s*,\s*(?\d+)\s*\)", RegexOptions.Singleline); 10 | public Uri SiteUrl { get; set; } 11 | public string JsCode { get; set; } 12 | public int Delay { get; set; } 13 | public JsForm Form { get; set; } 14 | private string JschlAnswer { get; set; } 15 | private static bool _debug; 16 | private static string _html; 17 | 18 | public static JsChallenge Parse(string html, Uri siteUrl, bool debug) 19 | { 20 | _debug = debug; 21 | return Parse(html, siteUrl); 22 | } 23 | 24 | public static JsChallenge Parse(string html, Uri siteUrl) 25 | { 26 | // remove html comments 27 | _html = Regex.Replace(html, "", "", RegexOptions.Singleline); 28 | 29 | // parse challenge 30 | var jsCodeMatch = JsCodeRegex.Match(_html); 31 | if (!jsCodeMatch.Success) 32 | throw new Exception("Error parsing JS challenge HTML"); 33 | 34 | return new JsChallenge 35 | { 36 | SiteUrl = siteUrl, 37 | JsCode = jsCodeMatch.Groups["js_code"].Value, 38 | Delay = int.Parse(jsCodeMatch.Groups["delay"].Value), 39 | Form = new JsForm 40 | { 41 | Action = System.Net.WebUtility.HtmlDecode(GetFieldAttrByAttr("id", "challenge-form", "action")), 42 | R = GetFieldAttrByAttr("name", "r", "value"), 43 | VerificationCode = GetFieldAttrByAttr("name", "jschl_vc", "value"), 44 | Pass = GetFieldAttrByAttr("name", "pass", "value") 45 | } 46 | }; 47 | } 48 | 49 | public string Solve() 50 | { 51 | var engine = new Engine().SetValue("invokeCSharp", this); 52 | 53 | // Jint only implements the Javascript language, we have to implement / mock the DOM methods 54 | // and some unimplemented methods like String.italics 55 | engine.Execute(@" 56 | 57 | //invokeCSharp.JsCallLog(""Example debug message from Javascript""); 58 | 59 | document = { 60 | getElementById: function(id) { 61 | var fakeObj = { 62 | submit: function () {} 63 | } 64 | Object.defineProperty(fakeObj, ""innerHTML"", { 65 | get: function () { return invokeCSharp.JsCallInnerHtml(id); } 66 | }); 67 | Object.defineProperty(fakeObj, ""value"", { 68 | get: function () { return invokeCSharp.JsCallGetAttribute(id, ""value""); }, 69 | set: function(value) { invokeCSharp.JsCallSetAttribute(id, ""value"", value); } 70 | }); 71 | Object.defineProperty(fakeObj, ""action"", { 72 | get: function () { return invokeCSharp.JsCallGetAttribute(id, ""action""); }, 73 | set: function(value) { invokeCSharp.JsCallSetAttribute(id, ""action"", value); } 74 | }); 75 | return fakeObj; 76 | }, 77 | createElement: function(element) { 78 | return { 79 | innerHTML: """", 80 | firstChild: { 81 | href: invokeCSharp.JsCallGetHref() 82 | } 83 | } 84 | } 85 | }; 86 | 87 | location = { 88 | hash: invokeCSharp.JsCallGetHash() 89 | } 90 | 91 | setInterval = function() { 92 | return; 93 | }; 94 | 95 | String.prototype.italics = function() { 96 | return """" + this + """"; 97 | }; 98 | 99 | (function() { " + JsCode + " }())"); 100 | 101 | return JschlAnswer; 102 | } 103 | 104 | // This method is used to print traces from Javascript code 105 | // ReSharper disable once UnusedMember.Global 106 | public static void JsCallLog(string message) 107 | { 108 | DebugLog($"JsCallLog message: {message}"); 109 | } 110 | 111 | // ReSharper disable once UnusedMember.Global 112 | public string JsCallGetHref() 113 | { 114 | // currently only used to get the base url (js: t.firstChild.href) 115 | var href = $"{SiteUrl.Scheme}://{SiteUrl.Host}/"; 116 | DebugLog($"JsCallGetHref return: {href}"); 117 | return href; 118 | } 119 | 120 | // ReSharper disable once UnusedMember.Global 121 | public string JsCallGetHash() 122 | { 123 | // currently only used to get url hash (js: f.action += location.hash) 124 | var hash = SiteUrl.Fragment; 125 | DebugLog($"JsCallGetHash return: {hash}"); 126 | return hash; 127 | } 128 | 129 | // ReSharper disable once UnusedMember.Global 130 | public string JsCallInnerHtml(string id) 131 | { 132 | var innerHtml = GetInnerHtmlById(id); 133 | DebugLog($"JsCallInnerHtml id: {id} return: {innerHtml}"); 134 | return innerHtml; 135 | } 136 | 137 | // ReSharper disable once UnusedMember.Global 138 | public string JsCallGetAttribute(string id, string attr) 139 | { 140 | // currently only used in form.action (js: f.action += location.hash;) 141 | DebugLog($"JsCallGetAttribute id: {id} attr: {attr}"); 142 | return Form.Action; 143 | } 144 | 145 | // ReSharper disable once UnusedMember.Global 146 | public void JsCallSetAttribute(string id, string attr, string value) 147 | { 148 | // currently only used in: 149 | // ]*id\s*=\s*""{id}""[^>]*>(?\S+?)<\s*/", RegexOptions.Singleline); 169 | var match = regEx.Match(_html); 170 | if (!match.Success) 171 | { 172 | throw new Exception($"GetInnerHtmlById not found! id: {id}"); 173 | } 174 | var innerHtml = match.Groups["innerHTML"].Value; 175 | DebugLog($"GetInnerHtmlById id: {id} return: {innerHtml}"); 176 | return innerHtml; 177 | } 178 | 179 | private static string GetFieldAttrByAttr(string sAttrId, string sAttrValue, string returnAttr) 180 | { 181 | var regEx = new Regex($@"<[^>]*{sAttrId}\s*=\s*""{sAttrValue}""[^>]*>", RegexOptions.Singleline); 182 | var match = regEx.Match(_html); 183 | if (!match.Success) 184 | { 185 | throw new Exception($"GetInputValueByName element not found! {sAttrId}: {sAttrValue}"); 186 | } 187 | var fHtml = match.Groups[0].Value; 188 | 189 | var regEx2 = new Regex($@"[^A-Za-z0-9]{returnAttr}\s*=\s*""([^""]*)""", RegexOptions.Singleline); 190 | var match2 = regEx2.Match(fHtml); 191 | if (!match2.Success) 192 | { 193 | throw new Exception($"GetInputValueByName attribute not found! returnAttr: {returnAttr} fHtml: {fHtml}"); 194 | } 195 | var res = match2.Groups[1].Value; 196 | DebugLog($"GetFieldAttrByAttr {sAttrId}={sAttrValue} returnAttr: {returnAttr} return: {res}"); 197 | return res; 198 | } 199 | 200 | private static void DebugLog(string message) 201 | { 202 | if (_debug) 203 | { 204 | Console.WriteLine($"JsChallenge DEBUG {message}"); 205 | } 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/Javascript/JsChallengeSolution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CloudflareSolverRe.Types.Javascript 5 | { 6 | /// 7 | /// Holds the information, which is required to pass the CloudFlare clearance. 8 | /// 9 | public class JsChallengeSolution : IEquatable 10 | { 11 | public string ClearancePage { get; } 12 | public string VerificationCode { get; } 13 | public string Pass { get; } 14 | public string R { get; } 15 | public string Answer { get; } 16 | 17 | public string ClearanceUrl => ClearancePage; 18 | 19 | public Dictionary ClearanceBody => new Dictionary 20 | { 21 | { "r", Uri.EscapeDataString(R) }, 22 | { "jschl_vc", VerificationCode}, 23 | { "pass", Pass }, 24 | { "jschl_answer", Answer } 25 | }; 26 | 27 | public JsChallengeSolution(string clearancePage, string r, string verificationCode, string pass, string answer) 28 | { 29 | ClearancePage = clearancePage; 30 | R = r; 31 | VerificationCode = verificationCode; 32 | Pass = pass; 33 | Answer = answer; 34 | } 35 | 36 | public JsChallengeSolution(string clearancePage, JsForm form, string answer) 37 | { 38 | ClearancePage = clearancePage; 39 | R = form.R; 40 | VerificationCode = form.VerificationCode; 41 | Pass = form.Pass; 42 | Answer = answer; 43 | } 44 | 45 | public JsChallengeSolution(Uri siteUrl, JsForm form, string answer) 46 | { 47 | ClearancePage = $"{siteUrl.Scheme}://{siteUrl.Host}{form.Action}"; 48 | R = form.R; 49 | VerificationCode = form.VerificationCode; 50 | Pass = form.Pass; 51 | Answer = answer; 52 | } 53 | 54 | public static bool operator ==(JsChallengeSolution solution1, JsChallengeSolution solution2) => 55 | (solution1 is null) ? (solution2 is null) : solution1.Equals(solution2); 56 | 57 | public static bool operator !=(JsChallengeSolution solution1, JsChallengeSolution solution2) => !(solution1 == solution2); 58 | 59 | public override bool Equals(object obj) => Equals(obj as JsChallengeSolution); 60 | 61 | public bool Equals(JsChallengeSolution other) => other != null && other.ClearanceUrl == ClearanceUrl; 62 | 63 | public override int GetHashCode() => ClearanceUrl.GetHashCode(); 64 | } 65 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/Javascript/JsForm.cs: -------------------------------------------------------------------------------- 1 | namespace CloudflareSolverRe.Types.Javascript 2 | { 3 | public class JsForm 4 | { 5 | public string Action { get; set; } 6 | public string R { get; set; } 7 | public string VerificationCode { get; set; } 8 | public string Pass { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/SessionCookies.cs: -------------------------------------------------------------------------------- 1 | using CloudflareSolverRe.Extensions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | 7 | namespace CloudflareSolverRe.Types 8 | { 9 | public class SessionCookies : IEquatable 10 | { 11 | internal const string IdCookieName = "__cfduid"; 12 | internal const string ClearanceCookieName = "cf_clearance"; 13 | 14 | private readonly Uri siteUri; 15 | 16 | public Cookie Cfduid { get; set; } 17 | public Cookie Cf_Clearance { get; set; } 18 | public bool Valid => Cfduid != null && Cf_Clearance != null; 19 | 20 | public SessionCookies(Uri siteUri) 21 | { 22 | this.siteUri = siteUri; 23 | } 24 | 25 | public SessionCookies(Cookie cfduid, Cookie cf_clearance, Uri siteUri) 26 | { 27 | this.Cfduid = cfduid; 28 | this.Cf_Clearance = cf_clearance; 29 | this.siteUri = siteUri; 30 | } 31 | 32 | 33 | public static SessionCookies FromCookieContainer(CookieContainer cookieContainer, Uri uri) 34 | { 35 | return new SessionCookies(uri) 36 | { 37 | Cfduid = cookieContainer.GetCookie(uri, IdCookieName), 38 | Cf_Clearance = cookieContainer.GetCookie(uri, ClearanceCookieName) 39 | }; 40 | } 41 | 42 | public static SessionCookies FromCookieCollection(CookieCollection cookieCollection, Uri uri) 43 | { 44 | var cookies = cookieCollection.Cast(); 45 | 46 | return new SessionCookies(uri) 47 | { 48 | Cfduid = cookies.FirstOrDefault(c => c.Name.Equals(IdCookieName)), 49 | Cf_Clearance = cookies.FirstOrDefault(c => c.Name.Equals(ClearanceCookieName)) 50 | }; 51 | } 52 | 53 | 54 | public CookieContainer AsCookieContainer() 55 | { 56 | var cookieContainer = new CookieContainer(); 57 | cookieContainer.Add(siteUri, Cfduid); 58 | cookieContainer.Add(siteUri, Cf_Clearance); 59 | return cookieContainer; 60 | } 61 | 62 | public CookieCollection AsCookieCollection() => new CookieCollection { Cfduid, Cf_Clearance }; 63 | 64 | public string AsHeaderString() => Valid ? $"{Cfduid.ToHeaderValue()};{Cf_Clearance.ToHeaderValue()};" : ""; 65 | 66 | 67 | public override bool Equals(object obj) => Equals(obj as SessionCookies); 68 | 69 | public bool Equals(SessionCookies other) => 70 | other != null && this.Cfduid == other.Cfduid && this.Cf_Clearance == other.Cf_Clearance; 71 | 72 | public override int GetHashCode() 73 | { 74 | var hashCode = 238132315; 75 | hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Cfduid); 76 | hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Cf_Clearance); 77 | return hashCode; 78 | } 79 | 80 | public static bool operator ==(SessionCookies cookies1, SessionCookies cookies2) => 81 | (cookies1 is null) ? (cookies2 is null) : cookies1.Equals(cookies2); 82 | 83 | public static bool operator !=(SessionCookies cookies1, SessionCookies cookies2) => !(cookies1 == cookies2); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Types/SolveResult.cs: -------------------------------------------------------------------------------- 1 | using CloudflareSolverRe.Constants; 2 | using System.Net.Http; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace CloudflareSolverRe.Types 6 | { 7 | public struct SolveResult 8 | { 9 | public bool Success; 10 | public string FailReason; 11 | public DetectResult DetectResult; 12 | public string UserAgent; 13 | public SessionCookies Cookies; 14 | internal DetectResult? NewDetectResult; 15 | internal HttpResponseMessage Response; 16 | 17 | public static readonly SolveResult NoProtection = new SolveResult 18 | { 19 | Success = true, 20 | Cookies = new SessionCookies(null) 21 | }; 22 | 23 | public static readonly SolveResult Banned = new SolveResult 24 | { 25 | Success = false, 26 | FailReason = Errors.IpAddressIsBanned, 27 | }; 28 | 29 | public static readonly SolveResult Unknown = new SolveResult 30 | { 31 | Success = false, 32 | FailReason = Errors.UnknownProtectionDetected, 33 | }; 34 | 35 | public SolveResult(bool success, string layer, string failReason, DetectResult detectResult, [Optional]SessionCookies cookies, [Optional]string userAgent, [Optional]HttpResponseMessage response) 36 | { 37 | Success = success; 38 | FailReason = !string.IsNullOrEmpty(failReason) ? $"Cloudflare [{layer}]: {failReason}" : null; 39 | DetectResult = detectResult; 40 | Cookies = cookies; 41 | UserAgent = userAgent; 42 | NewDetectResult = null; 43 | Response = response; 44 | } 45 | 46 | public SolveResult(bool success, string failReason, DetectResult detectResult, [Optional]SessionCookies cookies, [Optional]string userAgent, [Optional]HttpResponseMessage response) 47 | { 48 | Success = success; 49 | FailReason = failReason; 50 | DetectResult = detectResult; 51 | Cookies = cookies; 52 | UserAgent = userAgent; 53 | NewDetectResult = null; 54 | Response = response; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Utilities/SemaphoreLocker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace CloudflareSolverRe.Utilities 6 | { 7 | public class SemaphoreLocker 8 | { 9 | private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); 10 | 11 | public async Task LockAsync(Func worker) 12 | where T : Task 13 | { 14 | await _semaphore.WaitAsync(); 15 | try 16 | { 17 | await worker(); 18 | } 19 | finally 20 | { 21 | _semaphore.Release(); 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/CloudflareSolverRe/Utilities/Utils.cs: -------------------------------------------------------------------------------- 1 | using CloudflareSolverRe.Constants; 2 | using System; 3 | 4 | namespace CloudflareSolverRe.Utilities 5 | { 6 | public static class Utils 7 | { 8 | private static readonly Random random = new Random(); 9 | 10 | public static string GetGenerateRandomUserAgent() => 11 | UserAgents.UserAgentList[random.Next(0, UserAgents.UserAgentList.Count)]; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/CloudflareSolverRe/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | dotnet publish -c Release -f netstandard1.1 -------------------------------------------------------------------------------- /test/CloudflareSolverRe.Tests/ClearanceHandlerTests.cs: -------------------------------------------------------------------------------- 1 | using CaptchaSharp; 2 | using CaptchaSharp.Services; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using System; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace CloudflareSolverRe.Tests 10 | { 11 | [TestClass] 12 | public class ClearanceHandlerTests 13 | { 14 | private static readonly Uri speedcdUri = new Uri("https://speed.cd/"); 15 | private static readonly Uri japscanUri = new Uri("https://japscan.to"); 16 | 17 | [TestMethod] 18 | public async Task SolveWebsiteChallenge_speedcd() 19 | { 20 | var handler = new ClearanceHandler 21 | { 22 | MaxTries = 3, 23 | ClearanceDelay = 3000 24 | }; 25 | 26 | var client = new HttpClient(handler); 27 | 28 | HttpResponseMessage response = await client.GetAsync(speedcdUri); 29 | 30 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 31 | } 32 | 33 | [TestMethod] 34 | public async Task SolveWebsiteChallenge_speedcd_WithAntiCaptcha() 35 | { 36 | if (Settings.AntiCaptchaApiKey.Equals("YOUR_API_KEY")) 37 | return; 38 | 39 | var handler = new ClearanceHandler(new AntiCaptchaService(Settings.AntiCaptchaApiKey)) 40 | { 41 | MaxTries = 2, 42 | MaxCaptchaTries = 2 43 | }; 44 | 45 | var client = new HttpClient(handler); 46 | 47 | HttpResponseMessage response = await client.GetAsync(speedcdUri); 48 | 49 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 50 | } 51 | 52 | [TestMethod] 53 | public async Task SolveWebsiteChallenge_speedcd_With2Captcha() 54 | { 55 | if (Settings.TwoCaptchaApiKey.Equals("YOUR_API_KEY")) 56 | return; 57 | 58 | var handler = new ClearanceHandler(new TwoCaptchaService(Settings.TwoCaptchaApiKey)) 59 | { 60 | MaxTries = 2, 61 | MaxCaptchaTries = 2 62 | }; 63 | 64 | var client = new HttpClient(handler); 65 | 66 | HttpResponseMessage response = await client.GetAsync(speedcdUri); 67 | 68 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 69 | } 70 | 71 | 72 | [TestMethod] 73 | public async Task SolveWebsiteChallenge_japscan() 74 | { 75 | var handler = new ClearanceHandler 76 | { 77 | MaxTries = 3, 78 | ClearanceDelay = 3000 79 | }; 80 | 81 | var client = new HttpClient(handler); 82 | 83 | HttpResponseMessage response = await client.GetAsync(japscanUri); 84 | 85 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 86 | } 87 | 88 | [TestMethod] 89 | public async Task SolveWebsiteChallenge_japscan_WithAntiCaptcha() 90 | { 91 | if (Settings.AntiCaptchaApiKey.Equals("YOUR_API_KEY")) 92 | return; 93 | 94 | var handler = new ClearanceHandler(new AntiCaptchaService(Settings.AntiCaptchaApiKey)) 95 | { 96 | MaxTries = 2, 97 | MaxCaptchaTries = 2 98 | }; 99 | 100 | var client = new HttpClient(handler); 101 | 102 | HttpResponseMessage response = await client.GetAsync(japscanUri); 103 | 104 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 105 | } 106 | 107 | [TestMethod] 108 | public async Task SolveWebsiteChallenge_japscan_With2Captcha() 109 | { 110 | if (Settings.TwoCaptchaApiKey.Equals("YOUR_API_KEY")) 111 | return; 112 | 113 | var handler = new ClearanceHandler(new TwoCaptchaService(Settings.TwoCaptchaApiKey)) 114 | { 115 | MaxTries = 2, 116 | MaxCaptchaTries = 2 117 | }; 118 | 119 | var client = new HttpClient(handler); 120 | 121 | HttpResponseMessage response = await client.GetAsync(japscanUri); 122 | 123 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 124 | } 125 | 126 | 127 | [TestMethod] 128 | public async Task SolveWebsiteChallenge_github() 129 | { 130 | var target = new Uri("https://github.com/RyuzakiH/CloudflareSolverRe"); 131 | 132 | var handler = new ClearanceHandler 133 | { 134 | MaxTries = 3, 135 | ClearanceDelay = 3000 136 | }; 137 | 138 | var client = new HttpClient(handler); 139 | 140 | var content = await client.GetStringAsync(target); 141 | 142 | Assert.IsTrue(content.Contains("RyuzakiH")); 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /test/CloudflareSolverRe.Tests/CloudflareSolverRe.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | PreserveNewest 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/CloudflareSolverRe.Tests/CloudflareSolverTests.cs: -------------------------------------------------------------------------------- 1 | using CaptchaSharp; 2 | using CaptchaSharp.Services; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using System; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace CloudflareSolverRe.Tests 10 | { 11 | [TestClass] 12 | public class CloudflareSolverTests 13 | { 14 | private static readonly Uri soundparkUri = new Uri("https://sound-park.world/"); 15 | 16 | [TestMethod] 17 | public async Task SolveWebsiteChallenge_soundpark() 18 | { 19 | var cf = new CloudflareSolver 20 | { 21 | MaxTries = 3, 22 | ClearanceDelay = 3000 23 | }; 24 | 25 | var handler = new HttpClientHandler(); 26 | var client = new HttpClient(handler); 27 | 28 | var result = await cf.Solve(client, handler, soundparkUri); 29 | 30 | Assert.IsTrue(result.Success); 31 | 32 | HttpResponseMessage response = await client.GetAsync(soundparkUri); 33 | 34 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 35 | } 36 | 37 | [TestMethod] 38 | public async Task SolveWebsiteChallenge_soundpark_WithAntiCaptcha() 39 | { 40 | if (Settings.AntiCaptchaApiKey.Equals("YOUR_API_KEY")) 41 | return; 42 | 43 | var cf = new CloudflareSolver(new AntiCaptchaService(Settings.AntiCaptchaApiKey)) 44 | { 45 | MaxTries = 2, 46 | MaxCaptchaTries = 2 47 | }; 48 | 49 | var handler = new HttpClientHandler(); 50 | var client = new HttpClient(handler); 51 | 52 | var result = await cf.Solve(client, handler, soundparkUri); 53 | 54 | Assert.IsTrue(result.Success); 55 | 56 | HttpResponseMessage response = await client.GetAsync(soundparkUri); 57 | 58 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 59 | } 60 | 61 | [TestMethod] 62 | public async Task SolveWebsiteChallenge_soundpark_With2Captcha() 63 | { 64 | if (Settings.TwoCaptchaApiKey.Equals("YOUR_API_KEY")) 65 | return; 66 | 67 | var cf = new CloudflareSolver(new TwoCaptchaService(Settings.TwoCaptchaApiKey)) 68 | { 69 | MaxTries = 2, 70 | MaxCaptchaTries = 2 71 | }; 72 | 73 | var handler = new HttpClientHandler(); 74 | var client = new HttpClient(handler); 75 | 76 | var result = await cf.Solve(client, handler, soundparkUri); 77 | 78 | Assert.IsTrue(result.Success); 79 | 80 | HttpResponseMessage response = await client.GetAsync(soundparkUri); 81 | 82 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 83 | } 84 | 85 | } 86 | } -------------------------------------------------------------------------------- /test/CloudflareSolverRe.Tests/IntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.IO; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | 7 | namespace CloudflareSolverRe.Tests 8 | { 9 | [TestClass] 10 | public class IntegrationTests 11 | { 12 | private static readonly Uri soundparkUri = new Uri("https://sound-park.world/"); 13 | 14 | [TestMethod] 15 | public async Task SolveWebsiteChallenge_soundpark_WebClient() 16 | { 17 | var cf = new CloudflareSolver 18 | { 19 | MaxTries = 3, 20 | ClearanceDelay = 3000 21 | }; 22 | 23 | var result = await cf.Solve(soundparkUri); 24 | 25 | Assert.IsTrue(result.Success); 26 | 27 | var client = new WebClient(); 28 | client.Headers.Add(HttpRequestHeader.Cookie, result.Cookies.AsHeaderString()); 29 | client.Headers.Add(HttpRequestHeader.UserAgent, result.UserAgent); 30 | 31 | var content = await client.DownloadStringTaskAsync(soundparkUri); 32 | 33 | Assert.IsTrue(content.Contains("Music Torrent Tracker")); 34 | } 35 | 36 | [TestMethod] 37 | public async Task SolveWebsiteChallenge_soundpark_HttpWebRequest() 38 | { 39 | var cf = new CloudflareSolver 40 | { 41 | MaxTries = 3, 42 | ClearanceDelay = 3000 43 | }; 44 | 45 | var result = await cf.Solve(soundparkUri); 46 | 47 | Assert.IsTrue(result.Success); 48 | 49 | var request = (HttpWebRequest)WebRequest.Create(soundparkUri); 50 | request.Headers.Add(HttpRequestHeader.Cookie, result.Cookies.AsHeaderString()); 51 | request.Headers.Add(HttpRequestHeader.UserAgent, result.UserAgent); 52 | 53 | var response = (HttpWebResponse)await request.GetResponseAsync(); 54 | 55 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /test/CloudflareSolverRe.Tests/JsChallengeTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.IO; 4 | using CloudflareSolverRe.Types.Javascript; 5 | 6 | namespace CloudflareSolverRe.Tests 7 | { 8 | [TestClass] 9 | public class JsChallengeTests 10 | { 11 | [TestMethod] 12 | public void JsChallengeUnitTest() 13 | { 14 | var htmlFile = Path.Combine("resources", "js_challenge.html"); 15 | 16 | var html = File.ReadAllText(htmlFile); 17 | var siteUrl = new Uri("https://btdb.io/search/harry/0/#test_hash"); 18 | 19 | // parse the challenge and assert known values 20 | var challenge = JsChallenge.Parse(html, siteUrl, true); 21 | 22 | Assert.AreEqual(challenge.SiteUrl, siteUrl); 23 | Assert.IsTrue(challenge.JsCode.Contains("String.fromCharCode") && challenge.JsCode.Contains("submit(")); 24 | Assert.AreEqual(challenge.Delay, 4000); 25 | 26 | Assert.AreEqual(challenge.Form.Action, "/search/harry/0/?__cf_chl_jschl_tk__=111b63f2aa783fa1458a09cd562685cf6dcf28e8-1590518849-0-ASgHSNOYGTw7DJpKQcXxwYce-WrnT3c_j4iCBD_xmYH524kCC78aJVtx4_HecIhTYJRf012fmJuijjoHiCyOKxHa3YDbO3KhE6DBv5SMaIyNBdFrIpvHdKG5luYmWbYX7JH39Nv7QqGmbntmUnRpnioF4EJ0exYbpoluiSLqPKROL5UPRw_mm0DTxZi7wIGKWFCkYRx3agmVaPVTJElS9I8jsVj8__KCMs3b5C6r7TlDTdpIEO3CNYMy3uHdH3c-RmPYXb7YkQmrA-XOcWm9_jjduFlE-lEkWA5eF9GjaMfn"); 27 | Assert.AreEqual(challenge.Form.Pass, "1590518853.067-5CNneVTlD1"); 28 | Assert.AreEqual(challenge.Form.R, "c484b68db43ade37765408609249e35cbd805676-1590518849-0-Ae7887BqC1RYufgoYTaFOxcPptetydp7cdmrcPlil13gtGomFZwrejb40qWVZZ+WGKGDnBUkzIb56RdLSCKnlVjX1Uc8OW8poDo+U4E53xN6QknTWHFx4LnMNjzuZ8dpAzLoWLhPRkxp1uuERt5P0haaIY5bqeTssoI0YHer2JPZekGHHQtG3Ny2DQxmGO7e9STWE/OoCVpDxFmxdM3r7Qf/yYrpt1KUagghsxf4KTLPgKRUuBjuc6BS48wl1t/Xu9KcPhVFjWGqL8i3/z/Art4aqFmW5wNWoDFSWxvfaRCfeokAeHte7/yVogeJNGRBzNeLg6mg9cak2ZNgwbytBjRWHzTWpmWn27byCV/boEfjbWobQwTI4xfJvWxWDw0il2e9P5HVMTzFTK8M/q1fW+PfkJ9skkOnY0ygEtw4+M4o/VszJCDB3dp4EciRf2Rowp4k8JWi3x6fehwDETmd8vN58EAqVTyyHLuJD/FM21NMzZd8rSVuO8/2OKHfDZ0hFWc+SfAYEJApeswvH5IhhWin0Opgnfw5dMc3tgcYWqicIxS6geV000b0cpJE7QHqJGVqQjkWiUMz64UY8Tu2hxPsyunG2TOV1rk6Bi1p4Q8eHjJd0t7AOhiuRkch6cPBMNKhVVVColMyH3HAvL4sOIkMq0n+QLIzy4OHEquSjqEuWWPNk7i1jVVub3/FYe0JDrfAmJwy4T317pbklcAoHdLLuVsJkV72Veo2zvsnHOXgjxlYx4ale9+XIkZJ1Wp2LwcIsDAgc0syJc9BJrsQ8J8I3pc5ngOZZSpJnvx4vhLeV1C6i/nVOI9dNbyPLnLTX9qXsJSnSMC9f6Tx8NxGx/Z6E+aAg6sCQAv9jnufJxsgYaJa3BDIkv9LA/SBgH4Yp9YYLAh5p0FAsw+I80n8jiMT83eU1Yst9mrWhKblLUQ65TjumXIhOL+3zF9R64ich2pEaBALx74s7CEGyPshwNmbXtDUdN/XskqhQArx8O85RBfjbZjg+22fIGW1RMxjw3YwOxtfMR9SXCphsfQrO/bCjUL91jTJ8jFuX50lX1Gvvjpx9NRWIVdVm3cReJMPewIOopY5LQc9mM5gXcVrnM4hm4YRP4w9sTrMBN4zOh1y1lkj9mlwz9wZmzzdDRPLErad+LmrJIeOuc7QpyuQjuWgZS4rRxbEINvnf7x6Q+NzKxJN2Tl9q2EwBhKydryS/hFWGbwvecduhm9k+IOs97SPR8Js/tWp6GFemtHmkbZzDvrU1rv96KD3za3UAkUNdNVFhv6/J12ZCZypxFvcB5yEWL5CxCGir16MlsbC3QneoqOhJRPs5XbqV8M1jm0gszKT1fJZxW8i0iyhQWwDnGCznjh5Hu00PmBUv4kmAS7jHzJkofhG3UbO7ZaZj1pxF+W/qyddG+11HDqOBeofTpM="); 29 | Assert.AreEqual(challenge.Form.VerificationCode, "84b59970d68c15b786b5a85483d27cb4"); 30 | 31 | // solve the challenge and assert known values 32 | var jschlAnswer = challenge.Solve(); 33 | 34 | // the js code changes the form action to include the #hash (if there is hash) 35 | Assert.AreEqual(challenge.Form.Action, "/search/harry/0/?__cf_chl_jschl_tk__=111b63f2aa783fa1458a09cd562685cf6dcf28e8-1590518849-0-ASgHSNOYGTw7DJpKQcXxwYce-WrnT3c_j4iCBD_xmYH524kCC78aJVtx4_HecIhTYJRf012fmJuijjoHiCyOKxHa3YDbO3KhE6DBv5SMaIyNBdFrIpvHdKG5luYmWbYX7JH39Nv7QqGmbntmUnRpnioF4EJ0exYbpoluiSLqPKROL5UPRw_mm0DTxZi7wIGKWFCkYRx3agmVaPVTJElS9I8jsVj8__KCMs3b5C6r7TlDTdpIEO3CNYMy3uHdH3c-RmPYXb7YkQmrA-XOcWm9_jjduFlE-lEkWA5eF9GjaMfn#test_hash"); 36 | Assert.AreEqual(jschlAnswer, "18.0511703099"); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /test/CloudflareSolverRe.Tests/Settings.cs: -------------------------------------------------------------------------------- 1 | namespace CloudflareSolverRe.Tests 2 | { 3 | internal static class Settings 4 | { 5 | internal const string AntiCaptchaApiKey = "YOUR_API_KEY"; 6 | internal const string TwoCaptchaApiKey = "YOUR_API_KEY"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/CloudflareSolverRe.Tests/resources/js_challenge.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Just a moment... 11 | 30 | 31 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 124 | 125 | 126 |
86 |
87 | 90 | 105 | 106 |
107 | 108 | 109 | 110 | 111 | 112 |
113 |
+((!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![])+(!+[]-(!![]))+(!+[]+(!![])+!![]+!![])+(!+[]+(!![])-[])+(!+[]+(!![])+!![]+!![]+!![])+(!+[]+(!![])+!![]))/+((!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]-(!![]))+(!+[]+(!![])+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(+!![])+(!+[]+(!![])-[])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![]))
+((!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+[])+(!+[]+(!![])+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]-(!![]))+(!+[]+(!![])+!![]+!![])+(+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![])+(+!![]))/+((!+[]+(!![])-[]+[])+(!+[]-(!![]))+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])-[]))
+((!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+[])+(!+[]+(!![])+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]-(!![]))+(!+[]+(!![])+!![]+!![])+(+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]))/+((+!![]+[])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![])+(+!![])+(!+[]+(!![])+!![]+!![])+(!+[]-(!![]))+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]))
+((!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+[])+(!+[]+(!![])+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]-(!![]))+(!+[]+(!![])+!![]+!![])+(+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]))/+((!+[]+(!![])+!![]+!![]+!![]+!![]+[])+(!+[]+(!![])-[])+(!+[]+(!![])+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![])+(!+[]+(!![])+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]))
+((!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![])+(!+[]-(!![]))+(!+[]+(!![])+!![]+!![])+(!+[]+(!![])-[])+(!+[]+(!![])+!![]+!![])+(!+[]+(!![])+!![]))/+((!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+[])+(!+[]-(!![]))+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]-(!![]))+(!+[]+(!![])+!![])+(!+[]+(!![])+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(+!![]))
+((!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+[])+(!+[]+(!![])+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![])+(!+[]-(!![]))+(!+[]+(!![])+!![]))/+((!+[]+(!![])+!![]+!![]+!![]+!![]+[])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![])+(!+[]+(!![])+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]+!![]+!![]+!![])+(!+[]+(!![])+!![]))
114 |
115 |
116 | 117 | 118 |
119 | DDoS protection by Cloudflare 120 |
121 | Ray ID: 5999aa36aa48ff54 122 |
123 |
127 | 128 | 129 | --------------------------------------------------------------------------------