首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用C语言从汇编代码中设计一个函数

用C语言从汇编代码中设计一个函数
EN

Stack Overflow用户
提问于 2019-11-10 22:14:50
回答 3查看 356关注 0票数 0

我需要用C语言设计一个函数来实现在机器代码中所写的内容。我分步骤获得装配操作,但我的函数被认为实现错误。我很困惑。

这是函数的反汇编代码。

代码语言:javascript
复制
(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

我的代码(函数的签名没有给出或解决):

代码语言:javascript
复制
#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;
}

如果你认出来了,请解释一下是什么原因造成的。

EN

回答 3

Stack Overflow用户

发布于 2019-11-10 23:53:08

(编者注:这是一个只涉及循环结构的部分答案。它不涵盖movzbl字节加载,也不包括其中一些变量是指针或类型宽度这一事实。这个问题的其他部分还有其他的答案。)

C支持goto,尽管经常对它们的使用不屑一顾,但它们在这里非常有用。使用它们使其尽可能类似于程序集。这允许您在开始引入更合适的控制流机制(如while循环)之前确保代码工作正常。所以我会这样做:

代码语言:javascript
复制
    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;

您可以轻松地将上述代码转换为:

代码语言:javascript
复制
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给我们:

代码语言:javascript
复制
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语句,这将相当于

代码语言:javascript
复制
do {
    // Your code
} while(edx<ecx);

可以使用附加的goto将while-循环编译为do-while-循环.(见为什么循环总是被编译成"do...while“样式(尾跳)?)。很容易理解。

在程序集中,循环是用代码中向后跳的gotos生成的。你测试,然后决定你是否想要跳回来。因此,为了在第一次迭代之前进行测试,首先需要跳到测试。(编译器有时也会编译with循环,顶部是if()break,底部是jmp。但只有在优化被禁用的情况下。见汇编语言中的循环(emu8086))

前跳通常是编译if语句的结果。

我还意识到我现在有三种很好的方法来使用goto。前两种方法是打破嵌套循环,以相反的分配顺序释放资源。现在第三个是,当你逆向工程装配。

票数 1
EN

Stack Overflow用户

发布于 2019-11-11 05:21:51

我很确定它是这样的:交换两个内存区域:

代码语言:javascript
复制
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;
        }
}
票数 1
EN

Stack Overflow用户

发布于 2019-11-11 02:01:52

对于那些更喜欢GCC的.S格式的人,我使用:

代码语言:javascript
复制
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语法。因此:

代码语言:javascript
复制
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应用的逻辑相同。

因此,我们可以代表一个登记册,如下所示:

代码语言:javascript
复制
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;

哪个更好..。你可以自己选择。现在我们可以开始看说明书了。movsxdmovslq将32位寄存器移动到具有符号扩展名的64位寄存器中.

现在我们可以编写代码了:

代码语言:javascript
复制
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;
}

希望我没有搞砸任何事..。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58793444

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档