美文网首页
Python学习

Python学习

作者: 浅墨入画 | 来源:发表于2021-04-23 08:35 被阅读0次

一. Python配置

这里学习python,我们使用的是VSCode,首先在VSCode中安装Python插件,作用如下

  • 用来帮助我们跟系统安装的Python解释器进行关联,方便代码调试
  • 如果安装了多个版本python解释器,这里选择不同版本
image.png image.png
// 查看系统默认安装的python3信息
$ python3
Python 3.8.2 (default, Nov  4 2020, 21:23:28)
[Clang 12.0.0 (clang-1200.0.32.28)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()
// 查看系统安装的python3路径
$ which python3
/usr/bin/python3
// 注意这里安装最新版本,会跟系统自身的Python 3.8.2版本产生冲突
$ brew install python3
// 如果产生冲突,就卸载python3
$ brew uninstall python3
// 查看通过brew安装的python解释器信息
$ ls -l /usr/local/bin/python
ls: /usr/local/bin/python: No such file or directory
// 系统提供的环境变量,查看Python路径,如果要使用lldb  Python的api,可以把Python3的路径加入PYTHONPATH环境变量,这样Python3在去执行的时候可以直接通过PYTHONPATH环境变量找到lldb的api
$ echo $PYTHONPATH
// 查看lldb使用的Python3路径
$ lldb -P
/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python3
$ open /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources
// 如果要配置上面的环境变量,可以配置在下面文件中
$ vim ~/.bashrc
// 当把Python路径配置到环境变量PYTHONPATH,执行下面python3
$ python3
Python 3.8.2 (default, Nov  4 2020, 21:23:28)
[Clang 12.0.0 (clang-1200.0.32.28)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys  //引入系统库,类似引用Foundation
>>> print(sys.path)  //如果上面环境变量PYTHONPATH配置了Python路径,下面打印就可以看到
['', '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python38.zip', '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8', '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/lib-dynload', '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages']
>>> quit()

环境变量推荐配置在$ vim ~/.bashrc文件,配置在这个文件就可以和zsh进行关联。配置好PYTHONPATH环境变量,终端执行 $ echo $PYTHONPATH 就能输出Python的路径,要想使用lldb的api,就必须先配置PYTHONPATH,也就是Python路径

image.png

注意⚠️ 如果运行Python的时候发现runtime的错误,必然是Python解释器版本冲突

开发Python使用的工具

  • 使用VSCode,B.py文件中导入lldb模块 import lldb,实际上lldb是Xcode中的,目录为../Python3/lldb..
  • 使用PyCharm,这个工具比较简洁。但是这个工具不能直接读取配置的环境变量PYTHONPATH,需要对路径进行手动配置。

二. 打印与程序入口

Python引入库或文件的方式

  • 方式一 import lldb
  • 方式二 from module import Aimport module.A //从module目录下引入A,如果A.py文件与当前Python文件在同一目录下,可以直接import A。如果A.py文件与当前Python文件不在同一目录下,需要先找到A.py所在目录

当前Python文件引用其他Python文件的变量,比如引用A.py文件中的变量a

  • 如果使用import module.A引入的A.py文件,可以这样使用module.A.a
  • 如果使用from module import A引入的A.py文件,可以直接使用A.a
  • 如果使用from module import A as Person引入的A.py文件,Person相当于给A.py起了别名,可以这样使用Person.a

Python文件中的打印方式

  • 普通方式print(Person.a)
  • 使用% print("打印一个数据:'%d'" % Person.a) %d表示这里接收的是一个十进制数,传参数使用%,注意%左右各有一个空格
  • 使用{}占位符 .format是py中字符串的方法 print('你好,我是模块B……{}'.format(Person.a))
  • print(f'你好,我是模块Person……{Person.a}') 上面方式的一种简写形式

上面第三种打印方式 {} 作为占位符,那如果想打印 {} 符号该怎么办?
print('你好,我是模块B……{{}}{}'.format(Cat.a)),{} 里面的 {} 作为字符串打印出来,Python中也有转义字符,只是转义字符对 {} 无效。

Python文件中函数的使用

# pass占位,定义方法person(),注意后面要加冒号:  if判断后面也是要加:
def person():
    pass //前面一定要加空格 

- 方式一调用 person()
- 方式二调用 print("%s" % __name__)  //__表示私有属性,私有方法,注意Python中没有绝对的私有

Python文件中规避一些打印语句
这里以B.py文件作为主文件运行,B.py文件中调用A.py文件的方法,由于Python中没有类似iOS中的main入口函数,执行B.py文件的时候 print("%s" % __name__) 中的__name__ 会转变为 __main__,而执行A.py的时候__name__ 为module.A所以就不会打印print(a)

// A.py文件
a = 100
print('你好,我是模块A……')
print(__name__) //打印的name是module.A
// 规避print(a)语句
if __name__ == '__main__':
    print(a)

// B.py文件
if __name__ == '__main__':
    # 创建程序的入口函数
    print("%s" % __name__)

哪个文件作为主文件运行,哪个文件里面的__name__就是程序的入口__main__

三. Python与lldb插件

3.1 lldb中如何使用Python
这里直接拿的本地的一个可执行文件test来进行测试

  • 方式一 针对断点层面的lldb与Python的交互
$ lldb -file test
(lldb) target create "test"
Current executable set to '/上课代码/02-lldb使用Python/代码/test' (x86_64).
(lldb) b main   //添加一个断点
Breakpoint 1: where = test`main, address = 0x0000000100003f70
(lldb) script  //启用一个script进入Python交互环境
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> quit()
(lldb) br command add --script-type python 1   //给上面断点添加配置,通过Python添加额外功能
Enter your Python command(s). Type 'DONE' to end.
def function (frame, bp_loc, internal_dict):
    """frame: the lldb.SBFrame for the location at which you stopped
       bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information
       internal_dict: an LLDB support object not to be used"""
       name = frame.GetFunctionName()
       print(f"Person-----{name}")
       DONE    //表示上面function已经定义完成,回撤
(lldb) r
Process 27619 launched: '/上课代码/02-lldb使用Python/代码/test' (x86_64)
Person-----main   //这里成功打印上面function名称
Process 27619 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003f70 test`main
test`main:
->  0x100003f70 <+0>: pushq  %rbp
    0x100003f71 <+1>: movq   %rsp, %rbp
    0x100003f74 <+4>: subq   $0x10, %rsp
    0x100003f78 <+8>: movl   $0x0, -0x4(%rbp)
Target 0: (test) stopped.
  • 方式二自定义一个lldb命令,要想在lldb中使用Python,首先要在lldb中启用一个Python解释器。其中定义的方法参数必须是固定的,这样才能成为lldb的命令
(lldb) script   //启用Python解释器进行交互
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> def nm(debugger, command, result, internal_dict):   //定义一个function名称为nm,其中function接收的参数是固定的,debugger代表当前执行lldb中的debugger实例对象,command代表传递的参数,result代表对外返回的结果
...     cmd = "e -f x -- 4*2"  //执行一个加法的16进制打印,注意cmd前面有tab空格键      
...     lldb.debugger.HandleCommand(cmd) //通过lldb debugger来处理上面命令
...     // 这里继续回撤,下面不再出现... 表示nm函数编写完成
>>> quit()
(lldb) command script add nm -f nm  //转换成lldb命令,前面一个nm是给函数起的调用命令,后面一个nm是定义的Python函数,-f是将命令与Python函数进行关联
(lldb) nm
(int) $0 = 0x00000008  //正常将4*2的结果转换为16进制

上面使用Python自定义lldb命令,与自定义lldb插件时传递的参数是类似的,都有debugger实例对象 command传递的参数 result返回结果

  • 使用自定义lldb命令查看可执行文件信息
(lldb) image list
[  0] E8D1A92C-1C2A-3084-A138-36FEF3288E8C 0x0000000100000000 /上课代码/02-lldb使用Python/代码/test  // 可执行文件的地址
[  1] DEA51514-B4E8-3368-979B-89D0F8397ABC 0x0000000100010000 /usr/lib/dyld
[  2] A7FB4899-9E04-37ED-9DD8-8FFF0400879C 0x00007fff2a539000 /usr/lib/libSystem.B.dylib
[  3] 2F7F7303-DB23-359E-85CD-8B2F93223E2A 0x00007fff2a533000 /usr/lib/system/libcache.dylib
... 
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> def nm(debugger, command, result, internal_dict):
...     cmd = "platform shell nm {}".format(command)  //platform shell表示在lldb中执行shell相关的命令,后面表示使用nm命令,{}表示占位符
...     lldb.debugger.HandleCommand(cmd)
...
>>> quit()
(lldb) command script add nm -f nm
(lldb) nm -pa /上课代码/02-lldb使用Python/代码/test  //成功输出可执行文件的符号信息
0000000000000000 - 00 0000    SO /上课代码/02-lldb使用Python/
0000000000000000 - 00 0000    SO test.m
00000000603c8425 - 03 0001   OSO /上课代码/02-lldb使用Python/test.o
0000000100003f50 - 01 0000 BNSYM
0000000100003f50 - 01 0000   FUN _test
0000000000000010 - 00 0000   FUN
0000000000000010 - 01 0000 ENSYM
0000000100003f60 - 01 0000 BNSYM
0000000100003f60 - 01 0000   FUN _test_1
0000000000000010 - 00 0000   FUN
0000000000000010 - 01 0000 ENSYM
... 

上面nm -pa命令后面路径是通过image list获取的,现在用更好的方式获取路径?
可以提前执行image list,把它输出的语句转换成数组,取出数组第一条数据即可。或者指定数组中的某一个可执行文件路径进行符号查看

请思考怎么在一个函数中获取路径,再通过nm命令打印出可执行文件的符号信息?下去自己探讨?

  • 通过文件的方式查看可执行文件信息(只是把上面自定义lldb命令写在文件内,导入文件的形式查看)

创建nm.py文件,修改内容如下

import lldb
# 执行命令的具体函数
def nm(debugger, command, result, internal_dict):
    cmd = "platform shell nm {}".format(command)
    debugger.HandleCommand(cmd)
# 作为一个模块导入来使用,相当于文件入口,类似lldb插件中 PluginInitialize
def __lldb_init_module(debugger, internal_dict):
    debugger.HandleCommand('command script add -f nm.nm nm -h "nm command ----12312312"')  //nm.nm表示nm模块下的nm方法,后面nm表示命令
(lldb) command script import /上课代码/02-lldb使用Python/代码/nm.py
(lldb) help nm
nm command ----12312312  Expects 'raw' input (see 'help raw-input'.)
(lldb) nm /上课代码/02-lldb使用Python/代码/test
0000000000000000 - 00 0000    SO /上课代码/02-lldb使用Python/
0000000000000000 - 00 0000    SO test.m
00000000603c8425 - 03 0001   OSO /上课代码/02-lldb使用Python/test.o
0000000100003f50 - 01 0000 BNSYM
0000000100003f50 - 01 0000   FUN _test
...
// 创建cmd.py文件,内容如下
import lldb
import os   //os系统模块提供getcwd()

#创建debugger
debugger = lldb.SBDebugger.Create()
debugger.SetAsync(False)
#设置一个可执行文件路径
exe = "/上课代码/03-实现一个lldb plugin/test"
#创建target
target = debugger.CreateTargetWithFileAndArch(exe, "x86_64")
if target:
    #target设置断点
    main_bp = target.BreakpointCreateByName("main")
    # 创建一个进程,后面进程都在上面断点执行。os表示Python系统模块,这个系统模块提供了getcwd目录
    process = target.LaunchSimple(None, None, os.getcwd())
    if process:
        thread = process.GetThreadAtIndex(0)
        if thread:
            print(thread)
            frame = thread.GetFrameAtIndex(0)
            if frame:
                function = frame.GetFunction()
                if function:
                    print(function)
                    #获取当前汇编指令,并且for循环打印指令
                    ins = function.GetInstructions(target)
                    for i in ins:
                        #pass
                        print(f"指令:{i}")

//运行上面Python文件,成功打印出汇编指令
SBFunction: id = 0x100000079, name = main, type = main 
指令: test[0x100003f70]: pushq %rbp 
...

上面证明我们的测试工程是OK的,下面开始测试我们定义的lldb命令查看可执行文件信息

// cmd.py文件内容如下
import lldb
import os   //os系统模块提供getcwd()
import subprocess //进程处理相关

#创建debugger
debugger = lldb.SBDebugger.Create()
debugger.SetAsync(False)
#设置一个可执行文件路径
exe = "/上课代码/03-实现一个lldb plugin/test"
#创建target
target = debugger.CreateTargetWithFileAndArch(exe, "x86_64")
#获取当前Python命令解释器
interpreter = debugger.GetCommandInterpreter()
#拼接cmd.py文件路径
cmd = f"command script import '{os.getcwd()}/nm.py'"
result = lldb.SBCommandReturnObject()
interpreter.HandleCommand(cmd, result)
interpreter.HandleCommand(f"nm '{exe}'", result)
print(f"GetOutPut---{result.GetOutput()}---Error:{result.GetError()}")

// nm.py文件内容如下
import lldb
import os
import subprocess

# 执行命令的具体函数
def nm(_, command, result, internal_dict):
    print(f"nm----{command}")
    print("{}".format(subprocess.getoutput("nm {}".format(command))))

# __lldb_init_module
def __lldb_init_module(debu, internal_dict):
    debu.HandleCommand('command script add -f nm.nm nm -h "nm command ----12312312"')

// 以cmd.py文件作为主文件运行,成功打印可执行文件符号信息
__main__
nm----'/上课代码/03-实现一个lldb plugin/test'
0000000100000000 T __mh_execute_header
0000000100000000 0 _global
...

推荐Python开源插件学习

lldbinit
DerekSelander
LLDB与Python API

相关文章

网友评论

      本文标题:Python学习

      本文链接:https://www.haomeiwen.com/subject/fekwlltx.html