File size: 9,042 Bytes
fa9a9a3
3b4eae6
fa9a9a3
 
3b4eae6
 
fa9a9a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3b4eae6
 
 
fa9a9a3
3b4eae6
 
fa9a9a3
3b4eae6
fa9a9a3
3b4eae6
 
fa9a9a3
3b4eae6
 
fa9a9a3
 
 
 
 
 
 
3b4eae6
 
fa9a9a3
3b4eae6
 
 
 
 
 
fa9a9a3
 
 
3b4eae6
fa9a9a3
3b4eae6
 
 
 
fa9a9a3
3b4eae6
 
fa9a9a3
3b4eae6
 
fa9a9a3
3b4eae6
 
fa9a9a3
3b4eae6
 
 
 
 
 
 
 
 
fa9a9a3
3b4eae6
 
 
fa9a9a3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// server.js
const express=require('express');const {v4:uuidv4}=require('uuid');const {createWorker,PSM}=require('tesseract.js');const genericPool=require('generic-pool');const sharp=require('sharp');
const crypto = require('crypto'); // Для AES дешифрования

const app=express();const port=process.env.PORT||7860;app.use(express.json({limit:'5mb'}));
const cs={};const CEX=5*60*1000;const OCS="ACEFHJKMNPRTUVWXY23469";const OCL=6;
const WCC=[{n:"r",rgb:[1,0,0]},{n:"g",rgb:[0,1,0]},{n:"b",rgb:[0,0,1]},{n:"y",rgb:[1,1,0]}];

// --- AES-GCM Дешифрование ---
const AES_KEY_STRING = "ThisIsMySuperSecretKeyForAES128"; // ТОТ ЖЕ КЛЮЧ, ЧТО И НА КЛИЕНТЕ (16 байт для AES-128)
const AES_KEY_BUFFER = Buffer.from(AES_KEY_STRING.slice(0, 16), 'utf-8'); // Используем первые 16 байт
const AES_ALGORITHM = 'aes-128-gcm';
const IV_LENGTH = 12; // 96 bits
const AUTH_TAG_LENGTH = 16; // GCM auth tag

function decryptData(encryptedBase64) {
    try {
        const combinedBuffer = Buffer.from(encryptedBase64, 'base64');
        const iv = combinedBuffer.subarray(0, IV_LENGTH);
        const ciphertextWithTag = combinedBuffer.subarray(IV_LENGTH);
        const ciphertext = ciphertextWithTag.subarray(0, ciphertextWithTag.length - AUTH_TAG_LENGTH);
        const authTag = ciphertextWithTag.subarray(ciphertextWithTag.length - AUTH_TAG_LENGTH);

        const decipher = crypto.createDecipheriv(AES_ALGORITHM, AES_KEY_BUFFER, iv);
        decipher.setAuthTag(authTag);
        let decrypted = decipher.update(ciphertext, null, 'utf8'); // null для inputEncoding, если ciphertext это Buffer
        decrypted += decipher.final('utf8');
        return JSON.parse(decrypted);
    } catch (e) {
        console.error("AES Decrypt Error:", e.message.slice(0,100));
        return null;
    }
}
// --- Конец AES-GCM Дешифрования ---

