Для тех, кто предпочитает формат .S
для GCC, я использовал:
ex3:
mov $0x0, %ecx
jmp lpe
lps:
movslq %ecx, %rax
lea (%rdi, %rax, 1), %r8
movzbl (%r8), %r9d
add %rsi, %rax
movzbl (%rax), %r10d
mov %r10b, (%r8)
mov %r9b, (%rax)
add $0x1, %ecx
lpe:
cmp %edx, %ecx
jl lps
repz retq
.data
.text
.global _main
_main:
mov $0x111111111111, %rdi
mov $0x222222222222, %rsi
mov $0x5, %rdx
mov $0x333333333333, %r8
mov $0x444444444444, %r9
call ex3
xor %eax, %eax
ret
, затем вы можете скомпилировать его с помощью gcc main.S -o main
и запустить objdump -x86-asm-syntax=intel -d main
, чтобы увидеть его в формате Intel ИЛИ запуститьрезультирующий main
исполняемый файл в декомпиляторе ... но ме ... давайте сделаем некоторую ручную работу ..
Сначала я бы преобразовал синтаксис AT & T в более известный синтаксис Intel .. так:
ex3:
mov ecx, 0
jmp lpe
lps:
movsxd rax, ecx
lea r8, [rdi + rax]
movzx r9d, byte ptr [r8]
add rax, rsi
movzx r10d, byte ptr [rax]
mov byte ptr [r8], r10b
mov byte ptr [rax], r9b
add ecx, 0x1
lpe:
cmp ecx, edx
jl lps
rep ret
Теперь я ясно вижу, что от lps
(начало цикла) до lpe
(конец цикла) является циклом for.
Как? Потому что сначала он устанавливает регистр счетчика (ecx
) в 0. Затем он проверяет, является ли ecx < edx
, выполняя cmp ecx, edx
, за которым следует jl
(переход, если меньше). Если это так, он запускает коди увеличивает ecx
на 1 (add ecx, 1
) .. если нет, то существует блок.
Таким образом, он выглядит следующим образом: for (int32_t ecx = 0; ecx < edx; ++ecx)
.. (обратите внимание, что edx - младшие 32-битныеrdx).
Итак, теперь мы переведем остаток со знанием того, что:
r10
- это 64-битный регистр. r10d
- старшие 32 бита, r10b
- младшие 8 бит. r9
- это 64-битный регистр. Применяется та же логика, что и r10
.
Таким образом, мы можем представить регистр, как у меня ниже:
typedef union Register
{
uint64_t reg;
struct
{
uint32_t upper32;
uint32_t lower32;
};
struct
{
uint16_t uupper16;
uint16_t ulower16;
uint16_t lupper16;
uint16_t llower16;
};
struct
{
uint8_t uuupper8;
uint8_t uulower8;
uint8_t ulupper8;
uint8_t ullower8;
uint8_t luupper8;
uint8_t lulower8;
uint8_t llupper8;
uint8_t lllower8;
};
} Register;
Какое лучше ... Вы можете выбрать для себя ... Теперь мы можем начатьглядя на сами инструкции .. movsxd
или movslq
перемещает 32-битный регистр в 64-битный регистр с расширением знака.
Теперь мы можем написать код:
uint8_t* ex3(uint8_t* rdi, uint64_t rsi, int32_t edx)
{
uintptr_t rax = 0;
for (int32_t ecx = 0; ecx < edx; ++ecx)
{
rax = ecx;
uint8_t* r8 = rdi + rax;
Register r9 = { .reg = *r8 }; //zero extend into the upper half of the register
rax += rsi;
Register r10 = { .reg = *(uint8_t*)rax }; //zero extend into the upper half of the register
*r8 = r10.lllower8;
*(uint8_t*)rax = r9.lllower8;
}
return rax;
}
Надеюсь, я ничего не напортачил ..