精华内容
下载资源
问答
  • linux 下一般以 `.o`, `.so`, `.a`, `.la` 后缀的二进制文件,它们的工作原理,你知道吗?当得到一个二进制文件,要如何对其进行分析?可以直接盲目运行么?

    前言

    Linux 下共有七种文件类型,普通文件类型是七种文件类型中最常见也是最多的一种。二进制文件是普通文件类型中的一份子。其实,我们天天在和二进制文件打交道,但很少有人知道它们的工作原理。

    在 Linux 下,一切皆文件!!!

    本篇博客意在讲解在 Linux 中用来分析二进制文件的常用指令



    Linux 下共有七种文件类型,普通文件类型是七种文件类型中最常见也是最多的一种。二进制文件是普通文件类型中的一份子。其实,我们天天在和二进制文件打交道,举个例子,你天天要使用的 Linux 命令,也是二进制文件的一种,但很少有人知道它们的工作原理。linux 中的二进制文件,一般以 .o, .so, .a, .la 后缀

    Linux下文件的类型是不依赖于其后缀名的,但一般来讲:
    .o 后缀是:目标文件,相当于windows中的.obj文件
    .so 后缀是:共享库,是shared object,用于动态连接的,和dll差不多
    .a 后缀是:静态库,是好多个.o合在一起,用于静态连接
    .la 后缀是:libtool自动生成的一些共享库,可用 vi 编辑查看,主要记录了一些配置信息。

    推荐文章:动态链接库(.so)以及静态链接库(.a)的生成和使用


    二进制文件分析指令及工具

    file ldd ltrace strace hexdump strings readelf objdump nm gdb

    file —— 用于分析文件的类型

    如果你需要分析二进制文件,可以首先使用 file 命令来切入。我们知道,在 Linux 下,一切皆文件,但并不是所有的文件都具有可执行性,我们还有各种各样的文件,比如:文本文件,管道文件,链接文件,socket文件,等等。

    在对一个文件进行分析之前,我们可以首先使用 file 命令来分析它们的类型。当然除此之外,我们还可以看到一些其它信息。

    $ file /bin/pwd
    /bin/pwd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=f3c2ec3ab4ede5ab4588db69e8c1cfdb188b7ae7, stripped
    

    ldd —— 用于分析可执行文件的依赖

    我们使用 file 命令来分析一个可执行文件的时候,有时候可以看到输出中有 dynamically linked 这样的字眼。这个是啥意思呢?

    大部分程序,都会使用到第三方库,这样就可以不用重复造轮子,节约大量时间。最简单的,我们写C程序代码的话,肯定会使用到 libc 或者 glibc 库。当然,除此之外,还可能使用其它的库。

    那我们在什么情况下需要分析程序的依赖库呢?有一个场景大家肯定经历过。你去你同事那边拷备他写好的程序放到自己的环境下运行,有时候可能会跑不起来。当然跑不起来的原因可能很多,但其中一个原因可能就是缺少对应的依赖库。

    这时候,ldd 就派上用场了。它可以分析程序需要一些什么依赖库,你只要把对应的库放在对应的位置就可以了。

    $ ldd /bin/pwd
    	linux-vdso.so.1 =>  (0x00007ffef1ddb000)
    	libc.so.6 => /lib64/libc.so.6 (0x00007f490423a000)
    	/lib64/ld-linux-x86-64.so.2 (0x00007f490461e000)
    

    ltrace —— 能够跟踪进程的库函数调用

    编译工具 ltrace 如果没有安装,使用 ltrace 指令会显示:
    bash: ltrace: 未找到命令...
    安装 ltrace:
    sudo yum install -y ltrace
    再输入密码,即可

    我们可以使用 ldd 命令来找到程序的依赖库,但是,一个库里少则几个,多则几千个函数,怎么知道现在程序调用的是什么函数呢?

    ltrace 命令就是用来做这个事的。在下面的例子里,我们可以看到程序调用的函数,以及传递进去的参数,同时你也可以看到函数调用的输出。

    $ ltrace /bin/pwd
    __libc_start_main(0x401760, 1, 0x7ffd63b06418, 0x404a00 <unfinished ...>
    getenv("POSIXLY_CORRECT")                                = nil
    strrchr("/bin/pwd", '/')                                 = "/pwd"
    setlocale(LC_ALL, "")                                    = "zh_CN.UTF-8"
    bindtextdomain("coreutils", "/usr/share/locale")         = "/usr/share/locale"
    textdomain("coreutils")                                  = "coreutils"
    __cxa_atexit(0x4022f0, 0, 0, 0x736c6974756572)           = 0
    getopt_long(1, 0x7ffd63b06418, "LP", 0x606d00, nil)      = -1
    getcwd(nil, 0)                                           = ""
    puts("/home/JiangYu"/home/JiangYu
    )                                    = 14
    free(0x23fb040)                                          = <void>
    exit(0 <unfinished ...>
    __fpending(0x7fdddcbe0400, 0, 64, 0x7fdddcbe0eb0)        = 0
    fileno(0x7fdddcbe0400)                                   = 1
    __freading(0x7fdddcbe0400, 0, 64, 0x7fdddcbe0eb0)        = 0
    __freading(0x7fdddcbe0400, 0, 2052, 0x7fdddcbe0eb0)      = 0
    fflush(0x7fdddcbe0400)                                   = 0
    fclose(0x7fdddcbe0400)                                   = 0
    __fpending(0x7fdddcbe01c0, 0, 3328, 0xfbad000c)          = 0
    fileno(0x7fdddcbe01c0)                                   = 2
    __freading(0x7fdddcbe01c0, 0, 3328, 0xfbad000c)          = 0
    __freading(0x7fdddcbe01c0, 0, 4, 0xfbad000c)             = 0
    fflush(0x7fdddcbe01c0)                                   = 0
    fclose(0x7fdddcbe01c0)                                   = 0
    +++ exited (status 0) +++
    

    strace —— 用于追踪程序运行过程中的系统调用及信号

    编译工具 strace 如果没有安装,使用 ltrace 指令会显示:
    bash: strace: 未找到命令...
    安装 strace:
    sudo yum install -y strace
    再输入密码,即可

    通过上面的介绍,我们知道 ltrace 命令是用来追踪函数调用的。strace 命令类似,但它追踪的是系统调用。何为系统调用?简单说就是我们可以通过系统调用与内核进行交互,完成我们想要的任务。

    例如,如果我们想在屏幕上打印某些字符,可以使用 printf 或 puts 函数,而这两个都是 libc 的库函数,在更底层,他们都是调用 write 这个系统调用。

    $ strace -f /bin/pwd
    execve("/bin/pwd", ["/bin/pwd"], [/* 24 vars */]) = 0
    brk(NULL)                               = 0xbc9000
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f918ba69000
    access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
    open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
    fstat(3, {st_mode=S_IFREG|0644, st_size=38684, ...}) = 0
    mmap(NULL, 38684, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f918ba5f000
    close(3)                                = 0
    open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
    read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0"..., 832) = 832
    fstat(3, {st_mode=S_IFREG|0755, st_size=2156160, ...}) = 0
    mmap(NULL, 3985888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f918b47b000
    mprotect(0x7f918b63e000, 2097152, PROT_NONE) = 0
    mmap(0x7f918b83e000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f918b83e000
    mmap(0x7f918b844000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f918b844000
    close(3) 
    …………
    +++ exited with 0 +++
    

    hexdump —— 查看二进制文件的 16 进制编码

    一个二进制文件,如果你直接使用文本编辑器打开的话,将看到一堆乱码。这时候,你就可以使用 hexdump 命令来查看它的内容了。

    hexdump 的显示格式是:左边是字节序号,中间是文件的 16 进制编码,如果是可打印字符的话就会显示在右边。

    通过使用这个命令,我们就可以大概知道这个二进制文件里面有什么内容,后面要做什么处理就比较方便了。

    $ hexdump -C /bin/pwd | head
    00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
    00000010  02 00 3e 00 01 00 00 00  17 19 40 00 00 00 00 00  |..>.......@.....|
    00000020  40 00 00 00 00 00 00 00  50 7a 00 00 00 00 00 00  |@.......Pz......|
    00000030  00 00 00 00 40 00 38 00  09 00 40 00 1e 00 1d 00  |....@.8...@.....|
    00000040  06 00 00 00 05 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
    00000050  40 00 40 00 00 00 00 00  40 00 40 00 00 00 00 00  |@.@.....@.@.....|
    00000060  f8 01 00 00 00 00 00 00  f8 01 00 00 00 00 00 00  |................|
    00000070  08 00 00 00 00 00 00 00  03 00 00 00 04 00 00 00  |................|
    00000080  38 02 00 00 00 00 00 00  38 02 40 00 00 00 00 00  |8.......8.@.....|
    00000090  38 02 40 00 00 00 00 00  1c 00 00 00 00 00 00 00  |8.@.............|
    

    注:实际上 hexdump 命令可以用来查看任何文件,而不限于二进制文件


    strings —— 用来打印二进制文件中可显示的字符

    什么是可显示字符?简单说你在显示器上看到的字符都是可显示字符,比如:abcABC,.:。

    我们知道,一个二进制文件里面的内容很多是非显示字符,所以无法直接用文本处理器打开。程序在被开发的时候,我们经常会加一些调试信息,比如:debug log, warn log, error log,等等。这些信息我们就可以使用 strings 命令看得到。

    $ strings /bin/pwd | head
    /lib64/ld-linux-x86-64.so.2
    libc.so.6
    fflush
    strcpy
    __printf_chk
    readdir
    setlocale
    mbrtowc
    strncmp
    optind
    

    readelf —— 查看 ELF 格式的文件信息

    ELF(Executable and Linkable Format)即可执行连接文件格式,是一种比较复杂的文件格式,但其应用广泛。当你使用 file 命令发现某个文件是 ELF 文件时,你就可以使用 readelf 命令来读取这个文件的信息。

    $ readelf -h /bin/pwd
    ELF 头:
      Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
      Class:                             ELF64
      Data:                              2's complement, little endian
      Version:                           1 (current)
      OS/ABI:                            UNIX - System V
      ABI Version:                       0
      Type:                              EXEC (可执行文件)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      入口点地址:              0x401917
      程序头起点:              64 (bytes into file)
      Start of section headers:          31312 (bytes into file)
      标志:             0x0
      本头的大小:       64 (字节)
      程序头大小:       56 (字节)
      Number of program headers:         9
      节头大小:         64 (字节)
      节头数量:         30
      字符串表索引节头: 29
    

    objdump —— 查看目标文件或者可执行的目标文件的构成

    objdump是用查看目标文件或者可执行的目标文件的构成的GCC工具

    我们知道,程序在开发完成之后,需要经过编译,才可以生成计算机可以识别的二进制文件。我们写的代码计算机不能直接执行,需要编译成汇编程序,计算机才能依次执行。

    objdump 命令可以读取可执行文件,然后将汇编指令打印出来。所以如果你想看懂 objdump 的结果,你就需要有一些汇编基础才可以。

    $ objdump -d /bin/pwd | head
    
    /bin/pwd:     文件格式 elf64-x86-64
    
    
    Disassembly of section .init:
    
    0000000000401350 <.init>:
      401350:	48 83 ec 08          	sub    $0x8,%rsp
      401354:	48 8b 05 6d 5c 20 00 	mov    0x205c6d(%rip),%rax        # 606fc8 <__ctype_b_loc@plt+0x205878>
      40135b:	48 85 c0             	test   %rax,%rax
    

    nm —— 列出目标文件的符号 (函数和全局变量)

    如果你编译出来的程序没有经过 strip ,那么 nm 命令可以挖掘出隐含在可执行文件中的重大秘密。它可以帮你列出文件中的变量及函数,这对于我们进行反向操作具有重大意义。

    下面我们通过一小段简单的程序来讲解 nm 命令的用途。在编译这个程序时,我们加上了 -g 选项,这个选项可以使编译出来的文件包含更多有效信息。

    $ cat hello.c 
    #include <stdio.h>
    
    int main() {
        printf("Hello world!\n");
        return 0;
    }
    $ 
    $ gcc -g hello.c -o hello
    $ 
    $ file hello
    hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=3de46c8efb98bce4ad525d3328121568ba3d8a5d, not stripped
    $ 
    $ ./hello 
    Hello world!
    $ 
    $ 
    $ nm hello | tail
    0000000000600e20 d __JCR_END__
    0000000000600e20 d __JCR_LIST__
    00000000004005b0 T __libc_csu_fini
    0000000000400540 T __libc_csu_init
                     U __libc_start_main@@GLIBC_2.2.5
    000000000040051d T main
                     U printf@@GLIBC_2.2.5
    0000000000400490 t register_tm_clones
    0000000000400430 T _start
    0000000000601030 D __TMC_END__
    $
    

    gdb —— GNU debugger

    gdb 大家或多或少都有听说过。我们在使用一些 IDE 写代码的时候,可以进行打断点、步进、查看变量值等方式调试,其实这些 IDE 底层调用的也是 gdb 。

    对于 gdb 的用法,可以写很多,本文就暂且不深入了。下面先演示一小段 gdb 最基础的功能。

    $ gdb -q ./hello
    Reading symbols from /home/flash/hello...done.
    (gdb) break main
    Breakpoint 1 at 0x400521: file hello.c, line 4.
    (gdb) info break
    Num     Type           Disp Enb Address            What
    1       breakpoint     keep y   0x0000000000400521 in main at hello.c:4
    (gdb) run
    Starting program: /home/flash/./hello 
    
    Breakpoint 1, main () at hello.c:4
    4           printf("Hello world!");
    Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.6.x86_64
    (gdb) bt
    #0  main () at hello.c:4
    (gdb) c
    Continuing.
    Hello world![Inferior 1 (process 29620) exited normally]
    (gdb) q
    $
    

    应用说明

    当你在 linux 下拿到一个二进制文件但不知道它是什么的时候,可以通过以下方法得到一此提示

    (一)首先应该尝试 strings 命令
    比如拿到一个叫 cr1 的二进制文件,可以:

    $ strings cr1 | more
    

    显示的内容里可能会有一些对于这个 cr1 的描述,这些信息都是编译之后在程序中留下的一些文本性的说明,所以可能会告诉你这个文件是什么。

    比如有输出:

    $ strings cr1 | more
    ...
    Version: 2.3
    Usage: dsniff [-cdmn] [-i interface] [-s snaplen] [-f services]
    [-t trigger[,...]] [-r|-w savefile] [expression]
    ...
    /usr/local/lib/dsniff.magic
    /usr/local/lib/dsniff.services
    ...
    

    那么我们就可以知道,其实 cr1 就是 dsniff 命令.

    (二)如果第一步没有帮助,那么你接着可以尝试 nm

    $ /usr/ccs/bin/nm -p cr1 | more
    

    比如说可以得到如下输出:

    cr1:
      [Index]   Value    Size Type    Bind Other   Shndx       Name
      [180]     |0     |    0| FILE | LOCL | 0     |ABS |       decode_smtp.c
      [2198]    |160348| 320| FUNC | GLOB | 0     | 9 |       decode_sniffer
    

    这些都是生成这个二进制文件的 obj 文件的文件名称,这些名称会告诉你这个二进制文件的作用。

    同样,如果希望查看二进制文件调用到的静态库文件都有哪些的话,可以使用 nm -Du cr1 来实现.

    (三)当然还可以通过使用 dump 命令来得到任何一个二进制文件的选定部分信息

    $ /usr/ccs/bin/dump -c ./cr1 | more
    

    dump命令的参数说明:

    -c		Dump出字符串表
    -C		Dump出C++符号表
    -D		Dump出调试信息
    -f 		Dump出每个文件的头
    -h 		Dump出section的头
    -l 		Dump出行号信息
    -L 		Dump出动态与静态链接库部分内容
    -o		Dump出每个程序的可执行头
    -r		Dump出重定位信息
    -s 		用十六进制信息Dump出section的内容
    -t 		Dump符号表.
    

    (四)使用 file 命令得到二进制文件的信息

    $ file cr1
    

    (五)如果还是不清楚的话,那么我们可以使用 ldd 命令

    $ ldd cr1
    

    比如说输出为:

    	...
    	libsocket.so.1 =>       /usr/lib/libsocket.so.1
    	librpcsvc.so.1 =>       /usr/lib/librpcsvc.so.1
    	...
    

    那么我们就可以知道这个程序与网络库相关,我们就可以知道它的大概功能了.

    (六)我们也可以能过 adb 命令来得到一个二进制文件的执行过程
    比如说:

    $ adb cr1
    :r
    Using device /dev/hme0 (promiscuous mode)
    192.168.2.119 -> web      TCP D=22 S=1111 Ack=2013255208
    Seq=1407308568 Len=0 Win=17520
    web -> 192.168.2.119 TCP D=1111 S=22 Push Ack=1407308568
    

    我们知道这个程序是一个sniffer.

    (七)如果你确定要运行这个程序的话,你可以先通过 truss 命令打开系统的信号与调用输出:

    $ truss -f -o cr.out ./cr1
    listening on hme0
    ^C
    $
    

    这样就可以知道这个程序到底干了什么


    小结
    有了上面这些工具的话,我们就可以大概了解到一个未知的二进制程序到底是干什么的。但最后提示大家:运行不了解的二进制程序有严重的安全问题,请大家千万小心!!!


    ◆ 快速回顾

    ◆ 其他博客 @ https://blog.csdn.net/姜小逗

    ◆ 相关博客


    感谢阅读本篇博客,如果有不错的发现和建议,感谢私信或在评论区留言
    在这里插入图片描述

    展开全文
  • 基本概念编译器,解释器抽象语法树字节...编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成 为机器语言的文件运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平...

    基本概念

    编译器,解释器

    抽象语法树

    字节码和机器码

    编译器和解释器

    计算机不能直接理解高级语言,只能直接理解机器语言,所以必须要把高级语言翻译成机器语言,计算机才能执行高级语言编写的程序。根据语言的执行流程,可以把语言分成编译型语言和解释型语言。

    编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成 为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++、go等.

    解释型语言: 程序不需要编译,程序在运行时才翻译成机器语言(所以执行前需要环境中安装了解释器),每执行一次都要翻译一次。因此效率比较低。效率比较低,依赖解释器,跨平台性好。

    编译型与解释型,两者各有利弊, 不能一概而论。前者由于程序执行速度快,同等条件下对系统要求较低,因此像开发操作系统、大型应用程序、数据库系统等时都采用它,像C/C++、Pascal/Object Pascal(Delphi)等都是编译语言,而一些网页脚本、服务器脚本及辅助开发接口这样的对速度要求不高、对不同系统平台间的兼容性有一定要求的程序则通常使用解释性语言,如JavaScript、VBScript、Perl、Python、Ruby、MATLAB 等等。

    我们都知道 JavaScript 存在变量提升,在函数作用域内的任何变量的声明都会被提升到顶部并且值为 undefined。

    所以JS引擎好像对同一个脚本执行了两次,第一次完成所有声明,然后第二次才执行代码?还是先编译整个代码然后运行它?这两种都不对。

    其实变量声明不过只执行上下文的小把戏。在执行任何语句之前,解释器就要从创建执行上下文后已经存在的作用域中找到变量的值。

    抽象语法树

    抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于 if-condition-then 这样的条件跳转语句,可以使用带有两个分支的节点来表示。

    字节码和机器码

    字节码(Byte-code):是一种包含执行程序、由一序列 op 代码/数据对组成的二进制文件。字节码是一种中间码,它比机器码更抽象。

    机器码 (Machine-code):计算机直接使用的程序语言,其语句就是机器指令码,机器指令码是用于指挥计算机应做的操作和操作数地址的一组二进制数。

    JavaScript代码执行过程

    生成AST(抽象语法树)

    生成字节码

    执行代码

    生成AST

    生成AST的步骤可以拆分成以下两个小步骤:

    词法分析:将JavaScript代码解析成一个个词法单元(token)

    语法分析:将词法单元根据一定规则组装成抽象语法树

    通过 javascript-ast 网站,可以大概了解 代码生成的 Tokens 以及 AST大致的样子。

    词法分析:将JavaScript代码解析成一个个词法单元(token)

    例如let a = 2;,通常会被分解为下面这些词法单元 let、a、=、2、; 空格是否会被当做词法单元取决于空格在这门语言中是否会具有意义。

    语法分析:将词法单元根据一定规则组装成 AST

    let a = 2;

    console.log(a);

    我们可以看到生成的AST结构如下:

    高级语言是开发者可以理解的语言,编译器和解释器理解不了。所以无论你使用的是解释型语言还是编译型语言,在编译过程中,它们都会生成一个 AST。当生成 AST之后,编译器/解析器后续的工作都要依靠 AST而不是源码。

    AST是一个非常重要数据结构,比如Babel的工作原理就是: ES6 的代码解析成 AST -> 将 ES6 的 AST 转换成 ES5 的AST -> 将 ES5的 AST 转成 ES5的代码。Babel的相关文章推荐 深入浅出 Babel 上篇:架构和原理 + 实战;我们使用的 Eslint(检查JavaScript编写规范的插件) 的检测流程也是先将源码转换成 AST, 然后利用 AST 来检查代码规范的问题

    生成字节码

    JavaScript引擎通过解释器来将 AST 转换成字节码,字节码是无法直接执行的,需要将其转为机器码才能直接执行。V8早期的时候,是直接将AST转成机器码的,后来因为 V8 需要消耗大量的内存来存放转换后的机器码,导致严重的内存占用问题。为了解决这个问题,引入 了字节码。字节码是比机器码轻量得多的代码。

    字节码是介于 AST 和机器码之间的一种代码。但是与特定类型的机器码无关,字节码需要通过解释器将其转换成机器码后才能执行。

    执行代码

    生成字节码之后,就到了解释和执行字节码阶段了,

    监听热点代码并优化为二进制机器码

    解释器会逐条执行字节码,(解释器除了负责生成字节码,还会负责解释执行机器码) 如果发现一段代码重复执行多次,就会它记为热点代码(HotSpot),V8会将这段热点代码提交给优化编辑器,优化编辑器会在后台将字节码编译为二进制代码,然后在对编译后的二进制代码执行优化操作,并保存下来。保存下来的机器码的作用和缓存很类似,当解释器再次遇到相同的内容时,就可以直接执行保存下来的机器码。

    这样代码执行得越久,执行效率就会越快,因为会有越来越多的字节码被标记为 热点代码,遇到他们就可以直接执行,而不用转成机器码。

    反优化生成的二进制机器码

    JavaScript是一种非常灵活的动态语言,对象的结构和属性在运行时任意被改变,而经过优化后的代码只能针对某种固定结构。一旦在执行过程中,对象的结构被动态修改了,那么优化后的代码会变成无效的代码,这时候优化编辑器就需要执行反优化操作,经过反优化的代码下次执行时就会回退到解释器解释执行。

    字节码的执行是需要配合编译器和解释器的(这种技术称为即时编译 JIT)所以之前说 JS是一种解释型语言并不准确。

    总结

    整个过程如下面流程图所示:

    参考

    展开全文
  • 提供了两个端口,一个传输数据一个进行控制,进行基于tcp协议的二进制数据传输,做个局域网形式的就行,连上就能显示。 我的想法,显示肯定用canvas,用的echarts,现在接收数据解析数据什么都会,但是这个需要怎么...
  • Python程序打包工具Python是一个脚本语言,被解释器解释....pyc文件:有些公司或个人因为机密或者各种原因,不愿意源码被运行者看到,可以使用pyc文件发布,pyc文件是Python解释器可以识别的二进制码,故发布后也...

    5e71ab1813492923.jpg

    Python程序打包工具

    Python是一个脚本语言,被解释器解释执行。它的发布方式:

    .py文件:对于开源项目或者源码没那么重要的,直接提供源码,需要使用者自行安装Python并且安装依赖的各种库。(Python官方的各

    种安装包就是这样做的)。

    .pyc文件:有些公司或个人因为机密或者各种原因,不愿意源码被运行者看到,可以使用pyc文件发布,pyc文件是Python解释器可以识

    别的二进制码,故发布后也是跨平台的,需要使用者安装相应版本的Python和依赖库。

    可执行文件:对于非码农用户或者一些小白用户,你让他装个Python同时还要折腾一堆依赖库,那简直是个灾难。对于此类用户,最简

    单的方式就是提供一个可执行文件,只需要把用法告诉他即可。比较麻烦的是需要针对不同平台需要打包不同的可执行文件(Windows,

    Linux, Mac,...)。

    .py和.pyc都比较简单,Python本身就可以搞定。将Python脚本打包成可执行文件有多种方式。

    PyInstaller

    安装pyinstaller

    对于那些网络比较稳定,能够流畅使用pip源地址的用户,直接下面的命令就可以搞定:

    pip install pyinstaller

    通常我们会下载源码包,然后进入包目录,执行下面的命令(需要安装setuptools):

    python setup.py install

    安装完后,检查安装成功与否:

    pyinstaller --version

    安装成功后,就可以使用下面的命令了:

    pyinstaller : 打包可执行文件的主要命令,详细用法下面会介绍。

    pyi-archive_viewer : 查看可执行包里面的文件列表。

    pyi-bindepend : 查看可执行文件依赖的动态库(.so或.dll文件)

    pyi-... : 等等。

    使用PyInstallerpyinstaller的语法:pyinstaller [options] script [script...] | specfile

    最简单的用法,在和myscript.py同目录下执行命令:

    pyinstaller mycript.py

    然后会看到新增加了两个目录build和dist,dist下面的文件就是可以发布的可执行文件,对于上面的命令你会发现dist目录下面有一堆

    ,各种都动态库文件和myscrip可执行文件。有时这样感觉比较麻烦,需要打包dist下面的所有东西才能发布,万一丢掉一个动态库就无

    法运行了,好在pyInstaller支持单文件模式,只需要执行:pyinstaller -F mycript.py

    你会发现dist下面只有一个可执行文件,这个单文件就可以发布了,可以运行在你正在使用的操作系统类似的系统的下面。当然,

    pyinstaller还有各种选项,有通用选项,如-d选项用于debug。

    在执行pyInstaller命令的时候,会在和脚本相同目录下,生成一个.spec文件,该文件会告诉pyinstaller如何处理你的所有脚本,同时包含

    了命令选项。一般我们不用去理会这个文件,若需要打包数据文件,或者给打包的二进制增加一些Python的运行时选项时...一些高级打包

    选项时,需要手动编辑.spec文件。可以使用:pyi-makespec optionsscript [script ...]

    创建一个.spec文件,对于手动编辑的.spec文件,我们可以使用下面任意一条命令:pyinstaller specfile

    pyi-build specfile

    PyInstaller原理简介

    PyInstaller其实就是把python解析器和你自己的脚本打包成一个可执行的文件,和编译成真正的机器码完全是两回事,所以千万不要指

    望成打包成一个可执行文件会提高运行效率,相反可能会降低运行效率,好处就是在运行者的机器上不用安装python和你的脚本依赖的

    库。在Linux操作系统下,它主要用的binutil工具包里面的ldd和objdump命令。

    PyInstaller输入你指定的的脚本,首先分析脚本所依赖的其他脚本,然后去查找,复制,把所有相关的脚本收集起来,包括Python解析

    器,然后把这些文件放在一个目录下,或者打包进一个可执行文件里面。

    可以直接发布输出的整个文件夹里面的文件,或者生成的可执行文件。你只需要告诉用户,你的应用App是自我包含的,不需要安装其他

    包,或某个版本的Python,就可以直接运行了。

    需要注意的是,PyInstaller打包的执行文件,只能在和打包机器系统同样的环境下。也就是说,不具备可移植性,若需要在不同系统上运

    行,就必须针对该平台进行打包。

    展开全文
  • 匿名用户1级2017-11-08 回答...(Python官方的各种安装包就是这样做的).pyc文件:有些公司或个人因为机密或者各种原因,不愿意源码被运行者看到,可以使用pyc文件发布,pyc文件是Python解释器可以识别的二进制码...

    匿名用户

    1级

    2017-11-08 回答

    Python是一个脚本语言,被解释器解释执行。它的发布方式:

    .py文件:对于开源项目或者源码没那么重要的,直接提供源码,需要使用者自行安装Python并且安装依赖的各种库。(Python官方的各种安装包就是这样做的)

    .pyc文件:有些公司或个人因为机密或者各种原因,不愿意源码被运行者看到,可以使用pyc文件发布,pyc文件是Python解释器可以识别的二进制码,故发布后也是跨平台的,需要使用者安装相应版本的Python和依赖库。

    可执行文件:对于非码农用户或者一些小白用户,你让他装个Python同时还要折腾一堆依赖库,那简直是个灾难。对于此类用户,最简单的方式就是提供一个可执行文件,只需要把用法告诉Ta即可。比较麻烦的是需要针对不同平台需要打包不同的可执行文件(Windows,Linux,Mac,...)。

    本文主要就是介绍最后一种方式,.py和.pyc都比较简单,Python本身就可以搞定。将Python脚本打包成可执行文件有多种方式,本文重点介绍PyInstaller,其它仅作比较和参考。

    Freezing Your Code

    各种打包工具的对比如下(来自文章Freezing Your Code):

    Solution

    Windows

    Linux

    OS X

    Python 3

    License

    One-file mode

    Zipfile import

    Eggs

    pkg_resources support

    bbFreeze    yes    yes    yes    no    MIT    no    yes    yes    yes

    py2exe    yes    no    no    yes    MIT    yes    yes    no    no

    pyInstaller    yes    yes    yes    no    GPL    yes    no    yes    no

    cx_Freeze    yes    yes    yes    yes    PSF    no    yes    yes    no

    py2app    no    no    yes    yes    MIT    no    yes    yes    yes

    PS.其中pyInstaller和cx_Freeze都是不错的,stackoverflow上也有人建议用cx_Freeze,说是更便捷些。pkg_resources新版的pyInstaller貌似是支持的。

    安装PyInstaller

    对于那些网络比较稳定,能够流畅使用pip源地址的用户,直接下面的命令就可以搞定:

    pip install pyinstaller

    通常我们会下载源码包,然后进入包目录,执行下面的命令(需要安装setuptools):

    python setup.py install

    安装完后,检查安装成功与否:

    pyinstaller --version

    安装成功后,就可以使用下面的命令了:

    pyinstaller : 打包可执行文件的主要命令,详细用法下面会介绍。

    pyi-archive_viewer : 查看可执行包里面的文件列表。

    pyi-bindepend : 查看可执行文件依赖的动态库(.so或.dll文件)

    pyi-... : 等等。

    使用PyInstaller

    pyinstaller的语法:

    pyinstaller [options] script [script ...] | specfile

    最简单的用法,在和myscript.py同目录下执行命令:

    pyinstaller mycript.py

    然后会看到新增加了两个目录build和dist,dist下面的文件就是可以发布的可执行文件,对于上面的命令你会发现dist目录下面有一堆文件,各种都动态库文件和myscrip可执行文件。有时这样感觉比较麻烦,需要打包dist下面的所有东西才能发布,万一丢掉一个动态库就无法运行了,好在pyInstaller支持单文件模式,只需要执行:

    pyinstaller -F mycript.py

    你会发现dist下面只有一个可执行文件,这个单文件就可以发布了,可以运行在你正在使用的操作系统类似的系统的下面。

    当然,pyinstaller还有各种选项,有通用选项,如-d选项用于debug,了解pyInstaller执行的过程;还有一些针对不同平台的选项,具体用法可以访问PyInstaller官方WIKI。

    在执行pyInstaller命令的时候,会在和脚本相同目录下,生成一个.spec文件,该文件会告诉pyinstaller如何处理你的所有脚本,同时包含了命令选项。一般我们不用去理会这个文件,若需要打包数据文件,或者给打包的二进制增加一些Python的运行时选项时...一些高级打包选项时,需要手动编辑.spec文件。可以使用:

    pyi-makespec options script [script ...]

    创建一个.spec文件,对于手动编辑的.spec文件,我们可以使用下面任意一条命令:

    pyinstaller specfile pyi-build specfile

    PyInstaller的原理简介

    PyInstaller其实就是把python解析器和你自己的脚本打包成一个可执行的文件,和编译成真正的机器码完全是两回事,所以千万不要指望成打包成一个可执行文件会提高运行效率,相反可能会降低运行效率,好处就是在运行者的机器上不用安装python和你的脚本依赖的库。在Linux操作系统下,它主要用的binutil工具包里面的ldd和objdump命令。

    PyInstaller输入你指定的的脚本,首先分析脚本所依赖的其他脚本,然后去查找,复制,把所有相关的脚本收集起来,包括Python解析器,然后把这些文件放在一个目录下,或者打包进一个可执行文件里面。

    可以直接发布输出的整个文件夹里面的文件,或者生成的可执行文件。你只需要告诉用户,你的应用App是自我包含的,不需要安装其他包,或某个版本的Python,就可以直接运行了。

    需要注意的是,PyInstaller打包的执行文件,只能在和打包机器系统同样的环境下。也就是说,不具备可移植性,若需要在不同系统上运行,就必须针对该平台进行打包。

    参考资料

    展开全文
  • 1,很多资料都说,exe文件二进制码(指令),是可以直接被机器执行的; 2,但是,所谓的二进制码(指令)不是因机而异的吗?不同的机器,使用的cpu是不一样的,指令集也不一样,那为什么在一台机器上生成的exe可以...
  • python编译过程和执行原理(1)python执行原理这里的解释执行是相对于编译执行而言的。我们都知道,使用C/C++之类...但是对于Python而言,python源码不需要编译成二进制代码,它可以直接从源代码运行程序。当我们运行...
  • 编译型语言:通过编译器把源代码编译(compile)成机器语言,在经过链接(linker)将源代码中所使用的库串联起来生成可执行二进制文件,这样运行时 计算机可以直接以机器语言来运行程序。优点:运行效率高。缺点:编译...
  • python编译过程和执行原理(1)python执行原理这里的解释执行是相对于编译执行而言的。我们都知道,使用C/C++之...但是对于Python而言,python源码不需要编译成二进制代码,它可以直接从源代码运行程序。当我们运行...
  • 编译是把源程序的代码(源程序)编译成机器语言(目标程序),并保存成二进制文件,这样计算机可以直接根据二进制文件执行,速度很快,但编译器编写较复杂,生成的文件会占用外存空间。 解释则是只在执行程序时,将源...
  • 编译器是把源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样运行时计算机可以直接以机器语言来运行此程序,速度很快;解释器则是只在执行程序时,才一条一条的解释成机器语言给计算机来执行,所以运行速度是...
  • 可执行文件可以被裸机直接执行吗??? 2.可执行文件的执行依赖于操作系统吗? 即: 没有操作系统可执行文件能被机器运行吗??? 3.操作系统是由机器指令打包组成的程序吗??? 即:操作系统的下面直接是硬件吗...
  • 本项功能有独立的免费应用程序,同时支持X86和X64二进制文件。 Enigma Virtual Box 中文版 Enigma Virtual Box 中文版 Enigma 虚拟文件打包系统适用于各类文件,让您的程序附件文件无需释放到磁盘而可以直接调用。...
  • 如果编译通过则生成一个可执行的二进制文件(目标语言),可以直接在物理机上运行。 解释型语言 以PHP代码为例。PHP代码其实也需要经过编译器编译,因为是实时编译的,所以我们写的代码能直接看到结果。这个编译器是...
  • 要构建二进制文件,请在项目根目录中执行: go build 2开始 要启动项目,请在项目根目录中执行: go run main . go 3测试 要运行所有项目测试,请在项目根目录中执行: go test . / ... 4个Api文档 可以在以下...
  • Open1560-源码

    2021-03-19 08:23:54
    该项目是对Midtown Madness 1的自下而上的重写,使用原始二进制文件提供了尚未实现的功能。目的是允许修复错误,实现新功能以及移植到原始版本不支持的平台上。 当前的变化包括: OpenGL渲染器 音频修复 崩溃修复 ...
  • 2.Boot中包含了CPU的初始化代码,Memory与外围接口的初始化代码,随后会回引系统(OS),最后将控制权交给OS,编译完成后将二进制文件烧入FLASH。如果板卡复位,CPU异常矢量或复位矢量指的地址就是FLASH地址,Flash中...
  • 你必须知道的495个C语言问题

    千次下载 热门讨论 2015-05-08 11:09:25
    这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制结构域的对齐方式? 2.14 为什么sizeof返回的值大于结构大小的期望值,是不是尾部有填充? 2.15 如何确定域在结构中的字节偏移...
  • FREETXT是绿色软件,只有一个EXE文件,复制到哪都可以运行,生存适应能力强,可以随电子书文档一起走,随用随打开。也可以将本软件复制到U盘里使用,走到哪用到哪。 FREETXT电子书使用的是纯十六进制数字来贮存文章...
  • BIN:二进制文件 BINHex:苹果的一种编码格式 BMP:Windows或OS/2位图文件 BOOK:Adobe FrameMaker Book文件 BOX:Lotus Notes的邮箱文件 BPL:Borlard Delph 4打包库 BSP:Quake图形文件 BUN:CakeWalk 声音...
  • ASP源码加密工具6.0

    2008-11-25 23:39:32
    非编译型的加密工具要在运行时在内存中还原脚本执行,编译型代码不可还原,直接二进制的执行方式。 产品特点  1、支持各种ASP服务器脚本默认语言。  2、能够完全正确识别后缀名为ASP的伪ASP文件。  3、不论多么...
  • 这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制结构域的对齐方式? 27  2.14 为什么sizeof返回的值大于结构大小的期望值,是不是尾部有填充? 28 2.15 如何确定域在结构中的...
  • 《你必须知道的495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制结构域的对齐方式? 27  2.14 为什么sizeof返回的值大于结构大小的期望值,是不是尾部有填充? 28 2.15 如何确定域在结构中的...
  • 可以在所有的 FTP 客户端软件中找到有关二进制上传的选项,例如 LeapFTP, CuteFTP 等。以下举例说明这两种客户端 软件设置 *PHP 文件二进制上传的方法。 LeapFTP:在 Options(选项)菜单中选择 ...
  • (提示:把它们作为二进制文件打开) 16、PictureBox控件和Image控件有什么区别? 17、Visual Basic可处理哪些格式的图形文件? 18、在程序运行时怎样在图形(像)框中装入或删除图形? 19、简述Printer对象与...
  • 2.2 用java写服务器端api,跑在tomcat,客户端直接和8080口通讯,那这个java文件在服务器端怎么运行? 2.3 请求给apache,apache再把请求forwarding给tomcat,tomcat处理后返还给apache,apache再给客户,这个可以吗?要...
  • 如何读写大型的二进制文件 如何快速创建一个大文件 如何在Visual C++ 6.0中使用fopen()函数来读写文件 如何将路径转换为长路径名 如何用Visual C++ 6.0编写文件分割工具 第8章 数据库 ODBC访问数据库的原理是什么 ...
  • o 3.10 我的编译器在结构中留下了空洞, 这导致空间浪费而且无法与外部数据文件进行 "二进制" 读写。能否关掉填充, 或者控制结构域的对齐方式? o 3.11 为什么 sizeof 返回的值大于结构的期望值, 是不是尾部有填充? ...

空空如也

空空如也

1 2 3 4 5
收藏数 94
精华内容 37
关键字:

二进制文件可以直接运行吗