项目分为两部分组成,一个是加壳程序,主要用于加密目标文件后生成一个新的可执行程序;另一个是壳本身DLL程序,主要用于在程序运行之前先解密被加密的部分。
一、第一部分——加壳程序
第一步,打开加壳的目标程序,读取数据。初步解析一下是不是PE文件。
CPE obj;
//打开并获取文件PE数据
obj.GetFileBuf(PATH);
bool CPE::GetFileBuf(char * szFilePath)
{
DWORD dwRealSize = 0;
//1 打开文件
HANDLE hFile = CreateFileA( szFilePath, FILE_ALL_ACCESS,NULL,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL );
if (hFile == INVALID_HANDLE_VALUE)
{
goto error;
}
//2 获取目标文件大小并申请内存后初始化
m_dwFileSize = GetFileSize(hFile, NULL);
m_pFileBuf = new CHAR[m_dwFileSize];
ZeroMemory(m_pFileBuf, m_dwFileSize);
//3 读入
ReadFile(hFile, m_pFileBuf, m_dwFileSize, &dwRealSize, NULL);
//4 初步解析一下
m_pFileDosHeader = (PIMAGE_DOS_HEADER)m_pFileBuf;
if (m_pFileDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
goto error;
}
m_pFileNtHeader = (PIMAGE_NT_HEADERS)(m_pFileDosHeader->e_lfanew + m_pFileBuf);
if (m_pFileNtHeader->Signature != IMAGE_NT_SIGNATURE)
{
goto error;
}
m_pFileSection = IMAGE_FIRST_SECTION(m_pFileNtHeader);
return true;
error:
CloseHandle(hFile);
if (m_pFileBuf != NULL)
{
delete[]m_pFileBuf;
m_pFileBuf = NULL;
}
MessageBoxA(0, "失败", 0, 0);
return false;
}
第二步,用LoadLibrary把Stub.dll加载进来。因为DLL加载进来后被修改了,造成它退出的时候会报错。所以需要再拷贝一份后在这里做修改。
//1 把Stub.dll加载进来
//获取模块句柄 通过路径
HMODULE hModule = LoadLibrary(L"..\\Release\\Stub.dll");
//拷贝一份(Middle中间),修改这一份。因为DLL加载进来后被修改了,造成它退出的时候会报错。
//模块大小长度
DWORD MiddleSize = obj.GetModuleLen(hModule);
//申请空间存放模块
PCHAR pStubBuf = (PCHAR)VirtualAlloc(NULL, MiddleSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
//正式拷贝
memcpy(pStubBuf, hModule, MiddleSize);
//获取模块PE数据
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pStubBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pStubBuf);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
DWORD CPE::GetModuleLen(HMODULE hModule)
{
//模块基址
PBYTE pImage = (PBYTE)hModule;
//DOS头
PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)pImage;
//NT头
PIMAGE_NT_HEADERS pImageNtHeader = (PIMAGE_NT_HEADERS)(pImageDosHeader->e_lfanew + pImage);
//文件头中->扩展头->文件在内存中的大小
return pImageNtHeader->OptionalHeader.SizeOfImage;
}
第三步,设置壳代码运行完之后jmp跳转的位置(未加壳程序的OEP)
//2 找到全局结构体
pKeInfo g_KeInfo = (pKeInfo)GetProcAddress(hModule, "g_KeInfo");
//设置壳代码运行后的入口点
g_KeInfo->dwOep = obj.GetOep();
//修改DLL中的OEP结构体
DWORD dwOffset_Pack = (DWORD)g_KeInfo - (DWORD)hModule;
memcpy((PCHAR)(pStubBuf + dwOffset_Pack), (PCHAR)g_KeInfo, sizeof(KeInfo));
//DLL文件OEP相对基址的偏移 dwNewOep在DLL文件中设置
DWORD dwOffset = g_KeInfo->dwNewOep - (DWORD)hModule - pSection->VirtualAddress;
//获取原目标文件OEP的VA
DWORD CPE::GetOep()
{
return m_pFileNtHeader->OptionalHeader.AddressOfEntryPoint + m_pFileNtHeader->OptionalHeader.ImageBase;
}
第四步,循环遍历区段,找到.text代码段。
//3 找到代码段
for (int i=0;i<pNt->FileHeader.NumberOfSections;++i)
{
if ((strcmp((char*)pSection->Name,".text") == 0))
{
break;
}
pSection++;
}
第五步,因为在壳代码中会使用全局变量,访问全局变量在会变层面都是通过VA得到的,但是拷贝到目标程序中VA就发生变化了,这些地方都是在重定位表中的,所以我们要根据数据的相对虚拟地址和目标程序新区段的起始位置作为基址修改重定位表。
DWORD CPE::CalcNewSectionPoint()
{
//新区段头的RVA
DWORD AddNewSectionRva = 0;
//新区段个数
DWORD dwNumberOfSection = 0;
//最后一个区段的指针
PIMAGE_SECTION_HEADER pLastSection = NULL;
//最后一个区段大小
DWORD dwLastSectionSize = 0;
//最后一个区段是第几个(区段个数)
dwNumberOfSection = m_pFileNtHeader->FileHeader.NumberOfSections;
//最后一个区段的指针
pLastSection = (m_pFileSection + dwNumberOfSection - 1);
//最后一个区段的大小
dwLastSectionSize = pLastSection->Misc.VirtualSize;
//新区段的RVA=最后区段RVA+对齐后的大小
AddNewSectionRva =pLastSection->VirtualAddress + CalcAligment(dwLastSectionSize, 0x1000);
return AddNewSectionRva;
}
//修改重定位信息 (固定重定位)
void CPE::FixReloc(PCHAR pDllBuf, PCHAR pOldBuf)
{
/*基本思路
1 DLL中代码段访问全局变量的地方,全部都存储在重定位表中
2 我们把代码段拷贝到了目标程序,访问全局变量的地方,VA肯定都是错的
3 所以说要修复VA,需要到重定位表中找到这些位置,去计算新的VA
*/
typedef struct _TYPEOFFSET
{
WORD Offset : 12; // (1) 大小为12Bit的重定位偏移
WORD Type : 4; // (2) 大小为4Bit的重定位信息类型值
}TYPEOFFSET, *PTYPEOFFSET;
//1 找到重定位表
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pDllBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pDllBuf);
//重定位表在数据目录中的结构体
PIMAGE_DATA_DIRECTORY pRelocDir = (pNt->OptionalHeader.DataDirectory + 5);
//重定位表
/*typedef struct _IMAGE_BASE_RELOCATION
{
DWORD VirtualAddress;
DWORD SizeOfBlock;
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;*/
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
//重定位(中间那层)结构体数组的VA
PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)
(pRelocDir->VirtualAddress + pDllBuf);
//遍历每一个TYPEOFFSET结构体 typeoffset
while (pReloc->SizeOfBlock != 0)
{
//本0x1000字节内需要重定位的个数
DWORD dwCount = (pReloc->SizeOfBlock - 8) / 2;
//本0x1000字节的起始位置
DWORD dwBaseRva = pReloc->VirtualAddress;
//下一个WORD Block 最里面的结构体
PTYPEOFFSET pOffset = (PTYPEOFFSET)(pReloc + 1);
//新区段基址的RVA Target目标
DWORD dwTargetLastSectionRva = CalcNewSectionPoint();
//判断高4位的属性 0x3则需要重定位
for (int i = 0; i < dwCount; i++)
{
if (pOffset->Type == 0x3)
{
//需要重定位的点VA
PDWORD pRelocPoint = (PDWORD)(pOffset[i].Offset + dwBaseRva + pDllBuf);
//下面这种是偷懒的做法,我们应该判断一下当前重定位点在哪一个区段中,就减哪一个区段的RVA
//我们这里默认,肯定在代码段中,默认代码段的起始RVA为0x1000
//重定位点距离本区段的偏移 chazhi差值
DWORD dwChazhi = *pRelocPoint - (DWORD)pOldBuf - 0x1000;
//修改重定位信息(地址)
*pRelocPoint = dwChazhi + dwTargetLastSectionRva + 0x00400000;
}
}
//下一个外层结构体 IMAGE_BASE_RELOCATION
pReloc = (PIMAGE_BASE_RELOCATION)((PCHAR)pReloc + pReloc->SizeOfBlock);
}
}
第六步,向新程序中添加一个新区段,用来存放DLL的代码。分两步添加,一个是添加区段头表的信息,另外是添加区段数据。
//添加区段
obj.AddSection("ChiLeMa", pSection->VirtualAddress + pStubBuf, pSection->SizeOfRawData, pSection->Characteristics);
//申请一段新空间m_pNewBuf,将目标程序的数据放入其中,然后在最后面再加一个区段用来写入壳代码。
void CPE::AddSection
(
//区段名
PCHAR szName,
//新区段由来的地方
PCHAR pSectionBuf,
//区段大小
DWORD dwSize,
//区段属性
DWORD dwCharacteristics
)
{
//1 计算一个添加完区段之后是多大,,
m_dwNewSize = m_dwFileSize + CalcAligment(dwSize, 0x200);
//然后申请空间 并初始化
m_pNewBuf = new CHAR[m_dwNewSize];
ZeroMemory(m_pNewBuf, m_dwNewSize);
//把目标PE文件内容拷贝到新空间中
memcpy(m_pNewBuf, m_pFileBuf, m_dwFileSize);
//获取新的DOS头
m_pNewDosHeader = (PIMAGE_DOS_HEADER)m_pNewBuf;
//获取新的NT头
m_pNewNtHeader = (PIMAGE_NT_HEADERS)(m_pNewDosHeader->e_lfanew + m_pNewBuf);
//获取新的区段头
m_pNewSection = IMAGE_FIRST_SECTION(m_pNewNtHeader);
//文件对齐数
DWORD dwFileAligment = m_pNewNtHeader->OptionalHeader.FileAlignment;
//内存中 块对齐数
DWORD dwSectionAlignment = m_pNewNtHeader->OptionalHeader.SectionAlignment;
// 2 添加区段分为两个部分:
//2.1 在区段表中添加信息(因为区段头和区段之间是有空隙的,所以暂不用考虑数据重叠的问题)
PIMAGE_SECTION_HEADER pLastSection =
m_pNewSection + m_pNewNtHeader->FileHeader.NumberOfSections - 1;
//新区段应该添加的位置
PIMAGE_SECTION_HEADER pAddNewSection = pLastSection + 1;
//2.1.1 区段名
strcpy_s((char*)(pAddNewSection->Name), 8, szName);
//2.1.2 新添加区段头的起始RVA=最后一个区段的RVA+它在内存中的大小
pAddNewSection->VirtualAddress =
pLastSection->VirtualAddress +
CalcAligment(pLastSection->Misc.VirtualSize, dwSectionAlignment);
//2.1.3 内存中大小
pAddNewSection->Misc.VirtualSize = dwSize;
//2.1.4 起始Offset 区段的文件偏移
pAddNewSection->PointerToRawData = pLastSection->PointerToRawData + pLastSection->SizeOfRawData;
//2.1.5 文件中大小
pAddNewSection->SizeOfRawData = CalcAligment(dwSize, dwFileAligment);
//2.1.6 区段属性
pAddNewSection->Characteristics = dwCharacteristics;
//2.2 拷贝新区段
memcpy(m_pNewBuf + m_dwFileSize, pSectionBuf, dwSize);
//3 修改头部信息
m_pNewNtHeader->FileHeader.NumberOfSections += 1;
m_pNewNtHeader->OptionalHeader.SizeOfImage += CalcAligment(dwSize, dwSectionAlignment);
}
第七步,取消随机基址。
//取消随机重定位基址
obj.CancelDynamicBase();
void CPE::CancelDynamicBase()
{
m_pNewNtHeader->OptionalHeader.DllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
}
第八步,设置新OEP到新创建的程序中。
//设置OEP到新程序中(dwOffset:DLL文件OEP相对基址的偏移)
obj.SetOep(dwOffset);
void CPE::SetOep(DWORD dwOffset)
{
//目标程序未加壳之前 最后一个区段首地址
PIMAGE_SECTION_HEADER pLastSection = m_pNewSection + (m_pNewNtHeader->FileHeader.NumberOfSections) - 1;
//最后一个区段的最末尾 也就是新区段的开始 也就是我们要的新OEP (RVA)
//OEP= 区段起始RVA + DLL文件OEP相对基址的偏移
m_pNewNtHeader->OptionalHeader.AddressOfEntryPoint = pLastSection->VirtualAddress + dwOffset;
}
第九步,加密。
//加密
obj.Encode();
void CPE::Encode()
{
//区段基址+区段的文件偏移PointerToRawData
PCHAR Begin = m_pNewBuf + m_pNewSection->PointerToRawData;
//循环加密
for (int i = 0; i < m_pNewSection->SizeOfRawData; i++)
{
Begin[i] ^= 0x15;
}
}
第十步,创建加壳后的新文件。
//创建加壳后的新文件
obj.CreateNewFile("G:\\Temp\\Test_Ke.exe");
//创建一个新文件,将修改好的m_pNewBuf读入进来。
bool CPE::CreateNewFile(char* szFilePath)
{
DWORD dwRealSize = 0;
//1 创建文件
HANDLE hFile = CreateFileA(
szFilePath,
FILE_ALL_ACCESS,
NULL,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
//2 读入 m_pNewBuf
WriteFile(hFile, m_pNewBuf, m_dwNewSize, &dwRealSize, NULL);
if (GetLastError() == 0)
{
MessageBox(0, L"创建成功", 0, MB_OK);
}
//3 关闭句柄
CloseHandle(hFile);
return 0;
}
二、 第二部分 DLL壳程序
第一步,设置新程序的OEP,使加壳后的程序先运行壳代码,然后再JMP跳转到原程序的OEP。
//声明入口点函数
void Start();
//导出变量 并给dwNewOep设置为Start函数地址
extern "C" _declspec(dllexport) KeInfo g_KeInfo = { (DWORD)Start };
在头文件定义的结构体。
typedef struct _KeInfo
{
//新的程序入口点
DWORD dwNewOep;
DWORD dwOep;
}KeInfo,*pKeInfo;
这一步要和第一部分中第八步结合,会把新文件中的 OptionalHeader. AddressOfEntryPoint(OEP)设置为这里。
第二步,获取Kenrnel32模块基址,然后动态获取API函数的地址
//获取Kenrnel32模块基址
int GetKernel32Base()
{
int nAddress = 0;
_asm
{
push eax;
mov eax, fs:[0x30];
mov eax, [eax + 0xC];
mov eax, [eax + 0xC];
mov eax, [eax];
mov eax, [eax];
mov eax, dword ptr ds : [eax + 0x18];
mov nAddress, eax;
pop eax;
}
return nAddress;
}
先获取LoadLibrary和GetProcAddress函数
bool MyGetProcAddress()
{
//到kernel32.dll的导出表中,根据函数名得到函数地址
//1 得到kernel32.dll的基址并获取基本的PE信息
PCHAR pBuf = (PCHAR)GetKernel32Base();
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
// 2 得到导出表
//扩展头中 数据目录表+0=导入表结构体
PIMAGE_DATA_DIRECTORY pExportDir = pNt->OptionalHeader.DataDirectory + 0;
//导出表VA
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(pExportDir->VirtualAddress + pBuf);
//函数名称表VA
PDWORD pNameTable = PDWORD(pExport->AddressOfNames + pBuf);
//函数地址表VA
PDWORD pAddressTable = PDWORD(pExport->AddressOfFunctions + pBuf);
//函数序号表VA
PWORD pOrder = PWORD(pExport->AddressOfNameOrdinals + pBuf);
//循环遍历函数名 通过序号表的连接找到函数地址表 ->函数地址
for (int i = 0; i < pExport->NumberOfNames; i++)
{
char * pszFunName = pNameTable[i] + pBuf;
if (strcmp(pszFunName, "GetProcAddress") == 0)
{
int nOrder = pOrder[i];
g_MyGetProcAddress = (GETPROCADDRESS)(pAddressTable[nOrder] + pBuf);
}
if (strcmp(pszFunName, "LoadLibraryW") == 0)
{
int nOrder = pOrder[i];
g_MyLoadLibrary = (LOADLIBRARYW)(pAddressTable[nOrder] + pBuf);
}
}
return true;
}
然后通过上面这两个函数,获取出其他要使用到的API函数
//得到API函数
void InitApi()
{
//调用函数获取函数地址
MyGetProcAddress();
//获取GetModuleHandleW
g_MyGetModuleHandlew = (GETMODULEHANDLEW)g_MyGetProcAddress
(g_MyLoadLibrary(L"Kernel32.dll"), "GetModuleHandleW");
//获取VirtualProtect
g_MyVirtualProtect = (VIRTUALPROTECT)g_MyGetProcAddress
(g_MyLoadLibrary(L"Kernel32.dll"), "VirtualProtect");
//获取其他各种函数
MyGetModuleHandleW = (GetModuleHandleW1)g_MyGetProcAddress
(g_MyLoadLibrary(L"Kernel32.dll"), "GetModuleHandleW");
MyCreateWindowExW = (CreateWindowExW1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "CreateWindowExW");
MySendMessageW = (SendMessageW1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "SendMessageW");
MyDefWindowProcW = (DefWindowProcW1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "DefWindowProcW");
MyPostQuitMessage = (PostQuitMessage1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "PostQuitMessage");
MyShowWindow = (ShowWindow1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "ShowWindow");
MyTranslateMessage = (TranslateMessage1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "TranslateMessage");
MyGetMessageW = (GetMessageW1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "GetMessageW");
MyDispatchMessageW = (DispatchMessageW1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "DispatchMessageW");
MyDestroyWindow = (DestroyWindow1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "DestroyWindow");
MyRegisterClassW = (RegisterClassW1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "RegisterClassW");
MyExitProcess = (ExitProcess1)g_MyGetProcAddress
(g_MyLoadLibrary(L"Kernel32.dll"), "ExitProcess");
MyGetWindowTextW = (GetWindowTextW1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "GetWindowTextW");
Mywcscmp = (wcscmp1)g_MyGetProcAddress
(g_MyLoadLibrary(L"ucrtbased.dll"), "wcscmp");
MyMessageBoxW = (MessageBoxW1)g_MyGetProcAddress
(g_MyLoadLibrary(L"User32.dll"), "MessageBoxW");
}
当然每个函数都需要在这上面写以下定义:
举例:
typedef HMODULE(WINAPI *LOADLIBRARYW)(_In_ LPCWSTR lpLibFileName);
typedef FARPROC(WINAPI *GETPROCADDRESS)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef HMODULE(WINAPI *GETMODULEHANDLEW)(_In_opt_ LPCWSTR lpModuleName);
typedef VOID (WINAPI *PostQuitMessage1)(
_In_ int nExitCode);
typedef BOOL (WINAPI *ShowWindow1)(
_In_ HWND hWnd,
_In_ int nCmdShow);
typedef BOOL (WINAPI *TranslateMessage1)(
_In_ CONST MSG *lpMsg);
typedef LRESULT (WINAPI *DispatchMessageW1)(
_In_ CONST MSG *lpMsg);
LOADLIBRARYW g_MyLoadLibrary;
GETPROCADDRESS g_MyGetProcAddress;
GETMODULEHANDLEW g_MyGetModuleHandlew;
PostQuitMessage1 MyPostQuitMessage;
ShowWindow1 MyShowWindow;
TranslateMessage1 MyTranslateMessage;
DispatchMessageW1 MyDispatchMessageW;
第三步,写SDK弹框程序。在密码输入正确并且点击按钮之后开始解密代码。
HWND g_hBtn;
HINSTANCE g_hInstance;
//用于接收Edit中字符串
WCHAR Buf[10] = {};
//定义PE变量
PCHAR pExeBuf;
PIMAGE_DOS_HEADER pDos = NULL;
PIMAGE_NT_HEADERS pNt = NULL;
PIMAGE_SECTION_HEADER pSection = NULL;
//第一个区段VA 开始得地方
PCHAR Begin;
//区段大小 长度
DWORD dwTextSize;
//保存一下原区段属性
DWORD lpflOldProtect = 0;
LRESULT CALLBACK OnWndCommand1(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (HIWORD(wParam))// 通知码
{
case BN_CLICKED:
{
switch (LOWORD(wParam))
{
case 0x1001:
//这里写判断
MyGetWindowTextW(g_hBtn, Buf, 10);
if (!Mywcscmp(Buf, L"15PB"))
{
//获取本模块的句柄
pExeBuf = (PCHAR)g_MyGetModuleHandlew(NULL);//PE
pDos = (PIMAGE_DOS_HEADER)pExeBuf;
pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pExeBuf);
pSection = IMAGE_FIRST_SECTION(pNt);
//第一个区段VA(因为只有一个.text段的原因)
Begin = pSection->VirtualAddress + pExeBuf;
//得到区段大小 长度
dwTextSize = pSection->SizeOfRawData;
//修改区段属性 改成可读可写
g_MyVirtualProtect(Begin, dwTextSize, PAGE_EXECUTE_READWRITE, &lpflOldProtect);
//遍历解密
for (DWORD i = 0; i < pSection->SizeOfRawData; i++)
{
Begin[i] = Begin[i] ^ 0x15;
}
MySendMessageW(hwnd, WM_CLOSE, NULL, NULL);
}
else
{
MyMessageBoxW(hwnd, TEXT("恭喜你!项目不及格"), NULL, MB_OK);
}
return true;
case 0x1002:
MyExitProcess(0);
return true;
default:
break;
}
}
break;
default:
break;
}
return MyDefWindowProcW(hwnd, uMsg, wParam, lParam);
}
中间省略窗口回调函数
void SDK(_In_ HINSTANCE hInstance )
{
g_hInstance = hInstance;
// 创建窗口的过程
// 1. 注册窗口类
// 2. 根据窗口类创建窗口
// 3. 消息循环
WNDCLASS wnd = {};
// 下面两个是必须的
wnd.lpszClassName = L"Super";
// 所有通过"Super"类创建的窗口,他们的消息回调函数都是WindowProc
wnd.lpfnWndProc = WindowProc11;
// 设置窗口背景色
wnd.hbrBackground = (HBRUSH)(COLOR_INACTIVECAPTION);
// 注册窗口
if(!MyRegisterClassW(&wnd))
{
hInstance = 0;
}
// 捏人 人
HWND hWnd = MyCreateWindowExW(0L,
L"Super",// 窗口类名
L"Super_Ke",// 窗口名
WS_OVERLAPPEDWINDOW,// 窗口风格,个性话定义
300, 100,// 窗口的起始位置
500, 300,// 窗口的宽高
NULL,// 父窗口
NULL,// 菜单句柄
hInstance,// 实例句柄
NULL);// 附加信息
MyShowWindow(hWnd, SW_SHOW);
// 消息循环
MSG msg = {};
// 接收消息,分发给不同的窗口
while (MyGetMessageW(&msg, NULL, NULL, NULL ))
{
MyTranslateMessage(&msg);
// 把不同窗口的消息分发给对应的回调函数->WindowProc
MyDispatchMessageW(&msg);
}
}
第四步,把以上DLL壳程序代码用一个函数封装起来。
void Run()
{
//调用函数得到自定义API地址
InitApi();
//获取目标程序句柄
_In_ HINSTANCE hInstance = (_In_ HINSTANCE)MyGetModuleHandleW(NULL);
SDK(hInstance);
}
第五步,实现最开始声明的Start入口点函数。首先调用Run函数运行DLL的壳代码,然后跳转到原程序的OEP。
void _declspec(naked) Start()
{
_asm
{
call Run;
jmp g_KeInfo.dwOep;
}
}