herm1t LIVE!ng room - Hardware breakpoints [entries|archive|friends|userinfo]
herm1t

[ website | twilight corner in the herm1t's cave ]
[ userinfo | ljr userinfo ]
[ archive | journal archive ]

Hardware breakpoints [Sep. 21st, 2011|01:38 pm]
Previous Entry Add to Memories Tell A Friend Next Entry
[Tags|, , , , , , ]

Вдогонку. Долго вспоминал, как ставить аппаратные бряки.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <signal.h>
#include <wait.h>

typedef enum {
    BREAK_ON_EXEC    = 0x00,
    BREAK_ON_WRITE    = 0x01,
    BREAK_ON_RW    = 0x03,
} BreakFlags;

typedef enum {
    LEN_1 = 0x00,
    LEN_2 = 0x01,
    LEN_4 = 0x03,
} DataLength;

typedef struct {
    int    dr0_local:1;
    int    dr0_global:1;
    int    dr1_local:1;
    int    dr1_global:1;
    int    dr2_local:1;
    int    dr2_global:1;
    int    dr3_local:1;
    int    dr3_global:1;
    int    exact_local:1;
    int    exact_global:1;
    int    reserved:6;
    BreakFlags    dr0_break:2;
    DataLength    dr0_len:2;
    BreakFlags    dr1_break:2;
    DataLength    dr1_len:2;
    BreakFlags    dr2_break:2;
    DataLength    dr2_len:2;
    BreakFlags    dr3_break:2;
    DataLength    dr3_len:2;
} DR7;

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

int set_bp(int pid, uint32_t addr)
{
    uintptr_t dr0 = (uintptr_t)addr;
    DR7 dr7 = {0};
    dr7.dr0_local    = 1;
    dr7.dr0_break    = BREAK_ON_EXEC;
    dr7.dr0_len    = 0;
    return     ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[0]), dr0) ||
        ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[6]), 0) ||
        ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[7]), dr7);
}

void action(void)
{
    printf("Let's rock!\n");
}

void tracer(void)
{
    int pid, status;
    struct user_regs_struct regs;
    if ((pid = fork()) == -1)
        return;
    if (pid == 0) {
        ptrace(PTRACE_TRACEME, 0, 0, 0);
        kill(getpid(), SIGINT);
    } else {
        int first = 0;
        uint32_t next_bp = (uint32_t)action;
        for (;;) {
            wait(&status);
            if (WIFEXITED(status))
                break;
            ptrace(PTRACE_GETREGS, pid, 0, &regs);
            if (regs.eip != next_bp) {
                puts("Setting BP");
                set_bp(pid, next_bp);
            } else {
                printf("Reached BP @ %08x\n", regs.eip);
                set_bp(pid, 0);
            }
            ptrace(PTRACE_CONT, pid, 0, 0);
        }
        exit(0);
    }
}

int main(int argc, char **argv)
{
    tracer();
    action();
}
LinkLeave a comment

Comments:
From:[info]dennis.livejournal.com
Date:September 21st, 2011 - 02:23 pm
(Link)
У меня есть win32-утилита плотно юзающая это :) http://conus.info/gt/
[User Picture]
From:[info]herm1t
Date:September 22nd, 2011 - 09:59 am
(Link)
Хорошая. Ж-) А у меня попроще задачка - CFG получить, чуть-чуть быстрее, чем single-step.
From:[info]dennis.livejournal.com
Date:September 22nd, 2011 - 01:07 pm
(Link)
Я о таком читал, но что-то у меня не получилось это сделать:
http://www.openrce.org/blog/view/535/Branch_Tracing_with_Intel_MSR_Registers
[User Picture]
From:[info]herm1t
Date:September 22nd, 2011 - 01:17 pm
(Link)
У меня идейка простая - ставим бряк на начало блока, дизассемблируем до ближайшей команды передачи управления, эмулируем ее, ставим бряк на начало следующего блока и da capo. Довольно просто. Облом может быть на сигналах, и на некачественном дизасме, но меня настолько запущенные случаи и не интересуют. ))
From:(Anonymous)
Date:September 30th, 2011 - 12:58 pm
(Link)
Отлаживаемый может сам начать ставить аппаратные бряки. И это проэмулировать будет сложновато.

И еще отлаживание вместо эмуляции небезопасно.
[User Picture]
From:[info]herm1t
Date:September 30th, 2011 - 01:05 pm
(Link)
> Отлаживаемый может сам начать ставить аппаратные бряки. И это проэмулировать будет сложновато.

Во-первых, в юзерспейсе DRx выставляются ptrace()'ом, он перехватывается, данные подменяются, в DR6 кажется есть еще флаг, о том, что следующая инструкция меняет DRx, но это актуально в ядре, во-вторых, можно вообще избегать _такого_ кода.

> И еще отлаживание вместо эмуляции небезопасно.