const TPL=4;let tp=null;let pDO=false;
const fac={create:async()=>{const w=await createWorker('eng',1,{cachePath:'/tmp/.tesscache',cacheMethod:'fs'});await w.setParameters({tessedit_char_whitelist:OCS,tessedit_pageseg_mode:PSM.SINGLE_WORD});return w;},destroy:async(w)=>{await w.terminate();}};
const pO={min:TPL,max:TPL,acquireTimeoutMillis:7000};
async function iTP(){try{tp=genericPool.createPool(fac,pO);console.log(`Pool(s:${TPL})OK`);const ws=await Promise.all(Array(TPL).fill(null).map(()=>tp.acquire()));ws.forEach(w=>tp.release(w));console.log(`${TPL}wOK`);}catch(e){console.error("PoolInitFail:",e);process.exit(1);}}iTP();
async function pIFOCR(b){try{return await sharp(b).grayscale().normalize().sharpen({sigma:0.5,m1:0,m2:3,x1:0,y2:3,y3:3}).toBuffer();}catch(e){console.warn("PreProcFail:",e.message.slice(0,50));return b;}}
async function rWPW(b,h){if(!tp){console.error("PoolNA!");return null;}let w=null;try{w=await tp.acquire();const pB=await pIFOCR(b);const{data:{text}}=await w.recognize(pB);return text.trim().replace(new RegExp(`[^${OCS}]`,'g'),'');}catch(e){console.error(`OCR ${h} Err:`,e.message.slice(0,50));return null;}finally{if(w)await tp.release(w);}}
async function pSO(d){if(!d||!d.startsWith('data:image/png;base64,'))return{fT:null,aL:[]};const bD=d.replace(/^data:image\/png;base64,/,"");const iB=Buffer.from(bD,'base64');let rs=[];let aL=[];if(!pDO&&tp&&tp.available>=2){pDO=true;try{const[r1,r2]=await Promise.all([rWPW(iB,"D1"),rWPW(iB,"D2")]);rs.push(r1,r2);aL.push({t:"D1",x:r1},{t:"D2",x:r2});}catch(e){console.error("DualOCRErr:",e);}finally{pDO=false;}}else if(tp&&tp.available>=1){try{const rS=await rWPW(iB,"S1");rs.push(rS);aL.push({t:"S1",x:rS});}catch(e){console.error("SingleOCRErr:",e);}}else{console.warn("NoWForOCR");aL.push({t:"N",x:null,r:"NoW"});}const vR=rs.filter(t=>t!==null&&t!=="");const fT=vR.length>0?vR[0]:null;console.log(`OCRLog: ${aL.map(a=>`${a.t}:${a.x||'-'}`).join('; ')}. Fin:"${fT||'-'}"`);return{fT,aL};}
function ld(a,b){if(!a&&!b)return 0;if(!a)return b.length;if(!b)return a.length;const m=[];for(let i=0;i<=b.length;i++)m[i]=[i];for(let j=0;j<=a.length;j++)m[0][j]=j;for(let i=1;i<=b.length;i++){for(let j=1;j<=a.length;j++){if(b.charAt(i-1)===a.charAt(j-1))m[i][j]=m[i-1][j-1];else m[i][j]=Math.min(m[i-1][j-1]+1,Math.min(m[i][j-1]+1,m[i-1][j]+1));}}return m[b.length][a.length];}

app.use((req,res,next)=>{res.header('Access-Control-Allow-Origin','*');res.header('Access-Control-Allow-Headers','Origin,X-Requested-With,Content-Type,Accept');res.header('Access-Control-Allow-Methods','GET,POST,OPTIONS');if(req.method==='OPTIONS')return res.sendStatus(200);next();});
app.get('/api/challenge',(req,res)=>{const sT=uuidv4();let oCT="";for(let i=0;i<OCL;i++)oCT+=OCS[Math.floor(Math.random()*OCS.length)];const wI=WCC[Math.floor(Math.random()*WCC.length)];cs[sT]={iat:Date.now(),exp:Date.now()+CEX,usd:false,ip:req.ip,eOT:oCT,eWCN:wI.n,eWCR:wI.rgb};res.json({sT,oCT,wCC:wI.rgb});});

