Kwasek6 polymorphic code, modified Ruby Hash

An Empirical Study of Real-world Polymorphic Code Injection Attacks  An Empirical Study of Real-world Polymorphic Code Injection AttacksMichalis PolychronakisFORTH-ICS, Greece, email: mikepo@ics.forth.grKostas G. AnagnostakisI2R, Singapore, email: kostas@i 阅读详情
 

Introduzione

Kwasek6 è un crackme che basa la sua protezione su un keyfile, ed implementa contemporaneamente, codice polimorfico normale (soliti mov, add, sub, etc) ed MMX (paddx, psubd, pxor) oltre ad una versione modificata del RubyHash, e del TEA (Tiny Encryption Algorithm) riscritto in versione MMX.

Tools usati

  • IDA
  • OllyDbg
  • UnFSG (unpacker per FSG)
  • MASM

URL o FTP del programma

www.google.it

Notizie sul programma

Troverete (crackme + keygen) nell' allegato.

Essay

 

Il crackme in questione è protetto con FSG (si può notare immediatamente dalla signature "FSG!" presente fra i primi bytes del file), per risolvere il problema basta cercare su internet un qualsiasi unpacker per FSG. Come si può vedere non esistono editbox, ma analizzando la ImportTable possiamo trovare alcune funzioni interessanti: CreateFile, GetFileSize etc. (si tratta quindi di una keyfile protection). Passiamo a vedere il listato:

00401AFD push 0
00401AFF push 0
00401B01 push 3 ;OPEN_EXISTING
00401B03 push 0
00401B05 push 0
00401B07 push 80000000h ;GENERIC_READ
00401B0C push offset aKwasek_htb ;nome del keyfile -> Kwasek.htb
00401B11 call CreateFileA
00401B16 mov ds:hFile, eax
00401B1B inc eax
00401B1C jz loc_401EEA ;Se il file non esiste salta ad unregistered
00401B22 push 0
00401B24 push ds:hFile
00401B2A call GetFileSize ;Determina la grandezza del file
00401B2F cmp eax, 80h ;Se diverso da 80h (128 caratteri) salta ad unregistered
00401B34 jnz loc_401EEA
00401B3A push 0
00401B3C push offset NumberOfBytesRead
00401B41 push eax
00401B42 push offset Buffer
00401B47 push ds:hFile
00401B4D call ReadFile
00401B52 mov esi, offset Buffer ;Buffer con il contenuto del file
00401B57 mov edi, offset dword_4177D6 ;buffer di destinazione
00401B5C push edi
00401B5D mov ecx, 10h
00401B62 repe movsb
00401B64 pop edi

00401B65 push edi ;Nel buffer di destinazione ci sono i primi 16 bytes del keyfile

La routine che abbiamo appena visto è molto semplice, non fa altro che andare ad aprire un file già esistente, che dev'essere di 128 bytes, e ne legge il contenuto, estrapolandone da esso i primi 16 bytes che andranno a finire in un buffer specifico. Continuiamo con l'analisi:

00401B66 mov eax, [edi] ;Mette in eax una dword per volta, del buffer di destinazione
00401B68 bswap eax
00401B6A xor eax, 12E4B8Ch
00401B6F mov [edi], eax ;mette la dword 'decryptata' in edi
00401B71 inc edi
00401B72 inc ecx ;incrementa il loop counter
00401B73 cmp ecx, 0Dh
00401B76 jnz short loc_401B66 ;ripete per 13 volte
00401B78 pop edi
00401B79 cmp byte ptr [edi], 0 ;controlla che il primo byte non sia 0
00401B7C jz loc_401EEA
00401B82 cmp byte ptr [edi+0Fh], 0 ;e che l'ultimo (il sedicesimo) sia 0
00401B86 jnz loc_401EEA

I primi 16 bytes del nostro keyfile vengono decriptati (attraverso uno bswap ed uno xor), e alla fine la routine controlla che il primo byte non sia 0 ed il sedicesimo sia NULL (questo ci fa pensare che i primi 16 bytes opportunamente trattati devono darci una stringa NULL-Terminated)

00401B8C mov edx, 2 ;Index counter (fa si che la call 00401206 sia chiamata 2 volte)
00401B91 mov eax, offset dword_4177D6 ;Buffer che contiene la stringa decryptata
00401B96 mov ebx, offset dword_4177E7 ;Output buffer (In seguito chiamato Hash)
00401B9B mov cl, 10h
00401B9D push eax
00401B9E push ecx
00401B9F push ebx
00401BA0 call sub_401206 ;Questa call accetta 3 parametri: OutputBuff, 10h, DecryptBuff
00401BA5 mov eax, ebx
00401BA7 add ebx, 8
00401BAA mov cl, 8
00401BAC dec edx
00401BAD test edx, edx

00401BAF jnz short loc_401B9D

Adesso andiamo ad analizzare la subroutine 00401206

00401256 mov edi, A
00401258 rol edi, cl
0040125A mov esp, B
0040125C mov ecx, C
0040125E shr ecx, 1Bh
...
00401276 xor edi, esi
00401278 mov A, edi
0040127A add A, 456C6091h ;Magic Value 1
0040127F xchg A, B
00401280 xchg B, C
00401282 xchg C, D
...
004012A8 mov A, edi
004012AA imul A, 0AA7110C3h ;Magic Value 2
004012B0 xchg A, B
004012B1 xchg B, C

004012B3 xchg C, D

