我需要用C语言设计一个函数来实现在机器代码中所写的内容。我分步骤获得装配操作,但我的函数被认为实现错误。我很困惑。
这是函数的反汇编代码。
(Hand transcribed from an image, typos are possible
especially in the machine-code. See revision history for the image)
0000000000000000 <ex3>:
0: b9 00 00 00 00 mov 0x0,%ecx
5: eb 1b jmp L2 // 22 <ex3+0x22>
7: 48 63 c1 L1: movslq %ecx,%rax
a: 4c 8d 04 07 lea (%rdi,%rax,1),%r8
e: 45 0f b6 08 movzbl (%r8),%r9d
12: 48 01 f0 add %rsi,%rax
15: 44 0f b6 10 movzbl (%rax),%r10d
19: 45 88 10 mov %r10b,(%r8)
1c: 44 88 08 mov %r9b,(%rax)
1f: 83 c1 01 add $0x1,%ecx
22: 39 d1 L2: cmp %edx,%ecx
24: 7c e1 jl L1 // 7 <ex3+0x7>
26: f3 c3 repz retq我的代码(函数的签名没有给出或解决):
#include <assert.h>
int
ex3(int rdi, int rsi,int edx, int r8,int r9 ) {
int ecx = 0;
int rax;
if(ecx>edx){
rax = ecx;
r8 =rdi+rax;
r9 =r8;
rax =rsi;
int r10=rax;
r8=r10;
rax =r9;
ecx+=1;
}
return rax;
}如果你认出来了,请解释一下是什么原因造成的。
发布于 2019-11-10 23:53:08
(编者注:这是一个只涉及循环结构的部分答案。它不涵盖movzbl字节加载,也不包括其中一些变量是指针或类型宽度这一事实。这个问题的其他部分还有其他的答案。)
C支持goto,尽管经常对它们的使用不屑一顾,但它们在这里非常有用。使用它们使其尽可能类似于程序集。这允许您在开始引入更合适的控制流机制(如while循环)之前确保代码工作正常。所以我会这样做:
goto L2;
L1:
rax = ecx;
r8 =rdi+rax;
r9 =r8;
rax =rsi;
int r10=rax;
r8=r10;
rax =r9;
ecx+=1;
L2:
if(edx<ecx)
goto L1;您可以轻松地将上述代码转换为:
while(edx<ecx) {
rax = ecx;
r8 =rdi+rax;
r9 =r8;
rax =rsi;
int r10=rax;
r8=r10;
rax =r9;
ecx+=1;
}请注意,我没有检查L1-块内的代码,然后检查then块是否正确。(编者注:它缺少了所有的内存访问)。但是你的跳跃是错误的,现在被纠正了。
在这里(假设这是正确的),您可以做的是开始尝试查看模式。ecx似乎被用作某种索引变量。并且变量rax可以在开始时被替换。我们可以做一些其他类似的changes.This给我们:
int i=0;
while(edx<i) {
// rax = ecx;
// r8 =rdi+i; // r8=rdi+i
// r9 = rdi + i; // r9 = r8
// rax =rsi;
int r10 = rsi; // int r10=rax;
r8 = r10;
rax = r9 = rdi+i;
i++;
}很明显,这里似乎有什么东西有点不稳定。while条件是edx<i,但是i是递增的,而不是每次迭代时减少。这是个很好的迹象,表明出了问题。我在组装方面还不够熟练,不足以解决问题,但至少这是一种您可以使用的方法。一步一步地走。
add $0x1,%ecx是AT&T语法,用于将ecx递增1。根据使用英特尔语法的本站,结果存储在第一个操作数中。在AT&T语法中,这是最后一个操作数。
值得注意的一件有趣的事情是,如果我们删除了goto L2语句,这将相当于
do {
// Your code
} while(edx<ecx);可以使用附加的goto将while-循环编译为do-while-循环.(见为什么循环总是被编译成"do...while“样式(尾跳)?)。很容易理解。
在程序集中,循环是用代码中向后跳的gotos生成的。你测试,然后决定你是否想要跳回来。因此,为了在第一次迭代之前进行测试,首先需要跳到测试。(编译器有时也会编译with循环,顶部是if()break,底部是jmp。但只有在优化被禁用的情况下。见汇编语言中的循环(emu8086))
前跳通常是编译if语句的结果。
我还意识到我现在有三种很好的方法来使用goto。前两种方法是打破嵌套循环,以相反的分配顺序释放资源。现在第三个是,当你逆向工程装配。
发布于 2019-11-11 05:21:51
我很确定它是这样的:交换两个内存区域:
void memswap(unsigned char *rdi, unsigned char *rsi, int edx) {
int ecx;
for (ecx = 0; ecx < edx; ecx++) {
unsigned char r9 = rdi[ecx];
unsigned char r10 = rsi[ecx];
rdi[ecx] = r10;
rsi[ecx] = r9;
}
}发布于 2019-11-11 02:01:52
对于那些更喜欢GCC的.S格式的人,我使用:
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和一个jl (跳转如果小于)来检查是否是jl。如果是的话,它运行代码并将ecx增加1(add ecx, 1)。如果不存在,它就存在于..。
因此它看起来是:for (int32_t ecx = 0; ecx < edx; ++ecx)..。(请注意,edx是rdx的下32位)。
因此,现在我们用以下知识翻译其余的:
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;
}希望我没有搞砸任何事..。
https://stackoverflow.com/questions/58793444
复制相似问题