使用c++實(shí)現(xiàn)異或加密的代碼示例
加密原理
由于展示最基本最簡(jiǎn)單的實(shí)現(xiàn),使用算法加密就沒(méi)用復(fù)雜的。如果使用比較復(fù)雜的加密,首先你在C++代碼層面和匯編層面要有配套的代碼,C++負(fù)責(zé)加密,匯編負(fù)責(zé)自我解密,否則你加密完了,結(jié)果加密后的PE文件自己又解密不了,這就很尷尬。
在所有加密算法,異或加密是最簡(jiǎn)單的,也是最好是實(shí)現(xiàn)的。我們來(lái)介紹異或加密的原理。
已知兩個(gè)數(shù)A和B,如果A xor B = C,則C xor B = A,其中xor表示異或運(yùn)算符。如果不理解,這個(gè)是入門(mén)編程的最基本的知識(shí),請(qǐng)自行補(bǔ)缺,這里我就不嘮叨了。
異或加密的實(shí)現(xiàn)
下面是我們實(shí)現(xiàn)異或加密的相關(guān)函數(shù):
// GNU AFFERO GENERAL PUBLIC LICENSE
//Version 3, 19 November 2007
//Copyright(C) 2007 Free Software Foundation, Inc.
//Everyone is permitted to copyand distribute verbatim copies
//of this license document, but changing it is not allowed.
// Author : WingSummer (寂靜的羽夏)
//Warning: You can not use it for any commerical use,except you get
// my AUTHORIZED FORM ME!This project is used for tutorial to teach
// the beginners what is the PE structure and how the packer of the PE files works.
BOOL CWingProtect::XORCodeSection(BOOL NeedReloc, BOOL FakeCode)
{
using namespace asmjit;
if (_lasterror != ParserError::Success) return FALSE;
auto filesize = peinfo.FileSize.QuadPart;
CodeHolder holder;
/// <summary>
/// PointerToRawData
/// </summary> auto p = peinfo.PCodeSection->PointerToRawData;
/// <summary>
/// SizeOfRawData
/// </summary>
auto sizecode = peinfo.PCodeSection->SizeOfRawData;
auto repeat = sizecode;
BYTE* shellcode;
INT3264 ccount;
if (is64bit)
{
Environment env(Arch::kX64);
holder.init(env);
x86::Assembler a(&holder);
Label loop = a.newLabel();
x86::Mem mem;
mem.setSegment(x86::gs);
mem.setOffset(0x60);
//生成加密 shellcode,此處的 rax = ImageBase
a.push(x86::rcx);
a.push(x86::rdi);
//xor 解密
a.mov(x86::rax, mem);
a.mov(x86::rax, x86::qword_ptr(x86::rax, 0x10));
a.mov(x86::rdi, x86::rax);
a.add(x86::rdi, peinfo.PCodeSection->VirtualAddress);
a.mov(x86::rcx, repeat);
a.bind(loop);
if (FakeCode) FakeProtect(a);
a.xor_(x86::byte_ptr(x86::rdi), 0x55);
a.inc(x86::rdi);
a.dec(x86::rcx);
a.test(x86::rcx, x86::rcx);
a.jnz(loop);
//確保此時(shí) rax 或 eax 存放的是 ImageBase ,否則是未定義行為
if (NeedReloc)
RelocationSection(a);
a.pop(x86::rdi);
a.pop(x86::rcx);
a.ret();
shellcode = a.bufferData();
ccount = holder.codeSize();
}
else
{
Environment env(Arch::kX86);
holder.init(env);
x86::Assembler a(&holder);
Label loop = a.newLabel();
x86::Mem mem;
mem.setSegment(x86::fs);
mem.setOffset(0x30);
//生成加密 shellcode
a.push(x86::ecx);
a.push(x86::edi);
a.mov(x86::eax, mem);
a.mov(x86::eax, x86::dword_ptr(x86::eax, 0x8));
a.mov(x86::edi, x86::eax);
a.add(x86::edi, peinfo.PCodeSection->VirtualAddress);
a.mov(x86::ecx, repeat);
a.bind(loop);
if (FakeCode) FakeProtect(a);
a.xor_(x86::byte_ptr(x86::edi), 0x55);
a.inc(x86::edi);
a.dec(x86::ecx);
a.test(x86::ecx, x86::ecx);
a.jnz(loop);
//確保此時(shí) rax 或 eax 存放的是 ImageBase ,否則是未定義行為
if (NeedReloc)
RelocationSection(a);
a.pop(x86::edi);
a.pop(x86::ecx);
a.ret();
shellcode = a.bufferData();
ccount = holder.codeSize();
}
//異或加密
auto se = (BYTE*)b;
for (UINT i = 0; i < repeat; i++)
{
se[i] ^= (BYTE)0x55;
}
//加密完畢,寫(xiě) Shellcode
encryptInfo.XORDecodeShellCode = (UINT)peinfo.PointerOfWingSeciton;
auto ws = GetPointerByOffset(peinfo.WingSecitonBuffer, peinfo.PointerOfWingSeciton);
memcpy_s(ws, ccount, shellcode, ccount);
peinfo.PointerOfWingSeciton += ccount;
if (!NeedReloc)
{
auto tmp = (PIMAGE_SECTION_HEADER)TranModPEWapper(peinfo.PCodeSection);
tmp->Characteristics |= IMAGE_SCN_MEM_WRITE;
}
return TRUE;
}
在C++代碼層面,加密代碼區(qū)內(nèi)容相關(guān)的代碼如下:
//異或加密
auto se = (BYTE*)b;
for (UINT i = 0; i < repeat; i++)
{
se[i] ^= (BYTE)0x55;
}
^表示異或運(yùn)算符,在匯編層面,以64位為例,實(shí)現(xiàn)如下所示:
a.mov(x86::rax, mem); a.mov(x86::rax, x86::qword_ptr(x86::rax, 0x10)); a.mov(x86::rdi, x86::rax); a.add(x86::rdi, peinfo.PCodeSection->VirtualAddress); a.mov(x86::rcx, repeat); a.bind(loop); if (FakeCode) FakeProtect(a); a.xor_(x86::byte_ptr(x86::rdi), 0x55); a.inc(x86::rdi); a.dec(x86::rcx); a.test(x86::rcx, x86::rcx); a.jnz(loop);
可以看出來(lái)匯編寫(xiě)起來(lái)比寫(xiě)C++代碼麻煩多了,里面有一些代碼可能有一些其他的考慮,我們這里說(shuō)一下:
首先是FakeProtect,這個(gè)就是生成花指令,這里不多說(shuō),后面在介紹。還有一個(gè)函數(shù)比較注意RelocationSection,這個(gè)函數(shù)是用來(lái)生成做重定位的匯編代碼的,為什么要有這個(gè)函數(shù)呢?
比如我只有異或加密,我們是在硬編碼的層面進(jìn)行的加密,PE被加載進(jìn)入的時(shí)候如果基址不和預(yù)想的那樣,就會(huì)查是否有重定位表,如果有的話(huà)就解析并修復(fù)。但是,我們的代碼是加密的,而重定位表沒(méi)做修改,它就會(huì)錯(cuò)誤的把被加密的硬編碼進(jìn)行重定位,這個(gè)是不能夠允許的。所以我們需要摧毀重定位表,可以看到CWingProtect::Proctect里面有一個(gè)函數(shù)DestoryRelocation,這個(gè)作用就是用來(lái)銷(xiāo)毀它的,不讓PE加載器幫我們做重定位。
綜上所述,我們需要自己做重定位,我們需要在匯編層面來(lái)實(shí)現(xiàn)重定位表的修復(fù),我們來(lái)看一下相關(guān)代碼:
//
// GNU AFFERO GENERAL PUBLIC LICENSE
//Version 3, 19 November 2007
//
//Copyright(C) 2007 Free Software Foundation, Inc.
//Everyone is permitted to copyand distribute verbatim copies
//of this license document, but changing it is not allowed.
// Author : WingSummer (寂靜的羽夏)
//
//Warning: You can not use it for any commerical use,except you get
// my AUTHORIZED FORM ME!This project is used for tutorial to teach
// the beginners what is the PE structure and how the packer of the PE files works.
void CWingProtect::RelocationSection(asmjit::x86::Assembler& a)
{
using namespace asmjit;
Label loop_xor = a.newLabel();
Label loop_reloc = a.newLabel();
Label loop_rt = a.newLabel();
Label endproc = a.newLabel();
auto rdd = peinfo.PDataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (is64bit)
{
a.nop();
a.push(x86::rdi);
a.push(x86::rcx);
a.push(x86::rsi); //征用 rsi
a.mov(x86::rsi, rdd.VirtualAddress); //重定位表基址
a.add(x86::rsi, x86::rax);
a.push(x86::rdx); //征用 rdx
a.push(x86::r10);
a.mov(x86::r10, peinfo.ImageBase); //PE 加載后,該值會(huì)被重定位,只能寫(xiě)死
a.sub(x86::r10, x86::rax);
a.jz(endproc);
a.bind(loop_rt);
a.mov(x86::edi, x86::dword_ptr(x86::rsi)); //偏移基址地址
a.add(x86::rdi, x86::rax); //此時(shí) rdi 為加載到內(nèi)存的虛擬基址地址
//計(jì)數(shù)
a.mov(x86::ecx, x86::dword_ptr(x86::rsi, 4));
a.sub(x86::ecx, 8);
a.shr(x86::ecx, 1); //此時(shí)為重定位表的真實(shí)項(xiàng)目個(gè)數(shù)
a.add(x86::rsi, 8); //將指針指向該索引下的第一個(gè)重定位項(xiàng)目
a.bind(loop_reloc);
a.dec(x86::rcx);
a.mov(x86::dx, x86::word_ptr(x86::rsi, x86::rcx, 1));
a.test(x86::dx, 0xF000);
a.jz(loop_reloc); //contine;
a.and_(x86::edx, 0xFFF);
a.add(x86::rdx, x86::rdi);
a.sub(x86::qword_ptr(x86::rdx), x86::r10); //修正
a.cmp(x86::rcx, 0);
a.ja(loop_reloc);
a.sub(x86::rsi, 8); //重新指向表頭
a.mov(x86::edx, x86::dword_ptr(x86::rsi, 4));
a.add(x86::rsi, x86::rdx); //指向下一個(gè)
a.mov(x86::edx, x86::dword_ptr(x86::rsi));
a.test(x86::edx, x86::edx);
a.jnz(loop_rt);
a.bind(endproc);
a.pop(x86::r10);
a.pop(x86::rdx);
a.pop(x86::rsi); //釋放 rsi 自由身
a.pop(x86::rcx);
a.pop(x86::rdi);
}
else
{
a.push(x86::edi);
a.push(x86::ecx);
a.push(x86::esi); //征用 rsi
a.mov(x86::esi, rdd.VirtualAddress); //重定位表基址
a.add(x86::esi, x86::eax);
a.push(x86::edx); //征用 edx
a.push((DWORD32)peinfo.ImageBase); //x86寄存器沒(méi)那么多,只能自己維護(hù)一個(gè)局部變量
a.sub(x86::dword_ptr(x86::esp), x86::rax);
a.jz(endproc);
a.bind(loop_rt);
a.mov(x86::edi, x86::dword_ptr(x86::esi)); //偏移基址地址
a.add(x86::edi, x86::eax); //此時(shí) rdi 為加載到內(nèi)存的虛擬基址地址
//計(jì)數(shù)
a.mov(x86::ecx, x86::dword_ptr(x86::esi, 4));
a.sub(x86::ecx, 8);
a.shr(x86::ecx, 1); //此時(shí)為重定位表的真實(shí)項(xiàng)目個(gè)數(shù)
a.add(x86::esi, 8); //將指針指向該索引下的第一個(gè)重定位項(xiàng)目
a.bind(loop_reloc);
a.dec(x86::ecx);
a.mov(x86::dx, x86::word_ptr(x86::rsi, x86::ecx, 1));
a.test(x86::dx, 0xF000);
a.jz(loop_reloc); //contine;
a.and_(x86::edx, 0xFFF);
a.add(x86::edx, x86::edi);
a.push(x86::eax); //使用局部變量
a.mov(x86::eax, x86::dword_ptr(x86::esp, 4)); //注意被 push 了一個(gè),所以加個(gè)偏移
a.sub(x86::dword_ptr(x86::edx), x86::eax); //修正
a.pop(x86::eax);
a.cmp(x86::ecx, 0);
a.ja(loop_reloc);
a.sub(x86::esi, 8); //重新指向表頭
a.mov(x86::edx, x86::dword_ptr(x86::esi, 4));
a.add(x86::esi, x86::rdx); //指向下一個(gè)
a.mov(x86::edx, x86::dword_ptr(x86::esi));
a.test(x86::edx, x86::edx);
a.jnz(loop_rt);
a.bind(endproc);
a.add(x86::esp, 4); //釋放局部變量
a.pop(x86::edx);
a.pop(x86::esi); //釋放 rsi 自由身
a.pop(x86::ecx);
a.pop(x86::edi);
}
//將所有的節(jié)全部改為可寫(xiě)
auto length = peinfo.NumberOfSections;
for (UINT i = 0; i < length; i++)
{
((PIMAGE_SECTION_HEADER)TranModPEWapper(&peinfo.PSectionHeaders[i]))
->Characteristics |= IMAGE_SCN_MEM_WRITE;
}
}
對(duì)于以上代碼你可能有一些疑問(wèn),我這里說(shuō)一下:
為什么調(diào)用a.nop()來(lái)生成沒(méi)有用的指令,這個(gè)是我用來(lái)方便調(diào)試我生成的ShellCode用的,否則會(huì)生成一大坨匯編到后來(lái)自己也不清楚自己在調(diào)試啥的,通過(guò)這個(gè)nop我就可以清楚的直到我到那里了,如果出錯(cuò)的話(huà)我也方便進(jìn)行定位。
此函數(shù)最后生成完ShellCode之后又將所有的節(jié)全部改為可寫(xiě)屬性,這是為什么呢?因?yàn)榫€性?xún)?nèi)存是有屬性的,如果我沒(méi)有將其設(shè)置可寫(xiě),如果它是只讀內(nèi)存,如果我對(duì)它做重定位修改的話(huà),就會(huì)報(bào)內(nèi)存訪問(wèn)錯(cuò)誤,導(dǎo)致程序崩潰。
怎么用匯編來(lái)解析重定位表,這里就不贅述了。
ShellCode 編寫(xiě)注意事項(xiàng)
在編寫(xiě)ShellCode代碼的時(shí)候,請(qǐng)一定保證如下原則,避免一些麻煩,否則會(huì)出現(xiàn)出乎意料的錯(cuò)誤:
- 除了 eax / rax 其他寄存器用到的話(huà),一定要注意保存好,因?yàn)槠渌瘮?shù)調(diào)用有各種調(diào)用約定,一定不要影響它們,否則會(huì)出錯(cuò)。為什么要對(duì) eax / rax 區(qū)別對(duì)待,因?yàn)橥ǔ?lái)說(shuō)它只用做返回值,調(diào)用函數(shù)返回結(jié)果一定會(huì)修改它,所以大可不必。
- 在使用 ASMJIT 生成匯編的時(shí)候,使用類(lèi)似 MOV 的指令的時(shí)候,一定要注意如果要寫(xiě)入多大的數(shù)據(jù)一定要在目標(biāo)操作數(shù)體現(xiàn)數(shù)來(lái),比如要移動(dòng) WORD 大小的話(huà),用 ax 就不要用 eax,否則它正常生成匯編指令不報(bào)錯(cuò),結(jié)果和你想生成的代碼不一樣。
- 一定要注意堆棧平衡,這個(gè)是非常重要的東西,在64位尤甚,32位的操作系統(tǒng)也是十分注意堆棧平衡的。
以上就是使用c++實(shí)現(xiàn)異或加密的代碼示例的詳細(xì)內(nèi)容,更多關(guān)于c++異或加密示例的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)經(jīng)典24點(diǎn)算法
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)經(jīng)典24點(diǎn)算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10
C++?primer超詳細(xì)講解關(guān)聯(lián)容器
兩個(gè)主要的關(guān)聯(lián)容器為map和set,map中元素是一些關(guān)鍵字—值對(duì),關(guān)鍵字起索引的作用,值則表示與索引相關(guān)聯(lián)的數(shù)據(jù)。set中每個(gè)元素只包含一個(gè)關(guān)鍵字,set支持高效的關(guān)鍵字查詢(xún)操作——檢查一個(gè)給定關(guān)鍵字是否在set中2022-07-07
C++實(shí)現(xiàn)簡(jiǎn)易反彈小球游戲的示例代碼
我們利用printf 函數(shù)實(shí)現(xiàn)一個(gè)在屏幕上彈跳的小球。彈跳的小球游戲比較簡(jiǎn)單、容易入門(mén),也是反彈球消磚塊、接金幣、臺(tái)球等很多游戲的基礎(chǔ),感興趣的可以了解一下2022-10-10
C++實(shí)現(xiàn)LeetCode(157.用Read4來(lái)讀取N個(gè)字符)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(157.用Read4來(lái)讀取N個(gè)字符),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
深入淺出理解C語(yǔ)言初識(shí)結(jié)構(gòu)體
C?數(shù)組允許定義可存儲(chǔ)相同類(lèi)型數(shù)據(jù)項(xiàng)的變量,結(jié)構(gòu)是?C?編程中另一種用戶(hù)自定義的可用的數(shù)據(jù)類(lèi)型,它允許你存儲(chǔ)不同類(lèi)型的數(shù)據(jù)項(xiàng),本篇讓我們來(lái)了解C?的結(jié)構(gòu)體2022-02-02

