Вызов функции в полезной нагрузке на ARMv7 - PullRequest
0 голосов
/ 06 сентября 2018

Я хочу написать простую полезную нагрузку для моей платформы ARMv7. Сначала я попробовал простой цикл отправки символа через UART:

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

это моя полезная нагрузка, один раз в C и один раз в сборке.

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;
}

Это прекрасно работает, персонаж отправляется через UART. Теперь я хочу вызвать функцию из моей полезной нагрузки:

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

снова, мой желаемый груз, один раз в C и один раз в сборке.

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;
}

Символ теперь отправляется только 3 раза, затем я получаю сбой (обработчик ошибок процессора). Я проверил область памяти функции и мой буфер, оба выглядят одинаково:

0x8000358 <payload>:    0xaf00b480  0x22684b02  0xf7ff809a  0xe7f9fff1
0x8000368 <payload+16>: 0x40004800  0xb082b580  0xf001af00  0x2102f863

0x20004000 <_heap+3144>:    0xaf00b480  0x22684b02  0xf7ff809a  0xe7f9fff1
0x20004010 <_heap+3160>:    0x40004800  0x00000000  0x00000000  0x00000000

1 Ответ

0 голосов
/ 06 сентября 2018

goto * (void *) ((uint32_t) buffer | 1);

странно для тех, кто использует ненужную встроенную сборку, мог бы использовать некоторые здесь.

Когда я компилирую это, я получаю:

  38:   2201        movs    r2, #1
  3a:   4313        orrs    r3, r2
  3c:   469f        mov pc, r3
  3e:   46c0        nop 

и это должно сразу произойти,

Из документации на руку:

ADD (регистр) и MOV (регистр) ветвь без взаимодействия.

Что вы можете сделать, это использовать какой-нибудь настоящий асм

.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>

Теперь, если вам не нужна ссылка на ветвь, а вместо этого ветвь, и поскольку вы свободны и свободны с встроенной сборкой, это больше похоже на ваш стиль и не должно иметь проблемы с перемещением, которую генерировал по крайней мере мой компилятор. Кстати, нам нужно посмотреть, что сгенерировал ваш компилятор, чтобы понять, почему вы не сразу потерпели крах.

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

Вы не можете обрабатывать регистр данных Uart таким образом, как только fifo не заполняет больше символов.

вы не можете скопировать этот код и запустить его таким же образом.

 8000362:   f7ff fff1   bl  8000348 <function>

подходит для встроенной вспышки, пока 8000348 остается там, где есть эта функция, но bl относится к ПК, так что если вы захотите скопировать это в 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 

И я полагаю, вы не скопировали функцию () в 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-байтового выровненного адреса, и у него не будет проблемы с невозможностью вызова функция функция.

Да, я обманул ссылку для этого примера, у меня нет начальной загрузки. И человек, который действительно беспокоит меня, по умолчанию использует фрейм стека, что за пустая трата времени. Интересно, смогу ли я построить свой набор инструментов без этого? В противном случае это хакерское решение работает лучше:

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-m, несмотря на то, что называете его armv7. Вы можете использовать команду thumb2 orr r3, r3, # 1, которая мысленно верна, вы хотите, чтобы бит там не добавлялся. Но если это armv6-m, как cortex-m0, или вы хотите переносимости, просто сделайте сложение таким же уродливым, как add r3, # 1, потому что это не инструкция thumb2 и она переносима.

Так что, если у вас есть несколько персонажей, я подозреваю, что ваш goto работал каким-то образом, опять похоже, что вы используете qemu, возможно? А может, вот как? Но не пытаясь запустить копию кода в sram, вы вернулись назад, чтобы найти функцию (), но нашли там данные, и это либо привело вас к сбоям, либо, поскольку это произошло непосредственно перед скопированным кодом, оно несколько раз проходило через скопированный код и все, что было там, в конце концов сошло с рельсов. Или, может быть, из-за того, что вы не использовали права uart, вы переполнили буфер, что по крайней мере на одном из этих брендов может привести к прекращению работы uart, пока вы не сбросите флаг переполнения буфера. Предполагая, что вы полагаетесь на вывод UART, чтобы увидеть успех или неудачу. (вы могли увидеть этот сбой при запуске этой программы без вызова функции, из-за чего я считаю, что это симуляция, а не реальное оборудование, или реальное оборудование, вы просто видите часть значений, которые вы вставляете в UART).

Попробуйте вместо этого

void payload()
{
  uint32_t ra; 
  for(ra=0x30;;ra++)
  {
    ra&=0x37;
    USART3->DR = ra;
  }
}

в качестве отправной точки, чтобы увидеть, о чем я говорю, если вы видите 0123456701234567 навсегда, тогда я подозреваю, что вы используете симулятор, а не аппаратное обеспечение ....

EDIT:

У 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 ... Если копия является «эксплойтом», тогда все в порядке, но на микроконтроллере вы не копируете это поверх некоторого другого кода, большинство из время, когда весь код заканчивается флэш-памятью, поэтому одно местоположение 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 (регистр) ветвь без взаимодействия.

Поиск этой строки и / или этого термина в документации по оружию.

...