Puncte:0

Este posibil să sparg un generator liniar congruențial dacă știu doar modulul de ieșire?

drapel sz

Editare sugerată de fgrieu:

Am o sută de numere întregi $\{0,1,2,3,4,5\}$ despre care bănuiesc că sunt valori consecutive ale $\lfloor x_n/2^{16}\rfloor\bmod6$ calculat ca $x_{n+1}:=a\cdot x_n+b\bmod m$, cu $m=2^{31}$, și $(a,b)\in{(214013,2531011),(22695477,1)}$. Cum validez acea ipoteză, găsesc $(a,b)$ folosit și preziceți ce urmează în succesiune?


Întrebare despre „O implementare competentă într-un limbaj compilat ar dura ca o secundă pe un computer desktop modern.”

Am scris ceva cod, dar se așteaptă să ruleze 20 de ore.

Încerc să găsesc sămânța aleatorie. În primul rând, îmi introduc datele într-o matrice. Deoarece nu știu că primele mele date sunt ce-al-lea număr generat de server, trebuie să o aflu. Știu că serverul se oprește în fiecare joi la 14:00 și repornește în jurul orei 14:45-15:45 în aceeași zi. Când serverul este pornit, ir generează 3 numere la fiecare 45 de secunde. Datele pe care le am sunt colectate vineri, 1:50 am, deci primele mele date pot fi numărul 2400-2640 al LCG.

Mai întâi am rulat randul de 2399 de ori pentru a renunța la primele 2399 de numere. Apoi, fac bucla de 241 de ori pentru a găsi că primele mele date sunt ce-al-lea număr generat de server. (incertitudinea timpului de repornire a serverului 14:45-15:45, 240 de numere pe oră)

Metoda mea este: Dacă bitul 16 al celui de-al 2400-lea x este egal cu bitul 0 al $y_1$, apoi verific bitul 16 al lui 2401th x și bitul 0 al $y_2$, și așa mai departe. Dacă este inegală, întrerupeți bucla, apoi începeți o altă buclă, comparați al 2401-lea x și bitul 0 al $y_1$.

Care este cel mai bun mod de a o face? Tocmai am început să învăț c++ acum două săptămâni, vă rog să-mi iertați ignoranța.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <iostream>
#include <inttypes.h>

folosind namespace std;

const int REZULTAT[3][7] = {
    {0,1,0,1,1,1,1},
    {1,0,1,0,0,0,0},
    {0,1,1,0,1,0,0}
};

x lung static nesemnat;

int test_rand(void)
{
    x = 214013 * x + 2531011; // sau este 22695477*x+1
    return (int)((x >> 16) & 0x7FFF);
};

int onlyx16(void)
{
    x = 214013 * x + 2531011; // sau este 22695477*x+1
    return (x >> 16) & 1;
};

void chk_seed(sămânță lungă nesemnată)
{
    int d1[241]{};
    int d2[241]{};
    int d3[241]{};

    x = sămânță;

    pentru (int i = 0; i < 2399; i++) {
        test_rand();
    }

    pentru (int i = 0; i < 241; i++)
    {
        d1[i] = onlyx16();
        d2[i] = onlyx16();
        d3[i] = onlyx16();
    };

    int corect = 0;

    pentru (int k = 0; k < 236; k++)
    {
        corect = 0;
        pentru (int i = 0; i < 7; i++)
        {
            dacă ((d1[i + k]) == REZULTAT[0][i])
            {
                corect += 1;
            }
            else {
                corect = 0;
                pauză;
            };
            dacă ((d2[i + k]) == REZULTAT[1][i])
            {
                corect += 1;
            }
            else {
                corect = 0;
                pauză;
            };
            dacă ((d3[i + k]) == REZULTAT[2][i])
            {
                corect += 1;
            }
            else {
                corect = 0;
                pauză;
            };
        };
        dacă (corect == 21)
        {
            printf("seed 0x%d este OK\n", seed);
            printf("prognoza rezultate:\n");
            pentru (int round = 0; round < 5; round++)
            {
                printf("round%d", rotund + 1);
                int new_d[3]{};
                pentru (int i = 0; i < 3; i++)
                {
                    new_d[i] = test_rand()% 6;
                    printf("%d", new_d[i]);
                };
                printf("\n");
            }
        };
    }
};

