我想为我的ARMv7平台编写一个简单的有效负载。首先,我尝试了一个通过UART发送字符的简单循环:
{rand-guid}
这是我想要的有效载荷,一次在C中,一次在汇编中。
void payload()
{
while(1)
{
USART3->DR = 0x68;
}
}
08000358 <payload>:
8000358: b480 push {r7}
800035a: af00 add r7, sp, #0
800035c: 4b01 ldr r3, [pc, #4] ; (8000364 <payload+0xc>)
800035e: 2268 movs r2, #104 ; 0x68
8000360: 809a strh r2, [r3, #4]
8000362: e7fb b.n 800035c <payload+0x4>
8000364: 40004800 andmi r4, r0, r0, lsl #16
这很好用,字符通过UART发送。现在,我想在有效负载中调用一个函数:
int main()
{
uint32 buffer[4];
buffer[0] = 0xaf00b480;
buffer[1] = 0x22684b01;
buffer[2] = 0xe7fb809a;
buffer[3] = 0x40004800;
memcpy(0x20004000,&buffer,4*sizeof(uint32));
goto *(void *)((uint32_t) buffer | 1);
return 0;
}
再次,我想要的有效载荷,一次在C中,一次在汇编中。
void payload()
{
while(1)
{
USART3->DR = 0x68;
asm volatile(
"bl 0x08000348\n"
);
}
}
08000358 <payload>:
8000358: b480 push {r7}
800035a: af00 add r7, sp, #0
800035c: 4b02 ldr r3, [pc, #8] ; (8000368 <payload+0x10>)
800035e: 2268 movs r2, #104 ; 0x68
8000360: 809a strh r2, [r3, #4]
8000362: f7ff fff1 bl 8000348 <function>
8000366: e7f9 b.n 800035c <payload+0x4>
8000368: 40004800 andmi r4, r0, r0, lsl #16
角色现在仅发送3次,然后出现崩溃(处理器的故障处理程序)。我检查了函数的内存区域和缓冲区,两者看起来相同:
void function()
{
asm volatile(
"nop\n"
"nop\n"
);
}
int main()
{
uint32 buffer[5];
buffer[0] = 0xaf00b480;
buffer[1] = 0x22684b02;
buffer[2] = 0xf7ff809a;
buffer[3] = 0xe7f9fff1;
buffer[4] = 0x40004800;
memcpy(0x20004000,&buffer,5*sizeof(uint32));
goto *(void *)((uint32_t) buffer | 1);
return 0;
}
答案 0 :(得分:1)
goto *(void *)((uint32_t)buffer | 1);
对于使用不必要的内联汇编的人来说,很奇怪。
编译时,我得到:
38: 2201 movs r2, #1
3a: 4313 orrs r3, r2
3c: 469f mov pc, r3
3e: 46c0 nop
那应该马上失败,
来自arm文档:
ADD(寄存器)和MOV(寄存器)分支没有互通。
您可以做的是使用一些真实的asm
.thumb
.thumb_func
.globl HOP
HOP:
orr r0,#1
bx r0
void HOP ( uint32_t *);
void function()
{
asm volatile(
"nop\n"
"nop\n"
);
}
int main()
{
uint32_t buffer[5];
buffer[0] = 0xaf00b480;
buffer[1] = 0x22684b02;
buffer[2] = 0xf7ff809a;
buffer[3] = 0xe7f9fff1;
buffer[4] = 0x40004800;
// memcpy(0x20004000,&buffer,5*sizeof(uint32));
// goto *(void *)((uint32_t) buffer | 1);
HOP(buffer);
return 0;
}
38: 0018 movs r0, r3
3a: f7ff fffe bl 0 <HOP>
现在,如果您不希望使用分支链接,而是希望使用分支,并且由于您对内联程序集的使用是自由的,则这更像您的样式,并且应该不会产生至少我的编译器生成的mov问题。顺便说一句,我们需要查看您的编译器生成了什么,以查看为什么您没有立即崩溃。
void function()
{
asm volatile(
"nop\n"
"nop\n"
);
}
int main()
{
uint32_t buffer[5];
buffer[0] = 0xaf00b480;
buffer[1] = 0x22684b02;
buffer[2] = 0xf7ff809a;
buffer[3] = 0xe7f9fff1;
buffer[4] = 0x40004800;
asm (
"add r3,r7,#4\n"
"mov pc,r3\n"
);
return 0;
}
36: 1d3b adds r3, r7, #4
38: 469f mov pc, r3
bx可以用来使它感觉更好
int main()
{
uint32_t buffer[5];
buffer[0] = 0xaf00b480;
buffer[1] = 0x22684b02;
buffer[2] = 0xf7ff809a;
buffer[3] = 0xe7f9fff1;
buffer[4] = 0x40004800;
asm (
"add r3,r7,#5\n"
"bx r3\n"
);
return 0;
}
36: 1d7b adds r3, r7, #5
38: 4718 bx r3
一旦fifo不再填充字符,您就无法以这种方式对待uart数据寄存器。
您也无法复制此代码并以这种方式运行它。
8000362: f7ff fff1 bl 8000348 <function>
对于内置的Flash来说很好,只要8000348保留在该函数所在的位置,但是bl是与pc相关的,因此如果您要将其复制到sram(我知道我自己的问题的答案,将让您知道我知道这是一个微控制器,所以您想询问的是armv7-m而不是armv7)。
20004000: b480 push {r7}
20004002: af00 add r7, sp, #0
20004004: 4b02 ldr r3, [pc, #8] ; 20004010
20004006: 2268 movs r2, #104 ; 0x68
20004008: 809a strh r2, [r3, #4]
2000400A: f7ff fff1 bl 20003FF0
2000400E: e7f9 b.n 20004004
20004010: 40004800
我想您没有将function()复制到0x20003FF0吗?运行该程序时,您在该地址有哪些数据?看起来是什么样的?
因此,大多数情况下都是使用汇编语言来“获取它”,但是却错过了一些事情。
现在您可以做的就是编写所需的代码:
.thumb
top:
ldr r3,=0x40004800
mov r2,#68
str r2,[r3,#4]
ldr r0,=function
blx r0
b top
无需链接
00000000 <top>:
0: 4b02 ldr r3, [pc, #8] ; (c <top+0xc>)
2: 2244 movs r2, #68 ; 0x44
4: 605a str r2, [r3, #4]
6: 4802 ldr r0, [pc, #8] ; (10 <top+0x10>)
8: 4780 blx r0
a: e7f9 b.n 0 <top>
c: 40004800 andmi r4, r0, r0, lsl #16
10: 00000000 andeq r0, r0, r0
并使用您的样式
void function()
{
asm volatile(
"nop\n"
"nop\n"
);
}
int main()
{
uint32_t buffer[10];
buffer[0]=0x22444b02;
buffer[1]=0x4802605a;
buffer[2]=0xe7f94780;
buffer[3]=0x40004800;
buffer[4]=((uint32_t)function)|1;
asm (
"add r3,r7,#5\n"
"bx r3\n"
);
return 0;
}
使用我的编译器:
08000000 <function>:
8000000: b580 push {r7, lr}
8000002: af00 add r7, sp, #0
...
8000016: af00 add r7, sp, #0
8000018: 003b movs r3, r7
800001a: 4a0c ldr r2, [pc, #48] ; (800004c <main+0x3a>)
800001c: 601a str r2, [r3, #0]
...
800003a: 1d7b adds r3, r7, #5
800003c: 4718 bx r3
...
800004c: 22444b02
8000050: 4802605a
8000054: e7f94780
8000058: 40004800
800005c: 08000001
仍然像您的代码一样有点骇人听闻,但是现在您可以从4字节对齐的地址开始重新定位此代码,并且不会出现无法调用的问题 函数功能。
是的,在这个示例中,我欺骗了链接,但没有引导程序。真正困扰我的人默认情况下使用堆栈帧,这真是浪费。我想知道是否可以在没有该工具的情况下构建我的工具链。否则,确实可以使此hacky解决方案更好地工作:
08000000 <function>:
8000000: 46c0 nop ; (mov r8, r8)
8000002: 46c0 nop ; (mov r8, r8)
8000004: 46c0 nop ; (mov r8, r8)
8000006: 4770 bx lr
08000008 <main>:
8000008: b08a sub sp, #40 ; 0x28
800000a: 466b mov r3, sp
800000c: 4a0a ldr r2, [pc, #40] ; (8000038 <main+0x30>)
800000e: 601a str r2, [r3, #0]
8000010: 466b mov r3, sp
8000012: 4a0a ldr r2, [pc, #40] ; (800003c <main+0x34>)
8000014: 605a str r2, [r3, #4]
然后可以使用
void function()
{
asm volatile(
"nop\n"
"nop\n"
);
}
int main()
{
uint32_t buffer[10];
buffer[0]=0x22444b02;
buffer[1]=0x4802605a;
buffer[2]=0xe7f94780;
buffer[3]=0x40004800;
buffer[4]=((uint32_t)function);
asm (
"mov r3,sp\n"
"orr r3,r3,#1\n"
/* "add r3,#1\n" */
"bx r3\n"
);
return 0;
}
是的,是的,您不必为该工具链已经设置好就可以设置为1的功能。
08000008 <main>:
8000008: b08a sub sp, #40 ; 0x28
800000a: 4b09 ldr r3, [pc, #36] ; (8000030 <main+0x28>)
800000c: 9300 str r3, [sp, #0]
800000e: 4b09 ldr r3, [pc, #36] ; (8000034 <main+0x2c>)
8000010: 9301 str r3, [sp, #4]
8000012: 4b09 ldr r3, [pc, #36] ; (8000038 <main+0x30>)
8000014: 9302 str r3, [sp, #8]
8000016: 4b09 ldr r3, [pc, #36] ; (800003c <main+0x34>)
8000018: 9303 str r3, [sp, #12]
800001a: 4b09 ldr r3, [pc, #36] ; (8000040 <main+0x38>)
800001c: 9304 str r3, [sp, #16]
800001e: 466b mov r3, sp
8000020: f043 0301 orr.w r3, r3, #1
8000024: 4718 bx r3
8000026: 2300 movs r3, #0
8000028: 4618 mov r0, r3
800002a: b00a add sp, #40 ; 0x28
800002c: 4770 bx lr
800002e: bf00 nop
8000030: 22444b02 subcs r4, r4, #2048 ; 0x800
8000034: 4802605a stmdami r2, {r1, r3, r4, r6, sp, lr}
8000038: e7f94780 ldrb r4, [r9, r0, lsl #15]!
800003c: 40004800 andmi r4, r0, r0, lsl #16
8000040: 08000001 stmdaeq r0, {r0}
现在有了内联的asm魔术,您可以将缓冲区的地址加载到r3中,而不必依赖于代码的反汇编来解决这一问题。请注意,尽管您将其称为armv7,但您似乎仍在使用armv7-m。您可以使用thumb2指令orr r3,r3,#1在精神上是正确的,您希望在该位置上的位不添加它。但是,如果这是一个类似于cortex-m0的armv6-m,或者您想要可移植性,则只需像add r3,#1一样丑陋地进行添加,因为这不是thumb2指令,而且是可移植的。
因此,如果您输入了几个字符,我怀疑您的goto工作正常,也许听起来您正在使用qemu?也许那是怎么回事?但是没有尝试在sram中运行代码的副本,而是向后分支以查找function(),但在那里找到了数据,这使您崩溃了,或者因为那是在复制代码之前,它再次运行了复制代码几次然后回到那里的一切最终脱离了轨道。也许是因为您没有正确使用uart导致缓冲区溢出,这至少在这些品牌之一上可能导致uart停止工作,直到您清除缓冲区溢出标志。假设您依靠uart输出来查看成功或失败。 (您会在运行该程序时遇到失败,而没有调用函数,这使我相信这是一个模拟,而不是真正的硬件,或者真正的硬件,您只是看到了要推入uart的部分值)。
试试看
void payload()
{
uint32_t ra;
for(ra=0x30;;ra++)
{
ra&=0x37;
USART3->DR = ra;
}
}
作为起点,您将永远看到0123456701234567,然后再看我在说什么,那么我怀疑您使用的是模拟器而不是硬件。...
编辑:
orr 1是goto的问题,您可以这样做:
void function()
{
asm volatile(
"nop\n"
"nop\n"
);
}
int main()
{
uint32_t buffer[10];
buffer[0]=0x22444b02;
buffer[1]=0x4802605a;
buffer[2]=0xe7f94780;
buffer[3]=0x40004800;
buffer[4]=((uint32_t)function);
goto *(void *)((uint32_t) buffer);
return 0;
}
EDIT2
该程序已经在SRAM中,您只需将其从SRAM复制到SRAM ...如果该副本是“漏洞利用”,那么就可以了,但是在微控制器上,您不能将其复制到其他代码之上,大多数当所有代码用完Flash时,一个sram位置和另一个sram位置一样好。 无论如何,这个词在这里不是问题。
在下面的评论中
在主要情况下,我分支到地址0x20004001。
不是,这是同样的错误,如果您想使用一个正确的地址来计算地址,则需要使用正确的指令。
void function()
{
asm volatile(
"nop\n"
"nop\n"
);
}
int main()
{
uint32_t buffer[10];
buffer[0]=0x22444b02;
buffer[1]=0x4802605a;
buffer[2]=0xe7f94780;
buffer[3]=0x40004800;
buffer[4]=((uint32_t)function);
//memcpy...
//goto *(void *)((uint32_t) buffer);
goto *(void *)(0x20004000);
return 0;
}
计算机上的编译器给出
36: 4b06 ldr r3, [pc, #24] ; (50 <main+0x3e>)
38: 469f mov pc, r3
3a: 46c0 nop ; (mov r8, r8)
3c: 22444b02 subcs r4, r4, #2048 ; 0x800
40: 4802605a stmdami r2, {r1, r3, r4, r6, sp, lr}
44: e7f94780 ldrb r4, [r9, r0, lsl #15]!
48: 40004800 andmi r4, r0, r0, lsl #16
4c: 00000000 andeq r0, r0, r0
50: 20004000 andcs r4, r0, r0
链接后仍然可以使用。
但是,如果您这样做
goto *(void *)(0x20004001);
36: 4b06 ldr r3, [pc, #24] ; (50 <main+0x3e>)
38: 469f mov pc, r3
3a: 46c0 nop ; (mov r8, r8)
3c: 22444b02 subcs r4, r4, #2048 ; 0x800
40: 4802605a stmdami r2, {r1, r3, r4, r6, sp, lr}
44: e7f94780 ldrb r4, [r9, r0, lsl #15]!
48: 40004800 andmi r4, r0, r0, lsl #16
4c: 00000000 andeq r0, r0, r0
50: 20004001 andcs r4, r0, r1
与该答案的最高错误相同:
ADD(寄存器)和MOV(寄存器)分支没有互通。
在arm文档中搜索该行和/或该术语。