您的位置:首页 > 运维架构 > Shell

Shellcode和Payload入门102-代码中的加密-一如既往地源码和超详细注释

2017-11-16 09:38 483 查看
附 1:

测试碰撞率带接口源码

在之前的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;


}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息