這篇文章給大家分享的是有關(guān)如何在通過TCP通信的位置無關(guān)代碼中實現(xiàn)數(shù)據(jù)加密的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
公司主營業(yè)務(wù):成都網(wǎng)站制作、網(wǎng)站設(shè)計、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)建站是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)建站推出靈璧免費做網(wǎng)站回饋大家。
如何在通過TCP通信的位置無關(guān)代碼(PIC)中實現(xiàn)數(shù)據(jù)加密。
我將以Linux下的同步Shell作為演示樣例
當(dāng)我們在思考加密協(xié)議時,第一個想到的很可能是安全傳輸層協(xié)議(TLS),因為它是針對Web安全的工業(yè)級標(biāo)準(zhǔn)。有的人可能還會想到SSH或IPSec等等,但是考慮到這些協(xié)議所采用的底層算法,它們其實都不適用于資源受限環(huán)境。而類似SHA-2和分組密碼(例如Blowfish)這樣加密哈希函數(shù)也并不是為類似RFID芯片這樣的占用資源較少的電子設(shè)備設(shè)計的。
在2018年4月份,NIST曾為物聯(lián)網(wǎng)行業(yè)的輕量級加密算法推行過一個標(biāo)準(zhǔn)化進程,整個過程需要好幾年的時間才可以完成,但毫無疑問的是,整個行業(yè)并不會一直等待,因為這樣會導(dǎo)致不安全的產(chǎn)品暴露在互聯(lián)網(wǎng)中。某些密碼學(xué)家選擇采取主動的方式,通過自己的努力將他們設(shè)計的協(xié)議采用到這些低資源消耗的設(shè)備上,其中有兩個典型的算法就是BLINKER和STROBE,而相應(yīng)的適用于資源受限環(huán)境的代碼庫有LibHydrogen和MonoCypher。
分組密碼有很多種,但AES 128可能是目前最適合對在線流量進行加密的算法了,下面給出的是我們對不同種類分組密碼的測試結(jié)果:
雖然這些加密算法都非常優(yōu)秀,但是他們?nèi)孕枰愃朴嫈?shù)器(CTR)和基于認(rèn)證的加密模塊,其中最適合消息認(rèn)證碼(MAC)的加密算法就是LightMAC了,因為它在實現(xiàn)加密的過程中使用的是相同的分組密碼。
另外兩種針對認(rèn)證加密的熱門算法(AES-GCM的替換)就是ChaCha20和Poly1305了,但是ChaCha20采用的是200字節(jié),而Poly1305為330字節(jié)。雖然跟HMAC-SHA2相比,Poly1305已經(jīng)壓縮得非常小了,但仍然占用資源過多。
如果你花了很多時間去測試各種加密算法的話,你最終會發(fā)現(xiàn)在構(gòu)造流密碼、分組密碼、加密認(rèn)證模型、加密哈希函數(shù)和隨機數(shù)生成器時,你需要的僅僅只是一個置換函數(shù)。下面這個表格給出的是我們針對三種函數(shù)的測試結(jié)果:
這里我們選擇使用Gimli,因為它占用資源最少,并且可以用來構(gòu)造針對通信流量的加密算法。
接下來,我們實現(xiàn)一個針對數(shù)據(jù)流的簡單異或操作(Just For Fun?。?。下面的截圖中顯示的是一臺Windows虛擬機發(fā)送給Linux虛擬機的部分命令,其中Linux平臺運行的Shellcode是沒有采用任何加密的。
捕捉到兩臺主機間的通信數(shù)據(jù)之后,我們可以看到如下所示的TCP流數(shù)據(jù):
給Shellcode x86匯編代碼中添加部分命令后,我們就可以進行8位異或運算了:
; ; read(r, buf, BUFSIZ, 0); xor esi, esi ; esi = 0 mov ecx, edi ; ecx = buf cdq ; edx = 0 mov dl, BUFSIZ ; edx = BUFSIZ push SYS_read ; eax = SYS_read pop eax int 0x80 ; encrypt/decrypt buffer pushad xchg eax, ecxxor_loop: xor byte[eax+ecx-1], XOR_KEY loop xor_loop popad ; write(w, buf, len); xchg eax, edx ; edx = len mov al, SYS_write pop ebx ; s or in[1] int 0x80 jmp poll_wait
通過在新的會話中執(zhí)行相同的命令,通信數(shù)據(jù)將無法直接可讀,我這里使用了haxdump來查看發(fā)送的命令以及接收到的結(jié)果:
當(dāng)然了,長度為8位的密鑰是無法有效阻止攻擊者恢復(fù)出通信明文的,下圖給出的是Cyberchef爆破密鑰的過程:
一開始,我使用的是下面這段代碼來對數(shù)據(jù)包的加密進行驗證,它使用了Encrypt-then-MAC (EtM),而且這種方法比其他的方法要更安全,比如說MAC-then-Encrypt (MtE) 或Encrypt-and-MAC(E&M):
bits32 %defineSPECK_RNDS 27%defineN 8%defineK 16 ;*****************************************;Light MAC parameters based on SPECK64-128;; N =64-bits; K =128-bits;%defineCOUNTER_LENGTH N/2 ; should be <= N/2%defineBLOCK_LENGTH N ; equal to N%defineTAG_LENGTH N ; >= 64-bits && <= N%defineBC_KEY_LENGTH K ; K %defineENCRYPT_BLK speck_encrypt%defineGET_MAC lightmac%defineLIGHTMAC_KEY_LENGTH BC_KEY_LENGTH*2 ; K*2 %definek0 edi %definek1 ebp %definek2 ecx %definek3 esi %definex0 ebx %definex1 edx ; esi= IN data; ebp= IN key speck_encrypt: pushad push esi ; save M lodsd ; x0 = x->w[0] xchg eax, x0 lodsd ; x1 = x->w[1] xchg eax, x1 mov esi, ebp ; esi = key lodsd xchg eax, k0 ; k0 = key[0] lodsd xchg eax, k1 ; k1 = key[1] lodsd xchg eax, k2 ; k2 = key[2] lodsd xchg eax, k3 ; k3 = key[3] xor eax, eax ; i = 0spk_el: ; x0 = (ROTR32(x0, 8) + x1) ^ k0; ror x0, 8 add x0, x1 xor x0, k0 ; x1 = ROTL32(x1, 3) ^ x0; rol x1, 3 xor x1, x0 ; k1 = (ROTR32(k1, 8) + k0) ^ i; ror k1, 8 add k1, k0 xor k1, eax ; k0 = ROTL32(k0, 3) ^ k1; rol k0, 3 xor k0, k1 xchg k3, k2 xchg k3, k1 ; i++ inc eax cmp al, SPECK_RNDS jnz spk_el pop edi xchg eax, x0 ; x->w[0] = x0 stosd xchg eax, x1 ; x->w[1] = x1 stosd popad ret ; edx= IN len; ebx= IN msg; ebp= IN key; edi= OUT tag lightmac: pushad mov ecx, edx xor edx, edx add ebp, BLOCK_LENGTH + BC_KEY_LENGTH pushad ; allocate N-bytes for M ; zero initialize T mov [edi+0], edx ; t->w[0] = 0; mov [edi+4], edx ; t->w[1] = 0; ; while we have msg datalmx_l0: mov esi, esp ; esi = M jecxz lmx_l2 ; exit loop ifmsglen == 0lmx_l1: ; add byte to M mov al, [ebx] ; al = *data++ inc ebx mov [esi+edx+COUNTER_LENGTH], al inc edx ; idx++ ; M filled? cmp dl, BLOCK_LENGTH - COUNTER_LENGTH ; --msglen loopne lmx_l1 jne lmx_l2 ; add S counter in big endian format inc dword[esp+_edx]; ctr++ mov eax, [esp+_edx] ; reset index cdq ; idx = 0 bswap eax ; m.ctr =SWAP32(ctr) mov [esi], eax ; encrypt M with E using K1 call ENCRYPT_BLK ; update T lodsd ; t->w[0] ^= m.w[0]; xor [edi+0], eax lodsd ; t->w[1] ^= m.w[1]; xor [edi+4], eax jmp lmx_l0 ; keep goinglmx_l2: ; add the end bit mov byte[esi+edx+COUNTER_LENGTH], 0x80 xchg esi, edi ; swap T and Mlmx_l3: ; update T with any msg dataremaining mov al, [edi+edx+COUNTER_LENGTH] xor [esi+edx], al dec edx jns lmx_l3 ; advance key to K2 add ebp, BC_KEY_LENGTH ; encrypt T with E using K2 call ENCRYPT_BLK popad ; release memory for M popad ; restore registers ret ; IN:ebp = global memory, edi = msg, ecx = enc flag, edx = msglen;OUT: -1 or length of data encrypted/decryptedencrypt: push -1 pop eax ; set return valueto -1 pushad lea ebp, [ebp+@ctx] ; ebp crypto ctx mov ebx, edi ; ebx = msg pushad ; allocate 8-bytes fortag+strm mov edi, esp ; edi = tag ; if (enc) { ; verify tag + decrypt jecxz enc_l0 ; msglen -= TAG_LENGTH; sub edx, TAG_LENGTH jle enc_l5 ; return -1 if msglen <= 0 mov [esp+_edx], edx ; GET_MAC(ctx, msg, msglen, mac); call GET_MAC ; memcmp(mac, &msg[msglen],TAG_LENGTH) lea esi, [ebx+edx] ; esi = &msg[msglen] cmpsd jnz enc_l5 ; not equal? return-1 cmpsd jnz enc_l5 ; ditto ; MACs are equal ; zero the MAC xor eax, eax mov [esi-4], eax mov [esi-8], eaxenc_l0: mov edi, esp test edx, edx ; exit if (msglen== 0) jz enc_lx ; memcpy (strm, ctx->e_ctr,BLOCK_LENGTH); mov esi, [esp+_ebp]; esi = ctx->e_ctr push edi movsd movsd mov ebp, esi pop esi ; ENCRYPT_BLK(ctx->e_key, &strm); call ENCRYPT_BLK mov cl, BLOCK_LENGTH ; r=(len > BLOCK_LENGTH) ?BLOCK_LENGTH : len;enc_l2: lodsb ; al = *strm++ xor [ebx], al ; *msg ^= al inc ebx ; msg++ dec edx loopnz enc_l2 ; while (!ZF&& --ecx) mov cl, BLOCK_LENGTH enc_l3: ; do { ; update counter mov ebp, [esp+_ebp] inc byte[ebp+ecx-1] loopz enc_l3 ; } while (ZF&& --ecx) jmp enc_l0enc_lx: ; encrypting? add MAC of ciphertext dec dword[esp+_ecx] mov edx, [esp+_edx] jz enc_l4 mov edi, ebx mov ebx, [esp+_ebx] mov ebp, [esp+_ebp] ; GET_MAC(ctx, buf, buflen, msg); call GET_MAC ; msglen += TAG_LENGTH; add edx, TAG_LENGTHenc_l4: ; return msglen; mov [esp+32+_eax], edx enc_l5: popad popad ret
需要注意的是,這里還得用到一個協(xié)議,接收方在對數(shù)據(jù)有效性進行驗證之前需要知道發(fā)送方到底發(fā)送了多少數(shù)據(jù)過來,因此加密長度需要首先發(fā)送,接下來才是加密數(shù)據(jù)。但是請等一下,這里明明應(yīng)該是Shellcode,為什么現(xiàn)在搞得那么復(fù)雜呢?試一下RC4?不,請大家往下看!
為了使用Gimli來代替RC4,我編寫了下面這段代碼,這里的置換函數(shù)本質(zhì)上就是Gimli:
#defineR(v,n)(((v)>>(n))|((v)<<(32-(n))))#defineF(n)for(i=0;i<n;i++)#defineX(a,b)(t)=(s[a]),(s[a])=(s[b]),(s[b])=(t) voidpermute(void*p){ uint32_t i,r,t,x,y,z,*s=p; for(r=24;r>0;--r){ F(4) x=R(s[i],24), y=R(s[4+i],9), z=s[8+i], s[8+i]=x^(z+z)^((y&z)*4), s[4+i]=y^x^((x|z)*2), s[i]=z^y^((x&y)*8); t=r&3; if(!t) X(0,1),X(2,3), *s^=0x9e377900|r; if(t==2)X(0,2),X(1,3); }} typedefstruct _crypt_ctx { uint32_t idx; int fdr, fdw; uint8_t s[48]; uint8_t buf[BUFSIZ];}crypt_ctx; uint8_tgf_mul(uint8_t x) { return (x << 1) ^ ((x >> 7) *0x1b);} //initialize crypto contextvoidinit_crypt(crypt_ctx *c, int r, int w, void *key) { int i; c->fdr = r; c->fdw = w; for(i=0;i<48;i++) { c->s[i] = ((uint8_t*)key)[i % 16] ^gf_mul(i); } permute(c->s); c->idx = 0;} //encrypt or decrypt buffervoidcrypt(crypt_ctx *c) { int i, len; // read from socket or stdout len = read(c->fdr, c->buf, BUFSIZ); // encrypt/decrypt for(i=0;i<len;i++) { if(c->idx >= 32) { permute(c->s); c->idx = 0; } c->buf[i] ^= c->s[c->idx++]; } // write to socket or stdin write(c->fdw, c->buf, len);}
在Linux Shell中使用這段代碼之前,我們需要聲明兩個單獨的加密上下文來處理輸入、輸出和128位的靜態(tài)密鑰:
//using a static 128-bit key crypt_ctx *c, c1, c2; // echo -n top_secret_key | openssl md5-binary -out key.bin // xxd -i key.bin uint8_t key[] = { 0x4f, 0xef, 0x5a, 0xcc, 0x15, 0x78, 0xf6,0x01, 0xee, 0xa1, 0x4e, 0x24, 0xf1, 0xac, 0xf9,0x49 };
在進入主輸出循環(huán)之前,我們還需要對每一個上下文初始化文件讀取和寫入描述符,這樣可以減少代碼的行數(shù):
// // c1 is for reading from socket andwriting to stdin init_crypt(&c1, s, in[1], key); // c2 is for reading from stdout andwriting to socket init_crypt(&c2, out[0], s, key); // now loop until user exits or someother error for (;;) { r = epoll_wait(efd, &evts, 1,-1); // error? bail out if (r<=0) break; // not input? bail out if (!(evts.events & EPOLLIN))break; fd = evts.data.fd; c = (fd == s) ? &c1 : &c2; crypt(c); }
感謝各位的閱讀!關(guān)于“如何在通過TCP通信的位置無關(guān)代碼中實現(xiàn)數(shù)據(jù)加密”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
網(wǎng)頁名稱:如何在通過TCP通信的位置無關(guān)代碼中實現(xiàn)數(shù)據(jù)加密
鏈接分享:http://www.rwnh.cn/article40/jispeo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供虛擬主機、微信公眾號、品牌網(wǎng)站制作、移動網(wǎng)站建設(shè)、外貿(mào)建站、域名注冊
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)