• Python解析器源码加密系列之(二):一次使用标准c的FILE*访问内存块的尝试


        摘要:由于近期打算修改Python解释器以实现pyc文件的加密/解密,出于保密的要求,解密之后的数据只能放在内存中,不能写入到文件中。但是后续的解析pyc文件的代码又只能接受FILE*作为入参,所以就提出了一种把通过FILE*来访问内存的需求,下文是针对这个需求的几个方面的尝试及其结论。

        以下尝试的前提是:Win7 + VS2010.

      在vc中,FILE其实就是_iobuf,定义如下:

    struct _iobuf {
            char *_ptr;     //文件输入的下一个位置
            int   _cnt;     //当前缓冲区的相对位置
            char *_base;    //指基础位置(应该是文件的其始位置)
            int   _flag;    //文件标志
            int   _file;    //文件的有效性验证
            int   _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
            int   _bufsiz;  //文件的大小
            char *_tmpfname;//临时文件名
            };
    typedef struct _iobuf FILE;

    本文尝试了以下两种方法:

    一、如何通过FILE*访问内存块,尝试1:_open_osfhandle(), 失败

        其思路就是将解密之后的内容放入到内存映射文件中(实际上是一个句柄),然后把内存映射文件转换为FILE*,

    网上有一个这方面的帖子(如何通过FILE*操作内存文件?http://www.cppblog.com/mythma/archive/2005/10/15/681.html)。这个想法的核心是通过将一个内存映射文件的windows句柄(HANDLE)转换为一个标准C的FILE*,有一个这方面的函数(_open_osfhandle)但是这个方法之对普通文件的HANDLE有效,对内存映射文件的HANDLE是无效的.

    下面是实验代码:

    #include "stdafx.h"
    #include "TestMemFile.h"
    #include <io.h>
    #include <fcntl.h>
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    // 唯一的应用程序对象
    
    CWinApp theApp;
    using namespace std;
    
    HANDLE CreateMMapFileFromMem()
    {
        TCHAR* MF_NAME = _T("MF_FILE_NAME_TEST");
        int MF_SIZE = 4*1024;
        std::cout<<"create a mem map file from mem"<<std::endl;
        return CreateFileMapping(INVALID_HANDLE_VALUE, 
            NULL, PAGE_READWRITE, 0, 
            MF_SIZE, MF_NAME);
    }
    
    HANDLE CreateMMapFileFromFile()
    {
        std::cout<<"create a normal file"<<std::endl;
        //这里我事先已经在D盘创建好文件,并且内容是字符 aaa
        HANDLE hFile = CreateFile(L"D:\TestMemMapFile.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, NULL, NULL);
        return hFile;
    }
    
    /*一个用于创建内存映射文件的函数指针*/
    
    typedef HANDLE (*CREATE_FILE_HANDLE)(void);
    
    void TestConvertHANDLEToFILEPtr(CREATE_FILE_HANDLE lpCreateFile)
    {
        HANDLE hFile = lpCreateFile();
        if(hFile == nullptr)
        {
            std::cout<<"create file failed."<<std::endl;
            return;
        }
        int hfd = _open_osfhandle((intptr_t)hFile, _O_RDONLY);
    
        if(hfd == -1)
        {
            std::cout<<"convert File HANDLE to FILE* failed."<<std::endl;
            CloseHandle(hFile);
            return;
        }
        std::cout<<"convert File HANDLE to FILE*  success. "<<std::endl;
        FILE* fd = _fdopen(hfd,"r");
        char c = getc(fd);
        std::cout<<"use getc(fd) to read a char from converted fd success, the char value is :" << c << std::endl;
        fclose((FILE*)fd);
    }
    
    int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
    
    {
        int nRetCode = 0;
        HMODULE hModule = ::GetModuleHandle(NULL);
        if (hModule != NULL)
        {
            // 初始化 MFC 并在失败时显示错误
            if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
            {
                // TODO: 更改错误代码以符合您的需要
                _tprintf(_T("错误: MFC 初始化失败
    "));
                nRetCode = 1;
            }
            else
            {
            // TODO: 在此处为应用程序的行为编写代码。
                TestConvertHANDLEToFILEPtr(CreateMMapFileFromMem);
                TestConvertHANDLEToFILEPtr(CreateMMapFileFromFile);            
            }
        }
        else
        {
            // TODO: 更改错误代码以符合您的需要
            _tprintf(_T("错误: GetModuleHandle 失败
    "));
            nRetCode = 1;
        }
        return nRetCode;
    
    }

    输出结果如下:

    2fd83932-8bc2-4361-881a-2638f1f5f57b

    二、如何通过FILE*访问内存块,尝试2:查找开源的类似fopen的实现函数

    感觉我这种需求应该不算特殊,肯定有其他人和我有类似的需求。

    既然标准c的库中没有接受byte[] 参数作为入参的重载版本,那么会不会有一些相关的开源实现呢?

    用bing找到一篇和我类似需求的帖子,通过_iobuf关键字找到的:http://bytes.com/topic/c/answers/217166-typedef-struct-_iobuf-file

    该楼主的大致需求也是想要用一个库,但是这个库的入参只接受FILE*作为,而他能够提供的内容都是基于内存的,不想使用磁盘文件作为中转。在这篇帖子找到了一个有人提到了fmemopen,funopen之类的类似fopen实现,他们可以接受byte[]为入参,然后返回一个FILE*。可惜的是,这些都只有linux版本,windows没有对应实现。我一直在想是不是要自己去包装一个,考虑到c库的复杂性,还是没法鼓起这个勇气。

    三、如何通过FILE*访问内存块,尝试3:使用tmpfile_s()

    上面之所以想要把内存转换为FILE*来使用,是因为觉得如果把解密之后的pyc文件临时放到内存中,有心人就可以直接把这个文件扒出来,然后反编译,那么有一种文件能够让人找不到路径,那实际上也能够达成目标。而tmpfile()可能就是这么一种潜在的方法。关于tmpfile()的各种实验,请参考:我的另一篇博文:

    标准c的tmpfile()、tmpfile_s()生成的临时文件究竟放在哪里了?http://www.cnblogs.com/strinkbug/p/where_is_the_filepath_which_created_by_tmpfile_of_c.html  ),关于tmpfile研究的结果就是,window上,用tmpfile创建的文件,在磁盘上找不到对应的文件(虽然我也不太相信,但是确实没找到),所以使用tmpfile来创建FILE*可以部分的达到保密的目的。

    四 结论

    综上,没有找到可以在windows上直接把内粗块(比如byte[])的方法,linux上倒是有fmemopen之类的实现。考虑到tmpfile文件的隐蔽性,最终是选择使用tmpfile来实现我们的保密目的。

    那么后续我就只要把python的pyc文件先解密,然后写入到tmpfile创建的临时文件FILE*中,然后再把这个FILE*往后传。

    本系列还有:

    Python解析器源码加密系列之(一):标准c的tmpfile()、tmpfile_s()生成的临时文件究竟放在哪里了?

  • 相关阅读:
    编码原则 之 Once and Only Once
    编码原则 之 Stable Dependencies
    分布式锁
    DTS(待了解)
    BPMN(待了解)
    criteo marketing api 相关
    enum & json 之间的转换
    bootstrap:modal & iframe
    记Ubuntu Mongodb 和 Mysql的安装与使用
    齐次和非齐次线性方程组的解法
  • 原文地址:https://www.cnblogs.com/strinkbug/p/4734974.html
Copyright © 2020-2023  润新知