Assembly 存储器寻址

Assembly 存储器寻址,assembly,nasm,memory-address,Assembly,Nasm,Memory Address,我在玩弄程序命令行参数。特别是,我正在尝试对字符串argv[1]进行一些测试。如果使用两步方法获取argv[1]的地址,代码运行良好 mov ebx, [ebp+12] mov eax, [ebx+4] ; address of argv[1] 如果我使用一个步骤,我的程序将打印jibberish mov eax, [ebp+16] ; address of argv[1] 我认为这两种方法现在都会引用地址[ebp+16]是错误的吗?我错过了一些琐碎的事情吗?这就是你问题的答案 mov ea

我在玩弄程序命令行参数。特别是,我正在尝试对字符串argv[1]进行一些测试。如果使用两步方法获取argv[1]的地址,代码运行良好

mov ebx, [ebp+12]
mov eax, [ebx+4] ; address of argv[1]
如果我使用一个步骤,我的程序将打印jibberish

mov eax, [ebp+16] ; address of argv[1]

我认为这两种方法现在都会引用地址[ebp+16]是错误的吗?我错过了一些琐碎的事情吗?

这就是你问题的答案

mov eax, [ebp+16] 
lea ebx, [ebp+12] 
mov eax, [ebx+4]


前者可以节省几个字节的代码,但它们在功能上是等效的。

这就是您问题的答案

mov eax, [ebp+16] 
lea ebx, [ebp+12] 
mov eax, [ebx+4]


前者节省了几个字节的代码,但它们在功能上是等效的。

在汇编中使用指向指针的指针时,很容易混淆

argv
是一个“字符串数组”,或者更好地说是一个指向char的指针数组,因为在C中,数组在作为参数传递时会衰减为指向其项类型的指针,实际上
argv
是指向char或
char**argv
的指针

这告诉我们需要两个解引用来访问任何字符串的字符,一个去访问指向任何此类字符串的指针

假设cdecl约定,其中参数按相反顺序在堆栈上传递,并且假设设置标准帧指针的标准prolog,
argc
的值位于
ebp+0ch

请注意,
ebp
具有指针的语义,因此
ebp+0ch
只是获取另一个指针的指针算法,这次指向
argc

如果我们愿意给
ebp+0ch
一个C类型,它将是
char***
,因此需要两个解引用来访问指针
argv[1]

获取
argv[1]
ESI
的代码是:

;typeof(ebp+0ch) = char***

mov esi, DWORD [ebp+0ch]      ;1st defer, esi = argv, typeof(esi) = char**
mov esi, DWORD [esi+04h]      ;2nd defer, esi = argv[1], typeof(esi) = char*

;Optional, Get a char
mov al, BYTE [esi]            ;3rd defer, al = argv[1][0], typeof(al) = char
类型检查


听起来很困惑?
让我们画那些指针

       The stack                                     The memory

100ch | 2000h  | argv                         2000h | 2008h   | argv[0]
1008h | 2      | argc                         2004h | 2010h   | argv[1]
1004h | yyyyyy | return address               2008h | file    | argv[0][0..3]
1000h | xxxxxx | old frame pointer            200ch | .a\0\0  | argv[0][4..7]
                                              2010h | -arg    | argv[1][0..3]
EBP = 1000h                                   2014h | 1\0\0\0 | argv[1][4..7]
ebp+0ch
是1000h+0ch=100ch,它是
argv
值的地址。
mov-esi,DWORD[ebp+0ch]
mov-esi,DWORD[100ch]
类似,它将
esi
设置为2000h。
2000h是
argv
的值,它是一个数组,因此它是
argv[0]
的地址

argv[1]
的地址在前面四个字节,因此2000h+04h=2004h。
mov-esi,DWORD[esi+04h]
mov-esi,DWORD[2004h]
类似,它将
esi
设置为2010h。
2010h是字符串“-arg1”的地址


注意,上面的图片不是C或C++标准,符合<代码> > ARGV[ARC] 必须是0。


我没有提到这一点。

在汇编中使用指向指针的指针时,很容易混淆

argv
是一个“字符串数组”,或者更好地说是一个指向char的指针数组,因为在C中,数组在作为参数传递时会衰减为指向其项类型的指针,实际上
argv
是指向char或
char**argv
的指针

这告诉我们需要两个解引用来访问任何字符串的字符,一个去访问指向任何此类字符串的指针

假设cdecl约定,其中参数按相反顺序在堆栈上传递,并且假设设置标准帧指针的标准prolog,
argc
的值位于
ebp+0ch

请注意,
ebp
具有指针的语义,因此
ebp+0ch
只是获取另一个指针的指针算法,这次指向
argc

如果我们愿意给
ebp+0ch
一个C类型,它将是
char***
,因此需要两个解引用来访问指针
argv[1]

获取
argv[1]
ESI
的代码是:

;typeof(ebp+0ch) = char***

mov esi, DWORD [ebp+0ch]      ;1st defer, esi = argv, typeof(esi) = char**
mov esi, DWORD [esi+04h]      ;2nd defer, esi = argv[1], typeof(esi) = char*

;Optional, Get a char
mov al, BYTE [esi]            ;3rd defer, al = argv[1][0], typeof(al) = char
类型检查


听起来很困惑?
让我们画那些指针

       The stack                                     The memory

100ch | 2000h  | argv                         2000h | 2008h   | argv[0]
1008h | 2      | argc                         2004h | 2010h   | argv[1]
1004h | yyyyyy | return address               2008h | file    | argv[0][0..3]
1000h | xxxxxx | old frame pointer            200ch | .a\0\0  | argv[0][4..7]
                                              2010h | -arg    | argv[1][0..3]
EBP = 1000h                                   2014h | 1\0\0\0 | argv[1][4..7]
ebp+0ch
是1000h+0ch=100ch,它是
argv
值的地址。
mov-esi,DWORD[ebp+0ch]
mov-esi,DWORD[100ch]
类似,它将
esi
设置为2000h。
2000h是
argv
的值,它是一个数组,因此它是
argv[0]
的地址

argv[1]
的地址在前面四个字节,因此2000h+04h=2004h。
mov-esi,DWORD[esi+04h]
mov-esi,DWORD[2004h]
类似,它将
esi
设置为2010h。
2010h是字符串“-arg1”的地址


注意,上面的图片不是C或C++标准,符合<代码> > ARGV[ARC] 必须是0。


我把它忘在图片上了。

它们不一样。第一个是
*(*(ebp+12)+4)
第二个是
*(ebp+16)
。你不能忽略去引用。谢谢@DanMašek的幽默无益。@Jester非常感激。我会想一想,它们不一样。第一个是
*(*(ebp+12)+4)
第二个是
*(ebp+16)
。你不能忽略去引用。谢谢@DanMašek的幽默无益。@Jester非常感激。我会考虑的。嗯。。。现在还不清楚为什么在未使用它的情况下覆盖
eax
2/3条指令时使用
mov-eax[ebp+16]
。除此之外,您的代码只是编写
mov eax[ebp+16]
的一种愚蠢方式,它将访问main的第三个(非标准,但经常提供)参数,而不是
argv[1]
。这不是问题的答案。嗯。。。现在还不清楚为什么在未使用它的情况下覆盖
eax
2/3条指令时使用
mov-eax[ebp+16]
。除此之外,您的代码只是编写
mov eax[ebp+16]
的一种愚蠢方式,它将访问main的第三个(非标准,但经常提供)参数,而不是
argv[1]
。这不是OP问题的答案。谢谢你深思熟虑的详细回答。我从个人那里了解到