Come si può vedere, nella routine sono utilizzati due MagicValues (classica definizione delle costanti crittografiche) 456C6091h e 0AA7110C3h che con una piccola ricerca ci indicano che siamo in presenza di un Ruby Hash, da notare però che in questo caso la routine è leggermente modificata, se ricordate edx ha un counter settato a 2, e di conseguenza otteremo un hash di 16d bytes. Passiamo alla prossima porzione di routine, che implementa un semplicissimo Engine Polimorfico, che genera un set limitato di istruzioni basandosi sull'hash appena determinato:

 

00401BB1 mov edi, offset dword_40407B ;Buffer del codice
00401BB6 mov esi, offset dword_4177E7 ;Hash
00401BBB push esi
00401BBC lodsb
00401BBD cmp al, 0 ;se l'i-esimo carattere del nostro hash è 0
00401BBF jb short loc_401BE8
00401BC1 cmp al, 20h
00401BC3 ja short loc_401BE8 ;se superiore a 20h salta alla prossima routine
...
00401BD0 lodsb
00401BD1 call sub_401AA6 ;Call che analizzeremo inseguito
00401BD6 test cl, cl
00401BD8 jz loc_401CD7
00401BDE add al, 10h
00401BE0 jmp short loc_401BD1
00401BE2 mov al, 3 ;il nostro byte viene bilanciato poi viene chiamata la call 00401BD1h
00401BE4 stosb
00401BE5 lodsb ;Mette nel buffer del codice l'opcode ottenuto
00401BE6 jmp short loc_401BD1

Ho saltato molte parti di codice, perchè non vale la pena andare a commentare ogni singola linea codice, ma solamente capire come funziona il nostro Engine:

00401BE8 cmp al, 21h
00401BEA jb short loc_401C13 ;Salta al prossimo set di byte (Opcode nel nostro caso)
00401BEC cmp al, 2Fh
00401BEE ja short loc_401C13
stosb
lodsb ;Mette nel buffer del codice l'opcode ottenuto
...
00401C13 cmp al, 30h
00401C15 jb short loc_401C3E ;Salta al prossimo set di byte (Opcode nel nostro caso)
00401C17 cmp al, 80h
00401C19 ja short loc_401C3E
stosb
lodsb ;Mette nel buffer del codice l'opcode ottenuto
...
00401C3E cmp al, 81h
00401C40 jb short loc_401C5F ;Salta al prossimo set
00401C42 cmp al, 0A0h
00401C44 ja short loc_401C5F
...
00401C5F cmp al, 0A1h
00401C61 jb short loc_401C9C ;Salta al prossimo set
00401C63 cmp al, 0E0h
00401C65 ja short loc_401C9C
...
00401C9C cmp al, 0E1h
00401C9E jb short loc_401CD7 ;Salta al prossimo set
00401CA0 cmp al, 0FFh
00401CA2 ja short loc_401CD7
...
00401C9C cmp al, 0E1h
00401C9E jb short loc_401CD7 ;Salta al prossimo set
00401CA0 cmp al, 0FFh
00401CA2 ja short loc_401CD7
...
00401CD7 pop esi
00401CD8 inc esi
00401CD9 inc edx
00401CDA cmp edx, 0Dh ;Se non siamo al 13esimo carattere del nostro hash
00401CDD jnz loc_401BBB ;salta all'inizio dell'Engine
00401CE3 mov edx, 4
00401CE8 mov esi, offset dword_4177E7 ;esi punta al nostro Hash
00401CED loc_401CED:
00401CED xor dword ptr [esi], 499602D2h ;queste istruzioni modificano 4 bytes del nostro 00401CF3 ror dword ptr [esi], 7 ;hash
00401CF6 add esi, 4
00401CF9 dec edx
00401CFA jnz short loc_401CED
00401CFC add ds:dword_41774A, 1 ;incrementa il contatore
00401D03 cmp ds:dword_41774A, 10h
00401D0A jnz loc_401BB6 ;salta all'Engine che avrà come input l'hash modificato
00401D10 xor ds:dword_41774A, 10h ;azzera il counter
00401D17 mov al, 0C3h ;chiude il codice generato con un RET
00401D19 stosb

Come potete vedere il funzionamento dell'Engine non è molto complesso, ad ogni ciclo prende un carattere del nostro hash e lo filtra attraverso una struttura decisionale che contiene un ben definito range di opcodes. Dopo aver iterato l'intero hash, a 00401CEDh abbiamo una banale routine che modifica il nostro hash 4 bytes per volta, che vanno poi a costituire un nuovo input per l'Engine. Se andiamo a fare un pò di conti vediamo, che dal nostro hash vengono generati 1C6h bytes di codice, ed osservando approfonditamente gli opcodes usati nella struttura dell'engine, possiamo notare che il nuovo codice userà un set limitato di istruzioni, e cioè:

add reg, reg
sub reg, reg
rol reg, int
ror reg, int
xchg reg, reg
xor reg, reg
not reg

 

Andiamo ad analizzare adesso la prossima porzione di codice, che costituisce la prima applicazione del codice appena generato:

