Легко запутаться при работе с указателями на указатели в сборке.
argv
является «массивом строк» или лучше массив указателей на символ, так как в C-распаде массивов в указатели на их тип элемента при передаче в качестве аргументов, в действительности argv
это указатель на указатель на символ или char** argv
,
Это говорит нам, что нам нужно два разыменования для доступа к символам любой из строк и один для доступа к любому указателю на любую из таких строк.
Предполагая Cdecl конвенции, где параметры передаются через стек в обратном порядке, и предполагая, стандартный пролог, который устанавливает стандартный фрейм-указатель мы имеем, что значение argc
находится на ebp+0ch
.
Отметьте, что ebp
имеет семантику указателя, и поэтому ebp+0ch
- это просто указательная арифметика, чтобы получить другой указатель, на этот раз до значения argc
.
Если бы мы были готовы дать ebp+0ch
тип C, это было бы char***
, поэтому для доступа к указателю 0-требуется два разговора.
код, чтобы получить 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]
имеет длину 4 байта, таким образом 2000h + 04h = 2004h.
mov esi, DWORD [esi+04h]
как mov esi, DWORD [2004h]
и он устанавливает ESI
в 2010h.
2010h - адрес строки "-arg1".
Обратите внимание, что на картинке выше не C, ни C++ совместим со стандартом, как argv[argc]
должен быть равен 0.
я оставил, что из картины.
Это не то же самое. Первый делает '* (* (ebp + 12) +4)' второй просто '* (ebp + 16)'. Вы не можете отказаться от разыменования. – Jester
Спасибо @ DanMašek за то, что он бесполезен. – Eric
@Jester Много оценено. Я подумаю об этом. – Eric