app.post('/api/check', async (req, res) => {
    const p=req.body; let sc=0; const stgs=[]; let mF=false;
    const sT=p.sT; const oCDU=p.oCDU; 
    const decData = p.eD ? decryptData(p.eD) : null; // Используем AES дешифрование

    let s0P=false; if(decData){s0P=true;sc+=10;}else{mF=true;stgs.push({n:"E0(Dec)",p:false,pts:-100}); /* Добавил лог для этапа 0 */}
    if(s0P) stgs.push({n:"E0(Dec)",p:s0P,pts:10}); // Логируем успех если есть

    const wR=decData?.wgl; const nI=decData?.nav; const pT=decData?.perf; const aV=decData?.aut;
    let sV=false; let sD=null; let s1P=false;
    if(!mF){if(!sT||!cs[sT]||cs[sT].usd||Date.now()>cs[sT].exp){mF=true;}else{sD=cs[sT];sV=true;s1P=true;sc+=30;}}
    stgs.push({n:"E1(Ses)",p:s1P,pts:s1P?30:(mF&&!s0P?0:-100)});
    let oP=false; let oPts=0;
    if(!mF){
        if(!oCDU||!oCDU.startsWith('data:image/png;base64,')||oCDU.length<150){oPts=-80;mF=true;}
        else{const oRes=await pSO(oCDU); const rT=oRes.fT; const eT=sD.eOT;
            if(rT!==null){const dist=ld(rT,eT);if(dist<=1){oP=true;oPts=40+(dist===0?5:0);}else{oPts=-80;mF=true;}}
            else{oPts=-70;mF=true;}}
        if(!oP&&!mF){if(Math.random()<0.10){oP=true;oPts=5;stgs.push({n:"E2(OCR)",p:true,pts:oPts,note:"RND"});}else{mF=true;if(!stgs.find(s=>s.n==="E2(OCR)"))stgs.push({n:"E2(OCR)",p:false,pts:oPts});}}
        if(oP&&!stgs.find(s=>s.n==="E2(OCR)")){stgs.push({n:"E2(OCR)",p:true,pts:oPts});}
        else if(!oP&&!stgs.find(s=>s.n==="E2(OCR)")){stgs.push({n:"E2(OCR)",p:false,pts:oPts});}
        sc+=oPts;
    }else{stgs.push({n:"E2(OCR)",p:false,pts:0});}
    let wglP=false; let wglPts=0;
    if(sV&&wR){const eR=sD.eWCR;const eP=[Math.round(eR[0]*255),Math.round(eR[1]*255),Math.round(eR[2]*255)];
        if(wR.px&&wR.px.length>=3){const cP=wR.px.slice(0,3);if(cP.every((v,i)=>Math.abs(v-eP[i])<=8)){wglP=true;wglPts=70;}else{wglPts=-10;}}else{wglPts=-5;}
        const rdr=wR.rdr?.toLowerCase()||"";if(rdr.includes("swiftshader")||rdr.includes("llvmpipe")){wglPts-=20;wglP=false;}}else if(wR?.err){wglPts=-5;}
    sc+=wglPts;stgs.push({n:"E3(WebGL)",p:wglP,pts:wglPts});
    let navP=true; let navPts=0;
    if(nI){if(nI.wd===true){navPts-=30;navP=false;}if(!nI.ua||nI.ua===""){navPts-=10;navP=false;}else if(nI.ua.toLowerCase().includes("bot")||nI.ua.toLowerCase().includes("headless")){if(!nI.ua.toLowerCase().includes("headlesschrome")){navPts-=20;navP=false;}}}else{navPts-=5;navP=false;}
    sc+=navPts;stgs.push({n:"E4(Nav)",p:navP,pts:navPts});
    let perfP=true; let perfPts=0;
    if(pT&&sV){const{dct,ocrt,wrt}=pT;if(typeof dct==='number'&&dct<10){perfPts-=10;perfP=false;}if(typeof ocrt==='number'&&ocrt<3&&oCDU){perfPts-=10;perfP=false;}if(typeof wrt==='number'&&wrt<3&&wR?.px){perfPts-=10;perfP=false;}}else if(!pT){perfPts-=3;perfP=false;}
    sc+=perfPts;stgs.push({n:"E5(Perf)",p:perfP,pts:perfPts});
    let autoP=true; let autoPts=0;
    if(aV&&Object.keys(aV).length>0){autoPts-=40;autoP=false;}else{autoPts+=5;}
    sc+=autoPts;stgs.push({n:"E6(Auto)",p:autoP,pts:autoPts});
    sc=Math.max(0,Math.min(sc,150));
    let vT="";
    if(mF){vT="Блок (Критический провал)";}
    else{const pCM=oP||wglP; const sCM=navP||perfP||autoP;
        if(pCM&&sCM){vT="Разрешено";if(sD)sD.usd=true;}
        else{vT="Блокировать (Не пройдены условия)";}}
    console.log(`[${new Date().toISOString().slice(0,19).replace('T',' ')}] Chk: T=${sT||'N/A'},S=${sc},V=${vT},IP=${req.ip},MF=${mF},OCR=${oP},WebGL=${wglP},Nav=${navP},Perf=${perfP},Auto=${autoP}`);
    res.json({vT,fS:sc,cS:stgs});
});

setInterval(()=>{const n=Date.now();for(const t in cs){if(cs[t].exp<n||(cs[t].usd&&(n-cs[t].iat>CEX*2))){delete cs[t];}}},60000);
async function shutdown(){console.log("Shutdown...");if(tp){console.log("Draining pool...");await tp.drain().then(()=>tp.clear()).then(()=>console.log("Pool cleared.")).catch(e=>console.error("Pool drain err:",e));}process.exit(0);}
process.on('SIGTERM',shutdown);process.on('SIGINT',shutdown);
app.listen(port,()=>{console.log(`AES Encrypted API on ${port}`);});