00401D1A mov esi, offset dword_417762 ;Buffer del nostro keyfile+16
00401D1F mov eax, [esi] ;carica 24 bytes presi dal nostro keyfile
00401D21 mov ebx, [esi+4] ;|
00401D24 mov ecx, [esi+8] ;|
00401D27 mov edx, [esi+0Ch] ;|
00401D2A mov edi, [esi+10h] ;|
00401D2D mov esi, [esi+14h] ;|
00401D30 push ebp
00401D31 mov ebp, offset dword_40407B
00401D36 call ebp ;chiama il codice polimorfico
00401D38 mov ebp, offset dword_404062 ;ebp contiene la nostra stringa decriptata
00401D3D mov [ebp+0], eax
00401D40 mov [ebp+4], ebx
00401D43 mov [ebp+8], ecx
00401D46 mov [ebp+0Ch], edx
00401D49 mov [ebp+10h], edi
00401D4C mov [ebp+14h], esi
00401D4F mov eax, ebp
00401D51 pop ebp
00401D52 mov ecx, 18h ;ecx verrà usato come counter, ed è settato a 24
00401D57 mov esi, eax
00401D59 mov edi, offset aPolymorphisByK ;"Polymorphis by Kwasek :)"
00401D5E call sub_401F08 ;in questa call vengono confrontate la nostra stringa con quella
00401D63 or eax, eax ;in decriptata
00401D65 jnz short loc_401D6C ;vai alla prossima parte della routine di protezione
00401D67 jmp loc_401EEA ;salta ad UNREGISTERED

 

Il codice è molto semplice, vengono presi 24 bytes dal nostro keyfile e decriptati attraverso l'algoritmo polimorfico, ed infine confrontati con la stringa "Polymorphis by Kwasek :)" se sono uguali, possiamo passare alla prossima parte del crackme, altrimenti l'esecuzione salta ad unregistered. Il nostro compito è quindi generare un algoritmo polimorfico con le istruzioni reversate, ed applicare la stringa "Poly...." in modo da ottenere una serie di bytes, che decriptati ci danno la stessa "Poly...", come reversare le istruzioni lo vedremo in fase di keygenning, ora possiamo tranquillamente procedere con la nostra analisi..

00401D7D mov esi, offset dword_4177E7 ;esi continene il puntatore al nostro Hash
00401D82 loc_401D82:
00401D82 push esi
00401D83 mov al, 0Fh
00401D85 stosb
00401D86 lodsb
00401D87 cmp al, 0
00401D89 jb short loc_401DA0
00401D8B cmp al, 0EFh
00401D8D ja short loc_401DA0
00401D8F mov al, 0EFh
00401D91 stosb
00401D92 lodsb
...
0401DD2 pop esi
00401DD3 inc esi
00401DD4 inc edx
00401DD5 cmp edx, 0Dh
00401DD8 jnz short loc_401D82
00401DDA mov edx, 4
00401DDF mov esi, offset dword_4177E7 ;esi continene il puntatore al nostro Hash
00401DE4 xor dword ptr [esi], 3ADE68B1h
00401DEA ror dword ptr [esi], 3
00401DED add esi, 4
00401DF0 dec edx
00401DF1 jnz short loc_401DE4 ;queste istruzioni modificano 4 bytes del nostro hash
00401DF3 add ds:dword_41774A, 1 ;incrementa il contatore
00401DFA cmp ds:dword_41774A, 10h
00401E01 jnz loc_401D7D ;salta all'Engine che avrà come input l'hash modificato
00401E07 xor ds:dword_41774A, 10h ;azzera il counter
00401E0E mov al, 0C3h ;chiude il codice generato con un RET
00401E10 stosb

Questa parte dovrebbe risultarvi molto familiare, si tratta sempre del nostro Engine, che stavolta utilizza opcodes che rientrano nelle istruzioni MMX. La solita routine, modifica il nostro hash, per un totale di 1C6h bytes di istruzioni mmx polimorfiche. Il set di istruzioni in questo caso è:

paddd mmx,mmx
psubd mmx,mmx
pxor mmx,mmx
00401E13 mov esi, offset dword_41777A ;Buffer del nostro keyfile+40
00401E18 movq mm0, qword ptr [esi] ;carica 64 bytes presi dal nostro keyfile
00401E1B movq mm1, qword ptr [esi+8] ;|
00401E1F movq mm2, qword ptr [esi+10h] ;|
00401E23 movq mm3, qword ptr [esi+18h] ;|
00401E27 movq mm4, qword ptr [esi+20h] ;|
00401E2B movq mm5, qword ptr [esi+28h] ;|
00401E2F movq mm6, qword ptr [esi+30h] ;|
00401E33 movq mm7, qword ptr [esi+38h] ;|
00401E37 mov ebp, offset dword_40447B
00401E3C call ebp ; dword_40447B ;Codice polimorfico MMX appena generato
00401E3E mov esi, offset dword_404021 ;Stringa di 64 bytes decriptata
00401E43 movq qword ptr [esi], mm0
00401E46 movq qword ptr [esi+8], mm1
00401E4A movq qword ptr [esi+10h], mm2
00401E4E movq qword ptr [esi+18h], mm3
00401E52 movq qword ptr [esi+20h], mm4
00401E56 movq qword ptr [esi+28h], mm5
00401E5A movq qword ptr [esi+30h], mm6
00401E5E movq qword ptr [esi+38h], mm7
00401E62 mov ecx, 40h ;Lunghezza della stringa
00401E67 mov edi, offset aPolymorphismmx ;"PolymorphisMMX by Kwasek 2oo3 kwasek2@w..."
00401E6C call sub_401F08 ;Confronta le due stringhe
00401E71 or eax, eax
00401E73 jnz short loc_401E77 ;Se sono uguali procedi con la prossima routine
00401E75 jmp short loc_401EEA ;Altrimenti salta ad UNREGISTERED

Lo schema di protezione è praticamente identico a quello già visto, con la sola accezione che qui vengono usate istruzioni mmx e la stringa è lunga 64 bytes. La tecnica di reverse è sempre la stessa, dobbiamo elaborare un engine che generi le istruzioni mmx reversed. Procediamo con la prossima parte di codice:

