* Apply whats's patch fixing r_search for stripstr

- Thanks! :)
* Added dummy r_socket_proc_* api
  - Needs to be moved outside r_socket
  - Added two non-working usage examples
* Add some checks and enhacements to the r_buf API
  - Implemented in r_util.vapi
* R_APIfy the r_cache api
* Add missing methods in r_socket.vapi
* Update the README file in r_anal describing the
  new design of r_anal..not yet finished
This commit is contained in:
pancake 2009-10-12 17:41:52 +02:00
parent c67e0dec3d
commit deb263a5fb
32 changed files with 746 additions and 165 deletions

3
TODO
View File

@ -6,6 +6,9 @@
----------------------------------------[ todo
* if we use objcopy --stripsymbols on a library..the stripped symbols are not
listed by nm or rabin..but objcopy is able to see them O_O
* store version information in libraries ?
* public delegate void RangeEachFunc(int i);
* public void each(RangeEachFunc each_func) { ... }

View File

@ -4,9 +4,11 @@ Code analysis module
* TODO: Add static plugin support here
Analysis an = new Analysis ();
io.bind (an);
an.set ("x86");
an.set_bytes (buf, 1024);
an.analyze ();
var ctx = new Analysis.Context(an);
//ctx.set_bytes (0x8048000, buf, 1024); // this must be a callback
ctx.analyze (0x8048300);
var foo = an.get_function_preludes ();
var calls = an.get_calls ();
foreach var (calls) {
@ -15,3 +17,307 @@ foreach var (calls) {
stdout.printf("arg%d: %s\n" i, var.arg[i]);
}
}
an.accept(ctx);
////////////////////////////////////////
Global picture
(anal) -> can keep track of results of different context (functions ...)
|
`---> we get a context.. so we work there with
(anal context owns stack, regs, ...)
- able to detect function arguments
- we can configure the context in a way or other
- it is able to get info from global anal
- feeded with bytes
r_anal_get_bb(an, 0x804800);
r_anal_op_t * op = r_anal_get_op(an, 0x804800);
r_anal_get_fun(an, 0x804800);
void analyze_graph(Analysis an, uint64 addr)
{
var? op = an.get_op(addr);
while (op != null) {
if (op.type == Analysis.OpcodeType.BRANCH) {
foreach (var jmp in op.refs) {
if (jmp.type | Analysis.AccessType.EXEC)
print("0x%08llx : branch 0x%08llx\n", op.addr, jmp.addr);
analyze_graph(an, jmp.addr);
}
}
op = op.next();
}
}
void analyze_function(Analysis an, uint64 addr)
{
Analysis.Function? fun = an.get_fun(addr);
if (fun == null)
Log.err("No function found at address 0x%08llx".printf());
return;
}
}
----------------------------------------
// Must use r_alloc_pool for every type of structure (per function level)
// Must store all this info using r_db
// Only index when requested (tempral analysis are temporal)
// Do we have to enable jump/call toggles for breaking basicblocks?
// Memory selectors are just modifiers .. how?
// How to handle with self-modifying code?
- if its a conditional branch, refs are true , false
- if not and there is more than one branch is all the possibilities
- if an address is accessed in read|write and exec mode we should warn!
xrefs[] = {
addr = 0x8048480
type = R|W|X - executable xrefs are control flow branches,
- read/write are for data
}
refs[] = {
op = eq,add,mul ??
reg = regidx
addr = 0x8048580
type = R|W|X
}
// we need an api in r_buf to modify bits with endian and values..
struct bin {
int offset;
int size;
int endian;
};
enum type {
IMM
REG
MEM
};
struct r_anal_value_t {
int op; // NOP, ADD, SEL, ...
int type; // opcode, reg, imm, addr
ut64 num; // idxofreg, immvalue, addrnum
struct bin bin;
int size;
int nextop; // ADD, MUL, ...
struct r_anal_value_t *next;
};
struct arg {
int rw; // READ | WRITE direction
int nv; // number of values
struct r_anal_value_t *v;
};
mov eax, [0x8048+eax*4]
mov -> args = { "eax", {0x8048 {+eax*4}} }
struct r_anal_ref_t {
int type; // READ, WRITE, EXEC
struct r_anal_value_t value;
};
struct r_anal_op_t {
ut64 addr;
int frame;
int type;
int cond;
int nestlevel;
int length;
int crc;
struct r_anal_value_t rep;
int nargs;
struct arg args[];
struct r_anal_op_t *next;
int nrefs;
struct r_anal_ref_t refs[];
int nxrefs;
struct r_anal_ref_t xrefs[];
};
/* basic block */
struct r_anal_bb_t {
ut64 addr;
int type;
int size;
ut8 *bytes;
struct r_anal_op_t *head; // opcode heading this basic block
struct r_anal_ref_t refs[];
struct r_anal_ref_t xrefs[];
};
/* function */
struct r_anal_fun_t {
char *name;
ut64 addr;
int size;
// XXX: use r_ranges instead of addr+size?
struct r_anal_ref_t refs[];
struct r_anal_ref_t xrefs[];
};
/* used to emulate */
struct r_anal_arch_t {
struct r_reg_t reg;
char **regs;
int pc; // program counter
int sp; // stack pointer
int bp; // base pointer
int gp; // global pointer
int sr; // src
int dr; // dst
};
const char **regs = { "eax", "ebx", "ecx", "...", NULL };
if (opcode.xrefs[i].type & R_ANAL_XS_EXEC)
// compilation process defines a mapping between the binary representation
// of an opcode into an AST of structs describing the opcode itself or
// we can just serialize it into a evaluable string
// - evaluable strings are cheaper in memory consumption
// - strstr(es, "%eax") easy way to check if a register is used
// - the eval string should be converted into an AST at some point
Analysis levels:
================
- opcode level
- frame size
- conditional (used by branches(jumps) and arm opcodes)
- weight (importance) (if <0, it is a nop) trash detection
- XXX file/line (dwarf nfo??? here) i think no
- lifetime of register value (detect if
- nesting level (branch analysis)
- sign
- type
-- operand level:
- bitsize
- mem | reg | imm
- value
- direction (read|write)
- operand index
- basic block level
- bytes + length + (checksum?)
- type (head, tail, body, last)
- xrefs (branches to here)
- refs (must be an array)
- true branch
- false branch
- destinations[] // for call eax and so
- function level
- name
- offset range (r_range here, functions do not need to be linear)
- variables (use r_var) (( merge r_var here? ))
- arguments ("")
- xrefs
- calls (outrefs)
== graph simplification (serialize blocks with direct branches (jmp))
- program
- comprends data + code trees
- all references must be stored twice
- r_range of functions, data and other shit
Context analysis:
=================
- Merge r_vm here -- multiarchitecture code emulation
- Allows to track register lifetime,
- Detect possible values for 'call eax' f.ex
- Identify fake conditional branches
TEH RIR
=======
The radare intermediate representation.
- ascii representation of opcode level analysis
-- epilog/prolog bytez for extra function detection
Architecture language
=====================
Allows to describe an architecture (byte parsing, read/write)
- opcode reassembling
- automatic code analysis
r_anal_opcode_set(op, R_OPTYPE_ADD);
- opcode level analysis can be manually modified in runtime
- basic blocks can change
Decompilation
=============
Use ALT .. in a inverse way OMG thats freaking
/////////////////////////////////////////////////////////////
opcode_analyze ()
- parse bytes and fill an structure
- opcode type and arguments
- underlying vm code
opcode_modify ()
- modify the bytes based on the structure changes
- the structure should expose the bit level info to make this possible
// this is //
* modify reg, immediate or memory values
+--------------+
| AnalArchLang | **
+--------------+
if [arg0 == 0xff] {
reg = { eax, ecx, edx, ebx, esp, ebp, esi, edi }
jmp [0xe0+reg]
jmp [0xe8+reg]
reg = { eax, ecx, edx, ebx, esp, ebp, esi, edi }
push [0xf0+reg]
reg = { eax, ecx, edx, ebx, esp, ebp, esi, edi }
call [0xd0+reg]
call [0xd8+reg]
}
[0:7]=e8 {
type = "call"
addr = [8:31]
len = 5
}
[0:7]=50 && [0:7]<60 {
type = "push"
len = 1
}
[0:7]=c3 {
type = "ret"
len = 1
}
BASIC OPS we need for the IR
============================ -- this is RISC! :D
Each opcode must support a size value. The format is:
We need some intermediate temporal registers
lispy assembly:
(addi eax 3)
(addi *(+ eax 8) 3)
lea edi, [ecx*4-0x4]
(set edi (- (* ecx 4) 4)
(set edi (* ecx 4 - 4)) ; iterative format
1 byte 1 N N
[ opcode ] [ type|size ] [ arg ] [ arg ]
type = [ op | reg | mem | imm ] ; 2 bits is enought
size = 1, 2, 4, 8 ; byte level
ADD reg, reg
SUB reg,
JMP reg
JMP imm
JMP mem
SET reg, imm
STO mem, reg ; store register value into memory
LOA reg, mem ; load memory value into register
...

View File

@ -42,6 +42,7 @@ R_API int r_anal_add(struct r_anal_t *anal, struct r_anal_handle_t *foo)
return R_TRUE;
}
// TODO: Must be deprecated
R_API int r_anal_list(struct r_anal_t *anal)
{
struct list_head *pos;
@ -52,7 +53,7 @@ R_API int r_anal_list(struct r_anal_t *anal)
return R_FALSE;
}
R_API int r_anal_set(struct r_anal_t *anal, const char *name)
R_API int r_anal_use(struct r_anal_t *anal, const char *name)
{
struct list_head *pos;
list_for_each_prev(pos, &anal->anals) {
@ -164,6 +165,7 @@ R_API struct r_anal_refline_t *r_anal_reflines_get(struct r_anal_t *anal, ut8 *b
/* umf..this should probably be outside this file*/
R_API int r_anal_reflines_str(struct r_anal_t *anal, struct r_anal_refline_t *list, char *str, int opts)
{
struct r_anal_refline_t *ref;
struct list_head *pos;
int dir = 0;
char ch = ' ';
@ -178,8 +180,7 @@ R_API int r_anal_reflines_str(struct r_anal_t *anal, struct r_anal_refline_t *li
for (pos = linestyle?(&(list->list))->next:(&(list->list))->prev;
pos != (&(list->list)); pos = linestyle?pos->next:pos->prev) {
struct r_anal_refline_t *ref = list_entry(pos, struct r_anal_refline_t, list);
ref = list_entry(pos, struct r_anal_refline_t, list);
if (anal->pc == ref->to) dir = 1;
// TODO: use else here

View File

@ -8,10 +8,11 @@ R_API void r_anal_ctx_init(struct r_anal_ctx_t *ctx)
{
}
R_API struct r_anal_ctx_t *r_anal_ctx_new()
R_API struct r_anal_ctx_t *r_anal_ctx_new(struct r_anal_t *anal)
{
struct r_anal_ctx_t *ctx = MALLOC_STRUCT(struct r_anal_ctx_t);
r_anal_ctx_init(ctx);
ctx->anal = anal;
return ctx;
}

View File

@ -13,56 +13,56 @@
#define R_ANAL_MAXREG 16
enum {
R_ANAL_AOP_FAMILY_UNKNOWN = 0,
R_ANAL_AOP_FAMILY_CPU, /* normal cpu insturction */
R_ANAL_AOP_FAMILY_FPU, /* fpu (floating point) */
R_ANAL_AOP_FAMILY_MMX, /* multimedia instruction (packed data) */
R_ANAL_AOP_FAMILY_PRIV, /* priviledged instruction */
R_ANAL_AOP_FAMILY_LAST
R_ANAL_OP_FAMILY_UNKNOWN = 0,
R_ANAL_OP_FAMILY_CPU, /* normal cpu insturction */
R_ANAL_OP_FAMILY_FPU, /* fpu (floating point) */
R_ANAL_OP_FAMILY_MMX, /* multimedia instruction (packed data) */
R_ANAL_OP_FAMILY_PRIV, /* priviledged instruction */
R_ANAL_OP_FAMILY_LAST
};
enum {
R_ANAL_AOP_TYPE_NULL = 0,
R_ANAL_AOP_TYPE_JMP, /* mandatory jump */
R_ANAL_AOP_TYPE_UJMP, /* unknown jump (register or so) */
R_ANAL_AOP_TYPE_CJMP, /* conditional jump */
R_ANAL_AOP_TYPE_CALL, /* call to subroutine (branch+link) */
R_ANAL_AOP_TYPE_RCALL, /* call to register */
R_ANAL_AOP_TYPE_REP, /* repeats next instruction N times */
R_ANAL_AOP_TYPE_RET, /* returns from subrutine */
R_ANAL_AOP_TYPE_ILL, /* illegal instruction // trap */
R_ANAL_AOP_TYPE_UNK, /* unknown opcode type */
R_ANAL_AOP_TYPE_NOP, /* does nothing */
R_ANAL_AOP_TYPE_MOV, /* register move */
R_ANAL_AOP_TYPE_TRAP, /* it's a trap! */
R_ANAL_AOP_TYPE_SWI, /* syscall, software interrupt */
R_ANAL_AOP_TYPE_UPUSH, /* unknown push of data into stack */
R_ANAL_AOP_TYPE_PUSH, /* push value into stack */
R_ANAL_AOP_TYPE_POP, /* pop value from stack to register */
R_ANAL_AOP_TYPE_CMP, /* copmpare something */
R_ANAL_AOP_TYPE_ADD,
R_ANAL_AOP_TYPE_SUB,
R_ANAL_AOP_TYPE_MUL,
R_ANAL_AOP_TYPE_DIV,
R_ANAL_AOP_TYPE_SHR,
R_ANAL_AOP_TYPE_SHL,
R_ANAL_AOP_TYPE_OR,
R_ANAL_AOP_TYPE_AND,
R_ANAL_AOP_TYPE_XOR,
R_ANAL_AOP_TYPE_NOT,
R_ANAL_AOP_TYPE_STORE, /* store from register to memory */
R_ANAL_AOP_TYPE_LOAD, /* load from memory to register */
R_ANAL_AOP_TYPE_LAST
R_ANAL_OP_TYPE_NULL = 0,
R_ANAL_OP_TYPE_JMP, /* mandatory jump */
R_ANAL_OP_TYPE_UJMP, /* unknown jump (register or so) */
R_ANAL_OP_TYPE_CJMP, /* conditional jump */
R_ANAL_OP_TYPE_CALL, /* call to subroutine (branch+link) */
R_ANAL_OP_TYPE_RCALL, /* call to register */
R_ANAL_OP_TYPE_REP, /* repeats next instruction N times */
R_ANAL_OP_TYPE_RET, /* returns from subrutine */
R_ANAL_OP_TYPE_ILL, /* illegal instruction // trap */
R_ANAL_OP_TYPE_UNK, /* unknown opcode type */
R_ANAL_OP_TYPE_NOP, /* does nothing */
R_ANAL_OP_TYPE_MOV, /* register move */
R_ANAL_OP_TYPE_TRAP, /* it's a trap! */
R_ANAL_OP_TYPE_SWI, /* syscall, software interrupt */
R_ANAL_OP_TYPE_UPUSH, /* unknown push of data into stack */
R_ANAL_OP_TYPE_PUSH, /* push value into stack */
R_ANAL_OP_TYPE_POP, /* pop value from stack to register */
R_ANAL_OP_TYPE_CMP, /* copmpare something */
R_ANAL_OP_TYPE_ADD,
R_ANAL_OP_TYPE_SUB,
R_ANAL_OP_TYPE_MUL,
R_ANAL_OP_TYPE_DIV,
R_ANAL_OP_TYPE_SHR,
R_ANAL_OP_TYPE_SHL,
R_ANAL_OP_TYPE_OR,
R_ANAL_OP_TYPE_AND,
R_ANAL_OP_TYPE_XOR,
R_ANAL_OP_TYPE_NOT,
R_ANAL_OP_TYPE_STORE, /* store from register to memory */
R_ANAL_OP_TYPE_LOAD, /* load from memory to register */
R_ANAL_OP_TYPE_LAST
};
/* TODO: what to do with signed/unsigned conditionals? */
enum {
R_ANAL_AOP_COND_EQ,
R_ANAL_AOP_COND_NE,
R_ANAL_AOP_COND_GE,
R_ANAL_AOP_COND_GT,
R_ANAL_AOP_COND_LE,
R_ANAL_AOP_COND_LT,
R_ANAL_OP_COND_EQ,
R_ANAL_OP_COND_NE,
R_ANAL_OP_COND_GE,
R_ANAL_OP_COND_GT,
R_ANAL_OP_COND_LE,
R_ANAL_OP_COND_LT,
};
enum {
@ -125,22 +125,22 @@ struct r_anal_fcn_t {
ut64 to;
};
struct r_anal_t {
int bits;
int big_endian;
ut64 pc;
void *user;
struct r_anal_handle_t *cur;
struct list_head anals;
};
struct r_anal_ctx_t {
/* TODO: add more info here */
/* per opcode deep level */
/* per opcode stack size */
/* basic blocks */
int stacksize;
};
struct r_anal_t {
int bits;
int big_endian;
ut64 pc;
void *user;
struct r_anal_ctx_t *ctx;
struct r_anal_handle_t *cur;
struct list_head anals;
struct r_anal_t *anal;
};
struct r_anal_handle_t {
@ -159,7 +159,7 @@ struct r_anal_t *r_anal_new();
void r_anal_set_user_ptr(struct r_anal_t *anal, void *user);
int r_anal_add(struct r_anal_t *anal, struct r_anal_handle_t *foo);
int r_anal_list(struct r_anal_t *anal);
int r_anal_set(struct r_anal_t *anal, const char *name);
int r_anal_use(struct r_anal_t *anal, const char *name);
int r_anal_set_bits(struct r_anal_t *anal, int bits);
int r_anal_set_big_endian(struct r_anal_t *anal, int boolean);
int r_anal_set_pc(struct r_anal_t *a, ut64 pc);

View File

@ -13,7 +13,6 @@
#define CONS_MAX_USER 102400
#define CONS_BUFSZ 0x4f00
#define STR_IS_NULL(x) (!x || !x[0])
#define IS_PRINTABLE(x) (x>=' '&&x<='~')
R_API void r_cons_break(void (*cb)(void *u), void *user);
R_API void r_cons_break_end();

View File

@ -49,7 +49,7 @@ R_API struct r_reg_t *r_reg_init(struct r_reg_t *reg);
R_API struct r_reg_t *r_reg_new();
R_API int r_reg_set_profile_string(struct r_reg_t *reg, const char *profile);
R_API int r_reg_set_profile(struct r_reg_t *reg, const char *profile);
R_API struct r_reg_item_t *r_reg_get(struct r_reg_t *reg, const char *name);
R_API struct r_reg_item_t *r_reg_get(struct r_reg_t *reg, const char *name, int type);
R_API struct list_head *r_reg_get_list(struct r_reg_t *reg, int type);
R_API int r_reg_type_by_name(const char *str);

View File

@ -42,8 +42,8 @@ struct r_search_t {
int n_kws;
int mode;
ut32 pattern_size;
ut32 string_min;
ut32 string_max;
ut32 string_min; /* min number of matches */
ut32 string_max; /* max number of matches */
void *user; /* user data */
int (*callback)(struct r_search_kw_t *kw, void *user, ut64 where);
//struct r_search_binparse_t *bp;
@ -76,7 +76,7 @@ R_API int r_search_set_blocksize(struct r_search_t *s, ut32 bsize);
// TODO: this is internal API?
R_API int r_search_mybinparse_update(struct r_search_t *s, ut64 from, const ut8 *buf, int len);
R_API int r_search_aes_update(struct r_search_t *s, ut64 from, const ut8 *buf, int len);
R_API int r_search_strings_update_char(const unsigned char *buf, int min, int max, int enc, ut64 offset, const char *match);
R_API int r_search_strings_update(struct r_search_t *s, ut64 from, const ut8 *buf, int len, int enc);
R_API int r_search_regexp_update(struct r_search_t *s, ut64 from, const ut8 *buf, int len);
R_API int r_search_xrefs_update(struct r_search_t *s, ut64 from, const ut8 *buf, int len);

View File

@ -3,6 +3,40 @@
#include "r_types.h"
/* TODO: major refactoring of r_socket */
/* make it work like the rest of apis */
/* struct r_socket_t *sock = r_socket_new(R_SOCKET_TCP, "gogle.com", 80); */
/* struct r_socket_t *sock = r_socket_proc_new(R_SOCKET_PROCESS, "/bin/ls", 80); */
/* process */
struct r_socket_proc_t {
int fd0[2];
int fd1[2];
int pid;
};
R_API struct r_socket_proc_t *r_socket_proc_open(char *const argv[]);
R_API int r_socket_proc_close(struct r_socket_proc_t *sp);
#define r_socket_proc_read(x,y,z) r_socket_read(x->fd1[0],y,z)
#define r_socket_proc_fgets(x,y,z) r_socket_fgets(x->fd1[0],y,z)
#define r_socket_proc_write(x,y,z) r_socket_write(x->fd0[1],y,z)
#define r_socket_proc_printf(x,y) r_socket_printf(x->fd0[1],y)
#define r_socket_proc_ready(x,y,z) r_socket_ready(x->fd1[0],y,z)
// read from stdout of process is fd1[0]
// write to stdin of process is fd0[1]
/* socket */
#if __UNIX__
R_API int r_socket_unix_connect(const char *file);
R_API int r_socket_unix_listen(const char *file);
#endif
R_API int r_socket_flush(int fd);
R_API void r_socket_block(int fd, int block);
R_API int r_socket_ready(int fd, int secs, int usecs);
R_API int r_socket_read(int fd, unsigned char *read, int len);
R_API int r_socket_write(int fd, unsigned char *buf, int len);

View File

@ -73,7 +73,7 @@ static inline int ERR(char *str, ...)
#define MALLOC_STRUCTS(x,y) (x*)malloc(sizeof(x)*y)
#define MALLOC_STRUCT(x) (x*)malloc(sizeof(x))
#define IS_PRINTABLE(x) (x>=' '&&x<='~')
#define IS_WHITESPACE(x) (x==' '&&x=='\t')
#define IS_WHITESPACE(x) (x==' '||x=='\t')
/* operating system */

View File

@ -22,6 +22,7 @@ R_API struct r_mem_pool_t* r_mem_pool_init(struct r_mem_pool_t *pool, int nodesi
R_API struct r_mem_pool_t *r_mem_pool_new(int nodesize, int poolsize, int poolcount);
R_API struct r_mem_pool_t *r_mem_pool_free(struct r_mem_pool_t *pool);
R_API void* r_mem_pool_alloc(struct r_mem_pool_t *pool);
R_API int r_mem_count(ut8 **addr);
/* buf */
struct r_buf_t {
@ -30,6 +31,14 @@ struct r_buf_t {
ut64 base;
};
R_API struct r_buf_t *r_buf_init(struct r_buf_t *b);
R_API struct r_buf_t *r_buf_new();
R_API int r_buf_set_bits(struct r_buf_t *b, int bitoff, int bitsize, ut64 value);
R_API int r_buf_set_bytes(struct r_buf_t *b, ut8 *buf, int length);
R_API int r_buf_read_at(struct r_buf_t *b, ut64 addr, ut8 *buf, int len);
R_API int r_buf_write_at(struct r_buf_t *b, ut64 addr, const ut8 *buf, int len);
R_API void r_buf_free(struct r_buf_t *b);
/* r_cache */
// TOTHINK: move into a separated library?
struct r_cache_item_t {
@ -155,4 +164,5 @@ R_API const char *r_sys_getenv(const char *key);
R_API int r_sys_setenv(const char *key, const char *value, int ow);
R_API char *r_sys_cmd_str(const char *cmd, const char *input, int *len);
#endif

View File

@ -21,3 +21,7 @@ and virtual analysis
TODO:
- add support for sign/unsigned registers..or at least a way to cast them
- add push/pop of register states (maybe we only need 2 levels of states)
- can be used for diffing registers
- is also useful to store values before emulating code
- we can probably implement this in a clone() method? (too heavy? more orgtogonal)

View File

@ -195,13 +195,21 @@ R_API int r_reg_set_profile(struct r_reg_t *reg, const char *profile)
return ret;
}
R_API struct r_reg_item_t *r_reg_get(struct r_reg_t *reg, const char *name)
R_API struct r_reg_item_t *r_reg_get(struct r_reg_t *reg, const char *name, int type)
{
struct list_head *pos;
struct r_reg_item_t *r;
int i;
int i, e;
for(i=0;i<R_REG_TYPE_LAST;i++) {
if (type == -1) {
i = 0;
e = R_REG_TYPE_LAST;
} else {
i = type;
e = type+1;
}
for(;i<e;i++) {
list_for_each(pos, &reg->regset[i].regs) {
r = list_entry(pos, struct r_reg_item_t, list);
if (!strcmp(r->name, name))

View File

@ -4,6 +4,7 @@
#include <r_util.h>
/* XXX: reg get can be accessed using the print_format stuff */
// This is the same as r_buf_set_bits, arenas can be r_buf
R_API ut64 r_reg_get_value(struct r_reg_t *reg, struct r_reg_item_t *item)
{
struct r_reg_set_t *regset;

View File

@ -137,7 +137,7 @@ R_API int r_search_set_callback(struct r_search_t *s, int (*callback)(struct r_s
/* TODO: initialize update callback in _init */
R_API int r_search_update(struct r_search_t *s, ut64 *from, const ut8 *buf, long len)
{
int i, ret = 0;
int ret = 0;
switch(s->mode) {
case R_SEARCH_KEYWORD:
ret += r_search_mybinparse_update(s, *from, buf, len);
@ -153,8 +153,7 @@ R_API int r_search_update(struct r_search_t *s, ut64 *from, const ut8 *buf, long
*from -= R_SEARCH_AES_BOX_SIZE;
break;
case R_SEARCH_STRING:
for(i=0;i<len;i++)
ret += r_search_strings_update_char(buf+i, s->string_min, s->string_max, 0, *(from) +i/*enc*/, "");
ret += r_search_strings_update(s, *from, buf, len, 0);
break;
case R_SEARCH_PATTERN:
//ret += r_search_pattern_update(buf, s->pattern_size

View File

@ -54,27 +54,60 @@ static int is_encoded(int encoding, unsigned char c)
return 0;
}
// XXX last char is lost :(
R_API int r_search_strings_update_char(const ut8 *buf, int min, int max, int enc, ut64 offset, const char *match)
R_API int r_search_strings_update(struct r_search_t *s, ut64 from, const ut8 *buf, int len, int enc)
{
int i = 0;
static int widechar = 0;
static int matches = 0;
int widechar = 0;
int matches = 0;
char str[4096];
#define IS_PRINTABLE(x) (x>=' '&&x<='~')
if (IS_PRINTABLE(buf[i]) || (is_encoded(enc, buf[i]))) {
for (i=0; i<len; i++) {
char ch = buf[i];
if (IS_PRINTABLE(ch) || IS_WHITESPACE(ch) || is_encoded(enc, ch)) {
str[matches] = ch;
if (matches < sizeof(str))
matches++;
} else {
/* wide char check \x??\x00\x??\x00 */
if (matches && buf[i+2]=='\0' && buf[i]=='\0' && buf[i+1]!='\0') {
widechar = 1;
return 1; // widechar
}
/* check if the length fits on our request */
if (matches >= s->string_min && (s->string_max == 0 || matches <= s->string_max)) {
str[matches] = '\0';
int len = strlen(str);
if (len>2) {
if (widechar) {
ut64 off = (ut64)from+i-(len*2)+1;
printf("0x%08llx %3d W %s\n", off, len, str);
} else {
ut64 off = (ut64)from+i-matches;
printf("0x%08llx %3d A %s\n", off, len, str);
}
}
fflush(stdout);
}
matches = 0;
widechar = 0;
}
}
return 0;
}
#if 0
if (matches == 0)
offset += i;
#endif
str[matches] = buf[i];
R_API int r_search_strings_update_char(const ut8 *buf, int min, int max, int enc, ut64 offset, const char *match)
{
static int widechar = 0;
static int matches = 0;
static char str[4096];
if (IS_PRINTABLE(buf[0]) || is_encoded(enc, buf[0])) {
str[matches] = buf[0];
if (matches < sizeof(str))
matches++;
} else {
/* wide char check \x??\x00\x??\x00 */
if (matches && buf[i+2]=='\0' && buf[i]=='\0' && buf[i+1]!='\0') {
if (matches && buf[2]=='\0' && buf[0]=='\0' && buf[1]!='\0') {
widechar = 1;
return 1; // widechar
}
@ -108,30 +141,21 @@ R_API int r_search_strings_update_char(const ut8 *buf, int min, int max, int enc
}
}
#if 0
// XXX THIS IS UGLY AS SHIT
do {
flag = flag_get(msg);
if (flag && flag->offset != (offset-matches))
strcat(msg, "0");
else break;
} while(1);
#endif
printf("f %s @ 0x%08x\n", msg, (unsigned int)offset-matches);
} else
if ((!match) || (match && strstr(str, match)) ){
int len = strlen(str);
if (len>2) {
if (widechar) {
ut64 off = offset-(len*2)+1;
printf("0x%08llx %3d W %s\n", off, len, str);
} else {
printf("0x%08llx %3d A %s\n",
(ut64)offset-matches, len, str); //-matches, len, str);
} else {
if ((!match) || (match && strstr(str, match)) ){
int len = strlen(str);
if (len>2) {
if (widechar) {
ut64 off = offset-(len*2)+1;
printf("0x%08llx %3d W %s\n", off, len, str);
} else {
printf("0x%08llx %3d A %s\n",
(ut64)offset-matches, len, str);
}
}
fflush(stdout);
}
fflush(stdout);
}
}
matches = 0;
@ -139,6 +163,7 @@ R_API int r_search_strings_update_char(const ut8 *buf, int min, int max, int enc
}
return 0;
}
#endif
#if 0
int stripstr_from_file(const char *filename, int min, int max, int encoding, ut64 seek, ut64 limit)

View File

@ -1,10 +1,5 @@
/* radare - LGPL - Copyright 2009 pancake<nopcode.org> */
#if 0
TODO:
support for multiple keywords
#endif
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
@ -22,16 +17,21 @@ static int fd = -1;
static int rad = 0;
struct r_search_t *rs;
static ut64 from = 0LL, to = -1;
static char *str;
static char *mask = "";
static int nonstop = 0;
static int mode = R_SEARCH_KEYWORD;
static int mode = R_SEARCH_STRING;
static ut64 cur = 0;
static ut8 *buffer = NULL;
static char *curfile = NULL;
static ut64 bsize = 4096;
static int hexstr = 0;
static struct r_print_t *pr = NULL;
LIST_HEAD(kws_head);
struct str_t {
char *str;
struct list_head list;
};
static int hit(struct r_search_kw_t *kw, void *user, ut64 addr) {
//const ut8 *buf = (ut8*)user;
@ -39,6 +39,7 @@ static int hit(struct r_search_kw_t *kw, void *user, ut64 addr) {
if (rad) {
printf("f hit%d_%d 0x%08llx ; %s\n", 0, kw->count, addr, curfile);
} else {
if (!kw->count) printf("; %s\n", kw->keyword);
printf("%s: %03d @ 0x%llx\n", curfile, kw->count, addr);
if (pr) {
r_print_hexdump(pr, addr, (ut8*)buffer+delta, 78, 16, R_TRUE);
@ -49,27 +50,31 @@ static int hit(struct r_search_kw_t *kw, void *user, ut64 addr) {
}
static int show_help(char *argv0, int line) {
printf("Usage: %s [-Xnzh] [-f from] [-t to] [-s str] [-z] [-x hex] file ...\n", argv0);
printf("Usage: %s [-Xnzh] [-f from] [-t to] [-z] [-s str] [-x hex] file ...\n", argv0);
if (line) return 0;
printf(
" -z search for zero-terminated strings\n"
" -s [str] search for zero-terminated strings\n"
" -x [hex] search for hexpair string (909090)\n"
" -s [str] search for zero-terminated strings (can be used multiple times)\n"
" -m [str] set a mask\n"
" -x [hex] search for hexpair string (909090) (can be used multiple times)\n"
" -f [from] start searching from address 'from'\n"
" -f [to] stop search at address 'to'\n"
" -X show hexdump of search results\n"
" -n do not stop on read errors\n"
" -r print using radare commands\n"
" -b set block size\n"
" -h show this help\n"
" -V print version and exit\n"
);
return 0;
}
int radiff_open(char *file)
int rafind_open(char *file)
{
int ret, last = 0;
struct list_head *pos;
r_io_init(&io);
// TODO: add support for multiple files
fd = r_io_open(&io, file, R_IO_READ, 0);
if (fd == -1) {
fprintf(stderr, "Cannot open file '%s'\n", file);
@ -84,9 +89,13 @@ int radiff_open(char *file)
to = r_io_size(&io, fd);
}
if (mode == R_SEARCH_KEYWORD) {
if (hexstr)
r_search_kw_add_hex(rs, str, mask);
else r_search_kw_add(rs, str, mask);
list_for_each(pos, &(kws_head)) {
struct str_t *kw = list_entry(pos, struct str_t, list);
if (hexstr)
r_search_kw_add_hex(rs, kw->str, mask);
else r_search_kw_add(rs, kw->str, mask);
free(kw);
}
}
curfile = file;
r_search_begin(rs);
@ -115,7 +124,10 @@ int main(int argc, char **argv)
{
int c;
while ((c = getopt(argc, argv, "s:x:Xzf:t:rnhV")) != -1) {
while ((c = getopt(argc, argv, "b:m:s:x:Xzf:t:rnhV")) != -1) {
struct str_t *kw = MALLOC_STRUCT(struct str_t);
INIT_LIST_HEAD(&(kw->list));
switch(c) {
case 'r':
rad = 1;
@ -125,8 +137,9 @@ int main(int argc, char **argv)
break;
case 's':
mode = R_SEARCH_KEYWORD;
str = optarg;
hexstr = 0;
kw->str = optarg;
list_add(&(kw->list), &(kws_head));
break;
case 'b':
bsize = r_num_math(NULL, optarg);
@ -137,7 +150,8 @@ int main(int argc, char **argv)
case 'x':
mode = R_SEARCH_KEYWORD;
hexstr = 1;
str = optarg;
kw->str = optarg;
list_add(&(kw->list), &(kws_head));
break;
case 'm':
// XXX should be from hexbin
@ -164,7 +178,7 @@ int main(int argc, char **argv)
return show_help(argv[0], 1);
for (;optind < argc;optind++)
radiff_open(argv[optind]);
rafind_open(argv[optind]);
return 0;
}

View File

@ -1,5 +1,5 @@
NAME=r_socket
OBJ=socket.o
OBJ=socket.o proc.o
# on solaris only
#LINK=-lsocket
#LINK=-L../cons -lr_cons -Wl,-R../cons

62
libr/socket/proc.c Normal file
View File

@ -0,0 +1,62 @@
/* XXX : move to r_util??? rename method names.. to long? */
/* proc IO is not related to socket io.. this is shitty!! */
#include <r_socket.h>
#include <signal.h>
#if __UNIX__
#include <sys/wait.h>
#endif
R_API struct r_socket_proc_t *r_socket_proc_open(char *const argv[])
{
#if __UNIX__
struct r_socket_proc_t *sp = MALLOC_STRUCT(struct r_socket_proc_t);
int flags = O_CLOEXEC; //O_NONBLOCK|O_CLOEXEC;
if (pipe2(sp->fd0, flags)==-1) {
perror("pipe");
free(sp);
return NULL;
}
if (pipe2(sp->fd1, flags)==-1) {
perror("pipe");
free(sp);
return NULL;
}
sp->pid = fork();
switch(sp->pid) {
case 0:
close(0);
dup2(sp->fd0[0], 0);
close(1);
dup2(sp->fd1[1], 1);
execv(argv[0], argv);
exit(1);
case -1:
perror("fork");
r_socket_proc_close(sp);
free(sp);
break;
//r_socket_block(sp, R_FALSE);
}
return sp;
#endif
}
R_API int r_socket_proc_close(struct r_socket_proc_t *sp)
{
#if __UNIX__
/* this is wrong */
kill(sp->pid, 9);
waitpid(sp->pid, NULL, 0); //WNOHANG);
close(sp->fd0[0]);
close(sp->fd0[1]);
//close(sp->fd1[0]);
close(sp->fd1[1]);
//sp->fd[0] = -1;
//sp->fd[1] = -1;
#endif
return 0;
}

View File

@ -1,4 +1,5 @@
/* radare - LGPL - Copyright 2006-2009 pancake<nopcode.org> */
#define USE_SOCKETS
#include <errno.h>
@ -49,7 +50,7 @@ R_API int r_socket_write(int fd, unsigned char *buf, int len)
// XXX: rewrite it to use select //
/* waits secs until new data is received. */
/* returns -1 on error, 0 is false, 1 is true */
R_API int r_socket_ready(int fd, int secs,int usecs)
R_API int r_socket_ready(int fd, int secs, int usecs)
{
int ret;
#if __UNIX__
@ -110,7 +111,7 @@ R_API void r_socket_printf(int fd, const char *fmt, ...)
}
#if __UNIX__
R_API int r_socket_unix_connect(char *file)
R_API int r_socket_unix_connect(const char *file)
{
struct sockaddr_un addr;
int sock;
@ -191,14 +192,12 @@ R_API int r_socket_connect(char *host, int port)
if (connect(s, (const struct sockaddr*)&sa, sizeof(struct sockaddr)))
return -1;
return s;
}
R_API int r_socket_listen(int port)
{
int s;
int ret;
int ret, s;
struct sockaddr_in sa;
struct linger linger = { 0 };
linger.l_onoff = 1;
@ -223,7 +222,6 @@ R_API int r_socket_listen(int port)
ret = listen(s, 1);
if (ret < 0)
return -1;
return s;
}
@ -258,7 +256,6 @@ R_API int r_socket_flush(int fd)
return 0;
}
R_API int r_socket_fgets(int fd, char *buf, int size)
{
int i = 0;
@ -267,21 +264,23 @@ R_API int r_socket_fgets(int fd, char *buf, int size)
if (fd == -1)
return -1;
while(i<size-1) {
while(i<size) {
ret = r_socket_read(fd, (ut8 *)buf+i, 1);
if (ret==0)
return -1;
break;
if (ret<0) {
r_socket_close(fd);
return -1;
}
if (buf[i]=='\r'||buf[i]=='\n')
if (buf[i]=='\r'||buf[i]=='\n') {
buf[i]='\0';
break;
i+=ret;
}
i += ret;
}
buf[i]='\0';
return ret;
return i;
}
R_API char *r_socket_to_string(int fd)

26
libr/socket/t/test.c Normal file
View File

@ -0,0 +1,26 @@
#include <r_socket.h>
int main()
{
int ret;
struct r_socket_proc_t *sp;
char buf[256];
char *const args[4] = { "/usr/bin/telnet", "localhost", "9999", 0 };
sp = r_socket_proc_open(args);
// ret = r_socket_proc_read(sp, buf, 128);
r_socket_proc_printf(sp, "GET / HTTP/1.1\r\n\r\n");
printf("Waiting...\n");
for(;;){
if (!r_socket_proc_ready(sp, 0,0))
break;
ret = r_socket_proc_fgets(sp, buf, 128);
printf("RET=%d\n", ret);
if (ret<0) break;
else if (ret>0)
printf("%d=\"%s\"\n", ret, buf);
else printf("--\n");
}
r_socket_proc_close(sp);
return 0;
}

23
libr/socket/t/testls.c Normal file
View File

@ -0,0 +1,23 @@
#include <r_socket.h>
int main()
{
int ret;
struct r_socket_proc_t *sp;
char buf[256];
char *const args[3] = { "/bin/ls", "-l", 0 };
sp = r_socket_proc_open(args);
// ret = r_socket_proc_read(sp, buf, 128);
for(;;){
if (!r_socket_proc_ready(sp, 0,0))
break;
ret = r_socket_proc_fgets(sp, buf, 128);
if (ret>0)
printf("%d=\"%s\"\n", ret, buf);
else break;
}
r_socket_proc_close(sp);
return 0;
}

View File

@ -5,10 +5,12 @@ PFX=$2
LIST=$1.list
if [ "${PFX}" = "r_util" ]; then
echo "=> No stripping any symbol in libr_util O:)"
exit 0
PFX="r_"
fi
nm --defined-only -B ${FILE} 2>/dev/null | grep -v ${PFX}_ | awk '{print $3}' > ${LIST}
nm --defined-only -B ${FILE} 2>/dev/null | grep -v ^${PFX}_ | awk '{print $3}' > ${LIST}
#if [ -n "`cat /tmp/list`" ]; then
echo "=> Stripping unnecessary symbols for ${FILE}..."
objcopy --strip-symbols ${LIST} ${FILE} 2>/dev/null

View File

@ -3,11 +3,24 @@
#include "r_types.h"
#include "r_util.h"
#if 0
/* TODO: the basic object lifecycle must be simplified */
struct r_class_t {
// new/free are implicit
.init = &r_buf_init,
.fini = &r_buf_fini,
} r_buf_class;
#define r_buf_init(x) r_buf_class->init
#endif
R_API struct r_buf_t *r_buf_init(struct r_buf_t *b)
{
b->length = 0;
b->base = 0LL;
b->buf = NULL;
if (b) {
b->length = 0;
b->base = 0LL;
b->buf = NULL;
}
return b;
}
@ -17,16 +30,24 @@ R_API struct r_buf_t *r_buf_new()
return r_buf_init(b);
}
R_API void r_buf_set_bytes(struct r_buf_t *b, ut8 *buf, int length)
R_API int r_buf_set_bits(struct r_buf_t *b, int bitoff, int bitsize, ut64 value)
{
// TODO: implement r_buf_set_bits
// TODO: get the implementation from reg/value.c ?
}
R_API int r_buf_set_bytes(struct r_buf_t *b, ut8 *buf, int length)
{
free(b->buf);
b->buf = malloc(length);
if (b->buf == NULL)
return R_FALSE;
memcpy(b->buf, buf, length);
b->length = length;
return R_TRUE;
}
static int r_buf_memcpy(struct r_buf_t *b, ut64 addr, ut8 *dst, ut8 *src, int len)
{
static int r_buf_memcpy(struct r_buf_t *b, ut64 addr, ut8 *dst, ut8 *src, int len) {
int end;
addr -= b->base;
if (addr > b->length)
@ -43,13 +64,18 @@ R_API int r_buf_read_at(struct r_buf_t *b, ut64 addr, ut8 *buf, int len)
return r_buf_memcpy(b, addr, buf, b->buf, len);
}
R_API int r_buf_write_at(struct r_buf_t *b, ut64 addr, ut8 *buf, int len)
R_API int r_buf_write_at(struct r_buf_t *b, ut64 addr, const ut8 *buf, int len)
{
return r_buf_memcpy(b, addr, b->buf, buf, len);
}
R_API void r_buf_free(struct r_buf_t *b)
R_API void r_buf_deinit(struct r_buf_t *b)
{
free(b->buf);
}
R_API void r_buf_free(struct r_buf_t *b)
{
r_buf_deinit(b);
free(b);
}

View File

@ -2,24 +2,24 @@
#include <r_util.h>
void r_cache_init(struct r_cache_t *c)
R_API void r_cache_init(struct r_cache_t *c)
{
INIT_LIST_HEAD(&c->items);
}
struct r_cache_t *r_cache_new()
R_API struct r_cache_t *r_cache_new()
{
struct r_cache_t *a = MALLOC_STRUCT(struct r_cache_t);
r_cache_init(a);
return a;
}
void r_cache_free(struct r_cache_t *a)
R_API void r_cache_free(struct r_cache_t *a)
{
free(a);
}
char *r_cache_get(struct r_cache_t *c, ut64 addr)
R_API char *r_cache_get(struct r_cache_t *c, ut64 addr)
{
struct list_head *pos;
list_for_each_prev(pos, &c->items) {
@ -30,7 +30,7 @@ char *r_cache_get(struct r_cache_t *c, ut64 addr)
return NULL;
}
int r_cache_set(struct r_cache_t *c, ut64 addr, char *str)
R_API int r_cache_set(struct r_cache_t *c, ut64 addr, char *str)
{
struct r_cache_item_t *a = MALLOC_STRUCT(struct r_cache_item_t);
a->addr = addr;
@ -39,7 +39,7 @@ int r_cache_set(struct r_cache_t *c, ut64 addr, char *str)
return R_TRUE;
}
int r_cache_validate(struct r_cache_t *c, ut64 from, ut64 to)
R_API int r_cache_validate(struct r_cache_t *c, ut64 from, ut64 to)
{
int ret = R_FALSE;
struct list_head *pos, *n;
@ -55,7 +55,7 @@ int r_cache_validate(struct r_cache_t *c, ut64 from, ut64 to)
return ret;
}
int r_cache_invalidate(struct r_cache_t *c, ut64 from, ut64 to)
R_API int r_cache_invalidate(struct r_cache_t *c, ut64 from, ut64 to)
{
int ret = R_FALSE;
struct list_head *pos, *n;

View File

@ -3,7 +3,16 @@
#include <r_util.h>
#include <stdlib.h>
R_API void r_mem_copyloop (ut8 *dest, const ut8 *orig, int dsize, int osize)
// TODO: find better name
R_API int r_mem_count(ut8 **addr)
{
int i = 0;
while(*addr++)
i++;
return i;
}
R_API void r_mem_copyloop(ut8 *dest, const ut8 *orig, int dsize, int osize)
{
int i=0,j;
while(i<dsize)

View File

@ -6,6 +6,10 @@
#define ALLOC_POOL_SIZE 1024
#define ALLOC_POOL_COUNT 128
// TODO: add api to serialize/deserialize memory pools from/to disk
// This can be useful when the application is swapping (userland swapping?)
// Do user-swapping takes sense?
R_API struct r_mem_pool_t* r_mem_pool_deinit(struct r_mem_pool_t *pool)
{
int i;
@ -51,6 +55,8 @@ R_API void* r_mem_pool_alloc(struct r_mem_pool_t *pool)
return NULL;
}
pool->nodes[pool->npool] = malloc (pool->nodesize*pool->poolsize);
if (pool->nodes[pool->npool] == NULL)
return NULL;
pool->ncount = 0;
}
// TODO: fix warning

View File

@ -197,11 +197,11 @@ R_API char *r_str_trim(char *str)
}
/* memccmp("foo.bar", "foo.cow, '.') == 0 */
R_API int r_str_ccmp(const char *dst, const char *orig, int ch)
R_API int r_str_ccmp(const char *dst, const char *src, int ch)
{
int i;
for(i=0;orig[i] && orig[i] != ch; i++)
if (dst[i] != orig[i])
for(i=0;src[i] && src[i] != ch; i++)
if (dst[i] != src[i])
return 1;
return 0;
}
@ -217,11 +217,11 @@ R_API int r_str_cmp(const char *a, const char *b, int len)
return 0;
}
R_API int r_str_ccpy(char *dst, char *orig, int ch)
R_API int r_str_ccpy(char *dst, char *src, int ch)
{
int i;
for(i=0;orig[i] && orig[i] != ch; i++)
dst[i] = orig[i];
for(i=0;src[i] && src[i] != ch; i++)
dst[i] = src[i];
dst[i] = '\0';
return i;
}

View File

@ -1,10 +1,14 @@
#include <r_util.h>
char *buf[] = { "eax", "ebx", "ecx", NULL };
int main()
{
struct r_mem_pool_t *pool = r_mem_pool_new(128, 0, 0);
void *foo = r_mem_pool_alloc(pool);
foo = r_mem_pool_alloc(pool);
printf("%d\n", r_mem_count(buf));
r_mem_pool_free(pool);
}

View File

@ -1,4 +1,4 @@
/* radare - LGPL - Copyright 2009 nibble<.ds@gmail.com> */
/* radare - LGPL - Copyright 2009 pancake<@nopcode.org> */
[CCode (cheader_filename="r_reg.h", cprefix="r_reg_", lower_case_cprefix="r_reg_")]
namespace Radare {
@ -46,7 +46,7 @@ namespace Radare {
public Register();
public bool set_profile(string file);
public bool set_profile_string(string profile);
public Register.Item get(string name);
public Register.Item get(string name, int type = -1);
public Radare.List<Register.Item*> get_list(Register.Type type);
public uint64 get_value(Register.Item item);

View File

@ -1,5 +1,7 @@
/* This vapi has been manually generated by me */
/* TODO: Add simpletype instead of int so, we can use it as an object */
[CCode (cheader_filename="r_socket.h", cprefix="r_socket", lower_case_cprefix="r_socket_")]
namespace Radare.Socket {
public bool ready(int fd, int secs, int usecs);
@ -14,5 +16,11 @@ namespace Radare.Socket {
public int fgets(int fd, string *buf, int len);
public int printf(int fd, string *str, ...);
public int accept(int fd);
public string to_string(int fd);
public void block(int fd, bool blocking);
public bool flush(int fd);
public owned string to_string(int fd);
/* UNIX ONLY */
public int unix_connect(string file);
public int unix_listen(string file);
}

View File

@ -25,10 +25,19 @@ namespace Radare {
public int hash(string str);
}
[CCode (cprefix="r_log")]
public static class Log {
public bool msg(string str);
public bool err(string str);
}
[CCode (cprefix="r_buf")]
public class Buffer {
public Buffer();
public int read(uint64 addr, uint8 *buf, int len);
public int read_at(uint64 addr, uint8 *buf, int len);
public int write_at(uint64 addr, uint8 *buf, int len);
public bool set_bytes(uint8 *buf, int len);
public bool memcpy(uint64 addr, uint8 *dst, uint8 *src, int len);
/* ... */
}
@ -89,4 +98,6 @@ namespace Radare {
[CCode (cname="rarray_iterator")] //, generic_type_pos=2)]
public Rarray<G> iterator();
}
}