python最小解释器制作

平时经常用用到python脚本来实现一些小功能,可以通过pyinstaller之类的工具把py文件编译成exe程序,可是编译出来的程序动辄3mb以上,太大了很不爽。、、

我就想能不能把py编译成更小一点的exe。首先想到的是把py代码转换为c++代码,网上搜了很久也木有这样的工具。又一想,有没有python精简版之类的python库,又搜索很久没找到。

后来想到,自己编译python核心库,通过核心库来解析python代码不就好了嘛。于是有了下文方法:

(1)编译一个python解释器,单独一个exe,不依赖其他文件。

(2)把py文件附件到解释器的末尾,解释器读取自身py数据加载。

(3)用exe压缩工具压缩python解释器,把解释器程序压缩到最小。

 

一番捣鼓之下,成功了,有了一个1mb大小的python解释器,可以运行那些只import了内置库的py脚本,爽极了。详细操作步骤如下:

 

1、静态编译python为静态库嵌入到C++中

参照下文编译python静态库以及测试程序

 

1、下载 python2.7.6版本源代码(http://www.python.org/ftp/python/2.7.6/)

2、解压到Python-2.7.6文件夹

3、进入Python-2.7.6\PC\VS8.0文件夹,用Microsoft Visual Studio 2010打开解决方案 pcbuild.sln

4、切换到Release模式

5、更改C/C++—代码生成—运行库:多线程(/MT)

6、更改配置属性—配置类型:静态库(.lib)

7、更改配置属性—目标文件名:bin\python27.lib

8、在pythoncore中添加Modules文件夹中的getbuildinfo.c文件

9、打开PC文件夹中dl_nt.c文件,删除第14行的#ifdef Py_ENABLE_SHARED和第106行的#endif /* Py_ENABLE_SHARED */

10、先分别编译make_buildinfo和make_versioninfo,再编译pythoncore生成python27.lib

11、在Python-2.7.6文件夹下建立一个空的新解决方案pythonTest.sln

12、添加一个文件main.cpp

内容如下

#include <python.h>

#include <stdio.h>

 

int main()

{

       Py_Initialize();

       PyRun_SimpleString("print '\\nPython静态库编译成功!'");

       Py_Finalize();

       getchar();

       return 0;

}

13、配置pythonTest工程

①        在C/C++项的附加包含目录中添加:..\..\Include和..\..\PC两个目录

②        C/C++—预处理器—预处理器定义中添加一个宏: Py_NO_ENABLE_SHARED

③        更改C/C++—代码生成—运行库:多线程(/MT)

④        设置链接器—附加库目录中添加:..\..\\PC\VS8.0\bin目录

⑤        设置链接器—输入—附加依赖项:python27.lib

14、执行编译,生成 exe文件为:pythonTest.exe

成功!!!!!!!!!!!!!!!!!!!!!

来源: https://emonkey.iteye.com/blog/2012822

编译出来的库大小

exe

挺大的嘛。

 

2、修改exe,添加py数据load,运行功能。

(1)exe添加自身py数据加载代码

#include <python.h>
#include <Windows.h>
#include <stdio.h>

unsigned char gBuf[1024*1024];


void PyRunExeFile()
{
    int offset = 0;
    char szPath[MAX_PATH] = {0};
    ::GetModuleFileName(NULL, szPath, MAX_PATH);

    FILE *fp = fopen(szPath, "rb");
    if (NULL != fp)
    {
        fseek(fp, 0x20, SEEK_SET);
        fread(&offset, 1, sizeof(offset), fp); 
        if (0 == offset)
        {
            printf("PyData Offset = 0, Error!\n");
            fclose(fp);
        }
        else
        {
            fseek(fp, offset, SEEK_SET);
            fread(gBuf, 1, sizeof(gBuf), fp);        
            fclose(fp);

            if (0 == strlen((char *)gBuf))
            {
                printf("PyData Offset = 0x%x, Size = 0!\n", offset);
            }
            else
            {
                PyRun_SimpleString((const char *)gBuf);
            }
            
        }
        
        //PyRun_SimpleString((const char *)gBuf);
    }
}

int main(int argc, char **argv)

{
    Py_Initialize();
    PySys_SetArgv(argc, argv);

    PyRunExeFile();


    Py_Finalize();

    //getchar();

    return 0;

}

py数据存放在exe文件头偏移0x20指向的位置,exe头是一个_IMAGE_DOS_HEADER结构,偏移0x20的地方,刚好是e_res保留数据,可供我们利用。


typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

3、压缩exe

upx是一款优秀的exe加壳程序,可以通过它来对exe进行加壳压缩。

执行命令:

upx -k -9 test.exe

压缩后的exe大小:

小了很多,只有1MB了。

 

4、py文件附加到exe

(1)exe长度扩展,1kb长度对齐,添加py数据指针。

infile = 'Test.exe'
outfile = 'TestPy.exe'
# print (padto)

def uin32_to_array_pos(arrData, pos, data):
    arrData[pos+0] = (data>>0)&0xff
    arrData[pos+1] = (data>>8)&0xff
    arrData[pos+2] = (data>>16)&0xff
    arrData[pos+3] = (data>>24)&0xff

def main():
    fin = open(infile,'rb')
    fout = open(outfile, 'wb')
    d8 = array.array('B')

    size = os.path.getsize(infile)
    print (size)
    d8.fromfile(fin,size)
    fin.close()


    padto = size + 1024 - (size % 1024)
    print ("padto size = 0x%x, %d bytes, %d kb"% (padto, padto, padto/1024))
    uin32_to_array_pos(d8, 0x20, padto)
    padcnt = padto - size    
    for i in range(padcnt):
        d8.append(0)

    d8.tofile(fout)
    fout.close()

(2)合并py文件到exe

copy /b testpy.exe+test.py my.exe

至此,py解释器程序整合完毕,可以把那些只import了内置库的py文件合并到testpy.exe,把py文件变成一个exe程序。该exe只有1mb多一点,远比其他py编译exe程序编译出来的exe文件体积小,太爽了,哇咔咔!

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值