00401E77 push offset dword_4177BA ;Buffer del nostro keyfile+104
00401E7C push offset aPolymorphisByK ;"Polymorphis by Kwasek :)"
00401E81 call sub_401F33 ;Call molto importante, che analizzeremo in seguito
00401E86 mov ecx, 8 ;Lunghezza della stringa
00401E8B mov esi, offset dword_4177E7 ;Hash
00401E90 mov edi, offset dword_4177BA ;keyfile+104
00401E95 call sub_401F08 ;Confrontali
00401E9A or eax, eax
00401E9C jnz short loc_401EA0 ;Se sono uguali procedi con la prossima routine
00401E9E jmp short loc_401EEA ;Altrimenti salta ad UNREGISTERED
00401EA0 loc_401EA0:
00401EA0 movd eax, mm7
00401EA3 psllq mm7, 20h ;Equivalente dell'istruzione SHL
00401EA7 movd ebx, mm7
00401EAA test eax, eax
00401EAC jnz short loc_401EEA ;Se eax è diverso da 0 salta ad UNREGISTERED
00401EAE test ebx, ebx
00401EB0 jnz short loc_401EEA ;Se eax è diverso da 0 salta ad UNREGISTERED
00401EB2 mov ecx, 8
00401EB7 mov esi, offset dword_4177EF keyfile+08
00401EBC mov edi, offset dword_404010
00401EC1 call sub_401F08
00401EC6 or eax, eax
00401EC8 jnz short loc_401ECC ;Se sono uguali procedi vai a REGISTERED
00401ECA jmp short loc_401EEA ;Altrimenti salta ad UNREGISTERED

Questa che abbiamo visto, è l'ultima parte di protezione, la struttura rimane invariata, abbiamo infatti delle call che decriptano porzioni di keyfile, che poi vengono confrontate con le solite stringhe. Andiamo quindi ad analizzare queste call, e più precisamente la call 00401F33h:

00401F33 emms
0401F39 mov esi, offset dword_4177C2 ;keyfile+112
00401F3E mov edi, offset aWitamWszystkic ; "Witam wszystkich crackersow a szczegoln"
00401F43 movd mm4, dword ptr [edx] ;in edx keyfile+104
00401F46 call sub_40208D
00401F4B movq mm7, qword ptr [esi];in esi keyfile+112
00401F4E call sub_40208D
...

Ho tagliato molta parte del codice, ma possiamo una cosa molto strana, ci sono numerose chiamate a 0040208Dh, andiamo a vedere cosa succede in questa call:

00402093 mov esi, offset dword_4177CA ;keyfile+120
00402098 mov edi, offset dword_404010 ;buffer finale
0040209D mov ebx, offset aUnregistered ;"UNREGISTERED"
004020A2 lodsb
004020A3 xor al, [ecx+ebx]
004020A6 xor al, 7
004020A8 add al, 6
004020AA sub al, [ecx+ebx]
004020AD stosb
004020AE loop loc_4020A2
004020B0 mov ecx, 8
004020B5 mov esi, offset dword_404010
004020BA mov edi, offset dword_4177CA
004020BF repe movsb ;copia il buffer finale in keyfile+120
004020C1 popa
004020C2 retn

 

Il codice è abbastanza semplice, i bytes del keyfile vengono combinati con la stringa unregistered attraverso una serie di istruzioni come add, sub e xor che come sapete possono essere tranquillamente reversate, poichè non comportano perdite di bit (e quindi di informazione), ed il nostro compito è proprio questo, dobbiamo /notate all'uscita della call a 0040208Dh i valori contenuti nei vari registri rimangono invariati, e la routine (escluse appunto le chiamate a 0040208Dh) fa qualcos'altro..come avrete intuito, la seconda funzione di tutte queste call è confonderci le idee! proviamo infatti a riscrivere l'intera routine, eliminando tutte queste call:

00401F39 mov esi, offset dword_4177C2 ;keyfile+112
00401F3E mov edi, offset aWitamWszystkic
00401F43 movd mm4, dword ptr [edx] ;in mm4 keyfile+104
00401F4B movq mm7, qword ptr [esi] ;in mm7 keyfile+104
00401F53 movd mm1, dword ptr [edx+4]
00401F5C mov ecx, ds:dword_401F1B ;ecx=32375E7Eh
00401F67 movd mm0, ds:dword_401F23 ;MM0=12345678h
00401F73 movd mm6, ecx
00401F7B pxor mm6, mm0
00401F83 pxor mm5, mm5
00401F8B mov ecx, 20h ;Contatore settato a 32d
00401F95 push edx
00401F9B paddd mm5, mm6 ;MM5 + 020030806h
00401FA3 movq mm0, mm1
00401FAB movq mm3, mm1
00401FB3 call sub_402075 ;Equivale a shl MM0,4 cioè MM0*16
00401FBD psrld mm3, 5
00401FC6 paddd mm0, qword ptr [eax] ;"Polymorp" + MM0
00401FCE paddd mm3, qword ptr [eax+4];"hisMMX b" + MM3
00401FDB pxor mm0, mm1
00401FE9 paddd mm0, mm5
00401FF0 pxor mm0, mm3
00401FFE paddd mm0, mm4
00402006 movq mm4, mm0
0040200E call sub_402075 ;MM0*16
00402018 movq mm3, mm4
00402020 psrld mm3, 5
00402029 paddd mm0, qword ptr [eax+8] ;"y Kwasek" + MM0
00402036 pxor mm0, mm4
00402044 paddd mm3, qword ptr [eax+0Ch] " 2oo3 kw" + MM3
0040204D paddd mm0, mm5
00402054 pxor mm0, mm3
00402057 paddd mm7, qword ptr [edi]
00402062 paddd mm1, mm0
00402065 dec ecx
00402066 jnz loc_401F9B
0040206C pop edx ;Buffer del nostro keyfile+104
0040206D movd dword ptr [edx], mm4 ;Inserisci i valori appona trovati nel buffer
00402070 movd dword ptr [edx+4], mm1 ;|
00402074 retn

