加密与解密.溢出
缓冲区溢出
栈溢出
栈是什么,它里面是什么
- 首先,栈是一种基本的数据结构,由编译器自动分配释放。第二,栈是一种先进后出的线性表。
- 在程序中,栈用来保护重要的数据,主要来自于函数,并且于函数执行结束的时候被释放。同时栈也用来存放局部变量,函数返回地址。
栈实例
-
通过代码观察堆栈的使用与释放,以及存储的内容
// Stack.cpp : 定义控制台应用程序的入口点。 // Windows XP SP3 + Visual Studio 2008 DeBug编译(MTd) #include "stdafx.h" void testDemo(char *Buf) { char Demo[0x8]; memcpy(Demo,Buf,0x8); return; } int _tmain(int argc, _TCHAR* argv[]) { char Buf[0x40]; memset(Buf,'\0',0x40); testDemo(Buf); return 0; } -
采用Debug编译是为了不要优化,防止代码原意被改变,同时更有利于观察堆栈,然后使用OD加载程序。

-
这是
Main函数,当然,testDemo函数中也是这样开辟新的堆栈。
- 实际运行中,发现,开辟的栈空间相对实际存储的变量来说是一个很大的值,但是由于堆栈分配的低地址生长性,第一个申请的变量与返回地址之间只有8个字节。

-
将上面的例子简单修改,构造溢出。
-
首先设置Release编译,并且修改堆栈保护的选项。

-
生成原来的例子,寻找Main函数,观察正常堆栈的构造。

-
构造程序、恶意数据,模拟溢出。

-
堆溢出
-
首先使用一个简单的程序来认识一下堆的结构。有这么几点需要注意:
- 堆分配算法,与操作系统版本,编译器版本,编译选项,Build类型都有关系。
- 调试器加载的堆管理与常态堆管理策略有差异
- 无论申请的空间是多大的,分配的内存块都是8字节为一块。
-
首先简单的开辟堆然后申请空间。
// Heap.cpp : 定义控制台应用程序的入口点。 // Windows XP SP3 + Visual Studio 2008 Release编译(MD) #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { HLOCAL h1,h2,h3,h4,h5,h6; HANDLE hp; hp = HeapCreate(0,0x1000,0x10000); _tprintf(TEXT("Create Heap End!\r\n")); __asm int 3; h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,0x3); h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,0x5); h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,0x6); h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,0x8); h5 = HeapAlloc(hp,HEAP_ZERO_MEMORY,0x13); h6 = HeapAlloc(hp,HEAP_ZERO_MEMORY,0x18); HeapFree(hp,0,h1); //free to freelist[2] HeapFree(hp,0,h3); //free to freelist[2] HeapFree(hp,0,h5); //free to freelist[4] HeapFree(hp,0,h4); return 0; } -
接下来,我们在int 3的时候选择附加调试器,就可以看到常规堆块的分布方式。

默认实时调试器切换
-
OD中有设置可以直接切换实时调试器。

-
x64 dbg也一样的操作

-
然后在报错的时候选择“否”,就可以使用实时调试器调试程序了

-
-
首先是没有分配堆空间的时候,只有一个默认堆首,以及最后的双向链表。

-
然后我们开始申请堆空间,可以看到申请的大小是堆首部的前四个字节指示,而且大小为我们申请的空间加上块首部大小,最后拓展到0x8这个堆空间单位大小的大小。

-
然后是Free释放堆空间,可以看到,修改的不是对应的堆中的数据,而是堆句柄中的Freelist数组,新的指针对指向刚刚释放的空间。

