mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-23 21:29:49 +00:00
Refactor instruction $variables under $i ##shell
* "$in", ":{n}", "address of nth instruction forward", * "$ip", ":{n}", "address of nth instruction backward (s $I1@$Fe) #last instr in bb", * "$is", "[:{n}]", "N instruction size", * "$ij", "", "jump address (e.g. jmp 0x10, jz 0x10 => 0x10)", * "$if", "", "jump fail address (e.g. jz 0x10 => next instruction)", * "$ir", "", "instruction reference pointer value (e.g. lea rax, 0x8010 => 0x8010)", * "$iv", "", "opcode immediate value (e.g. lui a0,0x8010 => 0x8010)",
This commit is contained in:
parent
536effe6ce
commit
e70fb866b0
@ -7920,7 +7920,7 @@ static void r_anal_aefa(RCore *core, const char *arg) {
|
||||
r_core_cmdf (core, "aepc 0x%08"PFMT64x, at);
|
||||
r_core_cmd_call (core, "aeso");
|
||||
r_core_seek (core, at, true);
|
||||
int delta = r_num_get (core->num, "$l");
|
||||
int delta = r_num_get (core->num, "$is");
|
||||
if (delta < 1) {
|
||||
break;
|
||||
}
|
||||
|
@ -338,6 +338,16 @@ static RCoreHelpMessage help_msg_question_v = {
|
||||
"$?", "", "last comparison value",
|
||||
"$alias", "=value", "alias commands (simple macros)",
|
||||
"$b", "", "block size (see b command and the @! operator)",
|
||||
"$k", "{kv}", "get value of an sdb query value",
|
||||
|
||||
"$in", ":{n}", "address of nth instruction forward",
|
||||
"$ip", ":{n}", "address of nth instruction backward (s $I1@$Fe) #last instr in bb",
|
||||
"$is", "[:{n}]", "N instruction size",
|
||||
"$ij", "", "jump address (e.g. jmp 0x10, jz 0x10 => 0x10)",
|
||||
"$if", "", "jump fail address (e.g. jz 0x10 => next instruction)",
|
||||
"$ir", "", "instruction reference pointer value (e.g. lea rax, 0x8010 => 0x8010)",
|
||||
"$iv", "", "opcode immediate value (e.g. lui a0,0x8010 => 0x8010)",
|
||||
|
||||
"$B", "", "base address (aligned lowest map address)",
|
||||
"$c", "", "get terminal width in character columns",
|
||||
"$Cn", "", "get nth call of function",
|
||||
@ -347,7 +357,6 @@ static RCoreHelpMessage help_msg_question_v = {
|
||||
"$Dn", "", "get nth data reference in function",
|
||||
"$e", "", "1 if end of block, else 0",
|
||||
"$e", "{flag}", "end of flag (flag->offset + flag->size)",
|
||||
"$f", "", "jump fail address (e.g. jz 0x10 => next instruction)",
|
||||
"$F", "", "same as $FB",
|
||||
"$Fb", "", "begin of basic block",
|
||||
"$FB", "", "begin of function",
|
||||
@ -357,18 +366,13 @@ static RCoreHelpMessage help_msg_question_v = {
|
||||
"$Fi", "", "basic block instructions",
|
||||
"$FI", "", "function instructions",
|
||||
"$Fj", "", "function jump destination",
|
||||
"$fl", "", "flag length (size) at current address (fla; pD $l @ entry0)",
|
||||
"$fl", "", "flag length (size) at current address (fla; pD $is @ entry0)",
|
||||
"$FS", "", "function size (linear length)",
|
||||
"$Fs", "", "size of the current basic block",
|
||||
"$FSS", "", "function size (sum bb sizes)",
|
||||
"$i", "{n}", "address of nth instruction forward",
|
||||
"$I", "{n}", "address of nth instruction backward (s $I1@$Fe) #last instr in bb",
|
||||
"$j", "", "jump address (e.g. jmp 0x10, jz 0x10 => 0x10)",
|
||||
|
||||
"$Ja", "", "get nth jump of function",
|
||||
"$k", "{kv}", "get value of an sdb query value",
|
||||
"$l", "", "opcode length",
|
||||
"$M", "", "map address (lowest map address)",
|
||||
"$m", "", "opcode memory reference (e.g. mov eax,[0x10] => 0x10)",
|
||||
"$MM", "", "map size (lowest map address)",
|
||||
"$o", "", "here (current disk io offset)",
|
||||
"$p", "", "getpid()",
|
||||
@ -379,7 +383,6 @@ static RCoreHelpMessage help_msg_question_v = {
|
||||
"$S", "", "section offset",
|
||||
"$SS", "", "section size",
|
||||
"$s", "{flag}", "get size of flag",
|
||||
"$v", "", "opcode immediate value (e.g. lui a0,0x8010 => 0x8010)",
|
||||
"$w", "", "get word size, 4 if asm.bits=32, 8 if 64, ...",
|
||||
"$Xn", "", "get nth xref of function",
|
||||
"RNum", "", "$variables usable in math expressions",
|
||||
@ -1160,9 +1163,10 @@ static int cmd_help(void *data, const char *input) {
|
||||
int i = 0;
|
||||
const char *vars[] = {
|
||||
"$$", "$$c", "$$$", "$$$c", "$?", "$B", "$b", "$c", "$Cn", "$D", "$DB", "$DD", "$Dn",
|
||||
"$is", "$ij", "$if", "$ir", "$iv", "$in", "$ip",
|
||||
"$e", "$f", "$F", "$Fb", "$FB", "$Fe", "$FE", "$Ff", "$Fi", "$FI", "$Fj",
|
||||
"$fl", "$FS", "$Fs", "$FSS", "$i", "$j", "$Ja", "$l", "$M", "$m", "$MM",
|
||||
"$o", "$p", "$P", "$r", "$s", "$S", "$SS", "$v", "$w", "$Xn", NULL
|
||||
"$fl", "$FS", "$Fs", "$FSS", "$Ja", "$M", "$MM",
|
||||
"$o", "$p", "$P", "$s", "$S", "$SS", "$w", "$Xn", NULL
|
||||
};
|
||||
const bool wideOffsets = r_config_get_i (core->config, "scr.wideoff");
|
||||
while (vars[i]) {
|
||||
|
167
libr/core/core.c
167
libr/core/core.c
@ -4,9 +4,6 @@
|
||||
|
||||
#include <r_core.h>
|
||||
#include <r_vec.h>
|
||||
#if R2__UNIX__
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#define DB core->sdb
|
||||
|
||||
@ -466,15 +463,18 @@ static const char *str_callback(RNum *user, ut64 off, int *ok) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ut64 numvar_instruction_backward(RCore *core, const char *input) {
|
||||
static ut64 invalid_numvar(RCore *core, const char *str) {
|
||||
core->num->nc.errors ++;
|
||||
core->num->nc.calc_err = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ut64 numvar_instruction_prev(RCore *core, int n, int *ok) {
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
// N forward instructions
|
||||
int i, ret;
|
||||
int n = 1;
|
||||
if (isdigit ((unsigned char)input[0])) {
|
||||
n = atoi (input);
|
||||
} else if (input[0] == '{') {
|
||||
n = atoi (input + 1);
|
||||
}
|
||||
if (n < 1) {
|
||||
R_LOG_ERROR ("Invalid negative value");
|
||||
n = 1;
|
||||
@ -514,21 +514,12 @@ static ut64 numvar_instruction_backward(RCore *core, const char *input) {
|
||||
return val;
|
||||
}
|
||||
|
||||
static ut64 numvar_instruction(RCore *core, const char *input) {
|
||||
static ut64 numvar_instruction_next(RCore *core, ut64 addr, int n, int *ok) {
|
||||
RAnalOp op;
|
||||
ut64 addr = core->offset;
|
||||
// N forward instructions
|
||||
ut8 data[32];
|
||||
int i;
|
||||
ut64 val = addr;
|
||||
int n = 1;
|
||||
if (input[0] == '{') {
|
||||
n = atoi (input + 1);
|
||||
}
|
||||
if (n < 1) {
|
||||
R_LOG_ERROR ("Invalid negative value");
|
||||
n = 1;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
r_io_read_at (core->io, val, data, sizeof (data));
|
||||
r_anal_op_init (&op);
|
||||
@ -540,14 +531,79 @@ static ut64 numvar_instruction(RCore *core, const char *input) {
|
||||
val += op.size;
|
||||
r_anal_op_fini (&op);
|
||||
}
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
return val;
|
||||
|
||||
}
|
||||
|
||||
static ut64 invalid_numvar(RCore *core, const char *str) {
|
||||
core->num->nc.errors ++;
|
||||
core->num->nc.calc_err = str;
|
||||
return 0;
|
||||
static ut64 numvar_instruction(RCore *core, const char *str, int *ok) {
|
||||
#if 0
|
||||
* `$j` -> `$ij` jump destination
|
||||
* `$f` -> `$if` fail destination
|
||||
* `$i` -> `$in` next instruction (WIP)
|
||||
* `$l` -> `$is` opcode length (RFC) why not use `s` instead?
|
||||
* `$m` -> `$ir` memory opcode reference address
|
||||
* `$v` -> `$iv` opcode immediate (RFC)
|
||||
#endif
|
||||
const char ch0 = *str;
|
||||
int count = 1;
|
||||
if (ch0) {
|
||||
const char ch1 = str[1];
|
||||
if (ch1 == ':') {
|
||||
count = r_num_math (NULL, str + 2);
|
||||
} else if (ch1 == '{') {
|
||||
count = r_num_math (NULL, str + 2);
|
||||
} else if (isdigit (ch1)) {
|
||||
count = r_num_math (NULL, str + 1);
|
||||
} else if (ch1) {
|
||||
return invalid_numvar (core, "expected :,{ after $i?");
|
||||
}
|
||||
}
|
||||
if (ch0 == 'n') { // "$in"
|
||||
return numvar_instruction_next (core, core->offset, count, ok);
|
||||
}
|
||||
if (ch0 == 'p') { // "$ip"
|
||||
return numvar_instruction_prev (core, count, ok);
|
||||
}
|
||||
if (ch0 == 's') { // "$is"
|
||||
return numvar_instruction_next (core, 0, count, ok);
|
||||
}
|
||||
if (count != 1) {
|
||||
return invalid_numvar (core, "expected :,{ after $i?");
|
||||
}
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
ut64 ret = 0;
|
||||
RAnalOp op;
|
||||
r_anal_op_init (&op);
|
||||
r_anal_op (core->anal, &op, core->offset, core->block, core->blocksize, R_ARCH_OP_MASK_BASIC);
|
||||
r_anal_op_fini (&op); // we don't need strings or pointers, just values, which are not nullified in fini
|
||||
switch (ch0) {
|
||||
case 'j': // "$ij" instruction jump
|
||||
ret = op.jump;
|
||||
break;
|
||||
case 'f': // "$if" instruction fail
|
||||
ret = op.fail;
|
||||
break;
|
||||
case 's': // "$is" instruction size
|
||||
ret = op.size;
|
||||
break;
|
||||
case 'r': // "$ir" instruction reference
|
||||
ret = op.ptr;
|
||||
break;
|
||||
case 'v': // "$iv" instruction value
|
||||
ret = op.val;
|
||||
break;
|
||||
default:
|
||||
if (ok) {
|
||||
*ok = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ut64 numvar_k(RCore *core, const char *str, int *ok) {
|
||||
@ -588,6 +644,28 @@ static ut64 numvar_k(RCore *core, const char *str, int *ok) {
|
||||
return invalid_numvar (core, "unknown $k{key}");
|
||||
}
|
||||
|
||||
static ut64 numvar_dollar(RCore *core, const char *str, int *ok) {
|
||||
if (!strcmp (str, "$$")) {
|
||||
return core->offset;
|
||||
}
|
||||
if (!strcmp (str, "$$c")) {
|
||||
if (core->print->cur_enabled) {
|
||||
return core->offset + core->print->cur;
|
||||
}
|
||||
return core->offset;
|
||||
}
|
||||
if (!strcmp (str, "$$$")) {
|
||||
return core->prompt_offset;
|
||||
}
|
||||
if (!strcmp (str, "$$$c")) {
|
||||
if (core->print->cur_enabled) {
|
||||
return core->prompt_offset + core->print->cur;
|
||||
}
|
||||
return core->prompt_offset;
|
||||
}
|
||||
return invalid_numvar (core, str);
|
||||
}
|
||||
|
||||
static ut64 num_callback(RNum *userptr, const char *str, int *ok) {
|
||||
RCore *core = (RCore *)userptr; // XXX ?
|
||||
RAnalFunction *fcn;
|
||||
@ -688,13 +766,16 @@ static ut64 num_callback(RNum *userptr, const char *str, int *ok) {
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
// must be deprecated
|
||||
switch (str[1]) {
|
||||
case 'e':
|
||||
#if 0
|
||||
case 'j':
|
||||
case 'f':
|
||||
case 'm':
|
||||
case 'v':
|
||||
case 'l':
|
||||
#endif
|
||||
r_anal_op_init (&op);
|
||||
r_anal_op (core->anal, &op, core->offset, core->block, core->blocksize, R_ARCH_OP_MASK_BASIC);
|
||||
r_anal_op_fini (&op); // we don't need strings or pointers, just values, which are not nullified in fini
|
||||
@ -705,15 +786,7 @@ static ut64 num_callback(RNum *userptr, const char *str, int *ok) {
|
||||
// XXX the above line is assuming op after fini keeps jump, fail, ptr, val, size and r_anal_op_is_eob()
|
||||
switch (str[1]) {
|
||||
case 'i': // "$i"
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
return numvar_instruction (core, str + 2);
|
||||
case 'I': // "$I"
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
return numvar_instruction_backward (core, str + 2);
|
||||
return numvar_instruction (core, str + 2, ok);
|
||||
case '.': // can use pc, sp, a0, a1, ...
|
||||
return r_debug_reg_get (core->dbg, str + 2);
|
||||
case 'k': // "$k{ey}" "$k:ey"
|
||||
@ -784,23 +857,18 @@ static ut64 num_callback(RNum *userptr, const char *str, int *ok) {
|
||||
return UT64_MAX;
|
||||
}
|
||||
return r_anal_op_is_eob (&op);
|
||||
case 'j': // $j jump address
|
||||
return op.jump;
|
||||
case 'p': // $p
|
||||
return r_sys_getpid ();
|
||||
case 'P': // $P
|
||||
return core->dbg->pid > 0 ? core->dbg->pid : 0;
|
||||
case 'f': // $f jump fail address
|
||||
if (str[2] == 'l') { // $fl flag length
|
||||
{
|
||||
RFlagItem *fi = r_flag_get_i (core->flags, core->offset);
|
||||
if (fi) {
|
||||
return fi->size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return op.fail;
|
||||
case 'm': // $m memref
|
||||
return op.ptr;
|
||||
case 'B': // $B base address
|
||||
case 'M': { // $M map address
|
||||
ut64 lower = UT64_MAX;
|
||||
@ -823,10 +891,6 @@ static ut64 num_callback(RNum *userptr, const char *str, int *ok) {
|
||||
return (lower == UT64_MAX)? 0LL: lower;
|
||||
}
|
||||
break;
|
||||
case 'v': // $v immediate value
|
||||
return op.val;
|
||||
case 'l': // $l opcode length
|
||||
return op.size;
|
||||
case 'b': // "$b" block size
|
||||
return core->blocksize;
|
||||
case 's': // $s file size
|
||||
@ -876,22 +940,7 @@ static ut64 num_callback(RNum *userptr, const char *str, int *ok) {
|
||||
case '?': // $?
|
||||
return core->num->value; // rc;
|
||||
case '$': // $$ offset
|
||||
if (!strcmp (str, "$$")) {
|
||||
return core->offset;
|
||||
} else if (!strcmp (str, "$$c")) {
|
||||
if (core->print->cur_enabled) {
|
||||
return core->offset + core->print->cur;
|
||||
}
|
||||
return core->offset;
|
||||
} else if (!strcmp (str, "$$$")) {
|
||||
return core->prompt_offset;
|
||||
} else if (!strcmp (str, "$$$c")) {
|
||||
if (core->print->cur_enabled) {
|
||||
return core->prompt_offset + core->print->cur;
|
||||
}
|
||||
return core->prompt_offset;
|
||||
}
|
||||
return invalid_numvar (core, str);
|
||||
return numvar_dollar (core, str, ok);
|
||||
case 'o': // $o
|
||||
{
|
||||
RBinSection *s = r_bin_get_section_at (r_bin_cur_object (core->bin), core->offset, true);
|
||||
|
@ -17,7 +17,7 @@ CMDS=<<EOF
|
||||
e asm.arch=x86
|
||||
e asm.bits=16
|
||||
wx 9a34002001
|
||||
?v $j
|
||||
?v $ij
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x1234
|
||||
@ -30,7 +30,7 @@ CMDS=<<EOF
|
||||
e asm.arch=x86
|
||||
e asm.bits=16
|
||||
wx ea34002001
|
||||
?v $j
|
||||
?v $ij
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x1234
|
||||
|
@ -163,7 +163,7 @@ CMDS=<<EOF
|
||||
wx e9010f
|
||||
e asm.arch=x86
|
||||
e asm.bits=16
|
||||
?vi $l
|
||||
?vi $is
|
||||
pi 1
|
||||
ao 1~size[1]
|
||||
EOF
|
||||
|
@ -1,5 +1,6 @@
|
||||
NAME=f_123
|
||||
FILE=-
|
||||
ARGS=-1
|
||||
CMDS=<<EOF
|
||||
f _foo=123
|
||||
f _123=_foo
|
||||
@ -15,18 +16,17 @@ f $foo=456
|
||||
?v $foo
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
ERROR: Invalid flag name '123'
|
||||
ERROR: Invalid flag name '$123'
|
||||
ERROR: Invalid flag name '$foo'
|
||||
0x7b
|
||||
0x7b
|
||||
0x141
|
||||
0x7b
|
||||
0xffffffffffffffff
|
||||
0xffffffffffffffff
|
||||
EOF
|
||||
EXPECT_ERR=<<EOF
|
||||
ERROR: Invalid flag name '123'
|
||||
ERROR: Invalid flag name '$123'
|
||||
ERROR: Invalid flag name '$foo'
|
||||
0x0
|
||||
0x0
|
||||
EOF
|
||||
EXPECT_ERR=
|
||||
RUN
|
||||
|
||||
NAME=f?flag
|
||||
|
@ -57,10 +57,10 @@ FILE=bins//mach0/ired-arm64
|
||||
CMDS=<<EOF
|
||||
afr
|
||||
?v $Fe
|
||||
s $I1@$Fe
|
||||
s $ip:1@$Fe
|
||||
pi 1
|
||||
?v $Fb
|
||||
s $i1
|
||||
s $in:1
|
||||
pi 1
|
||||
?v $Fb
|
||||
EOF
|
||||
|
@ -145,18 +145,18 @@ EXPECT=<<EOF
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=$j (malloc)
|
||||
NAME=$ij (malloc)
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
e asm.bits=32
|
||||
e asm.arch = x86
|
||||
e anal.arch = x86
|
||||
wa jmp 0x30
|
||||
?v $j
|
||||
?v $ij
|
||||
wa jz 0x01
|
||||
?v $j
|
||||
?v $ij
|
||||
wa xor eax, eax
|
||||
?v $j
|
||||
?v $ij
|
||||
?e
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
@ -167,18 +167,18 @@ EXPECT=<<EOF
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=$f (malloc)
|
||||
NAME=$if (malloc)
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
e asm.bits=32
|
||||
e asm.arch = x86
|
||||
e anal.arch = x86
|
||||
wx eb2e
|
||||
?v $f
|
||||
?v $if
|
||||
wx 0f84fbffffff
|
||||
?v $f
|
||||
?v $if
|
||||
wx 31c0
|
||||
?v $f
|
||||
?v $if
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0xffffffffffffffff
|
||||
@ -187,15 +187,15 @@ EXPECT=<<EOF
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=$m (malloc)
|
||||
NAME=$ir (malloc)
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
e asm.arch=x86
|
||||
e asm.bits=32
|
||||
wa mov eax, [0x500]
|
||||
?v $m
|
||||
?v $ir
|
||||
wa mov eax, ebx
|
||||
?v $m
|
||||
?v $ir
|
||||
?e
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
@ -205,18 +205,18 @@ EXPECT=<<EOF
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=$l (malloc)
|
||||
NAME=$is (malloc)
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
e asm.bits=32
|
||||
e asm.arch = x86
|
||||
e anal.arch = x86
|
||||
wa xor eax, eax
|
||||
?v $l
|
||||
?v $is
|
||||
wa mov esp, 0x1
|
||||
?v $l
|
||||
?v $is
|
||||
wx c3
|
||||
?v $l
|
||||
?v $is
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x2
|
||||
|
@ -101,3 +101,49 @@ plus one
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=num instruction vars
|
||||
FILE=malloc://0x4000
|
||||
ARGS=-1 -a x86 -b32
|
||||
CMDS=<<EOF
|
||||
'wa call 32;nop;mov eax,33;int 0x33;ret
|
||||
?e jump call 32
|
||||
?vi $ij
|
||||
?e fail call 32
|
||||
?vi $if
|
||||
?e instr pos 1 2 3
|
||||
?vi $in
|
||||
?vi $in:1
|
||||
?vi $in:2
|
||||
?vi $in:3
|
||||
?e instr size 1 2 3
|
||||
?vi $is
|
||||
?vi $is{-1}
|
||||
?vi $is:1
|
||||
?vi $is:2
|
||||
?vi $is:3
|
||||
2so
|
||||
?e mov33
|
||||
?vi $iv
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
INFO: Written 14 byte(s) (call 32;nop;mov eax,33;int 0x33;ret) = wx e81b00000090b821000000cd33c3 @ 0x00000000
|
||||
jump call 32
|
||||
32
|
||||
fail call 32
|
||||
5
|
||||
instr pos 1 2 3
|
||||
5
|
||||
5
|
||||
6
|
||||
11
|
||||
instr size 1 2 3
|
||||
5
|
||||
-1
|
||||
5
|
||||
6
|
||||
11
|
||||
mov33
|
||||
33
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
@ -25,7 +25,7 @@ NAME=ljmp follow
|
||||
FILE=bins/firmware/bios_64k.bin
|
||||
CMDS=<<EOF
|
||||
e asm.bits
|
||||
s $j
|
||||
s $ij
|
||||
? $$
|
||||
e anal.cs=0xfff0
|
||||
? $$~segm
|
||||
|
Loading…
Reference in New Issue
Block a user