Proviamo a riscrivere in C questo pezzetto di codice:

void code(long* v, long* k) {
unsigned long y=v[0],z=v[1], sum=0, delta=0x20030806, n=32 ;
while (n-->0) {
sum += delta ;
y += (z>5)+k[1];
z += (y>5)+k[3];
}
v[0]=y ; v[1]=z;
}

Come per magia scopriamo, che si tratta semplicemente della procedura di Encryption del TEA, solo che riscritto in istruzioni MMX e con il delta cambiato :) Quello che dobbiamo fare è semplicemente reversare questo algoritmo, scrivendo cioè la routine di Decryption.

Piccola nota sul TEA:

TEA

Il TEA (Tiny Encryption Alorithm) e' stato creato da David Wheeler e Roger Needham. Questo algoritmo ha il pregio di essere abbastanza veloce e di offrire una "sicurezza-media", si basa principalmente su un'architettura di tipo Feistel Network, perciò la cifratura/decifratura di dati avviene "a blocchi" solitamente di 64 bits. E' un sistema di cifratura simmetrica quindi se invertiamo l'intero algoritmo ed usiamo la stessa chiave utilizzata per crittare, ed applichiamo in entrata il CipherText, in uscita otterremo il PlainText.

Un ultimo piccolo reverse ed abbiamo finalmente finito, se ricordate all'uscita della call 00401F33h, viene controllato se MM7 è uguale a zero, se non lo è, l'esecuzione salta ad Unregistered. La routine responsabile del valore di MM7 in uscita è contenuta all'interno della procedura del TEA, ben nascosta in modo da essere confusa con l'intero altro codice, eccola:

00401FD7 push ecx
00401FD8 push 0Bh
00401FDA pop ecx
00401FDB pxor mm0, mm1
00401FDE paddd mm7, qword ptr [edi] ;edi punta alla stringa "Witam wszystkich" la quale viene
00401FE1 loop loc_401FDB ;sommata a keyfile+112 che è contenuta in MM7
00401FE3 pop ecx 00402032 push ecx
00402033 push 29h
00402035 pop ecx
00402036 pxor mm0, mm4
00402039 paddd mm7, qword ptr [edi]
0040203C loop loc_402036

Anche qui si tratta di un banalissimo reverse, che vi riporto qui sotto:

pxor mm7, mm7
push 2688 ;(0Bh+0Bh+29h+15h)*20h
pop ecx mov edi, offset Witam
LOOP:
psubd mm7, qword ptr [edi]
loop LOOP
mov edi, offset RisultatoMM7
movq qword ptr [edi], mm7

E con questo abbiamo finalmente finito il reverse di Kwasek6! :)

Note per un eventuale keygen

Un imbocca al lupo per chi volesse cimentarsi nel keygenning di questo crackme, non perchè sia eccessivamente difficile, ma necessita sicuramente di un buon ordine di coding ed una gran pazienza :), credo che possa aiutarvi, suddividere il crackme in fasi successive:

  1. Nome
  2. Ruby_Hash(Nome)
  3. Engine Polimorfico 1
  4. Engine Polimorfico 2 (MMX)
  5. TEA (Decrypter)
  6. MM7 Check
  7. Unreg. loop

Ci vediamo al prossimo tute!!! :))

Dromara MaxKey单点登录认证系统是业界领先的IAM-IDaas身份管理和认证产品,寓意它能够像一把万能钥匙(最大钥匙)一样,解锁复杂的企业安全需求,提供简洁而高效的解决方案 单点登录认证系统是业界领先的IAM-IDaas身份管理和认证产品,遵循Apache-2.0开源免费,支持OAuth2.x、OpenID Connect、SAML2.0、CAS、JWT、SCIM等SSO标准协议,基于RBAC统一权限控制,实现用户生命周期管理,开源、安全、合规、自主可控 立即下载

相关推荐

多智能体-系统-形成-控制 附matlab代码.rar

多智能体-系统-形成-控制 附matlab代码.rar

cf 456B - Fedya and Maths

第一眼看就觉得是个规律题。。然后暴力

z8853797的专栏 684

SQLite Expert Professional v6.2.12.746 (x86 & x64) + License Key & Keygen.7z

SQLite Expert Professional v6.2.12.746 (x86 & x64) + License Key & Keygen.7z

JSP

jsp解决导入项目后出现:Multiple annotations found at this line: - String cannot be resolved to a type - No ...

qq_44102678的博客 7025

门禁监控报警等系统与消防联动规划方案(生态合作版).pptx

门禁监控报警等系统与消防联动规划方案(生态合作版).pptx

两相交错并联BuckBoost变换器仿真模型(Simulink仿真实现)