Эту штуку предполагается использовать в вирусах для поиска места для EPO. Полная эмуляция - это overkill. Даже, если хост продетектит отладку и сделает пакость или каким-то чудом "сбежит" из под отладчика - туда ему и дорога.
From:(Anonymous)
Date:September 30th, 2011 - 07:28 pm
(Link)
поиск EPO отладкой? да ну, скукота. вы бы лучше что-нибудь полезное написали.
[User Picture]
From:[info]herm1t
Date:October 1st, 2011 - 08:56 am
(Link)
А что вы считаете полезным?
From:(Anonymous)
Date:October 1st, 2011 - 03:42 pm
(Link)
что угодно, зависит от фантазии автора. сверхпроизводительный веб-сервер, виртуальная машина, СУБД, ось, трейд-робот. в программировании много областей, более интересных нежели вирусы.
[User Picture]
From:[info]herm1t
Date:October 1st, 2011 - 05:25 pm
(Link)
во-первых, мои интересы несколько шире, чем вам кажется, во-вторых, если вам не интересно, то зачем же вы сюда ходите?
From:(Anonymous)
Date:October 2nd, 2011 - 03:45 pm
(Link)
А в чем ваши интересы?

Мне интересно. Но вирусы - не самое интересное.
[User Picture]
From:[info]herm1t
Date:October 4th, 2011 - 03:59 am
(Link)
всё вам расскажи. :-) люблю соседей гитарой заёбывать. :-)
и много еще чего люблю, но в этом блоге будут только вирусы, сцена и все такое.

посмотрите на календарь постов, неужели вы думаете, что я месяц втыкаю, чтобы написать пятьдесят строк кода? если вам непременно хочется обсудить какой-нибудь оффтопик именно со мной, то пишите в стики-пост или мылом, хотя я и не понимаю зачем.
[User Picture]
From:[info]herm1t
Date:October 4th, 2011 - 09:15 am
(Link)
Клевая фича, но не хотелось бы связываться с /dev/*, права, ввод-вывод и все-такре. А с дизасмом все просто, только джампы эмулить муторно, вот что у меня получилось: uint32_t emu_cti(pid_t pid, yad_t *y, struct user_regs_struct *regs) { uint32_t mem; if (y-&gt;opcode == 0xcd) goto next_addr; if (y-&gt;opcode == 0xc2 || y-&gt;opcode == 0xc3) { mem = regs-&gt;esp; goto peek_data; } int32_t rel_arg = y-&gt;datasize == 1 ? (char)y-&gt;data1 : (int)y-&gt;data4; if (y-&gt;opcode == 0xe9 || y-&gt;opcode == 0xe8 || y-&gt;opcode == 0xeb) { goto next_rela; } if (y-&gt;flags & C_MODRM) { uint32_t mod, rm, reg[8]; mod = y-&gt;modrm &gt;&gt; 6; rm = y-&gt;modrm & 7; reg[0] = regs-&gt;eax; reg[1] = regs-&gt;ecx; reg[2] = regs-&gt;edx; reg[3] = regs-&gt;ebx; reg[4] = regs-&gt;esp; reg[5] = regs-&gt;ebp; reg[6] = regs-&gt;esi; reg[7] = regs-&gt;edi; if (mod == 3) { return reg[rm]; } if (mod == 0 && rm == 5) { mem = y-&gt;addr4; goto peek_data; } if (rm == 4) { uint32_t scale, index, base; scale = y-&gt;sib &gt;&gt; 6; index = (y-&gt;sib &gt;&gt; 3) & 7; base = y-&gt;sib & 7; mem = reg[base] + (reg[index] &lt;&lt; scale); } else { mem = reg[rm]; } if (mod != 0) { int d; if (mod == 1) d = (char)y-&gt;addr1; else d = (int)y-&gt;addr4; mem += d; } goto peek_data; } uint32_t flags = regs-&gt;eflags; #define CF ((flags &gt;&gt; 0) & 1) #define PF ((flags &gt;&gt; 2) & 1) #define AF ((flgas &gt;&gt; 4) & 1) #define ZF ((flags &gt;&gt; 6) & 1) #define SF ((flags &gt;&gt; 7) & 1) #define OF ((flags &gt;&gt; 11) & 1) if ((y-&gt;opcode & 0xfc) == 0xe0) { if (y-&gt;opcode == 0xe3) { if (regs-&gt;ecx == 0) goto next_rela; } else if (regs-&gt;ecx - 1 == 0) { if (y-&gt;opcode == 0xe2 || ZF == (y-&gt;opcode & 0xfe)) goto next_rela; } goto next_addr; } uint32_t cc = (y-&gt;opcode == 0x0f ? y-&gt;opcode2 : y-&gt;opcode) & 0x0f; #define CC(N, cond) if (cc == N) { if (cond) goto next_rela; else goto next_addr; } else CC(0x00, OF) /* jo */ CC(0x01, OF == 0) /* jno */ CC(0x02, CF) /* jb/jnae/jc */ CC(0x03, CF == 0) /* jnb/jae/jnc */ CC(0x04, ZF) /* je/jz */ CC(0x05, ZF == 0) /* jne/jnz */ CC(0x06, CF || ZF) /* jbe/jna */ CC(0x07, CF == 0 && ZF == 0) /* ja/jnbe */ CC(0x08, SF) /* js */ CC(0x09, SF == 0) /* jns */ CC(0x0a, PF) /* jp/jpe */ CC(0x0b, PF == 0) /* jnp/jpo */ CC(0x0c, SF != OF) /* jl/jnge */ CC(0x0d, SF == OF) /* jge/jnl */ CC(0x0e, ZF || SF != OF) /* jle/jng */ CC(0x0f, ZF == 0 && SF == OF) ; /* jg/jnle */ next_addr: return regs-&gt;eip + y-&gt;len; next_rela: return regs-&gt;eip + y-&gt;len + rel_arg; peek_data: return ptrace(PTRACE_PEEKDATA, pid, mem, 0); }