Shellcode和Payload入门102-代码中的加密-一如既往地源码和超详细注释
2017-11-16 09:38
483 查看
附 1:
测试碰撞率带接口源码
在之前的Shellcode代码中,
对于必要的一些字符串常量采用的方法是:
将字符串以16进制形式保存在代码中。
而大多数常用的调试工具能够对其进行转译,明眼人一看,诶,这有猫腻啊。
很容易导致我们的“计划”流产。
那顺水推舟的,就接着分享一下鄙人的字符串加密的算法以及需要留意之处。
在Shellcode内加密字符串除了增加保密性以外,很重要的一点是能够缩小体积,
而体积(即字节数)对于Shellcode是至关生死的一项。
在设计加密算法时需要注意碰撞率,即在统一语境下不同源数据生成的密码的雷同率。
为测试本算法碰撞率,分别将Kernal32、user32、ntdll中所有导出函数名加密,
下面是测试结果:
![](https://img-blog.csdn.net/20171116091330975?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXEzMjczMzI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
综合考虑体积和碰撞率后,这里尝试写一个简单的字符串加密使用2字节长的密码,
翠花,上代码:
C源代码
汇编源代码
}
注:
可以看到其实C代码只有几行,汇编却有大量篇幅。
汇编没有经过精简,只是完成了源码的基本逻辑,
而且有意的在尝试更环保的利用32位的寄存器,
将EAX拆成了4个部分使用,
所以将更多的代码用在处理EAX家事上。
我在精简汇编代码的路上,匍匐前进。
——本文作者
附 1:
测试碰撞率带接口源码
// ShellCode_WSA.cpp : 定义控制台应用程序的入口点。
//
}
测试碰撞率带接口源码
在之前的Shellcode代码中,
对于必要的一些字符串常量采用的方法是:
将字符串以16进制形式保存在代码中。
而大多数常用的调试工具能够对其进行转译,明眼人一看,诶,这有猫腻啊。
很容易导致我们的“计划”流产。
那顺水推舟的,就接着分享一下鄙人的字符串加密的算法以及需要留意之处。
在Shellcode内加密字符串除了增加保密性以外,很重要的一点是能够缩小体积,
而体积(即字节数)对于Shellcode是至关生死的一项。
在设计加密算法时需要注意碰撞率,即在统一语境下不同源数据生成的密码的雷同率。
为测试本算法碰撞率,分别将Kernal32、user32、ntdll中所有导出函数名加密,
下面是测试结果:
综合考虑体积和碰撞率后,这里尝试写一个简单的字符串加密使用2字节长的密码,
翠花,上代码:
C源代码
WORD Encode(CHAR* pFuncName) { WORD Code = 0; BOOL isOdd = FALSE;// 是否为奇数下标 WORD times = 1; // 循环遍历Kernal32函数名并加密 while (*pFuncName) { /* 思路: ********************************************* 将字符串数组奇数下标取出来的字节放在高字节,(<<8) 将偶数下标取出来的字节放在低字节, 这是一层加密, 由于结果有大于百分之一的碰撞, 逐增加一层加密, 每次乘以一个从0每次递增1的数, 后检查带0特例, 将此些特例进行相应的位移。 ********************************************* */ // 奇数下标pFuncName[奇数] if (isOdd) { Code += (*pFuncName << 8) * times; isOdd = FALSE;// 设置下次进入偶数下标 } // 偶数下标pFuncName[偶数] else { Code += *pFuncName * times; isOdd = TRUE;// 设置下次进入奇数下标 } ++pFuncName;// 字符串后移一个字节 ++times; } // 对于特殊情况的处理 if (Code >> 8 == 0) { Code <<= 3;// 左边有0 则填充左边 } else if (Code << 8 == 0) { Code >>= 3;// 右边有0 则填充右边 } return Code; }
汇编源代码
WORD Encode_asm(CHAR* pFuncName) { WORD code = 0; _asm{ func_encode: /* EAX 32位~24位恒为0可作为比较使用 EAX 24位~16位在判断当前加密次数是奇数次还是偶数此时作为bool使用 起始为0,每次加密完一个字节后设置 AH用于奇数次时保存字节和运算字节使用 AL用于偶数次时保存字节和运算字节使用 BX保存每次AX加密结果和最终的加密结果 CL用于保存字符串按字节加密的次数 注:密码中尽量不要出现字节0 */ XOR EAX, EAX; // / XOR EBX, EBX; //| XOR ECX, ECX; //| INC ECX; // \->Init MOV ESI, pFuncName; // ESI == pFuncName DEC ESI; // Init CL == 1 not 0 loop_whiletrue: MOV AL, FuncName_byte; // AL == BYTE pFuncName CMP AL, 0; // while(*pFuncName) <----------------------v JZ tag_checkbhforzero; // if ends see if contains 0 in byte --------------v PUSH EAX; // PU_1 | | SHR EAX, 16; // /Pre EAX:0x00 0X XXXX Pos EAX:0x0000 00 0X | | CMP AH, AL; // \CMP 00, 0X | | POP EAX; // PO_1 | | JNZ tag_isOdd; // 0X != 0 is Odd ---------------v | | tag_isEven: // /0X == 0 is even number | | | IMUL AX, CL; //|*pFuncName * times | | | ADD BX, AX; //|code += *pFuncName * times | | | OR EAX, 0 4000 X10000; //|0X == 1 next isOdd | | | INC ECX; //|++times | | | JMP loop_whiletrue; // \goto while -------------------------------^ | tag_isOdd: // /is odd number <-------------^ | | SHL AX, 8; //|(*pName<<8) | | IMUL AX, CL; //|(*pName<<8) * times | | ADD BX, AX; //|code += (*pName<<8) * times | | XOR EAX, EAX; //|EAX == 0 == 0X | | INC ECX; //|++times | | JMP loop_whiletrue; // \goto while -------------------------------^ | tag_checkbhforzero: // /Check for exceptions <----------------------^ CMP BH, 0X0; //|fir BH JNZ tag_checkblforzero; //|if noraml go check BL -----v SHL BX, 3; //|if not fill left 0 | tag_checkblforzero: //| <-----------------^ CMP BL, 0; //|sec BL JNZ tag_ret; //|if noraml goto ret -----v SHR BX, 3; // \if not fill right 0 | tag_ret: // / <----------------^ MOV code, BX; // \Save retVal tag_elfin: // } // return code;
}
注:
可以看到其实C代码只有几行,汇编却有大量篇幅。
汇编没有经过精简,只是完成了源码的基本逻辑,
而且有意的在尝试更环保的利用32位的寄存器,
将EAX拆成了4个部分使用,
所以将更多的代码用在处理EAX家事上。
我在精简汇编代码的路上,匍匐前进。
——本文作者
附 1:
测试碰撞率带接口源码
// ShellCode_WSA.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h" #include <windows.h> WORD FuncNameCodes[0x1000];// 存放整个模块函数名加密结果 WORD Code = 0; // 存放当前字符串加密结果 WORD Encode(CHAR* pFuncName) { // TODO : 加密代码 return Code; } float TestCrashs(DWORD DllBase, CHAR* ModuleName = "Kernal32.dll") { // 找到导出表及其数据 #pragma region Get_ExportTable CHAR* ModuleBuf = (CHAR*)DllBase; PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ModuleBuf; PIMAGE_NT_HEADERS pNT = PIMAGE_NT_HEADERS(pDos->e_lfanew + ModuleBuf); PIMAGE_OPTIONAL_HEADER pOpt = &pNT->OptionalHeader; PIMAGE_DATA_DIRECTORY pExportDir = pOpt->DataDirectory + 0; PIMAGE_EXPORT_DIRECTORY pExport = PIMAGE_EXPORT_DIRECTORY(pExportDir->VirtualAddress + ModuleBuf); PDWORD pEAT = PDWORD(pExport->AddressOfFunctions + ModuleBuf); PDWORD pENT = PDWORD(pExport->AddressOfNames + ModuleBuf); PWORD pEOT = PWORD(pExport->AddressOfNameOrdinals + ModuleBuf); DWORD NumONames = pExport->NumberOfNames;// 导出表名称表中名称个数 #pragma endregion Get_ExportTable ZeroMemory(FuncNameCodes, 0x1000); for (INT i = 0; i < NumONames; ++i) { CHAR* pName = pENT[i] + ModuleBuf;// 取出导出函数名称 #pragma region Encode FuncNameCodes[i] = Encode(pName); #pragma endregion Encode } // INT TotalNumOfCrash = 0;// 碰撞次数总和 INT nNumOfCrash = 0; // 碰撞次数 for (INT i = 0; i < NumONames - 1; ++i) { nNumOfCrash = 0; for (INT j = i + 1; j < NumONames - i - 1; ++j) { // 碰撞成功 if (FuncNameCodes[i] == FuncNameCodes[j]) { ++nNumOfCrash;// 碰撞次数+1 CHAR* pName1 = pENT[i] + ModuleBuf;// 当前加密函数名 CHAR* pName2 = pENT[j] + ModuleBuf;// 匹配到的重名函数 printf("No.[%04d] %s and No.[%04d] %s \n", i, pName1, j, pName2); } } if (nNumOfCrash != 0) { //printf("Number %d has crashed %d times\n", i, nNumOfCrash); TotalNumOfCrash += nNumOfCrash; } // 是否有字节为0的情况 if (FuncNameCodes[i] >> 8 == 0 || FuncNameCodes[i] << 8 == 0) { CHAR* pName1 = pENT[i] + ModuleBuf; printf("!!!No.[%04d] %s has 00 \n", i, pName1); } } // 总共碰撞率 float fCrashRate = (float)TotalNumOfCrash / (float)NumONames; // 总共碰撞次数 printf("************************************************************************************\n\ In %s crashed total %d times over %d names crash rate %0.1f%%\n************************************************************************************\n" , ModuleName, TotalNumOfCrash, NumONames, (float)TotalNumOfCrash / (float)NumONames * 100); return fCrashRate; } int _tmain(int argc, _TCHAR* argv[]) { // LoadLibrary("KERNAL32.DLL"); #pragma region LoadLibrary_KERNAL32.DLL DWORD KernalBaseAddr = 0; WCHAR* wzKERNAL32 = L"KERNAL32.DLL"; _asm { tag_shellcode: MOV EAX, FS : [0x30]; // EAX == _PEB MOV EAX, [EAX + 0xC]; // EAX == Ldr _PEB_LDR_DATA MOV EAX, [EAX + 0xC]; // EAX == _List_ENTRY == _LDR_DATA_TABLE_ENTRY JMP tag_checkname; tag_nextModule: MOV EAX, [EAX]; // _LIST_ENTRY == _LIST_ENTRY->(+0x000)Flink == Previous _LDR_DATA_TABLE_ENTRY Addr tag_checkname: MOV EBX, DWORD PTR DS : [EAX + 0x2C + 0x4]; // _UNICODE_STRING->BUFFER PUSH EAX; // Save List Addr MOV EAX, DWORD PTR DS : [EAX + 0x2C]; // _UNICODE_STRING->Length(word) AND EAX, 0X0000FFFF; // Save Loword : Length(word) SHR EAX, 2; // Length*2 == bytes MOV ECX, EAX; // rep cmps times MOV ESI, EBX; // POP EAX; // EAX == _List_ENTRY MOV EDI, wzKERNAL32; //********************************* Module Name in UNICODE REP CMPS; // JNZ tag_nextModule; // MOV EAX, DWORD PTR DS : [EAX + 0x18]; // _LDR_DATA_TABLE_ENTRY->DllBase MOV KernalBaseAddr, EAX; // [EBP-0x4]:PVOID KernalBaseAddr } #pragma endregion LoadLibrary_KERNAL32.DLL DWORD ntdllBaseAddr = (DWORD)LoadLibraryA("ntdll.dll"); DWORD user32BaseAddr = (DWORD)LoadLibraryA("user32.dll"); float fTotalRate = TestCrashs(KernalBaseAddr); fTotalRate += TestCrashs(ntdllBaseAddr, "ntdll.dll"); fTotalRate += TestCrashs(user32BaseAddr, "user32.dll"); printf("\n>>>>Result : Total crash rate is <%0.1f%%>\n", fTotalRate / 3 * 100); system("pause"); return 0;
}
相关文章推荐
- Shellcode和Payload入门101-超详细源码和注释以及Hex文本
- Prototype 1.4.0源码详细解释--脚本代码全文注释
- Ajax入门实例----HelloWorld附源码详细注释
- Ajax入门实例----HelloWorld附源码详细注释
- IO完成端口入门级详细注释代码
- HDOJ1175 宽搜BFS基础入门题(有详细注释的代码)
- 新浪微博自动转发评论 源码 按键精灵实现 详细注释 几十行代码实现 涉及图像识别模拟键盘鼠标
- Android 多线程下载源码实现详细注释
- XNA入门的代码注释
- (三层架构入门)最基本的三层架构搭建方法(详细过程及所有代码)
- EventBus源码解读详细注释(6)从事件发布到事件处理,究竟发生了什么!
- swift:使用NSXMLParser解析本地xml文件(详细代码注释)
- 亲测dropzone.js插件,并附自己做的上传附件案例前后台源码(详细注释)以及汉化后的效果图
- Jquery瀑布流布局(每行代码都有详细注释
- UDT源码剖析(六):UDT::socket()过程代码注释
- (转)发布一个Java写的俄罗斯方块源码 算法简单(300行) 注释详细
- Linux内核2.6.14源码分析-双向循环链表代码分析(巨详细)
- JAVA连接Oracle数据库实代码+详细注释
- 杭电acm1052解题代码(详细注释)
- PHP 缓存实现代码及详细注释