内容概要:本文详细介绍了两相交错并联Buck-Boost变换器的Simulink仿真模型设计与实现过程,系统阐述了该拓扑结构的工作原理及其在升降压模式下的运行特性。通过引入交错并联技术,有效降低了输入输出电流纹波,提升了系统效率与功率密度。文中构建了完整的电压电流双闭环PI控制系统,确保输出电压稳定并具备良好的动态响应能力,同时深入分析了占空比分配与相位交错对系统性能的影响。仿真模型全面验证了变换器在不同负载条件及输入电压波动下的静态与动态性能,充分体现了控制策略的有效性与系统的鲁棒性。; 适合人群:具备电力电子技术基础和Simulink仿真经验,从事开关电源、新能源变换系统研发的工程技术人员或高等院校电力电子与电力传动方向的研究生。; 使用场景及目标:①掌握两相交错并联Buck-Boost变换器的建模方法与工作机理;②学习双闭环PI控制器的设计流程与参数整定技巧;③深入理解多相变换器的均流控制、纹波抑制机制与动态响应特性;④为高效率、高功率密度DC-DC变换器的实际工程设计提供理论依据与仿真验证平台。; 阅读建议:建议读者结合所提供的Simulink模型文件进行同步操作,亲手搭建与调试电路,重点关注控制器参数调整对系统稳定性、响应速度及抗扰能力的影响,后续可进一步拓展至数字控制实现、硬件在环测试及实际样机开发。

破解凯撒密码

首先来看以下恺撒密码。 离散数学的一道作业题。 凯撒密码作为一种最为古老的对称加密体制,在古罗马的时候都已经很流行,他的基本思想是:通过把字母移动一定的位数来实现加密和解密。例如,如果密匙是把明文字母的位数向后移动三位,那么明文字母B就变成了密文的E,依次类推,X将变成A,Y变成B,Z变成C,由此可见,位数就是凯撒密码加密和解密的密钥。 题目如下: It is known that

爱冒险的技术宅 1万+

【CEEMDAN-WOA-LSTM】完备集合经验模态分解-鲸鱼优化-长短时记忆神经网络研究(Python代码实现)

内容概要:本文详细介绍了一种结合完备集合经验模态分解(CEEMDAN)、鲸鱼优化算法(WOA)与长短时记忆神经网络(LSTM)的时间序列预测模型,并提供了完整的Python代码实现。该模型首先利用CEEMDAN对原始非平稳、高噪声的时间序列信号进行自适应分解,获得一组相对平稳的本征模态函数(IMF)分量;接着采用WOA对LSTM的关键超参数进行智能寻优,以克服传统手动调参的局限性,提升模型的泛化性能与预测精度;最后构建多通道LSTM网络对各IMF分量分别建模预测,并将结果叠加重构得到最终预测输出。通过实验验证,该混合模型在处理非线性、非平稳且含强噪声的数据时,相较单一LSTM或其他组合模型展现出更优异的预测准确性与鲁棒性。; 适合人群:具备Python编程基础和机器学习基础知识的科研人员、研究生及工程师,特别适用于从事能源预测(如风电、光伏、电力负荷)、信号处理、故障诊断、金融时序分析等相关领域的研究人员。; 使用场景及目标:①应用于电力系统中的风速、光照强度、负荷等关键变量的高精度短期与中期预测;②用于处理工业现场中受噪声干扰严重的传感器信号,提升数据分析质量;③为需要高性能预测模型的科研项目提供可复现、可扩展的算法框架与代码支持,推动智能优化算法与深度学习在实际工程问题中的深度融合与应用。; 阅读建议:建议读者结合提供的Python代码逐模块学习,重点理解CEEMDAN的信号分解机制、WOA优化LSTM超参数的实现流程以及多分量预测结果的重构策略,鼓励在真实数据集上动手复现并调整参数,以深入掌握模型的设计思想与调优技巧。

VS Code + KXApp 开发 iOS 报错排查指南,工程、签名、真机、补全、配置

在 VS Code 里用 KXApp 开发 iOS,报错常不在代码而在环境。本文按失败阶段分五类排查:工程没出现 Xcode 视图(查 .xcodeproj 与 xcode-select)、构建失败(No profiles、Team 签名、CocoaPods 用 .xcworkspace)、真机调试失败(信任、开发者模式、设备未准备好)、没补全或 No such module(等索引、清 clangd 冲突)、设置保存不了(装 xcodeproj gem),每类给现象、日志通道和修法。

2501_91590906的博客 240

继电保护小电流接地系统故障仿真-中性点不接地与经消弧线圈接地仿真模型(Simulink仿真实现)

内容概要:本文详细介绍了三电平VIENNA整流器的双闭环控制与中点电位平衡的仿真研究,重点探讨了由电压外环与电流内环构成的双闭环PI控制结构,以及基于上下电容电压差值补偿的中点电位平衡策略。通过引入SVPWM调制技术,利用冗余小矢量的时间分配自由度,实现对中点电位的主动调节,从而在不牺牲输入电流谐波性能的前提下,显著提升了电容电压的均衡度。仿真结果表明,该控制策略能够有效稳定直流母线电压并实现中点电位的精确平衡,具备良好的动态响应能力和鲁棒性。; 适合人群:具备电力电子与电力系统基础知识,从事新能源发电、电动汽车充电、工业电源等领域的科研人员及工程技术人员,尤其是熟悉MATLAB/Simulink仿真环境的研发人员。; 使用场景及目标:①用于中大功率单向整流场合如充电桩、航空电源等系统中三电平VIENNA整流器的控制设计与优化;②为解决三电平拓扑固有的中点电位波动问题提供可行的技术方案,提升系统输出品质与可靠性;③作为高校研究生在电力电子变换器先进控制策略方面的教学与研究参考案例。; 阅读建议:建议读者结合文中提供的仿真模型与参数设置进行复现实验,重点关注双闭环带宽匹配、差值补偿环的设计边界及SVPWM调制细节,同时可进一步探究在电网不平衡、轻载等极端工况下的控制性能,以深化对系统动态特性的理解。