int main()
{
    pentru (sămânță lungă nesemnată = 0; sămânță < 0x100000000; sămânță++)
        chk_seed(seed);
};

$x_{n+1} = (a \cdot x_{n} + b) \mod m$

În situație normală, $x_{n+1}$ și $x_n$ sunt cunoscute. Dar acum doar știu $x_n\mod 6$ și $x_{n+1}\mod 6$.

Am căutat multe site-uri pe google, dar am găsit o singură întrebare care a vorbit despre această problemă.

Predicția valorilor dintr-un generator liniar congruențial

Cu toate acestea, nu este foarte clar și încă nu știu ce ar trebui să fac după ce am citit asta. Sper că cineva poate oferi niște coduri matematice sau exemple, astfel încât să pot învăța din încercări și erori.

Vreau să găsesc a, b, m, apoi folosesc un cod sursă C++ pe care l-am găsit aici pentru a forța semințele.

Am găsit aici un răspuns care vorbea despre cum să mă găsesc, dar nu știu $x_{n+1}$ și $x_n$.

https://security.stackexchange.com/questions/4268/cracking-a-linear-congruential-generator

Sunt nou pe acest subiect, dar am vrut cu disperare să sparg acest PRNG, acest PRNG m-a făcut să sufere mult, am decis să învăț programarea din cauza acestui PRNG. Vă mulţumesc pentru ajutor!

