如何在不使用数据段的情况下通过Keystone汇编引擎实现打印Hello World
问题根因
Keystone 仅负责汇编原生CPU指令,不支持 NASM 的 section、global、equ 这类汇编器伪指令,也不会自动处理段地址、跨段标号地址计算,因此你传入带段声明、数据段定义的完整NASM代码会报错。
无独立数据段的字符串实现方案
不需要单独定义.data段,可直接把字符串嵌入代码段,用跳转指令跳过字符串内容避免CPU把字符串解析为指令执行,这里提供两种常用实现方式:
方案1:代码段内嵌字符串(写法简单,推荐)
Keystone 支持用db直接定义字节数据,我们把字符串放在代码段的跳转空白区即可,不需要单独声明数据段。
注意你示例中用的int 0x80是32位Linux系统调用方式,初始化Keystone时需要指定KS_MODE_32,如果需要64位版本可以替换为对应syscall逻辑。
可直接运行的完整代码如下:
from keystone import * # 初始化32位x86模式,匹配int 0x80系统调用规范 ks = Ks(KS_ARCH_X86, KS_MODE_32) CODE = b''' _start: jmp short call_point ; 跳转到执行逻辑,跳过字符串数据区 string: db "Hello, world!", 0xa ; 字符串直接嵌在代码段中 call_point: call string ; 调用string标签会自动把字符串地址压入栈 ; 调用sys_write输出字符串 mov edx, 13 ; 固定写死字符串长度:"Hello, world!\n"共13字节 pop ecx ; 栈顶就是字符串地址,直接弹出到ecx寄存器 mov ebx, 1 ; stdout文件描述符 mov eax, 4 ; 32位Linux sys_write系统调用号 int 0x80 ; 调用sys_exit退出程序 mov ebx, 0 ; 退出状态码0 mov eax, 1 ; 32位Linux sys_exit系统调用号 int 0x80 ''' # 执行汇编 encoding, ins_count = ks.asm(CODE) print(f"汇编成功,共生成{ins_count}条指令,机器码总长度{len(encoding)}字节")
方案2:完全不用db伪指令,直接压栈构造字符串
如果需要完全避免数据定义伪指令,可以把字符串拆成小端序的立即数直接压入栈,用栈指针esp作为字符串首地址:
; 仅用原生指令构造字符串 xor eax, eax push 0x0a21646c ; 对应小端序的 "ld!\n" push 0x726f7720 ; 对应小端序的 " wor" push 0x6f6c6c65 ; 对应小端序的 "ello" push 0x48 ; 对应小端序的 "H" mov ecx, esp ; esp当前指向字符串首地址 ; 后续sys_write、sys_exit逻辑和方案1一致
这种写法完全由CPU原生指令构成,Keystone可以直接汇编,适合无法内嵌数据的特殊场景。
内容的提问来源于stack exchange,提问作者J.Todd
相关产品推荐
相关产品推荐