两路半桥LLC谐振变换器软开关Simulink仿真(模拟谐振参数不同、分裂电容)

内容概要:本文围绕两路半桥LLC谐振变换器的软开关特性展开Simulink仿真研究,重点分析了不同谐振参数(如谐振电感、谐振电容)以及分裂电容结构对系统性能的影响。通过构建详细的开环与闭环控制仿真模型,深入探究变换器在变频控制下的动态响应、能量传输效率及软开关实现条件,验证了在宽负载范围内实现零电压开通(ZVS)和零电流关断(ZCS)的可行性。研究特别关注分裂电容对谐振腔参数调节的作用机制,揭示其在优化高频高效电源设计中的关键价值,为高性能DC-DC变换器的设计提供了理论依据与实践指导。; 适合人群:电力电子、电气工程及其自动化等相关专业的高校师生,以及从事高频电源、新能源变换器、电动汽车充电模块等领域的研发工程师和技术人员。; 使用场景及目标:①深入理解LLC谐振变换器的工作原理及其软开关的实现条件;②掌握基于Simulink的电力电子系统建模与仿真分析方法;③探究分裂电容等关键结构对谐振参数与系统效率的影响,进而优化高效率、高功率密度电源的拓扑设计与参数匹配。; 阅读建议:读者应具备电路理论、电力电子变流技术的基础知识,建议在学习过程中结合所提供的Simulink模型进行实际操作,重点观察谐振参数变化对电流电压波形及软开关过程的影响,并尝试调整控制器参数以分析系统的动态响应与稳定性表现。

【混合缓存】五种同步通道与它们的失效模式:为什么 Pub/Sub 不可靠反而是对的(五)

本文剖析混合缓存同步层的五种实现(Redis Pub/Sub、RocketMQ、Kafka、RabbitMQ、NATS),指出选型关键不在延迟、吞吐或运维成本,而在于“丢、重、乱”三种失效形态。核心结论:同步消息仅传递“失效指令”,而非数据本身,因此“重”与“乱”因幂等性无害,唯有“丢”需兜底。通过分析各通道在消息丢失场景下的表现,揭示其本质差异——真正决定系统可靠性的,是“何时丢”及“靠什么兜底”。最终强调:同步层只需低延迟通知,可靠性由时间上界(如forceExpiryTime)保障,无需通道保证完全可

tantan的小记录 494

具有PWM控制VSC-HVDC链路的IEEE30总线传输系统(Simulink仿真实现)

内容概要:本文详细介绍了基于Simulink的具有PWM控制VSC-HVDC链路的IEEE30总线传输系统的建模与仿真研究。系统采用电压源型换流器(VSC)结合脉宽调制(PWM)技术实现高压直流输电(HVDC),并将其集成到标准的IEEE30节点电力系统中,以验证其在潮流控制、电压稳定和功率传输方面的性能。通过构建完整的系统模型,包括交流电网、VSC换流站、PWM控制器、直流输电线路及配套控制系统,实现了对有功功率与无功功率的独立精确调控。仿真结果表明,该VSC-HVDC系统在提升电网灵活性、增强系统稳定性以及优化电能传输效率方面具有显著优势,为现代柔性交流输电系统(FACTS)和智能电网建设提供了有力的技术支撑与参考依据。; 适合人群:具备电力系统分析、电力电子技术和自动控制理论基础知识的研究生、科研人员及电气工程领域的技术人员。; 使用场景及目标:①研究VSC-HVDC技术在复杂电网(如IEEE30节点系统)中的集成与应用;②掌握PWM控制策略在高压直流输电中的具体实现方法;③分析VSC-HVDC系统对电网潮流分布、电压稳定性和传输能力的影响。; 阅读建议:此资源以Simulink仿真为核心,不仅提供了系统的实现代码,更侧重于背后的技术原理和控制策略的解析。建议读者在学习时,应结合电力电子、电力系统暂稳态分析等理论知识,动手搭建并调试模型,通过改变系统参数和运行工况,深入理解VSC-HVDC的动态响应特性与控制机理。

GEM5架构解析

kkk12344的专栏 237

多目标粒子群算法求解综合能源系统最优容量配置研究(Matlab代码实现)

内容概要:本文围绕多目标粒子群算法在综合能源系统最优容量配置中的应用展开研究,旨在通过Matlab代码实现,解决包含多种能源设备(如光伏、风电、储能等)的综合能源系统在经济性、环保性和能效性等多目标下的容量优化配置问题。文中详细介绍了综合能源系统的构成与运行机理,建立了考虑设备投资成本、运行维护费用、购能支出及碳排放惩罚等多目标的优化模型,并采用多目标粒子群算法(MOPSO)进行求解,实现了Pareto最优前沿的搜索。研究通过具体算例验证了所提方法的有效性与优越性,能够为综合能源系统的规划设计提供科学依据和技术支持。; 适合人群:具备一定电力系统、能源系统基础知识和Matlab编程能力的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于综合能源系统、微电网等领域的科研与教学,帮助理解多目标优化问题的建模与求解过程;②为实际工程项目中的能源设备选型与容量配置提供决策支持,实现系统经济、环保、高效的运行目标。; 阅读建议:建议读者结合Matlab代码,深入理解算法实现细节与模型构建逻辑,通过调整算例参数和优化目标,探究不同因素对优化结果的影响,以加深对多目标粒子群算法及综合能源系统优化配置问题的理解。

虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型(simulink仿真实现)