fgrieu avatar
drapel ng
Dificultatea depinde de dacă sunt date $a$, $b$, $m$; on daca $m$ este o putere a doi, si care; si pe cate $x_n\bmod 6$ sunt. Dacă problema este cerută pentru LCG implicit Java: _nu_ obțineți $n\bmod6$ la ieșire! Nici `rand` a lui MSVC, nici lui Borland nu returnează $x_n$.Se pare că returnează biții 30..16 (adică 15 biți) de $x_n$, vezi [this](https://stackoverflow.com/q/6793065/903600) și [this](https://stackoverflow.com/ q/14672358/903600). Așa că mă îndoiesc de «știu $x_n\bmod6$».
fairytale avatar
drapel sz
@fgrieu stiu o suta de $x_n\bmod 6$. Am verificat fișierele .exe din folder, compilatorul unuia este MSVC, compilatorul altuia este Borland C++, așa că am încercat MSVC și Borland rand, dar nu-mi pot oferi rezultate corecte viitoare. Primesc doar 0-5 ca rezultat, așa că cred că este cauzat de „%6” https://stackoverflow.com/questions/1202687/how-do-i-get-a-specific-range-of-numbers- from-rand Cred că este o practică comună de a obține o gamă specifică de numere aleatorii. [editat de mod pentru a condensa mai multe comentarii]
fgrieu avatar
drapel ng
Dar nu aveți nicio indicație că este $x_n$ care este luat $\bmod6$, așa cum este scris în întrebare. Dacă acesta a fost cazul și dacă $m$ a fost par și $a$ impar, la fel de comun, atunci numerele pe care le obțineți ar fi alternativ pare și impare. Întrebarea pe care doriți să o puneți poate fi: «Am o sută de numere întregi în $\{0,1,2,3,4,5\}$ pe care le bănuiesc că sunt valori consecutive ale $\lfloor x_n/2^{16 }\rfloor\bmod6$ calculat ca $x_{n+1}:=a\cdot x_n+b\bmod m$, cu $m=2^{31}$ și $(a,b)\in\{ (214013,2531011),(22695477,1)\}$. Cum validez acea ipoteză, găsesc $(a,b)$ folosit și prezic ce urmează în succesiune?»
fairytale avatar
drapel sz
@fgrieu OK, hai să încercăm această abordare. Mulțumesc pentru sfat. Voi [edita] întrebarea mai târziu și voi încerca metoda dvs., nu pot folosi computerul acum. [editat de mod pentru a condensa mai multe comentarii]
Fractalice avatar
drapel in
Lucrarea „Reconstructing Truncated Integer Variables Satisfying Linear Congruences” poate fi foarte relevantă.
Puncte:3
drapel ng

Pe comentarii care revizuiesc întrebare originală: OP presupune că 100 de cifre $y_n$ în $\{0,1,2,3,4,5\}$ aflate în posesia lor sunt obținute folosind o expresie C(++) echivalentă cu rand()%6 cu rand() folosind un PRNG (non-criptografic) bazat pe un generator liniar congruențial, cu cod echivalent cu

x lung static nesemnat;
int rand(void) {
  x = 214013*x+2531011; // sau este 22695477*x+1
return (int)((x>>16)&0x7FFF);
}

Observa asta $x$ este de cel puțin 32 de biți, dar numai cei 31 de biți de ordin inferior contează din cauza (x>>16)&0x7FFF și C performanță nesemnat lung înmulțirea cu trunchiere a biților de ordin înalt care nu se încadrează în variabilă.

Abstragând detaliile de programare, presupunerea este că $x$ evolueaza per $x_{n+1}:=a\cdot x_n+b\bmod m$ cu $m=2^{31}$ și $(a,b)$ pentru unele constante fixe despre care se crede că fie $(214013,2531011)$ sau $(22695477,1)$. Și rand() scoate biții 30â¦16 din $x$ astfel cel $y_n:=\lfloor x_n/2^{16}\rfloor\bmod 6$ sunt date pentru $n=0$ la $99$ (cu sămânța un număr întreg necunoscut $x_{-1}$ într-un anumit interval imaterial, deoarece numai $x_{-1}\bmod m$ ar conta; și este mai bine să încercăm să găsim $x_0$ oricum).

OP întreabă dacă este posibil să se confirme sau să infirme acea presupunere și (dacă este adevărată) să găsească care $(a,b)$ sunt folosite și prezice ceea ce urmează în succesiune.

da, asta se poate, cu o incredere excelenta. Starea efectivă a PRNG-urilor luate în considerare are 31 de biți, sunt luate în considerare doar două PRNG-uri, acestea sunt acceptabile în scopuri de simulare, astfel încât ar trebui să putem găsi starea lor și care este folosită cu puțin mai mult decât $31+1=32$ un fragment de informație; și primim 100 USD\cdot\log_2(6)\aproximativ 258,5 USD bit, care va da o confirmare din plin.

Cel mai simplu este să încerci pentru ambele presupuneri $(a,b)$, și explorați posibilele valori ale $x_0$. Sunt doar $2^{31}$, știind $y_0$ permite reducerea sistematică a acesteia cu un factor de $6$. Fiecare următor $y_i$ elimină în continuare $5$ candidaţii din $6$. Dacă niciun candidat nu supraviețuiește tuturor $y_i$, ipoteza este infirmata. Dacă se găsește o potrivire, știm care $(a,b)$ am folosit și putem calcula suplimentar $y_i$. O implementare competentă într-un limbaj compilat ar dura ca o secundă pe un computer desktop modern.

Dar poate doriți să spargeți chestia în timp real cu un procesor modern de $0,5 care rulează de la o baterie de $0,2 sau sunteți blocați într-un calculator programabil din anii 1970, sau ENIAC. Observați că $6$ este par, iar divizorul este $2^{16}$. Urmează $y_n\bmod 2$ este un pic $16$ de $x_n$. De asemenea, remarcați că din moment ce $m$ este o putere a doi, schimbarea unui bit în $x_n$ nu se propagă la biți de ordin inferior ai $x_{n+1}$. Dacă urmează acel bit 16 din $x_n$ depinde doar de cei 17 biți mici de $x_0$. Știm bitul 16 din $x_0$, deci trebuie testat cel mult $2^{16}$ candidaţi pentru biţii 15â¦0 din $x_0$. Putem găsi apoi ceilalți 14 biți ca mai sus. Acea diviza și cuceri abordarea ar permite abordarea unor parametri mult mai mari, de ex. variabil X de tip uin64_t și întoarce x>>33, pe un procesor modern.

Mă întreb ce am putea face dacă $a$, $b$ și/sau $m$ erau necunoscute.


Note despre cele de mai sus:

  • Folosește convenția dominantă în informatică (și criptografie, cu câteva excepții, cum ar fi DES): biții sunt numărați de la 0 (bit de ordin inferior), astfel încât dacă un întreg nenegativ $v$ este reprezentat în binar ca $k$ biți $b_j$, atunci $v=\sum b_j$ cu $0\le j<k$. În convenția big-endian, bitul 0 este scris în dreapta: $6\times7=42$ în zecimală este 110$\times111=101010$ în binar.
  • Variabila computerului X este de cel puțin 32 de biți, dar numai 31 de biți (de la 0 la 30) contează și sunt utilizați în $x_n$, prin urmare $0\le x_n<m=2^{31}$. Ieșirea de rand() este de cel puțin 16 biți, dar contează doar 15 biți de ordin scăzut (de la 0 la 14), iar toți ceilalți sunt zero, deci $0\le$rand()$\le$RAND_MAX$=2^{15}-1$. Dacă $0\le i<15$ apoi bit $j$ a ieșirii de rand() se potrivește bit $j+16$ de X. Urmează bitul 0 din $y_n$ se potrivește cu bitul 16 din $x_n$.

Comentarii (în afara subiectului) despre codul curent:

  • Încearcă să folosească accelerația posibilă de 6 fiind egal. Eu susțin că asta este nu necesar pentru a atinge un timp de execuție în secunde, dacă
    • noi explorează posibilele valori ale $x_0$, mai degrabă decât sămânța cu mulți pași înainte.
    • folosim asta $x_0$ este de 31 de biți, astfel încât putem restricționa căutarea exterioară la [0, 0x7fffffff] acesta este $2^{31}$ candidat $x_0$.
    • folosim ceea ce știm $y_0$, astfel că $x_0=2^{16}\cdot(6\cdot u+ y_0)+v$ pentru $0\le u<\lceil2^{15}/6\rceil$ și $0\le v<2^{16}$, care se limitează la aproximativ $2^{31}/6$ candidati pentru $x_0$.
    • optimizăm testul pentru verificarea candidatului $x_0$ împotriva $y_1$ în bucla interioară pe $v$.
  • Esența opțional accelerarea notării 6 este chiar este să găsiți mai întâi biții 16â¦0 din $x_0$ potrivirea valorilor $y_0$ adunat, apoi biți 30â¦17. Codul nu face asta! Cu această accelerare, timpul de execuție s-ar reduce la milisecundă.
  • Avem nevoie de valorile depline ale $y_n$ adunate (atât în ​​căutarea neoptimizată, cât și în a doua parte a căutării optimizate), dar nu par să fie disponibile în intrare, ceea ce cred că este $y_n\bmod2$. Mai mult, nu înțeleg de ce este în matrice bidimensională REZULTAT[3][7].
  • Când $x_0$ se găsește, dacă a fost necesar să se găsească sămânța (sau mai degrabă biți 30â¦0 din aceasta) un număr cunoscut de pași înainte, acest lucru poate fi făcut eficient prin întoarcerea LCG folosind $x_{n-1}:=a^{-1}\,(x_n-b)\bmod m$ Unde $a^{-1}$ este inversă modulară de $a$ modulo $m$.

Aici este codul de lucru fără accelerarea opțională (deci capabilă să lucreze cu un modul de reducere final impar $r$ unde intrebarea are 6). Încearcă online!

#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>

#define A 214013 // pentru VC; 22695477 pentru BC
#define B 2531011 // pentru VC; 1 pentru BC
#define R 6 // modul, în [2, 32768]
// static_assert(A > 0 && A % 8 == 5, „Un mod 8 trebuie să fie 5”);
// static_assert(B > 0 && B % 2 == 1, „B mod 2 trebuie să fie 1”);
// static_assert(R >= 2 && R <= 32768, „R trebuie să fie în [2, 32768]”);

// decide modulo, redus când R este o putere a doi
#define M ((uint32_t)(((R-1)&R)!=0 ? 0x8000 : R)<<16)

// Secvența lui yn pentru VC (a=214013, b=2531011), n=6, sămânță=1639326023
// Dacă R este o putere de două, ceil(16/log2(R))+1 intrări sunt suficiente
// În caz contrar, acestea sunt intrările ceil(31/log2(R)), deci 12 pentru R=6.
const int16_t y[] = {
  0,5,3,0,4,3,1,0,4,5,4,4,4,5,5,3,0,2,0,5,4,5,0, // 0, 2,5,1,3,5,5,5,
};

// INVA inversă modulară a lui A modulo M
#define INVA (IN_A(IN_A(IN_A(IN_A((uint32_t)A))))&(M-1))
#define IN_A(v) (v*(2-v*A))

int main(void) {
  // decide să începem x0 astfel încât să se potrivească cu y0
  const uint32_t y0 = y[0], y1 = y[1];
  int32_t x0 = (int32_t)(((M >> 16) - y0) / R * R + y0) << 16 | 0xffff;
  uint32_t găsit = 0;
  printf("generator: ((int)((x = %" PRIu32 " * x + %" PRIu32 ") >> 16) & 0x7fff) %% %u\n",
    (uint32_t)A, (uint32_t)B, (nesemnat)R);
  în timp ce (x0 >= 0) {
    uint32_t x1 = A * (uint32_t)x0 + B;
    face {
      // assert( (x0 >> 16) % R == y0);
      // assert( x1 == A * (uint32_t)x0 + B);
      dacă (((x1 >> 16) & 0x7fff) % R == y1) {
        uint32_t x = x1;
        int n;
        pentru (n = 2; n < sizeof(y) / sizeof(*y); ++n)
          dacă ((((x = A * x + B) >> 16) & 0x7fff) % R != y[n])
            du-te la nxt;
        // am găsit o soluție
        x = (INVA * ((uint32_t)x0 - B)) & (M - 1);
        printf("x0 poate fi %" PRId32 ", adică seed=%" PRIu32
          " modulo 0x%" PRIx32 ", rezultând:\n", x0, x, M);
        // dovediți punctul arătând rezultatul
        pentru (n = 0; n < sizeof(y) / sizeof(*y) + 8; ++n)
          printf(" %d", ((int)((x = A * x + B) >> 16) & 0x7fff) % R);
        printf("\n");
        ++găsit;
      }
    nxt: x1 -= (uint32_t)A;
    } în timp ce (((x0--) & 0xffff) != 0);
    x0 -= (int32_t)(R - 1) << 16;
  }
  printf("găsit %" PRIu32 "soluție%s\n", găsită, găsită > 1 ? "s" : "");
  întoarce 0;
}

// ceda:
// generator: ((int)((x = 214013 * x + 2531011) >> 16) & 0x7fff) % 6
// x0 poate fi 531633902, adică seed=1639326023 modulo 0x80000000, rezultând:
// 2 3 4 1 5 1 1 5 4 0 3 2 2 5 5 3 5 5 3 4 0 1 1 4 1 3 3 2 5 4 4
// am găsit 1 soluție
fairytale avatar
drapel sz
Dacă $x_n$ este 32768(1000 0000 0000 0000), bitul 16 este 0, dacă $y_n\bmod 2$ este de asemenea 0, atunci acest $y_n$ se potrivește cu $x_n$. Apoi, testez următorul $x_n$. Am dreptate? (Presupun că biții numără de la stânga dacă nu spui „scăzut”)
fgrieu avatar
drapel ng
@fairytale: uh, nu. Vezi secțiunea nouă „_Note despre cele de mai sus_”, în special ultimele două propoziții.
fairytale avatar
drapel sz
Secvența mea este 4,5,0,1,4,3,4,5,1,1,0,2,1,2,3,1,0,2,5,2,0. Din păcate, nu s-a găsit... Am încercat și 1103515245 și 12345 (glibc rand), dar tot nu s-a găsit. Suspin. Programul este foarte vechi, scris înainte de 2006, de asemenea, nu are legătură cu un scop serios, așa că cred că nu este PRNG criptografic sigur.Acum cred că autorul ar putea folosi propriul său A,B,M sau a folosit Mersenne Twister.
fgrieu avatar
drapel ng
@fairytale: la întrebarea reformulată i se răspunde negativ. Aveți executabilul ("_am verificat fișierele .exe_"), deci mai există cursul de acțiune al ingineriei inverse; decompilarea sau/și observarea programului care rulează într-un mediu instrumentat. Acest lucru este chiar mai în afara subiectului despre cripto-SE decât este programarea. Dar poate pe [reverseengineering-SE](https://reverseengineering.stackexchange.com/)?

Postează un răspuns

Majoritatea oamenilor nu înțeleg că a pune multe întrebări deblochează învățarea și îmbunătățește legătura interpersonală. În studiile lui Alison, de exemplu, deși oamenii își puteau aminti cu exactitate câte întrebări au fost puse în conversațiile lor, ei nu au intuit legătura dintre întrebări și apreciere. În patru studii, în care participanții au fost implicați în conversații ei înșiși sau au citit transcrieri ale conversațiilor altora, oamenii au avut tendința să nu realizeze că întrebarea ar influența – sau ar fi influențat – nivelul de prietenie dintre conversatori.