用汇编代码在C中设计函数
我需要用C语言设计一个函数来实现机器代码中写的东西。我分步完成组装操作,但据说我的功能实现错误。我很困惑 这是函数的反汇编代码用汇编代码在C中设计函数,c,assembly,x86-64,reverse-engineering,C,Assembly,X86 64,Reverse Engineering,我需要用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
(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
我的代码未给出或结算函数的签名:
如果您认识到任何错误,请解释导致这些错误的原因。编者注:这是一个仅针对循环结构的部分答案。它不包括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对于那些喜欢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以英特尔格式查看它,或者在反编译器中运行生成的主可执行文件。。但是我。。让我们做些手工活
首先,我要将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。然后,它通过执行cmp ecx检查ecx是否小于edx,如果小于,edx随后执行jl跳转。。如果是,则运行代码并将ecx增加1 add ecx,1。。如果不是,则存在块
因此它看起来像:对于int32_t ecx=0;ecxtypedef 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;
}
希望我没有把事情搞砸。我很确定是这样的:交换两个内存区域:
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;
}
}
r寄存器是64位的。int是x86-64 C实现中的32位类型。如果int edi实际上是int,就应该使用int edi,就像在edx中一样。更重要的是,asm包含一个向后的条件分支,但是您的C缺少一个循环。此外,asm从内存中加载了大量零扩展字节,但您的C没有任何指针变量。请编辑您的问题,以包含asm代码。不要发布图片链接,也不要用图片本身替换该链接。慢慢来,在问题中键入它。ASM代码看起来像一个循环。我已经否决了你的问题,因为你发布了代码的图片。一旦你将我的否决票替换为文本代码,它将被删除。@fuz:Steve Friedl努力将图片翻译成文本。我删除了图片,所以现在,问题应该没问题了。在C中实际使用goto的三种好方法?或者你的意思是作为逆向工程的一部分?问题中的asm是GCC如何编译C中位于-O0和-O1的while{}或for循环。在C++模式下,G++-O0喜欢用顶部的IFFACK和底部的无条件JMP来生成一个愚蠢的循环。另请参见-启用优化后,编译器剥离第一个测试,然后使用底部的条件进行正常的do{}while循环,如下图所示。顺便说一句,这只是一个非常部分的答案。您很好地介绍了循环结构,但类型和寻址模式仍然是一个巨大的问题。OP甚至还没有接近正确地获得movzbl字节加载。我现在意识到了跳跃和循环。但我更关心的是如何在C中显示不同长度的位寄存器,以及如何在C中显示类似movzbl的操作。我是否使用任何转换器?@PeterCordes,我的意思是作为逆向工程的一部分。是的,我知道这是部分答案。剩下的我觉得不舒服。我投了你提到的那部分的赞成票。这完全可以,只要我们避免暗示这是唯一的问题。否则OP会浪费他们的时间希望你的改变足够。非常感谢。我对这些很陌生,你知道如何用C写重新解释或者仅仅是int8 int64等等吗?我不知道如何显示不同的位寄存器。联合登记部分在开始时做什么?@ CuloEZQL添加C翻译代码和删除C++代码。int64_t是在IMO中声明的,您的注册联合应该更简单,并且只包括可以作为x86部分注册直接访问的成员。所以唯一的结构 您需要的t成员是一对8位AL/AH两半。您如何从OP的asm中获得int64_t*r8?我只看到字节指针uint8\u t*。甚至指针数学也是无标度的,所以还是char*或unsigned char*。你把这件事搞得太复杂了。哦,我看到你把movzbl误译成了mov r9d,dword ptr[r8]。实际上是movzxr9d,字节ptr[r8]
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
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;
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;
}
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;
}
}