内容概要:本文详细阐述了虚拟同步发电机(VSG)惯量阻尼自适应控制的Simulink仿真模型构建与研究过程,旨在通过仿真手段深入探究VSG在电力系统中的动态响应特性及其控制性能。文章首先介绍了VSG的基本工作原理,重点剖析其如何通过模拟传统同步发电机的惯性和阻尼特性来增强电力电子接口电源的频率支撑能力。在此基础上,提出了惯量和阻尼参数的自适应控制策略,该策略能够根据系统频率变化率和偏差实时调整VSG的控制参数,从而有效提升微电网或高比例可再生能源接入系统在面对负荷突变或电源波动时的频率稳定性和抗干扰能力。文中系统地描述了Simulink仿真模型的设计与实现过程,包括主电路拓扑、VSG控制算法模块、自适应逻辑判断模块的搭建,以及关键参数的设定与优化流程。通过对不同工况(如负载投切、电源功率波动)下的仿真结果进行分析,全面验证了所提自适应控制策略相较于固定参数控制在抑制频率波动、减小频率偏差和加快恢复速度等方面的显著优越性。; 适合人群:具备一定电力系统自动化、新能源并网技术或电力电子技术基础,熟悉Simulink/Matlab仿真工具的应用,从事相关领域研究的研发人员、高校研究生及科研工作者。; 使用场景及目标:①研究虚拟同步发电机在微电网、孤岛运行系统或高比例可再生能源并网场景下的频率稳定控制机制;②探索并设计基于系统状态反馈的惯量和阻尼自适应控制算法,以优化电力系统的动态性能;③为高等院校相关专业的课程设计、毕业设计或科研项目提供一个完整的、可复现的仿真案例和技术参考。; 阅读建议:建议读者在阅读时结合Simulink软件进行实操,亲手搭建或调试文中的仿真模型,通过改变自适应控制策略的阈值、增益等参数,观察系统频率响应的变化,从而深刻理解VSG的工作机理和自适应控制的精髓。同时,鼓励读者进一步查阅关于VSG虚拟阻抗、一次调频与二次调频协同控制等方面的文献,以拓宽知识视野,为后续的创新研究奠定基础。

固定电容器可控晶闸管无功补偿装置 (FCTCR)(simulink仿真实现)

内容概要:本文详细介绍了固定电容器可控晶闸管无功补偿装置(FCTCR)的Simulink仿真实现方法,系统阐述了其整体结构、工作原理及核心控制策略。FCTCR通过结合固定电容器组与基于晶闸管的相控电抗器,实现对电网无功功率的连续、快速、精确调节,有效提升系统功率因数、改善电压稳定性并抑制谐波。文中深入剖析了晶闸管的触发控制逻辑、无功动态响应特性,并利用Simulink构建了完整的仿真模型,通过在不同负载工况下的仿真测试,验证了该装置在动态无功补偿方面的优异性能和良好的响应能力。; 适合人群:具备电力系统分析、电力电子技术基础知识以及Simulink仿真操作经验的电气工程专业本科生、研究生、科研人员,以及从事无功补偿设备研发与应用的工程技术人员。; 使用场景及目标:①深入理解FCTCR装置的物理结构、工作机理与数学建模方法;②应用于高校电力系统自动化、电力电子课程设计或毕业设计,完成无功补偿系统的仿真验证;③为工业界开发新型动态无功补偿方案提供理论依据和技术参考,并可用于优化现有补偿装置的控制参数。; 阅读建议:建议读者结合提供的Simulink模型文件,按照文档步骤动手搭建与调试仿真系统,重点关注晶闸管触发角与系统等效电抗、无功输出之间的非线性关系,通过改变负载和控制参数,观察系统的瞬态与稳态响应,从而深刻掌握FCTCR的控制精髓与工程应用要点。

单相PWM整流器基于直流电流控制+虚拟dq两种闭环控制仿真【2个仿真+参考资料】

内容概要:本文系统介绍了单相PWM整流器的两种闭环控制策略——基于直流电流控制和基于虚拟dq变换的控制方法,并提供了相应的Simulink仿真模型及参考资料。通过构建两种控制方案的仿真系统,详细阐述了各自的控制原理、系统建模流程与关键参数设计思路,重点对比分析了二者在直流电压稳定性、功率因数校正能力、谐波抑制性能及动态响应特性等方面的差异。基于虚拟dq变换的控制通过构造虚拟中性点实现单相系统的类三相化处理,提升了电流控制精度;而直流电流控制则结构简洁、易于实现。仿真结果验证了两种策略在实现高效能量转换、提升电能质量方面的有效性,为实际工程应用提供了理论依据和技术参考。; 适合人群:具备电力电子、自动控制理论基础,熟悉MATLAB/Simulink仿真环境,从事新能源发电、电力传动、电能质量治理或电力电子装置研发的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握单相PWM整流器的工作原理与数学建模方法;②深入理解直流电流控制与虚拟dq控制的实现机制、控制自由度及其在稳态与动态性能上的优劣;③通过搭建仿真平台验证控制策略,优化控制器参数,提升系统在负载扰动、输入波动等工况下的鲁棒性与稳定性,服务于实际产品开发与性能评估。; 阅读建议:建议结合所提供的仿真模型与参考资料进行同步学习与实践操作。在仿真过程中应重点关注PI控制器参数整定对系统动态响应和稳态精度的影响,尝试设置不同负载、输入电压波动等工况,观察系统的抗干扰能力与控制性能变化,从而深化对控制机理的理解,全面提升对单相PWM整流器闭环控制技术的掌握水平。

上一篇: RSA studing 'n reversing
下一篇: DNS缓存投毒细节泄露
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值