-
接下来试图构造利用。
// Heap.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" HANDLE MyProcessHandle = GetCurrentProcess(); TCHAR DemoFilePath[] = TEXT("./Heap.data"); // 打印成功的语句,也就是被Attach的部分,也可以是通过覆盖函数返回地址来完成目标 void AttachFunction() { LPVOID Waiting_For_Changed_Data = VirtualAllocEx(MyProcessHandle,NULL,0x100,MEM_COMMIT,PAGE_READWRITE); strcpy((char*)Waiting_For_Changed_Data,"Yeah!! You Win!!\r\n"); printf("%s",Waiting_For_Changed_Data); ExitProcess(0); return; } // char* __stdcall GetStrAddr(LPVOID OrgStr,char *SubStr) { if (OrgStr == NULL || SubStr == NULL) return NULL; char *ret = strstr((char*)OrgStr,SubStr); if (ret != NULL) return ret; char* temp = (char*)OrgStr; do { do { ++temp; } while (*temp != '\0'); do { ++temp; } while (*temp == '\0'); ret = strstr(temp,SubStr); } while (!ret); return ret; } // 主逻辑函数,同时也是存在堆溢出的函数 void MainFunction(LPVOID UserID,LPVOID PassWD) { // 创建堆 HANDLE Handle_Of_Heap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE,0,0); // 申请两次堆空间 HLOCAL Writ_For_Overflowed = HeapAlloc(Handle_Of_Heap,HEAP_ZERO_MEMORY,0x10); HLOCAL For_Placeholder_One = HeapAlloc(Handle_Of_Heap,HEAP_ZERO_MEMORY,0x10); // 释放第二个堆空间,构造溢出条件 HeapFree(Handle_Of_Heap,0,For_Placeholder_One); // strlen() int argvlen = -1; char *i = (char *)UserID; do { ++argvlen; ++i; } while (*i != '/'); // 溢出覆盖下一个的堆空间的两个指针。 memcpy(Writ_For_Overflowed,UserID,argvlen); // 申请堆空间,返回为第二个堆空间的地址,溢出覆盖的是下一个分配堆的地址 HLOCAL For_Placeholder_Twe = HeapAlloc(Handle_Of_Heap,HEAP_ZERO_MEMORY,0x10); // 返回需要的返回地址,接下来返回时修改为函数AttachFunction()的地址 HLOCAL Write_Data = HeapAlloc(Handle_Of_Heap,NULL,0x10); memcpy(Write_Data,PassWD,0x4); return; } // 读取文件,分割文件,同时保证函数不被优化,接下来交给主要函数 int _tmain(int argc, _TCHAR* argv[]) { _tprintf(TEXT("Please construct the data in \"Heap.data\" file!\r\n")); _tprintf(TEXT("eg:\r\n")); _tprintf(TEXT(" <name>UserID</name>\r\n")); _tprintf(TEXT(" <pass>PassWD</pass>\r\n")); // 确保函数不会被优化 if (!&AttachFunction || !&MainFunction) return 0; // 读取文件并且处理文件,分割为两个指针。 HANDLE DemoFile = CreateFile(DemoFilePath, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (DemoFile == INVALID_HANDLE_VALUE) { printf("Open Error!"); return 0; } LARGE_INTEGER FileSize; if (!GetFileSizeEx(DemoFile, &FileSize)) { printf("Get File Size Error!"); return 0; } LPVOID DataBuffer = VirtualAllocEx(MyProcessHandle,NULL,FileSize.QuadPart,MEM_COMMIT,PAGE_READWRITE); DWORD NumberOfBytesToRead; if (!ReadFile(DemoFile, DataBuffer, FileSize.QuadPart, &NumberOfBytesToRead, NULL)) { printf("Read File Error!"); return 0; } CloseHandle(DemoFile); char* UserID = GetStrAddr(DataBuffer,"<name>")+0x6; char* PassWD = GetStrAddr(DataBuffer,"<pass>")+0x6; if (UserID==NULL||PassWD==NULL) return 0; MainFunction(UserID,PassWD); printf("You Failed !!\r\n"); ExitProcess(0); return 0; } -
演示

整形溢出
存储溢出
- 简单说就是各种存储单位的存储空间大小不一致,导致转换或者存储超大的数的时候出现溢出。
- 比如说
Short Int(16位)和Int(32位)当他们相互转换的时候就会存在安全问题。

运算溢出
- 同样的问题出现在超长的大数以及反常理的输入。

符号问题
- 无符号整数和有符号整数之间的存储存在一个bit位的区别,这不仅存在存储和运算上出现错误和漏洞,同时会出现问题——在数据拷贝时设置的数据大小。
Use-After-Free
- 漏洞的成因是悬空指针的问题
直接看代码会很清楚
// UAF.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
class FakeSever
{
public:
FakeSever();
~FakeSever();
void Print();
protected:
private:
char *m_String_Hello;
};
FakeSever::FakeSever()
{
DWORD Time;
__asm
{
RDTSC;
mov Time,EAX;
}
m_String_Hello = (char *)VirtualAlloc(NULL,0x100,MEM_COMMIT,PAGE_READWRITE);
sprintf(m_String_Hello,"%s%X","HelloWorld,NEW SELF ID IS:",Time);
_tprintf(TEXT("[ New :此对象的String为:%s]\r\n"),m_String_Hello);
return;
}
FakeSever::~FakeSever()
{
_tprintf(TEXT("[Free :此对象的String为:%s]\r\n"),m_String_Hello);
return;
}
void FakeSever::Print()
{
_tprintf(TEXT("%s\r\n"),this->m_String_Hello);
return;
}
int _tmain(int argc, _TCHAR* argv[])
{
int InputKey = 0;
int countex = 0;
int freeID = 0;
FakeSever *Fake[3];
_tprintf(TEXT("请输入以下指令方便实现程序:(最多同时实现三个对象)\r\n"));
_tprintf(TEXT(" 1:新建一个FakeSever对象,该对象具有Print方法,其私有属性根据当前时间生成。\r\n"));
_tprintf(TEXT(" 2:释放一个FakeSever对象。\r\n"));
_tprintf(TEXT(" 3:执行一个FakeSever对象的Print方法。\r\n"));
_tprintf(TEXT(" 0:结束程序并退出。\r\n"));
while(TRUE)
{
_tprintf(TEXT("[ in > ] Scanf: "));
scanf("%d",&InputKey);
switch (InputKey)
{
case 0:
ExitProcess(0);
break;
case 1:
{
_tprintf(TEXT("[out > ] 新建的对象为[对象ID:%d]...\r\n"),countex);
Fake[++countex] = new FakeSever();
break;
}
case 2:
{
_tprintf(TEXT("[MSG ! ] 请输入要释放的对象ID...\r\n"));
_tprintf(TEXT("[ in > ] Input ID: "));
scanf("%d",&freeID);
free(Fake[freeID]);
break;
}
case 3:
{
_tprintf(TEXT("[MSG ! ] 请输入要执行的对象ID...\r\n"));
_tprintf(TEXT("[ in > ] Input ID: "));
scanf("%d",&freeID);
Fake[freeID]->Print();
break;
}
default:
_tprintf(TEXT("[ERROR!] 输入有误...\r\n"));
break;
}
}
return 0;
}
结果

欲望不知处,随否?

通常情况下,逆向方面的漏洞专指二进制漏洞
浙公网安备 33010602011771号