├── LICENSE ├── Makefile ├── README.md ├── TESTING ├── TODO ├── build └── test │ ├── debug.sh │ └── memcry.gdb ├── media ├── memcry.png ├── overview.png └── resources │ ├── COLOURS │ ├── memcry.xcf │ ├── overview.drawio │ └── phage.png └── src ├── lib ├── Makefile ├── debug.h ├── error.c ├── error.h ├── iface.c ├── iface.h ├── krncry.h ├── krncry_iface.c ├── krncry_iface.h ├── map.c ├── map.h ├── map_util.c ├── map_util.h ├── memcry.h ├── procfs_iface.c ├── procfs_iface.h ├── util.c └── util.h └── test ├── Makefile ├── check_krncry_iface.c ├── check_map.c ├── check_map_util.c ├── check_procfs_iface.c ├── check_util.c ├── iface_helper.c ├── iface_helper.h ├── info.c ├── info.h ├── main.c ├── map_helper.c ├── map_helper.h ├── suites.c ├── suites.h ├── target ├── Makefile └── unit_target.c ├── target_helper.c └── target_helper.h /Makefile: -------------------------------------------------------------------------------- 1 | .RECIPEPREFIX:=> 2 | 3 | 4 | #[set as required] 5 | INSTALL_DIR=/usr/local/lib 6 | INCLUDE_INSTALL_DIR=/usr/local/include 7 | LD_DIR=/etc/ld.so.conf.d 8 | 9 | CC=gcc 10 | CFLAGS= 11 | CFLAGS_TEST=-ggdb3 -O0 12 | WARN_OPTS=-Wall -Wextra 13 | LDFLAGS=-lcmore 14 | 15 | 16 | #[build constants] 17 | LIB_DIR=./src/lib 18 | TEST_DIR=./src/test 19 | BUILD_DIR=${shell pwd}/build 20 | PACKAGE_DIR=./package 21 | 22 | 23 | #[installation constants] 24 | SHARED=libmcry.so 25 | STATIC=libmcry.a 26 | HEADER=memcry.h 27 | 28 | 29 | #[set build options] 30 | ifeq ($(build),debug) 31 | CFLAGS += -O0 -ggdb3 -fsanitize=address -DDEBUG 32 | CFLAGS_TEST += -DDEBUG 33 | LDFLAGS += -static-libasan 34 | else 35 | CFLAGS += -O3 -flto 36 | endif 37 | 38 | 39 | #[set static analysis options] 40 | ifeq ($(fanalyzer),true) 41 | CFLAGS += -fanalyzer 42 | endif 43 | 44 | 45 | #[process targets] 46 | .PHONY prepare: 47 | > mkdir -p ${BUILD_DIR}/test ${BUILD_DIR}/lib ${PACKAGE_DIR} 48 | 49 | test: shared 50 | > $(MAKE) -C ${TEST_DIR} tests CC='${CC}' _CFLAGS='${CFLAGS_TEST}' \ 51 | _WARN_OPTS='${WARN_OPTS}' \ 52 | BUILD_DIR='${BUILD_DIR}/test' \ 53 | LIB_BIN_DIR='${BUILD_DIR}/lib' 54 | 55 | all: shared static 56 | 57 | shared: 58 | > $(MAKE) -C ${LIB_DIR} shared CC='${CC}' _CFLAGS='${CFLAGS} -fPIC' \ 59 | _WARN_OPTS='${WARN_OPTS}' \ 60 | _LDFLAGS='${LDFLAGS}' \ 61 | BUILD_DIR='${BUILD_DIR}/lib' 62 | 63 | static: 64 | > $(MAKE) -C ${LIB_DIR} static CC='${CC}' _CFLAGS='${CFLAGS}' \ 65 | _WARN_OPTS='${WARN_OPTS}' \ 66 | _LDFLAGS='${LDFLAGS}' \ 67 | BUILD_DIR='${BUILD_DIR}/lib' 68 | 69 | clean: 70 | > $(MAKE) -C ${TEST_DIR} clean BUILD_DIR='${BUILD_DIR}/test' 71 | > $(MAKE) -C ${LIB_DIR} clean BUILD_DIR='${BUILD_DIR}/lib' 72 | > -rm ${PACKAGE_DIR}/* 73 | 74 | install: 75 | > mkdir -pv ${INSTALL_DIR} 76 | > cp -v ${BUILD_DIR}/lib/${SHARED} ${INSTALL_DIR} 77 | > cp -v ${BUILD_DIR}/lib/${STATIC} ${INSTALL_DIR} 78 | > mkdir -pv ${INCLUDE_INSTALL_DIR} 79 | > cp -v ${LIB_DIR}/${HEADER} ${INCLUDE_INSTALL_DIR} 80 | > echo "${INSTALL_DIR}" > ${LD_DIR}/90memcry.conf 81 | > ldconfig 82 | 83 | uninstall: 84 | > -rm -v ${INSTALL_DIR}/{${SHARED},${STATIC}} 85 | > -rm -v ${INCLUDE_INSTALL_DIR}/${HEADER} 86 | > -rm ${LD_DIR}/90memcry.conf 87 | > ldconfig 88 | 89 | package: all 90 | > -cp ${BUILD_DIR}/lib/${SHARED} ${PACKAGE_DIR} 91 | > -cp ${BUILD_DIR}/lib/${STATIC} ${PACKAGE_DIR} 92 | > -cp ${LIB_DIR}/memcry.h ${PACKAGE_DIR} 93 | > -tar cvjf ${PACKAGE_DIR}/memcry.tar.bz2 ${PACKAGE_DIR}/* 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MemCry 2 | 3 |

4 | 5 |

6 | 7 | 8 | ### ABOUT: 9 | 10 | **The MemCry Library provides**: 11 | 12 | - Graph-like data structure (*map*) for representing the memory map of a target process. 13 | - The ability to update the *map* as the target's memory mappings change without invalidating pointers to said map. 14 | - Tracking of (assumed) ownership of unnamed *vm_area*s. 15 | - Support for **multiple interfaces** for acquiring the memory maps, reading and writing memory. 16 | - Multiple convenient utilities. 17 | 18 |

19 | 20 |

21 | 22 | See the example below. Feel free to contact me on discord: *@vykt* or email: *vykt[at]disroot[dot]org* 23 | 24 | 25 | ### DEPENDENCIES: 26 | 27 | If you're not using a packaged release, you'll need to install: 28 | 29 | - [CMore](https://github.com/vykt/cmore) - Data structures for C. 30 | 31 | 32 | ### EXAMPLE: 33 | 34 | ```c 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | 42 | 43 | int main() { 44 | 45 | int ret; 46 | 47 | 48 | /* 49 | * First, find the PID of the target based on the target's name. You can 50 | * optionally pass a pointer to an uninitialised CMore vector if you want 51 | * to find PIDs of multiple processes with the same name. 52 | */ 53 | pid_t pid; 54 | pid = mc_pid_by_name("target_name", NULL); 55 | 56 | 57 | /* 58 | * Open a session on your target. For the procfs interface, this will 59 | * open file descriptors on /proc/pid/{mem,maps} 60 | */ 61 | mc_session s; 62 | ret = mc_open(&s, PROCFS, pid); 63 | if (ret != 0) { 64 | /* on error, a perror() function is provided */ 65 | mc_perror("[error]"); 66 | } 67 | 68 | 69 | /* 70 | * Read the target's memory map for the first time. 71 | */ 72 | mc_vm_map m; 73 | ret = mc_update_map(&s, &m); 74 | 75 | 76 | /* 77 | * Find the "libfoo.so" object in the target. 78 | */ 79 | cm_lst_node * libfoo_node = NULL; 80 | libfoo_node = mc_get_obj_node_by_basename(&m, "libfoo.so"); 81 | if (libfoo_node == NULL) {/*...*/} 82 | 83 | 84 | /* 85 | * Print libfoo.so's starting address. 86 | */ 87 | mc_vm_obj * libfoo_obj = MC_GET_NODE_OBJ(libfoo_node); 88 | printf("libfoo.so start addr: 0x%lx, end addr: 0x%lx\n", 89 | libfoo_obj->start_addr, libfoo_obj->end_addr); 90 | 91 | 92 | /* 93 | * Print the full path of the object after libfoo.so. 94 | */ 95 | cm_lst_node * next_node = libfoo_node->next; 96 | mc_vm_obj * next_obj = MC_GET_NODE_OBJ(next_node); 97 | printf("after libfoo.so: %s\n", next_obj->pathname); 98 | 99 | 100 | /* 101 | * Get the first area of libfoo.so. The object of libfoo (libfoo_obj) 102 | * stores pointers to area nodes. 103 | */ 104 | cm_lst_node * area_node_p = libfoo_obj->vm_area_node_ps.head; 105 | cm_lst_node * area_node = MC_GET_NODE_PTR(area_node_p); 106 | mc_vm_area * area = MC_GET_NODE_AREA(area_node); 107 | printf("is first area writable?: %d\n, area->access & MC_ACCESS_WRITE); 108 | 109 | 110 | /* 111 | * Get the next area and print its address range. 112 | */ 113 | mc_vm_area * next_area = MC_GET_NODE_AREA(area_node->next); 114 | printf("next area start addr: 0x%lx, end addr: 0x%lx\n", 115 | next_area->start_addr, next_area->end_addr); 116 | 117 | 118 | /* 119 | * The target's (OS-wide) memory map may have been updated; we should update 120 | * our local map. 121 | */ 122 | ret = mc_update_map(&s, &m); 123 | if (ret != 0) {/*...*/} 124 | 125 | 126 | /* 127 | * Check if libfoo.so is still mapped. If not, fetch the next mapped 128 | * object. Even if libfoo.so and its constituent areas have been unmapped, 129 | * their nodes and object pointers will remain valid. 130 | */ 131 | cm_lst_node * iter_node; 132 | mc_vm_obj * iter_obj; 133 | if (libfoo_obj->mapped == false) { 134 | iter_node = libfoo_node->next; 135 | while (iter_node != m.vm_objs.head) { 136 | iter_obj = MC_GET_NODE_OBJ(iter_node); 137 | if (iter_obj->mapped == true) break; 138 | } 139 | } 140 | 141 | 142 | /* 143 | * Clean up unmapped objects & areas. This will cause `libfoo_node` and 144 | * `libfoo_obj` pointers to become invalid. 145 | */ 146 | ret = mc_map_clean_unmapped(&m); 147 | if (ret != 0) {/*...*/} 148 | 149 | 150 | /* 151 | * Clean up and exit. 152 | */ 153 | ret = mc_close(&s); 154 | if (ret != 0) {/*...*/} 155 | 156 | ret = mc_del_vm_map(&m); 157 | if (ret != 0) {/*...*/} 158 | 159 | return 0; 160 | } 161 | ``` 162 | -------------------------------------------------------------------------------- /TESTING: -------------------------------------------------------------------------------- 1 | [Summary]: 2 | 3 | TL;DR: Inspect failing unit tests with GDB. 4 | 5 | Build the unit tests with `make test [build=debug]`. This produces a 6 | `test` executable in `$PROJROOT/build/test`. To run tests for a given 7 | component, pass one (or more) of these flags: 8 | 9 | -m : Core map data structure. 10 | -p : `procfs` interface. 11 | -k : `krncry` interface. 12 | -n : Map utilities. 13 | -u : Generic utilities. 14 | 15 | When debugging with GDB (see whole document), use the `debug.sh` 16 | script in `$PROJROOT/build/test/debug.sh`. 17 | 18 | 19 | [Unit tests & ASAN]: 20 | 21 | Unit tests use the 'Check' library: 22 | 23 | https://libcheck.github.io/check/ 24 | 25 | Check forks off new processes for each unit test. This can be undesirable 26 | because debug builds of MemCry compile with GCC's address sanitizer, 27 | which will fail to report memory leaks if they occur in a child process. 28 | 29 | Check will not fork new processes if the environment's `CK_FORK` 30 | variable is set to `no`. 31 | 32 | 33 | [GDB scripts (essential)]: 34 | 35 | MemCry's datastructures are very tedious to navigate with raw gdb. 36 | Instead of writing 50 character long casts, make use these gdb scripts 37 | defined in `$PROJROOT/build/test/init.gdb`: 38 | 39 | pmapa 40 | 41 | Pretty print all areas inside `m`. 42 | 43 | pmapo 44 | 45 | Pretty print all objects of a map `m` and their constituent 46 | areas. 47 | 48 | pmapua 49 | 50 | Pretty print all unmapped areas inside `m` 51 | 52 | pmapuo 53 | 54 | Pretty print all unmapped objects of a map `m` and their 55 | constituent areas. 56 | 57 | parean * n> 58 | 59 | Dump a `mc_vm_area` held by a list node `cm_lst_node`. 60 | 61 | pxarean * n> 62 | 63 | Dump (hex) a `mc_vm_area` held by a list node `cm_lst_node` 64 | 65 | pobjn * n> 66 | 67 | Dump a `mc_vm_obj` held by a list node `cm_lst_node`. 68 | 69 | pxobjn * n> 70 | 71 | Dump (hex) a `mc_vm_obj` held by a list node `cm_lst_node`. 72 | 73 | pnode * n> 74 | 75 | Dump a nested list node held by another list node. 76 | 77 | pnodea *> * n> 78 | 79 | `mc_vm_obj` stores a list of pointers to its constituent area 80 | nodes. Use `pnodea` to easily print areas in this list. 81 | 82 | pxnodea *> * n> 83 | 84 | See above. 85 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | [features]: 2 | - add 3rd interface that uses process_vm_readv/process_vm_writev 3 | -------------------------------------------------------------------------------- /build/test/debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | gdb -x memcry.gdb --args ./test -p "$@" 3 | -------------------------------------------------------------------------------- /build/test/memcry.gdb: -------------------------------------------------------------------------------- 1 | # ask libcheck to not fork each unit test when GDB is attached 2 | set environment CK_FORK=no 3 | 4 | # print vm_area at a node 5 | define parean 6 | if $argc != 1 7 | printf "Use: parean <*area_node>\n" 8 | else 9 | p *((mc_vm_area *) ($arg0->data)) 10 | end 11 | end 12 | 13 | # hex print vm_area at a node 14 | define pxarean 15 | if $argc != 1 16 | printf "Use: parean <*area_node>\n" 17 | else 18 | p/x *((mc_vm_area *) ($arg0->data)) 19 | end 20 | end 21 | 22 | # print vm_obj at a node 23 | define pobjn 24 | if $argc != 1 25 | printf "Use: pobjn <*obj_node>\n" 26 | else 27 | p *((mc_vm_obj *) ($arg0->data)) 28 | end 29 | end 30 | 31 | # hex print vm_obj at a node 32 | define pxobjn 33 | if $argc != 1 34 | printf "Use: pobjn <*obj_node>\n" 35 | else 36 | p/x *((mc_vm_obj *) ($arg0->data)) 37 | end 38 | end 39 | 40 | # print a node at the specified node 41 | define pnode 42 | if $argc != 1 43 | printf "Use: pnode <*obj_area_node>\n" 44 | else 45 | p *(*((cm_lst_node **) ($arg0->data))) 46 | end 47 | end 48 | 49 | # print the area at a node at the specified node: 50 | define pnodea 51 | if $argc != 1 52 | printf "Use: pnodea <*obj_area_node>\n" 53 | else 54 | p *((mc_vm_area *) ((*((cm_lst_node **) ($arg0->data)))->data)) 55 | end 56 | end 57 | 58 | # hex print the area at a node at the specified node: 59 | define pxnodea 60 | if $argc != 1 61 | printf "Use: pnodea <*obj_area_node>\n" 62 | else 63 | p/x *((mc_vm_area *) ((*((cm_lst_node **) ($arg0->data)))->data)) 64 | end 65 | end 66 | 67 | # print the object at a node at the specified node: 68 | define pnodeo 69 | if $argc != 1 70 | printf "Use: pnodea <*obj_area_node>\n" 71 | else 72 | p *((mc_vm_obj *) ((*((cm_lst_node **) ($arg0->data)))->data)) 73 | end 74 | end 75 | 76 | # hex print the object at a node at the specified node: 77 | define pxnodeo 78 | if $argc != 1 79 | printf "Use: pnodea <*obj_area_node>\n" 80 | else 81 | p/x *((mc_vm_obj *) ((*((cm_lst_node **) ($arg0->data)))->data)) 82 | end 83 | end 84 | 85 | # print areas of a map 86 | define pmapa 87 | if $argc != 1 88 | printf "Use: pmapa <*map>\n" 89 | else 90 | # print header 91 | printf " --- [AREAS] ---\n" 92 | 93 | # bootstrap iteration 94 | set $iter = 0 95 | set $iter_node = $arg0->vm_areas.head 96 | 97 | # for every area 98 | while $iter != $arg0->vm_areas.len 99 | 100 | # fetch & typecast next area 101 | set $area = ((mc_vm_area *) ($iter_node->data)) 102 | 103 | # fetch relevant entries for this area 104 | set $id = $area->id 105 | set $basename = $area->basename 106 | set $start_addr = $area->start_addr 107 | set $end_addr = $area->end_addr 108 | 109 | # fetch area's object id if one is present 110 | if $area->obj_node_p != 0 111 | set $obj_id = ((mc_vm_obj *) ($area->obj_node_p->data))->id 112 | else 113 | set $obj_id = -1337 114 | end 115 | 116 | # fetch area's last object i if one is present 117 | if $area->last_obj_node_p != 0 118 | set $last_obj_id = ((mc_vm_obj *) ($area->last_obj_node_p->data))->id 119 | else 120 | set $last_obj_id = -1337 121 | end 122 | 123 | # print relevant entries of this area 124 | printf "%-3d: 0x%-12lx - 0x%-12lx | obj: %-5d - last_obj: %-5d | \"%s\"\n", $id, $start_addr, $end_addr, $obj_id, $last_obj_id, $basename 125 | 126 | # advance iteration 127 | set $iter = $iter + 1 128 | set $iter_node = $iter_node->next 129 | end 130 | end 131 | end 132 | 133 | # print unmapped areas of a map 134 | define pmapua 135 | if $argc != 1 136 | printf "Use: pmapua <*map>\n" 137 | else 138 | # print header 139 | printf " --- [UNMAPPED AREAS] ---\n" 140 | 141 | # bootstrap iteration 142 | set $iter = 0 143 | set $iter_node = $arg0->vm_areas_unmapped.head 144 | 145 | # for every area 146 | while $iter != $arg0->vm_areas_unmapped.len 147 | 148 | # fetch & typecast next area 149 | set $area = ((mc_vm_area *) ((*((cm_lst_node **) ($iter_node->data)))->data)) 150 | 151 | # fetch relevant entries for this area 152 | set $id = $area->id 153 | set $basename = $area->basename 154 | set $start_addr = $area->start_addr 155 | set $end_addr = $area->end_addr 156 | 157 | # fetch area's object id if one is present 158 | if $area->obj_node_p != 0 159 | set $obj_id = ((mc_vm_obj *) ($area->obj_node_p->data))->id 160 | else 161 | set $obj_id = -1337 162 | end 163 | 164 | # fetch area's last object i if one is present 165 | if $area->last_obj_node_p != 0 166 | set $last_obj_id = ((mc_vm_obj *) ($area->last_obj_node_p->data))->id 167 | else 168 | set $last_obj_id = -1337 169 | end 170 | 171 | # print relevant entries of this area 172 | printf "%-3d: 0x%-12lx - 0x%-12lx | obj: %-5d - last_obj: %-5d | \"%s\"\n", $id, $start_addr, $end_addr, $obj_id, $last_obj_id, $basename 173 | 174 | # advance iteration 175 | set $iter = $iter + 1 176 | set $iter_node = $iter_node->next 177 | end 178 | end 179 | end 180 | 181 | # print objs of a map 182 | define pmapo 183 | if $argc != 1 184 | printf "Use: pmapo <*map>\n" 185 | else 186 | # print header 187 | printf " --- [OBJS] ---\n" 188 | 189 | # bootstrap iteration over objects 190 | set $iter = 0 191 | set $iter_node = $arg0->vm_objs.head 192 | 193 | # for every object 194 | while $iter != $arg0->vm_objs.len 195 | 196 | # fetch & typecast next object 197 | set $obj = ((mc_vm_obj *) ($iter_node->data)) 198 | 199 | # fetch relevant entries of this object 200 | set $id = $obj->id 201 | set $basename = $obj->basename 202 | set $start_addr = $obj->start_addr 203 | set $end_addr = $obj->end_addr 204 | 205 | # print relevant entries of this object 206 | printf "<%-3d: 0x%lx - 0x%lx | \"%s\">\n", $id, $start_addr, $end_addr, $basename 207 | 208 | # setup iteration over areas belonging to this object 209 | set $inner_iter = 0 210 | set $inner_iter_node = ((mc_vm_obj *) ($iter_node->data))->vm_area_node_ps.head 211 | 212 | # for every area that is part of this object 213 | printf " [areas]:\n" 214 | while $inner_iter != ((mc_vm_obj *) ($iter_node->data))->vm_area_node_ps.len 215 | 216 | # fetch and typecast next area 217 | set $inner_area = ((mc_vm_area *) ((*((cm_lst_node **) ($inner_iter_node->data)))->data)) 218 | 219 | # fetch relevant entries of this area 220 | set $id = $inner_area->id 221 | set $basename = $inner_area->basename 222 | set $start_addr = $inner_area->start_addr 223 | set $end_addr = $inner_area->end_addr 224 | 225 | #fetch area's object id if one is present 226 | if $inner_area->obj_node_p != 0 227 | set $inner_obj_id = ((mc_vm_obj *) ($inner_area->obj_node_p->data))->id 228 | else 229 | set $inner_obj_id = -1337 230 | end 231 | 232 | #fetch area's last object i if one is present 233 | if $inner_area->last_obj_node_p != 0 234 | set $inner_last_obj_id = ((mc_vm_obj *) ($inner_area->last_obj_node_p->data))->id 235 | else 236 | set $inner_last_obj_id = -1337 237 | end 238 | 239 | # print relevant entries of this area 240 | printf " %-3d: 0x%-12lx - 0x%-12lx | obj: %-5d - last_obj: %-5d | \"%s\"\n", $id, $start_addr, $end_addr, $inner_obj_id, $inner_last_obj_id, $basename 241 | 242 | # advance iteration over areas 243 | set $inner_iter = $inner_iter + 1 244 | set $inner_iter_node = $inner_iter_node->next 245 | end 246 | 247 | # setup iteration over last areas belonging to this object 248 | set $inner_iter = 0 249 | set $inner_iter_node = ((mc_vm_obj *) ($iter_node->data))->last_vm_area_node_ps.head 250 | 251 | # for every area that is part of this object 252 | printf " [last areas]:\n" 253 | while $inner_iter != ((mc_vm_obj *) ($iter_node->data))->last_vm_area_node_ps.len 254 | 255 | # fetch and typecast next area 256 | set $inner_area = ((mc_vm_area *) ((*((cm_lst_node **) ($inner_iter_node->data)))->data)) 257 | 258 | # fetch relevant entries of this area 259 | set $id = $inner_area->id 260 | set $basename = $inner_area->basename 261 | set $start_addr = $inner_area->start_addr 262 | set $end_addr = $inner_area->end_addr 263 | 264 | #fetch area's object id if one is present 265 | if $inner_area->obj_node_p != 0 266 | set $inner_obj_id = ((mc_vm_obj *) ($inner_area->obj_node_p->data))->id 267 | else 268 | set $inner_obj_id = -1337 269 | end 270 | 271 | #fetch last area's object i if one is present 272 | if $inner_area->last_obj_node_p != 0 273 | set $inner_last_obj_id = ((mc_vm_obj *) ($inner_area->last_obj_node_p->data))->id 274 | else 275 | set $inner_last_obj_id = -1337 276 | end 277 | 278 | # print relevant entries of this area 279 | printf " %-3d: 0x%-12lx - 0x%-12lx | obj: %-5d - last_obj: %-5d | \"%s\"\n", $id, $start_addr, $end_addr, $inner_obj_id, $inner_last_obj_id, $basename 280 | 281 | # advance iteration over areas 282 | set $inner_iter = $inner_iter + 1 283 | set $inner_iter_node = $inner_iter_node->next 284 | end 285 | 286 | # advance iteration over objects 287 | set $iter = $iter + 1 288 | set $iter_node = $iter_node->next 289 | end 290 | end 291 | end 292 | 293 | # print unmapped objs of a map 294 | define pmapuo 295 | if $argc != 1 296 | printf "Use: pmapuo <*map>\n" 297 | else 298 | # print header 299 | printf " --- [UNMAPPED OBJS] ---\n" 300 | 301 | # bootstrap iteration over objects 302 | set $iter = 0 303 | set $iter_node = $arg0->vm_objs_unmapped.head 304 | 305 | # for every object 306 | while $iter != $arg0->vm_objs_unmapped.len 307 | 308 | # fetch & typecast next object 309 | set $obj = ((mc_vm_obj *) ((*((cm_lst_node **) ($iter_node->data)))->data)) 310 | 311 | # fetch relevant entries of this object 312 | set $id = $obj->id 313 | set $basename = $obj->basename 314 | set $start_addr = $obj->start_addr 315 | set $end_addr = $obj->end_addr 316 | set $num_area = $obj->vm_area_node_ps.len 317 | set $num_last_area = $obj->last_vm_area_node_ps.len 318 | 319 | # print relevant entries with conversion to MC_UNDEF_ADDR 320 | if ($start_addr == -1) && ($end_addr == -1) 321 | printf "<%-3d: MC_UNDEF_ADDR - MC_UNDEF_ADDR | areas: %d - last areas: %d | \"%s\">\n", $id, $num_area, $num_last_area, $basename 322 | else 323 | printf "<%-3d: 0x%-12lx - 0x%-12lx | areas: %d - last areas: %d | \"%s\">\n", $id, $start_addr, $end_addr, $num_area, $num_last_area, $basename 324 | end 325 | 326 | # advance iteration over objects 327 | set $iter = $iter + 1 328 | set $iter_node = $iter_node->next 329 | end 330 | end 331 | end 332 | 333 | 334 | # session dependent (modify from here onwards) 335 | tb main 336 | run 337 | layout src 338 | -------------------------------------------------------------------------------- /media/memcry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vykt/memcry/a8ad0c35e5bd9cbf4f28dba192c1d361e05a20f8/media/memcry.png -------------------------------------------------------------------------------- /media/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vykt/memcry/a8ad0c35e5bd9cbf4f28dba192c1d361e05a20f8/media/overview.png -------------------------------------------------------------------------------- /media/resources/COLOURS: -------------------------------------------------------------------------------- 1 | #6e2a2c - red (border) 2 | #342025 - grox red (background) 3 | #a29d88 - sand (symbol) 4 | 5 | -------------------------------------------------------------------------------- /media/resources/memcry.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vykt/memcry/a8ad0c35e5bd9cbf4f28dba192c1d361e05a20f8/media/resources/memcry.xcf -------------------------------------------------------------------------------- /media/resources/overview.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /media/resources/phage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vykt/memcry/a8ad0c35e5bd9cbf4f28dba192c1d361e05a20f8/media/resources/phage.png -------------------------------------------------------------------------------- /src/lib/Makefile: -------------------------------------------------------------------------------- 1 | .RECIPEPREFIX:=> 2 | 3 | # This makefile takes the following variables: 4 | # 5 | # CC - Compiler. 6 | # BUILD_DIR - Library build directory. 7 | # 8 | # _CFLAGS - Compiler flags. 9 | # _WARN_OPTS - Compiler warnings. 10 | # _LDFLAGS - Linker flags. 11 | 12 | 13 | CFLAGS=${_CFLAGS} 14 | WARN_OPTS=${_WARN_OPTS} -Wno-unused-parameter 15 | LDFLAGS=${_LDFLAGS} 16 | 17 | SOURCES_LIB=error.c iface.c krncry_iface.c \ 18 | map.c map_util.c procfs_iface.c util.c 19 | OBJECTS_LIB=${SOURCES_LIB:%.c=${BUILD_DIR}/%.o} 20 | 21 | SHARED=libmcry.so 22 | STATIC=libmcry.a 23 | 24 | 25 | shared: ${SHARED} 26 | > mkdir -p ${BUILD_DIR} 27 | > mv ${SHARED} ${BUILD_DIR} 28 | 29 | static: ${STATIC} 30 | > mkdir -p ${BUILD_DIR} 31 | > mv ${STATIC} ${BUILD_DIR} 32 | 33 | ${SHARED}: ${OBJECTS_LIB} 34 | > ${CC} ${CFLAGS} -shared -o $@ $^ ${LDFLAGS} 35 | 36 | ${STATIC}: ${OBJECTS_LIB} 37 | > ar rcs $@ $^ 38 | 39 | ${BUILD_DIR}/%.o: %.c 40 | > ${CC} ${CFLAGS} ${WARN_OPTS} -c $< -o $@ 41 | 42 | clean: 43 | > -rm -v ${BUILD_DIR}/${SHARED} 44 | > -rm -v ${BUILD_DIR}/${STATIC} 45 | > -rm -v ${OBJECTS_LIB} 46 | -------------------------------------------------------------------------------- /src/lib/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | 4 | 5 | /* 6 | * Do not define internal functions as static in debug builds 7 | */ 8 | 9 | #ifdef DEBUG 10 | #define DBG_STATIC 11 | #define DBG_INLINE 12 | #else 13 | #define DBG_STATIC static 14 | #define DBG_INLINE inline 15 | #endif 16 | 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/lib/error.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | 4 | //local headers 5 | #include "memcry.h" 6 | #include "error.h" 7 | 8 | 9 | __thread int mc_errno; 10 | 11 | 12 | /* 13 | * TODO: Find a better way to do this. 14 | */ 15 | 16 | 17 | void mc_perror(const char * prefix) { 18 | 19 | switch(mc_errno) { 20 | // 1XX - user errors 21 | case MC_ERR_PROC_MEM: 22 | fprintf(stderr, "%s: %s", prefix, MC_ERR_PROC_MEM_MSG); 23 | break; 24 | 25 | case MC_ERR_PROC_MAP: 26 | fprintf(stderr, "%s: %s", prefix, MC_ERR_PROC_MAP_MSG); 27 | break; 28 | 29 | case MC_ERR_SEEK_ADDR: 30 | fprintf(stderr, "%s: %s", prefix, MC_ERR_SEEK_ADDR_MSG); 31 | break; 32 | 33 | // 2XX - internal errors 34 | case MC_ERR_INTERNAL_INDEX: 35 | fprintf(stderr, "%s: %s", prefix, MC_ERR_INTERNAL_INDEX_MSG); 36 | break; 37 | 38 | case MC_ERR_AREA_IN_OBJ: 39 | fprintf(stderr, "%s: %s", prefix, MC_ERR_AREA_IN_OBJ_MSG); 40 | break; 41 | 42 | case MC_ERR_UNEXPECTED_NULL: 43 | fprintf(stderr, "%s: %s", prefix, MC_ERR_UNEXPECTED_NULL_MSG); 44 | break; 45 | 46 | case MC_ERR_CMORE: 47 | fprintf(stderr, "%s: %s", prefix, MC_ERR_CMORE_MSG); 48 | break; 49 | 50 | case MC_ERR_READ_WRITE: 51 | fprintf(stderr, "%s: %s", prefix, MC_ERR_READ_WRITE_MSG); 52 | break; 53 | 54 | case MC_ERR_MEMU_TARGET: 55 | fprintf(stderr, "%s: %s", prefix, MC_ERR_MEMU_TARGET_MSG); 56 | break; 57 | 58 | case MC_ERR_MEMU_MAP_SZ: 59 | fprintf(stderr, "%s: %s", prefix, MC_ERR_MEMU_MAP_SZ_MSG); 60 | break; 61 | 62 | case MC_ERR_MEMU_MAP_GET: 63 | fprintf(stderr, "%s: %s", prefix, MC_ERR_MEMU_MAP_GET_MSG); 64 | break; 65 | 66 | case MC_ERR_PROC_STATUS: 67 | fprintf(stderr, "%s: %s", prefix, MC_ERR_PROC_STATUS_MSG); 68 | break; 69 | 70 | case MC_ERR_PROC_NAV: 71 | fprintf(stderr, "%s: %s", prefix, MC_ERR_PROC_NAV_MSG); 72 | break; 73 | 74 | // 3XX - environmental errors 75 | case MC_ERR_MEM: 76 | fprintf(stderr, "%s: %s", prefix, MC_ERR_MEM_MSG); 77 | break; 78 | 79 | case MC_ERR_PAGESIZE: 80 | fprintf(stderr, "%s: %s", prefix, MC_ERR_PAGESIZE_MSG); 81 | break; 82 | 83 | case MC_ERR_KRNCRY_MAJOR: 84 | fprintf(stderr, "%s: %s", prefix, MC_ERR_KRNCRY_MAJOR_MSG); 85 | break; 86 | 87 | case MC_ERR_MEMU_OPEN: 88 | fprintf(stderr, "%s: %s", prefix, MC_ERR_MEMU_OPEN_MSG); 89 | break; 90 | 91 | default: 92 | fprintf(stderr, "Undefined error code.\n"); 93 | break; 94 | } 95 | 96 | return; 97 | } 98 | 99 | 100 | const char * mc_strerror(const int mc_errnum) { 101 | 102 | switch (mc_errnum) { 103 | // 1XX - user errors 104 | case MC_ERR_PROC_MEM: 105 | return MC_ERR_PROC_MEM_MSG; 106 | 107 | case MC_ERR_PROC_MAP: 108 | return MC_ERR_PROC_MAP_MSG; 109 | 110 | case MC_ERR_SEEK_ADDR: 111 | return MC_ERR_SEEK_ADDR_MSG; 112 | 113 | // 2xx - internal errors 114 | case MC_ERR_INTERNAL_INDEX: 115 | return MC_ERR_INTERNAL_INDEX_MSG; 116 | 117 | case MC_ERR_AREA_IN_OBJ: 118 | return MC_ERR_AREA_IN_OBJ_MSG; 119 | 120 | case MC_ERR_UNEXPECTED_NULL: 121 | return MC_ERR_UNEXPECTED_NULL_MSG; 122 | 123 | case MC_ERR_CMORE: 124 | return MC_ERR_CMORE_MSG; 125 | 126 | case MC_ERR_READ_WRITE: 127 | return MC_ERR_READ_WRITE_MSG; 128 | 129 | case MC_ERR_MEMU_TARGET: 130 | return MC_ERR_MEMU_TARGET_MSG; 131 | 132 | case MC_ERR_MEMU_MAP_SZ: 133 | return MC_ERR_MEMU_MAP_SZ_MSG; 134 | 135 | case MC_ERR_MEMU_MAP_GET: 136 | return MC_ERR_MEMU_MAP_GET_MSG; 137 | 138 | case MC_ERR_PROC_STATUS: 139 | return MC_ERR_PROC_STATUS_MSG; 140 | 141 | case MC_ERR_PROC_NAV: 142 | return MC_ERR_PROC_NAV_MSG; 143 | 144 | // 3XX - environmental errors 145 | case MC_ERR_MEM: 146 | return MC_ERR_MEM_MSG; 147 | 148 | case MC_ERR_PAGESIZE: 149 | return MC_ERR_PAGESIZE_MSG; 150 | 151 | case MC_ERR_KRNCRY_MAJOR: 152 | return MC_ERR_KRNCRY_MAJOR_MSG; 153 | 154 | case MC_ERR_MEMU_OPEN: 155 | return MC_ERR_MEMU_OPEN_MSG; 156 | 157 | default: 158 | return "Undefined error code.\n"; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/lib/error.h: -------------------------------------------------------------------------------- 1 | #ifndef ERROR_H 2 | #define ERROR_H 3 | 4 | 5 | //external 6 | void mc_perror(const char * prefix); 7 | const char * mc_strerror(const int mc_errnum); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/lib/iface.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | 5 | //external libraries 6 | #include 7 | 8 | //local headers 9 | #include "iface.h" 10 | #include "memcry.h" 11 | #include "procfs_iface.h" 12 | #include "krncry_iface.h" 13 | #include "debug.h" 14 | 15 | 16 | 17 | /* 18 | * --- [INTERNAL] --- 19 | */ 20 | 21 | DBG_STATIC DBG_INLINE 22 | void _set_procfs_session(mc_session * session) { 23 | 24 | session->iface.open = procfs_open; 25 | session->iface.close = procfs_close; 26 | session->iface.update_map = procfs_update_map; 27 | session->iface.read = procfs_read; 28 | session->iface.write = procfs_write; 29 | 30 | return; 31 | } 32 | 33 | 34 | DBG_STATIC DBG_INLINE 35 | void _set_krncry_session(mc_session * session) { 36 | 37 | session->iface.open = krncry_open; 38 | session->iface.close = krncry_close; 39 | session->iface.update_map = krncry_update_map; 40 | session->iface.read = krncry_read; 41 | session->iface.write = krncry_write; 42 | 43 | return; 44 | } 45 | 46 | 47 | 48 | /* 49 | * --- [EXTERNAL] --- 50 | */ 51 | 52 | int mc_open(mc_session * session, 53 | const enum mc_iface_type iface, const pid_t pid) { 54 | 55 | 56 | int ret; 57 | 58 | //if requesting procfs interface 59 | if (iface == PROCFS) { 60 | _set_procfs_session(session); 61 | } else { 62 | _set_krncry_session(session); 63 | } 64 | 65 | ret = session->iface.open(session, pid); 66 | if (ret) return -1; 67 | 68 | return 0; 69 | } 70 | 71 | 72 | int mc_close(mc_session * session) { 73 | 74 | int ret; 75 | 76 | ret = session->iface.close(session); 77 | if (ret) return -1; 78 | 79 | return 0; 80 | } 81 | 82 | 83 | int mc_update_map(const mc_session * session, mc_vm_map * vm_map) { 84 | 85 | int ret; 86 | 87 | ret = session->iface.update_map(session, vm_map); 88 | if (ret) return -1; 89 | 90 | return 0; 91 | } 92 | 93 | 94 | int mc_read(const mc_session * session, const uintptr_t addr, 95 | cm_byte * buf, const size_t buf_sz) { 96 | 97 | int ret; 98 | 99 | ret = session->iface.read(session, addr, buf, buf_sz); 100 | if (ret == -1) return -1; 101 | 102 | return 0; 103 | } 104 | 105 | 106 | int mc_write(const mc_session * session, const uintptr_t addr, 107 | const cm_byte * buf, const size_t buf_sz) { 108 | 109 | int ret; 110 | 111 | ret = session->iface.write(session, addr, buf, buf_sz); 112 | if (ret == -1) return -1; 113 | 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /src/lib/iface.h: -------------------------------------------------------------------------------- 1 | #ifndef IFACE_H 2 | #define IFACE_H 3 | 4 | //standard library 5 | #include 6 | 7 | //system headers 8 | #include 9 | 10 | //local headers 11 | #include "memcry.h" 12 | 13 | 14 | #ifdef DEBUG 15 | //internal 16 | void _set_procfs_session(mc_session * session); 17 | void _set_krncry_session(mc_session * session); 18 | #endif 19 | 20 | 21 | //external 22 | int mc_open(mc_session * session, 23 | const enum mc_iface_type iface, const pid_t pid); 24 | int mc_close(mc_session * session); 25 | int mc_update_map(const mc_session * session, mc_vm_map * vm_map); 26 | int mc_read(const mc_session * session, const uintptr_t addr, 27 | cm_byte * buf, const size_t buf_sz); 28 | int mc_write(const mc_session * session, const uintptr_t addr, 29 | const cm_byte * buf, const size_t buf_sz); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/lib/krncry.h: -------------------------------------------------------------------------------- 1 | #ifndef KRNCRY_H 2 | #define KRNCRY_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | //system headers 9 | #include 10 | 11 | 12 | /* 13 | * This header comes from the krncry project. 14 | */ 15 | 16 | //krncry ioctl call numbers 17 | #define KRNCRY_IOCTL_OPEN_TGT 0 18 | #define KRNCRY_IOCTL_RELEASE_TGT 1 19 | #define KRNCRY_IOCTL_GET_MAP 2 20 | #define KRNCRY_IOCTL_GET_MAP_SZ 3 21 | 22 | //templates for ioctl calls 23 | #define KRNCRY_TEMPLATE_OPEN_TGT 0x40080000 24 | #define KRNCRY_TEMPLATE_RELEASE_TGT 0x00000001 25 | #define KRNCRY_TEMPLATE_GET_MAP 0xc0080002 26 | #define KRNCRY_TEMPLATE_GET_MAP_SZ 0x40080003 27 | 28 | //template macro 29 | #define KRNCRY_APPLY_TEMPLATE(major, krncry_template) (((major << 8) & 0x0000ff00) | krncry_template) 30 | 31 | 32 | //vma protection - taken from linux/pgtable_types.h 33 | typedef unsigned long krncry_pgprot_t; 34 | 35 | //permission bitmask 36 | #define VM_PROT_MASK 0x0000000F 37 | 38 | //specific permission bitmasks 39 | #define VM_READ 0x00000001 40 | #define VM_WRITE 0x00000002 41 | #define VM_EXEC 0x00000004 42 | #define VM_SHARED 0x00000008 43 | 44 | 45 | /* 46 | * --- [DATA TYPES] --- 47 | */ 48 | 49 | // [byte] 50 | typedef unsigned char kc_byte; 51 | 52 | 53 | // [ioctl argument] 54 | struct ioctl_arg { 55 | kc_byte * u_buf; 56 | int target_pid; 57 | }; 58 | 59 | 60 | // [map entry] 61 | struct vm_entry { 62 | 63 | unsigned long vm_start; 64 | unsigned long vm_end; 65 | 66 | unsigned long file_off; 67 | krncry_pgprot_t prot; 68 | char file_path[PATH_MAX]; 69 | }; 70 | 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/lib/krncry_iface.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | #include 5 | 6 | //system headers 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | //external libraries 16 | #include 17 | 18 | //local headers 19 | #include "krncry_iface.h" 20 | #include "memcry.h" 21 | #include "krncry.h" 22 | #include "map.h" 23 | #include "debug.h" 24 | 25 | 26 | 27 | /* 28 | * --- [INTERNAL] --- 29 | */ 30 | 31 | //read krncry's major number 32 | DBG_STATIC DBG_INLINE 33 | char _krncry_iface_get_major() { 34 | 35 | int fd; 36 | 37 | ssize_t read_bytes; 38 | char major, read_buf[8]; 39 | 40 | 41 | //open major attribute 42 | fd = open(KRNCRY_MAJOR_PATH, O_RDONLY); 43 | if (fd == -1) { 44 | mc_errno = MC_ERR_KRNCRY_MAJOR; 45 | return -1; 46 | } 47 | 48 | //read string representation into buffer 49 | read_bytes = read(fd, &read_buf, 8); 50 | if (read_bytes == -1) { 51 | mc_errno = MC_ERR_KRNCRY_MAJOR; 52 | return -1; 53 | } 54 | 55 | //convert back to binary representation 56 | major = (char) strtol(read_buf, NULL, 10); 57 | 58 | close(fd); 59 | return major; 60 | } 61 | 62 | 63 | 64 | /* 65 | * --- [INTERFACE] --- 66 | */ 67 | 68 | int krncry_open(mc_session * session, const pid_t pid) { 69 | 70 | int ret; 71 | char device_path[PATH_MAX]; 72 | uint32_t ioctl_call; 73 | struct ioctl_arg arg; 74 | 75 | 76 | //get page size to determine maximum read/write size 77 | session->page_size = sysconf(_SC_PAGESIZE); 78 | if (session->page_size < 0) { 79 | mc_errno = MC_ERR_PAGESIZE; 80 | return -1; 81 | } 82 | 83 | //get major of the module, returns -1 if unloaded 84 | session->major = _krncry_iface_get_major(); 85 | if (session->major == -1) return -1; 86 | 87 | //build krncry device path 88 | snprintf(device_path, PATH_MAX, "/dev/char/%d:%d", 89 | (unsigned char) session->major, KRNCRY_MINOR); 90 | 91 | //open the krncry device 92 | session->fd_dev_krncry = open(device_path, O_RDWR); 93 | if (session->fd_dev_krncry == -1) { 94 | mc_errno = MC_ERR_MEMU_OPEN; 95 | return -1; 96 | } 97 | 98 | //call ioctl to set the target process 99 | arg.target_pid = pid; 100 | ioctl_call = KRNCRY_APPLY_TEMPLATE((char) session->major, 101 | KRNCRY_TEMPLATE_OPEN_TGT); 102 | ret = ioctl(session->fd_dev_krncry, ioctl_call, &arg); 103 | if (ret) { 104 | close(session->fd_dev_krncry); 105 | mc_errno = MC_ERR_MEMU_TARGET; 106 | return -1; 107 | } 108 | 109 | return 0; 110 | } 111 | 112 | 113 | int krncry_close(mc_session * session) { 114 | 115 | int ret; 116 | uint32_t ioctl_call; 117 | struct ioctl_arg arg; //not used 118 | 119 | 120 | //call ioctl to release target 121 | ioctl_call = KRNCRY_APPLY_TEMPLATE((char) session->major, 122 | KRNCRY_TEMPLATE_RELEASE_TGT); 123 | ret = ioctl(session->fd_dev_krncry, ioctl_call, &arg); 124 | if (ret == -1) { 125 | 126 | return -1; 127 | } //FIXME check this is correct 128 | 129 | //close device 130 | close(session->fd_dev_krncry); 131 | 132 | return 0; 133 | } 134 | 135 | 136 | int krncry_update_map(const mc_session * session, mc_vm_map * vm_map) { 137 | 138 | int ret, count; 139 | uint32_t ioctl_call; 140 | size_t u_buf_sz; 141 | 142 | struct ioctl_arg arg; 143 | _traverse_state state; 144 | 145 | 146 | //call ioctl to get the map size 147 | ioctl_call = KRNCRY_APPLY_TEMPLATE((char) session->major, 148 | KRNCRY_TEMPLATE_GET_MAP_SZ); 149 | count = ioctl(session->fd_dev_krncry, ioctl_call, &arg); 150 | if (count <= 0) { 151 | mc_errno = MC_ERR_MEMU_MAP_SZ; 152 | return -1; 153 | } 154 | 155 | //allocate buffer to hold the map 156 | u_buf_sz = count * sizeof(struct vm_entry); 157 | arg.u_buf = mmap(NULL, u_buf_sz, 158 | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 159 | if (arg.u_buf == MAP_FAILED) { 160 | mc_errno = MC_ERR_MEM; 161 | return -1; 162 | } 163 | 164 | //get the map 165 | ioctl_call = KRNCRY_APPLY_TEMPLATE((char) session->major, 166 | KRNCRY_TEMPLATE_GET_MAP); 167 | 168 | count = ioctl(session->fd_dev_krncry, ioctl_call, &arg); 169 | if (count <= 0) { 170 | munmap(arg.u_buf, u_buf_sz); 171 | mc_errno = MC_ERR_MEMU_MAP_GET; 172 | return -1; 173 | } 174 | 175 | 176 | //update the map with each received segment 177 | map_init_traverse_state(&state, vm_map); 178 | 179 | for (int i = 0; i < count; ++i) { 180 | 181 | ret = map_send_entry((struct vm_entry *) 182 | (arg.u_buf + (i * sizeof(struct vm_entry))), 183 | &state, vm_map); 184 | if (ret) { 185 | munmap(arg.u_buf, u_buf_sz); 186 | return -1; 187 | } 188 | } //end for 189 | 190 | //unmap map buffer 191 | munmap(arg.u_buf, u_buf_sz); 192 | 193 | return 0; 194 | } 195 | 196 | 197 | int krncry_read(const mc_session * session, const uintptr_t addr, 198 | cm_byte * buf, const size_t buf_sz) { 199 | 200 | off_t off_ret; 201 | ssize_t read_bytes, read_done, read_left; 202 | 203 | 204 | //initialise read state 205 | read_done = read_left = 0; 206 | 207 | //seek to address 208 | off_ret = lseek(session->fd_dev_krncry, (off_t) addr, SEEK_SET); 209 | if (off_ret == -1) { 210 | mc_errno = MC_ERR_SEEK_ADDR; 211 | return -1; 212 | } 213 | 214 | //read page_size bytes repeatedly until done 215 | do { 216 | 217 | //calc how many bytes left to read 218 | read_left = buf_sz - read_done; 219 | 220 | //read into buffer 221 | read_bytes = read(session->fd_dev_krncry, buf + read_done, 222 | read_left > session->page_size 223 | ? session->page_size : read_left); 224 | //if error or EOF before reading len bytes 225 | if (read_bytes == -1 || (read_bytes == 0 226 | && read_done < (ssize_t) buf_sz)) { 227 | 228 | mc_errno = MC_ERR_READ_WRITE; 229 | return -1; 230 | } 231 | read_done += read_bytes; 232 | 233 | } while (read_done < (ssize_t) buf_sz); 234 | 235 | return 0; 236 | } 237 | 238 | 239 | int krncry_write(const mc_session * session, const uintptr_t addr, 240 | const cm_byte * buf, const size_t buf_sz) { 241 | 242 | off_t off_ret; 243 | ssize_t write_bytes, write_done, write_left; 244 | 245 | 246 | //initialise write state 247 | write_done = write_left = 0; 248 | 249 | //seek to address 250 | off_ret = lseek(session->fd_dev_krncry, (off_t) addr, SEEK_SET); 251 | if (off_ret == -1) { 252 | mc_errno = MC_ERR_SEEK_ADDR; 253 | return -1; 254 | } 255 | 256 | //write page_size bytes repeatedly until done 257 | do { 258 | 259 | //calc how many bytes left to write 260 | write_left = buf_sz - write_done; 261 | 262 | //write into buffer 263 | write_bytes = write(session->fd_dev_krncry, buf + write_done, 264 | write_left > session->page_size 265 | ? session->page_size : write_left); 266 | //if error or EOF before writing len bytes 267 | if (write_bytes == -1 || (write_bytes == 0 268 | && write_done < (ssize_t) buf_sz)) { 269 | 270 | mc_errno = MC_ERR_READ_WRITE; 271 | return -1; 272 | } 273 | write_done += write_bytes; 274 | 275 | } while (write_done < (ssize_t) buf_sz); 276 | 277 | return 0; 278 | } 279 | -------------------------------------------------------------------------------- /src/lib/krncry_iface.h: -------------------------------------------------------------------------------- 1 | #ifndef KRNCRY_IFACE_H 2 | #define KRNCRY_IFACE_H 3 | 4 | //external libraries 5 | #include 6 | 7 | //local headers 8 | #include "memcry.h" 9 | #include "debug.h" 10 | 11 | 12 | #define KRNCRY_MAJOR_PATH "/sys/class/krncry/krncry_major" 13 | #define KRNCRY_MINOR 0 14 | 15 | 16 | #ifdef DEBUG 17 | //internal 18 | char _krncry_iface_get_major(); 19 | #endif 20 | 21 | 22 | //interface 23 | int krncry_open(mc_session * session, const pid_t pid); 24 | int krncry_close(mc_session * session); 25 | int krncry_update_map(const mc_session * session, mc_vm_map * vm_map); 26 | int krncry_read(const mc_session * session, const uintptr_t addr, 27 | cm_byte * buf, const size_t buf_sz); 28 | int krncry_write(const mc_session * session, const uintptr_t addr, 29 | const cm_byte * buf, const size_t buf_sz); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/lib/map.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | #include 5 | 6 | //system headers 7 | #include 8 | 9 | //external libraries 10 | #include 11 | #include 12 | 13 | //local headers 14 | #include "memcry.h" 15 | #include "map.h" 16 | #include "util.h" 17 | #include "debug.h" 18 | 19 | 20 | 21 | /* 22 | * --- [INTERNAL] --- 23 | */ 24 | 25 | /* 26 | * Note that while the vm_area initialiser function does take a vm_obj node 27 | * as a parameter, the state of this vm_obj is not changed. 28 | */ 29 | 30 | DBG_STATIC 31 | void _map_init_vm_area(mc_vm_area * area, const struct vm_entry * entry, 32 | const cm_lst_node * obj_node, 33 | const cm_lst_node * last_obj_node, mc_vm_map * map) { 34 | 35 | mc_vm_obj * obj; 36 | 37 | 38 | //zero the area 39 | memset(area, 0, sizeof(*area)); 40 | 41 | //set pathname for area if applicable 42 | if (obj_node != NULL) { 43 | 44 | obj = MC_GET_NODE_OBJ(obj_node); 45 | 46 | area->pathname = obj->pathname; 47 | area->basename = obj->basename; 48 | 49 | } else { 50 | area->pathname = NULL; 51 | area->basename = NULL; 52 | 53 | } //end if 54 | 55 | area->obj_node_p = (cm_lst_node *) obj_node; 56 | area->last_obj_node_p = (cm_lst_node *) last_obj_node; 57 | 58 | area->start_addr = entry->vm_start; 59 | area->end_addr = entry->vm_end; 60 | 61 | area->access = (cm_byte) (entry->prot & 0x0000000F); 62 | 63 | area->mapped = true; 64 | 65 | //set id & increment map's next id 66 | area->id = map->next_id_area; 67 | ++map->next_id_area; 68 | 69 | return; 70 | } 71 | 72 | 73 | /* 74 | * Note that while the vm_obj constructor does take a vm_map pointer as a 75 | * parameter, the vm_obj is not added to the vm_map in the constructor. Only 76 | * the `next_id_obj` counter is incremented. 77 | */ 78 | 79 | DBG_STATIC 80 | void _map_new_vm_obj(mc_vm_obj * obj, 81 | mc_vm_map * map, const char * pathname) { 82 | 83 | size_t len; 84 | 85 | 86 | //zero the obj 87 | memset(obj, 0, sizeof(*obj)); 88 | 89 | //allocate space for the pathname 90 | len = strnlen(pathname, PATH_MAX); 91 | obj->pathname = calloc(sizeof(char), len + 1); 92 | 93 | //construct pathname 94 | strncpy(obj->pathname, pathname, len); 95 | obj->basename = mc_pathname_to_basename(obj->pathname); 96 | 97 | obj->start_addr = MC_UNDEF_ADDR; 98 | obj->end_addr = MC_UNDEF_ADDR; 99 | 100 | //initialise area list 101 | cm_new_lst(&obj->vm_area_node_ps, sizeof(cm_lst_node *)); 102 | cm_new_lst(&obj->last_vm_area_node_ps, sizeof(cm_lst_node *)); 103 | 104 | obj->mapped = true; 105 | 106 | //set id & increment map's next id 107 | obj->id = map->next_id_obj; 108 | ++map->next_id_obj; 109 | 110 | return; 111 | } 112 | 113 | 114 | DBG_STATIC 115 | void _map_del_vm_obj(mc_vm_obj * obj) { 116 | 117 | free(obj->pathname); 118 | cm_del_lst(&obj->vm_area_node_ps); 119 | cm_del_lst(&obj->last_vm_area_node_ps); 120 | 121 | return; 122 | } 123 | 124 | 125 | DBG_STATIC DBG_INLINE 126 | void _map_make_zero_obj(mc_vm_obj * obj) { 127 | 128 | obj->id = MC_ZERO_OBJ_ID; 129 | obj->start_addr = obj->end_addr = 0x0; 130 | 131 | return; 132 | } 133 | 134 | 135 | DBG_STATIC 136 | int _map_obj_add_area_insert(cm_lst * obj_area_lst, 137 | const cm_lst_node * area_node) { 138 | 139 | cm_lst_node * ret_node, * iter_node, * inner_node; 140 | mc_vm_area * area = MC_GET_NODE_AREA(area_node); 141 | 142 | //if list is empty, append 143 | if (obj_area_lst->len == 0) { 144 | 145 | cm_lst_apd(obj_area_lst, &area_node); 146 | 147 | //list is not empty, find appropriate insert point 148 | } else { 149 | 150 | ret_node = NULL; 151 | 152 | //setup iteration 153 | iter_node = obj_area_lst->head; 154 | for (int i = 0; i < obj_area_lst->len; ++i) { 155 | 156 | //get area node that iter_node points to 157 | inner_node = MC_GET_NODE_PTR(iter_node); 158 | 159 | //new area ends at a lower address than some existing area 160 | if (area->end_addr < MC_GET_NODE_AREA(inner_node)->end_addr) { 161 | 162 | ret_node = cm_lst_ins_nb(obj_area_lst, iter_node, &area_node); 163 | break; 164 | } 165 | 166 | //new area ends later than any existing area 167 | if ((iter_node->next == NULL) 168 | || (iter_node->next == obj_area_lst->head)) { 169 | 170 | ret_node = cm_lst_ins_na(obj_area_lst, iter_node, &area_node); 171 | break; 172 | } 173 | 174 | //increment node iteration 175 | iter_node = iter_node->next; 176 | } 177 | 178 | //check the new area was inserted successfully 179 | if (ret_node == NULL) { 180 | mc_errno = MC_ERR_CMORE; 181 | return -1; 182 | } 183 | } 184 | 185 | return 0; 186 | } 187 | 188 | 189 | DBG_STATIC 190 | cm_lst_node * _map_obj_find_area_outer_node(cm_lst * obj_area_lst, 191 | cm_lst_node * area_node) { 192 | 193 | cm_lst_node * iter_node; 194 | 195 | 196 | //list should never be empty 197 | if (obj_area_lst->len == 0) { 198 | 199 | mc_errno = MC_ERR_AREA_IN_OBJ; 200 | return NULL; 201 | } 202 | 203 | //setup iteration 204 | iter_node = obj_area_lst->head; 205 | for (int i = 0; i < obj_area_lst->len; ++i) { 206 | 207 | if (area_node == MC_GET_NODE_PTR(iter_node)) return iter_node; 208 | iter_node = iter_node->next; 209 | } 210 | 211 | mc_errno = MC_ERR_AREA_IN_OBJ; 212 | return NULL; 213 | } 214 | 215 | 216 | // This function only updates the vm_obj structure itself, not the area list 217 | DBG_STATIC DBG_INLINE 218 | int _map_obj_add_area(mc_vm_obj * obj, 219 | const cm_lst_node * area_node) { 220 | 221 | int ret; 222 | mc_vm_area * area = MC_GET_NODE_AREA(area_node); 223 | 224 | 225 | //if this object has no areas yet 226 | if (obj->start_addr == MC_UNDEF_ADDR) { 227 | //set new addr bounds 228 | obj->start_addr = area->start_addr; 229 | obj->end_addr = area->end_addr; 230 | 231 | //if this object has areas, only update the start and end addr if necessary 232 | } else { 233 | //set new addr bounds if necessary 234 | if (area->start_addr < obj->start_addr) 235 | obj->start_addr = area->start_addr; 236 | if (area->end_addr > obj->end_addr) 237 | obj->end_addr = area->end_addr; 238 | } 239 | 240 | //insert new area & ensure the area list remains sorted 241 | ret = _map_obj_add_area_insert(&obj->vm_area_node_ps, area_node); 242 | if (ret) return -1; 243 | 244 | return 0; 245 | } 246 | 247 | 248 | DBG_STATIC DBG_INLINE 249 | int _map_obj_add_last_area(mc_vm_obj * obj, 250 | const cm_lst_node * last_area_node) { 251 | 252 | int ret; 253 | 254 | //insert new last area & ensure the last area list remains sorted 255 | ret = _map_obj_add_area_insert(&obj->last_vm_area_node_ps, last_area_node); 256 | if (ret) return -1; 257 | 258 | return 0; 259 | } 260 | 261 | 262 | DBG_STATIC 263 | int _map_obj_rmv_area_fast(mc_vm_obj * obj, cm_lst_node * outer_area_node) { 264 | 265 | int ret, index; 266 | cm_lst_node * temp_node; 267 | 268 | 269 | //remove area node from object 270 | ret = cm_lst_rmv_n(&obj->vm_area_node_ps, outer_area_node); 271 | if (ret != 0) { 272 | mc_errno = MC_ERR_CMORE; 273 | return -1; 274 | } 275 | 276 | 277 | //update object's address range 278 | 279 | //if no area nodes left, address range is now undefined (unmapped) 280 | if (obj->vm_area_node_ps.len == 0) { 281 | obj->end_addr = obj->start_addr = MC_UNDEF_ADDR; 282 | return 0; 283 | } 284 | 285 | //get start addr 286 | ret = cm_lst_get(&obj->vm_area_node_ps, 0, &temp_node); 287 | if (ret) { 288 | mc_errno = MC_ERR_CMORE; 289 | return -1; 290 | } 291 | 292 | obj->start_addr = MC_GET_NODE_AREA(temp_node)->start_addr; 293 | 294 | //get end addr 295 | index = obj->vm_area_node_ps.len == 1 ? 0 : -1; 296 | 297 | if (index != 0) { 298 | ret = cm_lst_get(&obj->vm_area_node_ps, index, &temp_node); 299 | if (ret) { 300 | mc_errno = MC_ERR_CMORE; 301 | return -1; 302 | } 303 | } 304 | 305 | obj->end_addr = MC_GET_NODE_AREA(temp_node)->end_addr; 306 | 307 | return 0; 308 | } 309 | 310 | 311 | DBG_STATIC DBG_INLINE 312 | int _map_obj_rmv_area(mc_vm_obj * obj, cm_lst_node * inner_area_node) { 313 | 314 | int ret; 315 | cm_lst_node * outer_area_node; 316 | 317 | //find index of area in object 318 | outer_area_node = _map_obj_find_area_outer_node(&obj->vm_area_node_ps, 319 | inner_area_node); 320 | if (outer_area_node == NULL) { 321 | mc_errno = MC_ERR_AREA_IN_OBJ; 322 | return -1; 323 | } 324 | 325 | ret = _map_obj_rmv_area_fast(obj, outer_area_node); 326 | if (ret != 0) return -1; 327 | 328 | return 0; 329 | } 330 | 331 | 332 | DBG_STATIC 333 | int _map_obj_rmv_last_area_fast(mc_vm_obj * obj, 334 | cm_lst_node * outer_area_node) { 335 | 336 | int ret; 337 | 338 | ret = cm_lst_rmv_n(&obj->last_vm_area_node_ps, outer_area_node); 339 | if (ret != 0) { 340 | mc_errno = MC_ERR_CMORE; 341 | return -1; 342 | } 343 | 344 | return 0; 345 | } 346 | 347 | 348 | DBG_STATIC DBG_INLINE 349 | int _map_obj_rmv_last_area(mc_vm_obj * obj, 350 | cm_lst_node * inner_area_node) { 351 | 352 | int ret; 353 | cm_lst_node * outer_area_node; 354 | 355 | 356 | //remove area node from the object 357 | 358 | //find index of area in object 359 | outer_area_node = _map_obj_find_area_outer_node(&obj->last_vm_area_node_ps, 360 | inner_area_node); 361 | if (outer_area_node == NULL) { 362 | mc_errno = MC_ERR_AREA_IN_OBJ; 363 | return -1; 364 | } 365 | 366 | ret = _map_obj_rmv_last_area_fast(obj, outer_area_node); 367 | if (ret != 0) return -1; 368 | 369 | return 0; 370 | } 371 | 372 | 373 | DBG_STATIC 374 | bool _map_is_pathname_in_obj(const char * pathname, const mc_vm_obj * obj) { 375 | 376 | if (obj == NULL) return false; 377 | 378 | //if looking at pseudo object, '\0' pathname is a match 379 | if (obj->id == MC_ZERO_OBJ_ID) { 380 | if (*pathname == '\0') return true; 381 | return false; 382 | } 383 | 384 | //else require a string match 385 | bool ret = (strncmp(pathname, obj->pathname, PATH_MAX) ? false : true); 386 | 387 | return ret; 388 | } 389 | 390 | 391 | DBG_STATIC DBG_INLINE 392 | int _map_find_obj_for_area(const struct vm_entry * entry, 393 | const _traverse_state * state) { 394 | 395 | cm_lst_node * prev_node, * next_node; 396 | mc_vm_obj * prev_obj, * next_obj; 397 | 398 | //check for null 399 | if (state->prev_obj_node == NULL) return _MAP_OBJ_NEW; 400 | 401 | //get prev obj 402 | prev_node = state->prev_obj_node; 403 | prev_obj = MC_GET_NODE_OBJ(prev_node); 404 | 405 | if (prev_node->next == NULL) { 406 | next_obj = NULL; 407 | } else { 408 | next_node = prev_node->next; 409 | next_obj = MC_GET_NODE_OBJ(next_node); 410 | } 411 | 412 | //return appropriate index 413 | if (_map_is_pathname_in_obj(entry->file_path, prev_obj)) 414 | return _MAP_OBJ_PREV; 415 | if (_map_is_pathname_in_obj(entry->file_path, next_obj)) 416 | return _MAP_OBJ_NEXT; 417 | 418 | return _MAP_OBJ_NEW; 419 | } 420 | 421 | 422 | //called when deleting an object; moves `last_obj_node_p` back if needed 423 | DBG_STATIC DBG_INLINE 424 | int _map_backtrack_unmapped_obj_last_vm_areas(cm_lst_node * obj_node) { 425 | 426 | int ret; 427 | cm_lst_node * ret_p; 428 | 429 | int iterations; 430 | 431 | mc_vm_area * last_area; 432 | cm_lst_node * last_area_node, * temp_area_node; 433 | 434 | mc_vm_obj * temp_obj, * temp_prev_obj; 435 | 436 | 437 | //setup iteration 438 | temp_obj = MC_GET_NODE_OBJ(obj_node); 439 | temp_prev_obj = MC_GET_NODE_OBJ(obj_node->prev); 440 | 441 | //get first node 442 | last_area_node = temp_obj->last_vm_area_node_ps.head; 443 | if (last_area_node == NULL) return 0; 444 | 445 | //get last area from first node 446 | temp_area_node = MC_GET_NODE_PTR(last_area_node); 447 | last_area = MC_GET_NODE_AREA(temp_area_node); 448 | 449 | 450 | //for every area, backtrack last object pointer 451 | iterations = temp_obj->last_vm_area_node_ps.len; 452 | for (int i = 0; i < iterations; ++i) { 453 | 454 | //update pointer 455 | last_area->last_obj_node_p = obj_node->prev; 456 | ret_p = cm_lst_apd(&temp_prev_obj->last_vm_area_node_ps, 457 | &temp_area_node); 458 | if (ret_p == NULL) { 459 | mc_errno = MC_ERR_CMORE; 460 | return -1; 461 | } 462 | 463 | //advance iteration (part 1) 464 | last_area_node = last_area_node->next; 465 | if (last_area_node != NULL) { 466 | temp_area_node = MC_GET_NODE_PTR(last_area_node); 467 | } 468 | 469 | //remove this area from the object's last_vm_area_node_ps list 470 | ret = cm_lst_rmv(&temp_obj->last_vm_area_node_ps, 0); 471 | if (ret == -1) { 472 | mc_errno = MC_ERR_CMORE; 473 | return -1; 474 | } 475 | 476 | //advance iteration (part 2) 477 | if (last_area_node != NULL) { 478 | last_area = MC_GET_NODE_AREA(temp_area_node); 479 | } 480 | } 481 | 482 | return 0; 483 | } 484 | 485 | 486 | //called when adding an object; moves `last_obj_node_p` forward if needed 487 | DBG_STATIC DBG_INLINE 488 | int _map_forward_unmapped_obj_last_vm_areas(cm_lst_node * obj_node) { 489 | 490 | int ret; 491 | 492 | cm_lst_node * last_area_node, * temp_area_node, * prev_obj_node; 493 | mc_vm_area * last_area; 494 | mc_vm_obj * obj, * prev_obj; 495 | 496 | 497 | //setup objects 498 | obj = MC_GET_NODE_OBJ(obj_node); 499 | prev_obj_node = obj_node->prev; 500 | prev_obj = MC_GET_NODE_OBJ(prev_obj_node); 501 | 502 | 503 | //setup iteration 504 | last_area_node = prev_obj->last_vm_area_node_ps.head; 505 | if (last_area_node == NULL) return 0; 506 | 507 | temp_area_node = MC_GET_NODE_PTR(last_area_node); 508 | last_area = MC_GET_NODE_AREA(temp_area_node); 509 | 510 | //for every area, move last object pointer forward if necessary 511 | for (int i = 0; i < prev_obj->last_vm_area_node_ps.len; ++i) { 512 | 513 | //if this area's address range comes completely after this object 514 | if (last_area->start_addr >= obj->end_addr) { 515 | 516 | //set this object as the new last object pointer 517 | last_area->last_obj_node_p = obj_node; 518 | 519 | //add this area to this object's last_vm_area_node_ps list 520 | ret = _map_obj_add_last_area(obj, temp_area_node); 521 | if (ret == -1) return -1; 522 | 523 | //advance iteration (part 1) 524 | temp_area_node = last_area_node->next; 525 | 526 | //remove this area from the previous 527 | //last object's last_vm_area_node_ps 528 | ret =_map_obj_rmv_last_area_fast(prev_obj, last_area_node); 529 | if (ret == -1) return -1; 530 | 531 | //correct iteration index 532 | i -= 1; 533 | 534 | //otherwise just update iteration 535 | } else { 536 | temp_area_node = last_area_node->next; 537 | } 538 | 539 | //advance iteration 540 | if (temp_area_node != NULL) { 541 | 542 | last_area_node = temp_area_node; 543 | temp_area_node = MC_GET_NODE_PTR(last_area_node); 544 | last_area = MC_GET_NODE_AREA(temp_area_node); 545 | } 546 | 547 | } //end for 548 | 549 | return 0; 550 | } 551 | 552 | 553 | DBG_STATIC DBG_INLINE 554 | int _map_unlink_unmapped_obj(cm_lst_node * obj_node, 555 | _traverse_state * state, mc_vm_map * map) { 556 | 557 | int ret; 558 | cm_lst_node * ret_node; 559 | 560 | mc_vm_obj * obj; 561 | 562 | 563 | //backtrack traverse state if it is currently on this object 564 | if (obj_node == state->prev_obj_node) { 565 | state->prev_obj_node = state->prev_obj_node->prev; 566 | } 567 | 568 | //fetch object 569 | obj = MC_GET_NODE_OBJ(obj_node); 570 | 571 | //if this is the pseydo object, just reset it 572 | if (obj->id == MC_ZERO_OBJ_ID) { 573 | obj->start_addr = 0x0; 574 | obj->end_addr = 0x0; 575 | return 0; 576 | } 577 | 578 | //correct last_obj_node_p of every vm_area 579 | //with this object as its last object 580 | ret = _map_backtrack_unmapped_obj_last_vm_areas(obj_node); 581 | if (ret == -1) return -1; 582 | 583 | //unlink this node from the list of mapped vm objs 584 | ret_node = cm_lst_uln_n(&map->vm_objs, obj_node); 585 | if (ret_node != obj_node) { 586 | mc_errno = MC_ERR_CMORE; 587 | return -1; 588 | } 589 | 590 | //disconnect object node's attributes 591 | obj->mapped = false; 592 | obj->start_addr = MC_UNDEF_ADDR; 593 | obj->end_addr = MC_UNDEF_ADDR; 594 | obj_node->next = NULL; 595 | obj_node->prev = NULL; 596 | 597 | //move this object node to the unmapped list 598 | ret_node = cm_lst_apd(&map->vm_objs_unmapped, &obj_node); 599 | if (ret_node == NULL) { 600 | mc_errno = MC_ERR_CMORE; 601 | return -1; 602 | } 603 | 604 | return 0; 605 | } 606 | 607 | 608 | DBG_STATIC DBG_INLINE 609 | int _map_unlink_unmapped_area(cm_lst_node * area_node, 610 | _traverse_state * state, mc_vm_map * map) { 611 | 612 | int ret; 613 | cm_lst_node * ret_node; 614 | 615 | mc_vm_area * area; 616 | 617 | mc_vm_obj * obj; 618 | cm_lst_node * obj_node; 619 | 620 | 621 | //get vm area of node 622 | area = MC_GET_NODE_AREA(area_node); 623 | 624 | //remove this area from its parent object if necessary 625 | if (area->obj_node_p != NULL) { 626 | 627 | obj_node = area->obj_node_p; 628 | obj = MC_GET_NODE_OBJ(obj_node); 629 | 630 | //remove this area from the object 631 | ret = _map_obj_rmv_area(obj, area_node); 632 | if (ret == -1) return -1; 633 | 634 | //if this area's object now has no areas, unmap it 635 | if (obj->vm_area_node_ps.len == 0) { 636 | 637 | ret = _map_unlink_unmapped_obj(obj_node, state, map); 638 | if (ret) return -1; 639 | } 640 | } 641 | 642 | 643 | //remove this area from its last parent object if necessary 644 | if (area->last_obj_node_p != NULL) { 645 | 646 | obj_node = area->last_obj_node_p; 647 | obj = MC_GET_NODE_OBJ(obj_node); 648 | 649 | //remove this area from the last object 650 | ret = _map_obj_rmv_last_area(obj, area_node); 651 | if (ret == -1) return -1; 652 | } 653 | 654 | 655 | //unlink this node from the list of mapped vm areas 656 | ret_node = cm_lst_uln_n(&map->vm_areas, area_node); 657 | if (ret_node != area_node) { 658 | mc_errno = MC_ERR_CMORE; 659 | return -1; 660 | } 661 | 662 | //disconnect area node's attributes 663 | area->mapped = false; 664 | area->obj_node_p = NULL; 665 | area->last_obj_node_p = NULL; 666 | area_node->next = NULL; 667 | area_node->prev = NULL; 668 | 669 | //move this area node to the unmapped list 670 | ret_node = cm_lst_apd(&map->vm_areas_unmapped, &area_node); 671 | if (ret_node == NULL) { 672 | mc_errno = MC_ERR_CMORE; 673 | return -1; 674 | } 675 | 676 | return 0; 677 | } 678 | 679 | 680 | DBG_STATIC DBG_INLINE 681 | int _map_check_area_eql(const struct vm_entry * entry, 682 | const cm_lst_node * area_node) { 683 | 684 | mc_vm_area * area = MC_GET_NODE_AREA(area_node); 685 | 686 | //check misc 687 | if ((entry->vm_start != area->start_addr) 688 | || (entry->vm_end != area->end_addr)) return -1; 689 | if ((entry->prot & VM_PROT_MASK) != area->access) return -1; 690 | 691 | //check pathname 692 | if (entry->file_path[0] == '\0' && area->pathname == NULL) return 0; 693 | if (((uintptr_t) entry->file_path[0] == '\0') 694 | || (area->pathname == NULL)) return -1; 695 | if (strncmp(entry->file_path, area->pathname, PATH_MAX)) return -1; 696 | 697 | return 0; 698 | } 699 | 700 | 701 | DBG_STATIC 702 | void _map_state_inc_area(_traverse_state * state, const int inc_type, 703 | const cm_lst_node * assign_node, mc_vm_map * map) { 704 | 705 | switch (inc_type) { 706 | 707 | case _STATE_AREA_NODE_KEEP: 708 | 709 | break; 710 | 711 | case _STATE_AREA_NODE_ADVANCE: 712 | 713 | //advance next area if we haven't reached the end 714 | //& dont circle back to the start 715 | if (state->next_area_node != NULL 716 | && state->next_area_node->next != map->vm_areas.head) { 717 | state->next_area_node = state->next_area_node->next; 718 | 719 | } else { 720 | state->next_area_node = NULL; 721 | } 722 | break; 723 | 724 | case _STATE_AREA_NODE_REASSIGN: 725 | 726 | state->next_area_node = (cm_lst_node *) assign_node; 727 | break; 728 | 729 | } //end switch 730 | 731 | return; 732 | } 733 | 734 | 735 | DBG_STATIC 736 | void _map_state_inc_obj(_traverse_state * state, mc_vm_map * map) { 737 | 738 | //if there is no prev obj, initialise it 739 | if (state->prev_obj_node == NULL) { 740 | state->prev_obj_node = map->vm_objs.head; 741 | 742 | //if there is a prev obj 743 | } else { 744 | //only advance next object if we won't circle back to start 745 | if (state->prev_obj_node->next != map->vm_objs.head) { 746 | state->prev_obj_node = state->prev_obj_node->next; 747 | } 748 | } 749 | 750 | return; 751 | } 752 | 753 | 754 | //return: 0: removed all areas up to entry->end_addr 755 | // 1: stopped iterating when matching area found 756 | DBG_STATIC DBG_INLINE 757 | int _map_resync_area(const struct vm_entry * entry, 758 | _traverse_state * state, mc_vm_map * map) { 759 | 760 | int ret; 761 | 762 | mc_vm_area * area; 763 | cm_lst_node * area_node; 764 | 765 | 766 | //setup iteration 767 | area_node = state->next_area_node; 768 | area = MC_GET_NODE_AREA(area_node); 769 | 770 | //while there are vm areas left to discard 771 | while (entry->vm_end > area->start_addr 772 | && _map_check_area_eql(entry, area_node) == -1) { 773 | 774 | //advance state 775 | _map_state_inc_area(state, _STATE_AREA_NODE_ADVANCE, NULL, map); 776 | 777 | //remove this area node 778 | ret = _map_unlink_unmapped_area(area_node, state, map); 779 | if (ret) return -1; 780 | 781 | //update iterator nodes 782 | if (state->next_area_node == NULL) break; 783 | area_node = state->next_area_node; 784 | area = MC_GET_NODE_AREA(area_node); 785 | 786 | } //end while 787 | 788 | if (entry->vm_end > area->start_addr) return 1; 789 | return 0; 790 | } 791 | 792 | 793 | DBG_STATIC 794 | cm_lst_node * _map_add_obj(const struct vm_entry * entry, 795 | _traverse_state * state, mc_vm_map * map) { 796 | 797 | mc_vm_obj vm_obj; 798 | cm_lst_node * obj_node; 799 | 800 | 801 | //create new object 802 | _map_new_vm_obj(&vm_obj, map, entry->file_path); 803 | 804 | //insert obj into map 805 | obj_node = cm_lst_ins_na(&map->vm_objs, state->prev_obj_node, &vm_obj); 806 | if (obj_node == NULL) { 807 | mc_errno = MC_ERR_CMORE; 808 | return NULL; 809 | } 810 | 811 | //advance state 812 | _map_state_inc_obj(state, map); 813 | 814 | return obj_node; 815 | } 816 | 817 | 818 | DBG_STATIC 819 | int _map_add_area(const struct vm_entry * entry, 820 | _traverse_state * state, mc_vm_map * map) { 821 | 822 | int ret; 823 | bool use_obj; 824 | bool forward_obj = false; 825 | 826 | mc_vm_area area; 827 | cm_lst_node * area_node; 828 | 829 | mc_vm_obj * obj; 830 | cm_lst_node * obj_node; 831 | 832 | 833 | //determine if this area belongs to a backing object 834 | use_obj = (entry->file_path[0] == '\0') ? false : true; 835 | 836 | 837 | //if this area does not belong to a backing object, create a new area 838 | if (!use_obj) { 839 | 840 | /* 841 | * It should never be possible for prev_obj 842 | * to point at/ahead of this vm_area. 843 | */ 844 | _map_init_vm_area(&area, entry, NULL, state->prev_obj_node, map); 845 | 846 | 847 | //else there is a backing object for this area 848 | } else { 849 | 850 | //determine which of the adjascent backing objects this area belongs to 851 | ret = _map_find_obj_for_area(entry, state); 852 | 853 | //dispatch case 854 | switch (ret) { 855 | 856 | //area belongs to the previous object 857 | case _MAP_OBJ_PREV: 858 | break; 859 | 860 | //area is the start of a new object 861 | case _MAP_OBJ_NEW: 862 | obj_node = _map_add_obj(entry, state, map); 863 | if (obj_node == NULL) return -1; 864 | forward_obj = true; 865 | break; 866 | 867 | //area belongs to the next object 868 | case _MAP_OBJ_NEXT: 869 | _map_state_inc_obj(state, map); 870 | break; 871 | 872 | } //end switch 873 | 874 | //initialise the area 875 | _map_init_vm_area(&area, entry, state->prev_obj_node, NULL, map); 876 | 877 | } //end if-else 878 | 879 | 880 | //add area to the map list 881 | if (state->next_area_node == NULL) { 882 | 883 | //if map is empty, insert at start 884 | if (map->vm_areas.head == NULL) { 885 | area_node = cm_lst_ins(&map->vm_areas, 0, &area); 886 | } else { 887 | area_node = cm_lst_ins(&map->vm_areas, -1, &area); 888 | } 889 | 890 | } else { 891 | area_node = cm_lst_ins_nb(&map->vm_areas, 892 | state->next_area_node, &area); 893 | } 894 | if (area_node == NULL) { 895 | mc_errno = MC_ERR_CMORE; 896 | return -1; 897 | } 898 | 899 | //add area to the object pointer list 900 | obj = MC_GET_NODE_OBJ(state->prev_obj_node); 901 | if (use_obj) { 902 | ret = _map_obj_add_area(obj, area_node); 903 | if (ret == -1) return -1; 904 | 905 | } else { 906 | ret = _map_obj_add_last_area(obj, area_node); 907 | if (ret == -1) return -1; 908 | } 909 | 910 | /* 911 | * Only now that the new object has an area associated with it (and hence 912 | * a valid address range) can areas be forwarded to it. 913 | */ 914 | if (forward_obj) { 915 | ret = _map_forward_unmapped_obj_last_vm_areas(state->prev_obj_node); 916 | if (ret == -1) return -1; 917 | } 918 | 919 | /* 920 | * Increment area state if this new area is higher than the state's area. 921 | * It should be impossible for areas to have address overlap due to 922 | * _map_resync_area() eliminating old areas that overlap with new areas. 923 | * Because of this, comparing just end addresses should suffice. 924 | */ 925 | if ((state->next_area_node != NULL) 926 | && (entry->vm_end 927 | >= MC_GET_NODE_AREA(state->next_area_node)->end_addr)) { 928 | _map_state_inc_area(state, _STATE_AREA_NODE_ADVANCE, NULL, map); 929 | } 930 | 931 | return 0; 932 | } 933 | 934 | 935 | 936 | /* 937 | * --- [INTERFACE] --- 938 | */ 939 | 940 | int map_send_entry(const struct vm_entry * entry, 941 | _traverse_state * state, mc_vm_map * map) { 942 | 943 | int ret; 944 | 945 | 946 | //if reached the end of the old map 947 | if (state->next_area_node == NULL) { 948 | 949 | ret = _map_add_area(entry, state, map); 950 | if (ret == -1) return -1; 951 | 952 | //if not reached the end end of the old map 953 | } else { 954 | 955 | //if entry doesn't match next area (a change in the map) 956 | if (_map_check_area_eql(entry, state->next_area_node) == -1) { 957 | 958 | ret = _map_resync_area(entry, state, map); 959 | if (ret == -1) return -1; 960 | 961 | //replace area 962 | if (ret == 0) { 963 | ret = _map_add_area(entry, state, map); 964 | if (ret == -1) return -1; 965 | //area match found, advance state 966 | } else { 967 | _map_state_inc_area(state, 968 | _STATE_AREA_NODE_ADVANCE, NULL, map); 969 | } 970 | 971 | // else entry matches next area 972 | } else { 973 | 974 | _map_state_inc_area(state, 975 | _STATE_AREA_NODE_ADVANCE, NULL, map); 976 | 977 | //check if area belongs to the next obj 978 | if (_map_find_obj_for_area(entry, state) == _MAP_OBJ_NEXT) { 979 | _map_state_inc_obj(state, map); 980 | } 981 | 982 | } //end if match 983 | 984 | } //end if map end 985 | 986 | return 0; 987 | } 988 | 989 | 990 | void map_init_traverse_state(_traverse_state * state, const mc_vm_map * map) { 991 | 992 | state->next_area_node = map->vm_areas.len == 0 ? NULL : map->vm_areas.head; 993 | state->prev_obj_node = map->vm_objs.len == 0 ? NULL : map->vm_objs.head; 994 | 995 | return; 996 | } 997 | 998 | 999 | 1000 | /* 1001 | * --- [EXTERNAL] --- 1002 | */ 1003 | 1004 | void mc_new_vm_map(mc_vm_map * map) { 1005 | 1006 | //pseudo object, will adopt leading parentless vm_areas 1007 | mc_vm_obj zero_obj; 1008 | 1009 | //zero the map 1010 | memset(map, 0, sizeof(*map)); 1011 | 1012 | //initialise lists 1013 | cm_new_lst(&map->vm_areas, sizeof(mc_vm_area)); 1014 | cm_new_lst(&map->vm_objs, sizeof(mc_vm_obj)); 1015 | 1016 | cm_new_lst(&map->vm_areas_unmapped, sizeof(cm_lst_node *)); 1017 | cm_new_lst(&map->vm_objs_unmapped, sizeof(cm_lst_node *)); 1018 | 1019 | //setup pseudo object at start of map 1020 | _map_new_vm_obj(&zero_obj, map, "0x0"); 1021 | _map_make_zero_obj(&zero_obj); 1022 | 1023 | cm_lst_apd(&map->vm_objs, &zero_obj); 1024 | 1025 | //set next IDs to 0 1026 | map->next_id_area = map->next_id_obj = 0; 1027 | 1028 | return; 1029 | } 1030 | 1031 | 1032 | int mc_del_vm_map(mc_vm_map * map) { 1033 | 1034 | int ret, len_obj; 1035 | 1036 | cm_lst_node * obj_node; 1037 | mc_vm_obj * obj; 1038 | 1039 | 1040 | //unallocate all unmapped nodes 1041 | ret = mc_map_clean_unmapped(map); 1042 | if (ret) return -1; 1043 | 1044 | 1045 | //setup iteration 1046 | len_obj = map->vm_objs.len; 1047 | obj_node = map->vm_objs.head; 1048 | 1049 | //manually free all object lists 1050 | for (int i = 0; i < len_obj; ++i) { 1051 | 1052 | //fetch the object 1053 | obj = MC_GET_NODE_OBJ(obj_node); 1054 | 1055 | //destroy the object's list 1056 | _map_del_vm_obj(obj); 1057 | //cm_del_lst(&obj->vm_area_node_ps); 1058 | //cm_del_lst(&obj->last_vm_area_node_ps); 1059 | 1060 | //advance iteration 1061 | obj_node = obj_node->next; 1062 | 1063 | } //end for 1064 | 1065 | 1066 | //destroy all lists 1067 | cm_del_lst(&map->vm_areas); 1068 | cm_del_lst(&map->vm_objs); 1069 | cm_del_lst(&map->vm_areas_unmapped); 1070 | cm_del_lst(&map->vm_objs_unmapped); 1071 | 1072 | return 0; 1073 | } 1074 | 1075 | 1076 | int mc_map_clean_unmapped(mc_vm_map * map) { 1077 | 1078 | int ret, len; 1079 | 1080 | mc_vm_obj * obj; 1081 | cm_lst_node * node, * del_node; 1082 | 1083 | 1084 | //setup unmapped area iteration 1085 | len = map->vm_areas_unmapped.len; 1086 | node = map->vm_areas_unmapped.head; 1087 | 1088 | 1089 | //manually free all unmapped area nodes 1090 | for (int i = 0; i < len; ++i) { 1091 | 1092 | //delete the unmapped area node 1093 | del_node = MC_GET_NODE_PTR(node); 1094 | cm_del_lst_node(del_node); 1095 | 1096 | //advance iteration 1097 | node = node->next; 1098 | 1099 | } //end for 1100 | 1101 | 1102 | //setup unmapped object iteration 1103 | len = map->vm_objs_unmapped.len; 1104 | node = map->vm_objs_unmapped.head; 1105 | 1106 | //manually free all unmapped obj nodes 1107 | for (int i = 0; i < len; ++i) { 1108 | 1109 | //delete the unmapped object and its node 1110 | del_node = MC_GET_NODE_PTR(node); 1111 | obj = MC_GET_NODE_OBJ(del_node); 1112 | 1113 | _map_del_vm_obj(obj); 1114 | cm_del_lst_node(del_node); 1115 | 1116 | //advance iteration 1117 | node = node->next; 1118 | 1119 | } //end for 1120 | 1121 | 1122 | //empty out both unmapped lists 1123 | ret = cm_lst_emp(&map->vm_areas_unmapped); 1124 | if (ret) { 1125 | mc_errno = MC_ERR_CMORE; 1126 | return -1; 1127 | } 1128 | 1129 | ret = cm_lst_emp(&map->vm_objs_unmapped); 1130 | if (ret) { 1131 | mc_errno = MC_ERR_CMORE; 1132 | return -1; 1133 | } 1134 | 1135 | return 0; 1136 | } 1137 | -------------------------------------------------------------------------------- /src/lib/map.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_H 2 | #define MAP_H 3 | 4 | //local headers 5 | #include "memcry.h" 6 | #include "krncry.h" 7 | 8 | 9 | #define _MAP_OBJ_PREV 0 10 | #define _MAP_OBJ_NEW 1 11 | #define _MAP_OBJ_NEXT 2 12 | 13 | #define _STATE_AREA_NODE_KEEP 0 14 | #define _STATE_AREA_NODE_ADVANCE 1 15 | #define _STATE_AREA_NODE_REASSIGN 2 16 | 17 | 18 | /* 19 | * Initialise _traverse_state manually on the first call to 20 | * send_map_node() for a map generated by a memory interface. 21 | * 22 | * send_map_node() will automatically update it for use by 23 | * all subsequent calls. 24 | */ 25 | 26 | typedef struct { 27 | 28 | cm_lst_node * next_area_node; //mc_vm_area 29 | cm_lst_node * prev_obj_node; //mc_vm_obj 30 | 31 | } _traverse_state; 32 | 33 | 34 | #ifdef DEBUG 35 | //internal 36 | void _map_init_vm_area(mc_vm_area * area, const struct vm_entry * entry, 37 | const cm_lst_node * obj_node, 38 | const cm_lst_node * last_obj_node, mc_vm_map * map); 39 | void _map_new_vm_obj(mc_vm_obj * obj, 40 | mc_vm_map * map, const char * pathname); 41 | void _map_del_vm_obj(mc_vm_obj * obj); 42 | 43 | void _map_make_zero_obj(mc_vm_obj * obj); 44 | 45 | int _map_obj_add_area_insert(cm_lst * obj_area_lst, 46 | const cm_lst_node * area_node); 47 | cm_lst_node * _map_obj_find_area_outer_node(cm_lst * obj_area_lst, 48 | cm_lst_node * area_node); 49 | 50 | int _map_obj_add_area(mc_vm_obj * obj, const cm_lst_node * area_node); 51 | int _map_obj_add_last_area(mc_vm_obj * obj, const cm_lst_node * last_area_node); 52 | 53 | int _map_obj_rmv_area_fast(mc_vm_obj * obj, cm_lst_node * outer_area_node); 54 | int _map_obj_rmv_area(mc_vm_obj * obj, cm_lst_node * inner_area_node); 55 | int _map_obj_rmv_last_area_fast(mc_vm_obj * obj, cm_lst_node * outer_area_node); 56 | int _map_obj_rmv_last_area(mc_vm_obj * obj, cm_lst_node * inner_area_node); 57 | 58 | bool _map_is_pathname_in_obj(const char * pathname, const mc_vm_obj * obj); 59 | int _map_find_obj_for_area(const struct vm_entry * entry, 60 | const _traverse_state * state); 61 | 62 | int _map_backtrack_unmapped_obj_last_vm_areas(cm_lst_node * obj_node); 63 | int _map_forward_unmapped_obj_last_vm_areas(cm_lst_node * obj_node); 64 | 65 | int _map_unlink_unmapped_obj(cm_lst_node * obj_node, 66 | _traverse_state * state, mc_vm_map * map); 67 | int _map_unlink_unmapped_area(cm_lst_node * area_node, 68 | _traverse_state * state, mc_vm_map * map); 69 | 70 | int _map_check_area_eql(const struct vm_entry * entry, 71 | const cm_lst_node * area_node); 72 | 73 | void _map_state_inc_area(_traverse_state * state, const int inc_type, 74 | const cm_lst_node * assign_node, mc_vm_map * map); 75 | void _map_state_inc_obj(_traverse_state * state, mc_vm_map * map); 76 | int _map_resync_area(const struct vm_entry * entry, 77 | _traverse_state * state, mc_vm_map * map); 78 | 79 | cm_lst_node * _map_add_obj(const struct vm_entry * entry, 80 | _traverse_state * state, mc_vm_map * map); 81 | int _map_add_area(const struct vm_entry * entry, 82 | _traverse_state * state, mc_vm_map * map); 83 | #endif 84 | 85 | 86 | //interface 87 | int map_send_entry(const struct vm_entry * entry, 88 | _traverse_state * state, mc_vm_map * map); 89 | void map_init_traverse_state(_traverse_state * state, const mc_vm_map * map); 90 | 91 | 92 | //external 93 | void mc_new_vm_map(mc_vm_map * map); 94 | int mc_del_vm_map(mc_vm_map * map); 95 | int mc_map_clean_unmapped(mc_vm_map * map); 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /src/lib/map_util.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | 4 | //external libraries 5 | #include 6 | 7 | //local headers 8 | #include "memcry.h" 9 | #include "map_util.h" 10 | #include "debug.h" 11 | 12 | 13 | #define _MAP_UTIL_GET_AREA 0 14 | #define _MAP_UTIL_GET_OBJ 1 15 | 16 | #define _MAP_UTIL_USE_PATHNAME 0 17 | #define _MAP_UTIL_USE_BASENAME 1 18 | 19 | 20 | 21 | /* 22 | * --- [INTERNAL] --- 23 | */ 24 | 25 | DBG_STATIC DBG_INLINE 26 | bool _is_map_empty(const mc_vm_map * vm_map) { 27 | 28 | if (vm_map->vm_areas.len == 0) return true; 29 | if (vm_map->vm_objs.len == 0) return true; 30 | 31 | return false; 32 | } 33 | 34 | 35 | /* 36 | * Determines starting object for iteration. 37 | * Will skip the pseudo-object if it is empty. 38 | */ 39 | 40 | DBG_STATIC DBG_INLINE 41 | cm_lst_node * _get_starting_obj(const mc_vm_map * vm_map) { 42 | 43 | cm_lst_node * obj_node; 44 | mc_vm_obj * vm_obj; 45 | 46 | obj_node = vm_map->vm_objs.head; 47 | vm_obj = MC_GET_NODE_OBJ(obj_node); 48 | 49 | //if pseudo object has no areas 50 | if (vm_obj->start_addr == MC_UNDEF_ADDR || vm_obj->start_addr == 0x0) { 51 | obj_node = obj_node->next; 52 | } 53 | 54 | return obj_node; 55 | } 56 | 57 | 58 | DBG_STATIC DBG_INLINE 59 | cm_lst_node * _get_obj_last_area(const mc_vm_obj * vm_obj) { 60 | 61 | cm_lst_node * last_node; 62 | 63 | //if this object has multiple areas 64 | if (vm_obj->vm_area_node_ps.len > 1) { 65 | last_node = MC_GET_NODE_PTR(vm_obj->vm_area_node_ps.head->prev); 66 | 67 | //else this object only has one area 68 | } else { 69 | last_node = MC_GET_NODE_PTR(vm_obj->vm_area_node_ps.head); 70 | } 71 | 72 | return last_node; 73 | } 74 | 75 | 76 | DBG_STATIC 77 | cm_lst_node * _fast_addr_find(const mc_vm_map * vm_map, 78 | const uintptr_t addr, const int mode) { 79 | 80 | cm_lst_node * iter_obj_node; 81 | cm_lst_node * iter_area_node; 82 | 83 | mc_vm_obj * iter_vm_obj; 84 | mc_vm_area * iter_vm_area; 85 | 86 | mc_vm_area * prev_area; 87 | 88 | 89 | //check map is not empty 90 | if (_is_map_empty(vm_map)) return NULL; 91 | 92 | //init object iteration 93 | iter_obj_node = _get_starting_obj(vm_map); 94 | iter_vm_obj = MC_GET_NODE_OBJ(iter_obj_node); 95 | 96 | iter_area_node = MC_GET_NODE_PTR(iter_vm_obj->vm_area_node_ps.head); 97 | iter_vm_area = MC_GET_NODE_AREA(iter_area_node); 98 | 99 | 100 | //if the address is in the very first object 101 | if (!(iter_vm_obj->end_addr > addr)) { 102 | 103 | //while can still iterate through objects 104 | for (int i = 1; i < vm_map->vm_objs.len; ++i) { 105 | 106 | //check if addr may be in this object 107 | if (iter_vm_obj->end_addr > addr) break; 108 | 109 | iter_area_node = _get_obj_last_area(iter_vm_obj); 110 | iter_vm_area = MC_GET_NODE_AREA(iter_area_node); 111 | 112 | iter_obj_node = iter_obj_node->next; 113 | iter_vm_obj = MC_GET_NODE_OBJ(iter_obj_node); 114 | 115 | } //end object search 116 | 117 | }//end if 118 | 119 | //if an obj was requested 120 | if (mode == _MAP_UTIL_GET_OBJ) { 121 | 122 | //if the address is in range of the obj 123 | if (iter_vm_obj->start_addr <= addr && iter_vm_obj->end_addr > addr) { 124 | return iter_obj_node; 125 | } else { 126 | return NULL; 127 | } 128 | } //end if an obj was requested 129 | 130 | 131 | //switch to area search and continue the search from last object's end area 132 | while (1) { 133 | 134 | //if found a matching area 135 | if (iter_vm_area->start_addr <= addr 136 | && iter_vm_area->end_addr > addr) { 137 | return iter_area_node; 138 | } 139 | 140 | //move to next object 141 | prev_area = iter_vm_area; 142 | iter_area_node = iter_area_node->next; 143 | iter_vm_area = MC_GET_NODE_AREA(iter_area_node); 144 | 145 | //check if back to start of dl-list, and backtrack if true 146 | if (prev_area->start_addr > iter_vm_area->end_addr) { 147 | break; 148 | } 149 | 150 | } //end area search 151 | 152 | 153 | //exhausted all areas and address was not found 154 | return NULL; 155 | } 156 | 157 | 158 | DBG_STATIC 159 | cm_lst_node * _obj_name_find(const mc_vm_map * vm_map, 160 | const char * name, const int mode) { 161 | 162 | int ret; 163 | 164 | cm_lst_node * iter_obj_node; 165 | mc_vm_obj * iter_vm_obj; 166 | 167 | //check map is not empty 168 | if (_is_map_empty(vm_map)) return NULL; 169 | 170 | 171 | //init search 172 | iter_obj_node = vm_map->vm_objs.head; 173 | iter_vm_obj = MC_GET_NODE_OBJ(iter_obj_node); 174 | 175 | //while can still iterate through objects 176 | for (int i = 0; i < vm_map->vm_objs.len; ++i) { 177 | 178 | //carry out comparison 179 | if (mode == _MAP_UTIL_USE_PATHNAME) { 180 | ret = strncmp(name, iter_vm_obj->pathname, PATH_MAX); 181 | } else { 182 | ret = strncmp(name, iter_vm_obj->basename, NAME_MAX); 183 | } 184 | 185 | //if found a match 186 | if (ret == 0) { 187 | return iter_obj_node; 188 | } 189 | 190 | iter_obj_node = iter_obj_node->next; 191 | iter_vm_obj = MC_GET_NODE_OBJ(iter_obj_node); 192 | 193 | } //end object search 194 | 195 | return NULL; 196 | } 197 | 198 | 199 | 200 | /* 201 | * --- [EXTERNAL] --- 202 | */ 203 | 204 | off_t mc_get_area_off(const cm_lst_node * area_node, 205 | const uintptr_t addr) { 206 | 207 | mc_vm_area * vm_area = MC_GET_NODE_AREA(area_node); 208 | 209 | return (addr - vm_area->start_addr); 210 | } 211 | 212 | 213 | off_t mc_get_obj_off(const cm_lst_node * obj_node, 214 | const uintptr_t addr) { 215 | 216 | mc_vm_obj * vm_obj = MC_GET_NODE_OBJ(obj_node); 217 | 218 | return (addr - vm_obj->start_addr); 219 | } 220 | 221 | 222 | off_t mc_get_area_off_bnd(const cm_lst_node * area_node, 223 | const uintptr_t addr) { 224 | 225 | mc_vm_area * vm_area = MC_GET_NODE_AREA(area_node); 226 | 227 | if ((addr >= vm_area->end_addr) || (addr < vm_area->start_addr)) return -1; 228 | return (addr - vm_area->start_addr); 229 | } 230 | 231 | 232 | off_t mc_get_obj_off_bnd(const cm_lst_node * obj_node, 233 | const uintptr_t addr) { 234 | 235 | mc_vm_obj * vm_obj = MC_GET_NODE_OBJ(obj_node); 236 | 237 | if ((addr >= vm_obj->end_addr) || (addr < vm_obj->start_addr)) return -1; 238 | return (addr - vm_obj->start_addr); 239 | } 240 | 241 | 242 | cm_lst_node * mc_get_area_by_addr(const mc_vm_map * vm_map, 243 | const uintptr_t addr, off_t * offset) { 244 | 245 | cm_lst_node * area_node; 246 | 247 | area_node = _fast_addr_find(vm_map, addr, _MAP_UTIL_GET_AREA); 248 | if (!area_node) return NULL; 249 | 250 | if (offset != NULL) *offset = mc_get_area_off(area_node, addr); 251 | 252 | return area_node; 253 | } 254 | 255 | 256 | cm_lst_node * mc_get_obj_by_addr(const mc_vm_map * vm_map, 257 | const uintptr_t addr, off_t * offset) { 258 | 259 | cm_lst_node * obj_node; 260 | 261 | obj_node = _fast_addr_find(vm_map, addr, _MAP_UTIL_GET_OBJ); 262 | if (!obj_node) return NULL; 263 | 264 | if (offset != NULL) *offset = mc_get_obj_off(obj_node, addr); 265 | 266 | return obj_node; 267 | } 268 | 269 | 270 | cm_lst_node * mc_get_obj_by_pathname(const mc_vm_map * vm_map, 271 | const char * pathname) { 272 | 273 | cm_lst_node * obj_node; 274 | 275 | obj_node = _obj_name_find(vm_map, pathname, _MAP_UTIL_USE_PATHNAME); 276 | if (!obj_node) return NULL; 277 | 278 | return obj_node; 279 | } 280 | 281 | 282 | cm_lst_node * mc_get_obj_by_basename(const mc_vm_map * vm_map, 283 | const char * basename) { 284 | 285 | cm_lst_node * obj_node; 286 | 287 | obj_node = _obj_name_find(vm_map, basename, _MAP_UTIL_USE_BASENAME); 288 | if (!obj_node) return NULL; 289 | 290 | return obj_node; 291 | } 292 | 293 | 294 | void mc_access_to_str(const cm_byte access, char * str_buf) { 295 | 296 | cm_byte bit_mask = 1; 297 | const char * on = "rwxs"; 298 | const char * off = "---p"; 299 | 300 | //for every bit in the access mask 301 | for (int i = 0; i < 4; ++i) { 302 | 303 | //convert the permission bit to its character representation 304 | str_buf[i] = (bit_mask << i) & access ? on[i] : off[i]; 305 | } 306 | 307 | //add a NULL terminator 308 | str_buf[4] = '\0'; 309 | 310 | return; 311 | } 312 | -------------------------------------------------------------------------------- /src/lib/map_util.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_UTIL_H 2 | #define MAP_UTIL_H 3 | 4 | //external libraries 5 | #include 6 | 7 | //local headers 8 | #include "memcry.h" 9 | 10 | 11 | #ifdef DEBUG 12 | //internal 13 | bool _is_map_empty(const mc_vm_map * vm_map); 14 | cm_lst_node * _get_starting_obj(const mc_vm_map * vm_map); 15 | cm_lst_node * _get_obj_last_area(const mc_vm_obj * vm_obj); 16 | cm_lst_node * _fast_addr_find(const mc_vm_map * vm_map, 17 | const uintptr_t addr, const int mode); 18 | cm_lst_node * _obj_name_find(const mc_vm_map * vm_map, 19 | const char * name, const int mode); 20 | #endif 21 | 22 | 23 | //external 24 | off_t mc_get_area_off(const cm_lst_node * area_node, 25 | const uintptr_t addr); 26 | off_t mc_get_obj_off(const cm_lst_node * obj_node, 27 | const uintptr_t addr); 28 | off_t mc_get_area_off_bnd(const cm_lst_node * area_node, 29 | const uintptr_t addr); 30 | off_t mc_get_obj_off_bnd(const cm_lst_node * obj_node, 31 | const uintptr_t addr); 32 | 33 | cm_lst_node * mc_get_area_by_addr(const mc_vm_map * vm_map, 34 | const uintptr_t addr, off_t * offset); 35 | cm_lst_node * mc_get_obj_by_addr(const mc_vm_map * vm_map, 36 | const uintptr_t addr, off_t * offset); 37 | 38 | cm_lst_node * mc_get_obj_by_pathname(const mc_vm_map * vm_map, 39 | const char * pathname); 40 | cm_lst_node * mc_get_obj_by_basename(const mc_vm_map * vm_map, 41 | const char * basename); 42 | 43 | void mc_access_to_str(const cm_byte access, char * str_buf); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/lib/memcry.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMCRY_H 2 | #define MEMCRY_H 3 | 4 | #ifdef __cplusplus 5 | extern "C"{ 6 | #endif 7 | 8 | //standard library 9 | #include 10 | #include 11 | #include 12 | 13 | //system headers 14 | #include 15 | #include 16 | 17 | //external libraries 18 | #include 19 | 20 | 21 | //easy extract from a cm_list_node pointer 22 | #define MC_GET_NODE_AREA(node) ((mc_vm_area *) (node->data)) 23 | #define MC_GET_NODE_OBJ(node) ((mc_vm_obj *) (node->data)) 24 | #define MC_GET_NODE_PTR(node) *((cm_lst_node **) (node->data)) 25 | 26 | //mc_vm_area.access bitmasks 27 | #define MC_ACCESS_READ 0x01 28 | #define MC_ACCESS_WRITE 0x02 29 | #define MC_ACCESS_EXEC 0x04 30 | #define MC_ACCESS_SHARED 0x08 31 | 32 | //pseudo object id 33 | #define MC_ZERO_OBJ_ID -1 34 | 35 | //-Wsign-compare '-1' 36 | #define MC_UNDEF_ADDR UINTPTR_MAX 37 | 38 | 39 | //interface types 40 | enum mc_iface_type { 41 | PROCFS = 0, 42 | KRNCRY = 1 43 | }; 44 | 45 | 46 | /* 47 | * --- [DATA TYPES] --- 48 | */ 49 | 50 | // [memory area] 51 | typedef struct { 52 | 53 | char * pathname; 54 | char * basename; 55 | 56 | uintptr_t start_addr; 57 | uintptr_t end_addr; 58 | 59 | cm_byte access; //logically AND with MC_ACCESS macros 60 | 61 | //mutually exclusive 62 | cm_lst_node * obj_node_p; //STORES: parent mc_vm_obj * 63 | cm_lst_node * last_obj_node_p; //STORES: last encountered mc_vm_obj * 64 | 65 | int id; 66 | bool mapped; //set to false when a map update discovers area to be unmapped 67 | 68 | } mc_vm_area; 69 | 70 | 71 | // [backing object] 72 | typedef struct { 73 | 74 | uintptr_t start_addr; 75 | uintptr_t end_addr; 76 | 77 | cm_lst vm_area_node_ps; //STORES: cm_lst_node * of mc_vm_area 78 | cm_lst last_vm_area_node_ps; //STORES: cm_lst_node * of mc_vm_area 79 | 80 | int id; 81 | bool mapped; //set to false when a map update discovers obj. to be unmapped 82 | 83 | char * basename; //[restrict .NAME_MAX] 84 | char * pathname; //[restrict .PATH_MAX] 85 | 86 | } mc_vm_obj; 87 | 88 | 89 | // [memory map] 90 | typedef struct { 91 | 92 | //up to date entries 93 | cm_lst vm_areas; //STORES: mc_vm_area 94 | cm_lst vm_objs; //STORES: mc_vm_obj 95 | 96 | //unmapped entries (storage for future deallocation) 97 | cm_lst vm_areas_unmapped; //STORES: cm_list_node * of mc_vm_area 98 | cm_lst vm_objs_unmapped; //STORES: cm_list_node * of mc_vm_obj 99 | 100 | // [internal] 101 | int next_id_area; 102 | int next_id_obj; 103 | 104 | } mc_vm_map; 105 | 106 | 107 | 108 | // [session] 109 | struct _mc_session; 110 | 111 | typedef struct { 112 | 113 | int (*open)(struct _mc_session *, const int); 114 | int (*close)(struct _mc_session *); 115 | int (*update_map)(const struct _mc_session *, mc_vm_map *); 116 | int (*read)(const struct _mc_session *, 117 | const uintptr_t, cm_byte *, const size_t); 118 | int (*write)(const struct _mc_session *, 119 | const uintptr_t, const cm_byte *, const size_t); 120 | 121 | } mc_iface; 122 | 123 | 124 | struct _mc_session { 125 | 126 | union { 127 | struct { 128 | int fd_mem; 129 | pid_t pid; 130 | }; //procfs_data 131 | struct { 132 | char major; 133 | int fd_dev_krncry; 134 | }; //krncry_data 135 | }; 136 | 137 | long page_size; 138 | mc_iface iface; 139 | 140 | }; 141 | typedef struct _mc_session mc_session; 142 | 143 | 144 | 145 | /* 146 | * --- [FUNCTIONS] --- 147 | */ 148 | 149 | // [util] 150 | //return: basename = success, NULL = fail/error 151 | extern char * mc_pathname_to_basename(const char * pathname); 152 | //must destroy 'pid_vector' manually on success | pid = success, -1 = fail/error 153 | extern pid_t mc_pid_by_name(const char * comm, cm_vct * pid_vector); 154 | //`name_buf` must be at least NAME_MAX in size. 155 | //return: 0 = success, -1 = fail/error 156 | extern int mc_name_by_pid(const pid_t pid, char * name_buf); 157 | //'out' must have space for double the length of 'inp' + 1 158 | extern void mc_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out); 159 | 160 | 161 | // [virtual interface] 162 | //all return 0 = success, -1 = fail/error 163 | extern int mc_open(mc_session * session, 164 | const enum mc_iface_type iface, const pid_t pid); 165 | extern int mc_close(mc_session * session); 166 | extern int mc_update_map(const mc_session * session, mc_vm_map * vm_map); 167 | extern int mc_read(const mc_session * session, const uintptr_t addr, 168 | cm_byte * buf, const size_t buf_sz); 169 | extern int mc_write(const mc_session * session, const uintptr_t addr, 170 | const cm_byte * buf, const size_t buf_sz); 171 | 172 | // --- [map] 173 | //all return 0 = success, -1 = fail/error 174 | extern void mc_new_vm_map(mc_vm_map * map); 175 | extern int mc_del_vm_map(mc_vm_map * map); 176 | extern int mc_map_clean_unmapped(mc_vm_map * map); 177 | 178 | 179 | // --- [map util] 180 | //return: offset from start of area/object 181 | extern off_t mc_get_area_off(const cm_lst_node * area_node, 182 | const uintptr_t addr); 183 | extern off_t mc_get_obj_off(const cm_lst_node * obj_node, 184 | const uintptr_t addr); 185 | //return: offset from start of area/object = success, -1 = not in area/object 186 | extern off_t mc_get_area_off_bnd(const cm_lst_node * area_node, 187 | const uintptr_t addr); 188 | extern off_t mc_get_obj_off_bnd(const cm_lst_node * obj_node, 189 | const uintptr_t addr); 190 | 191 | //return: area node * = success, NULL = fail/error 192 | extern cm_lst_node * mc_get_area_by_addr(const mc_vm_map * vm_map, 193 | const uintptr_t addr, off_t * offset); 194 | //return: obj node * = success, NULL = fail/error 195 | extern cm_lst_node * mc_get_obj_by_addr(const mc_vm_map * vm_map, 196 | const uintptr_t addr, off_t * offset); 197 | extern cm_lst_node * mc_get_obj_by_pathname(const mc_vm_map * vm_map, 198 | const char * pathname); 199 | extern cm_lst_node * mc_get_obj_by_basename(const mc_vm_map * vm_map, 200 | const char * basename); 201 | //`str_buf` must be at least 5 bytes in size. 202 | //return: 0 = success, -1 = fail/error 203 | extern void mc_access_to_str(const cm_byte access, char * str_buf); 204 | 205 | 206 | // --- [error handling] 207 | //void return 208 | extern void mc_perror(const char * prefix); 209 | extern const char * mc_strerror(const int mc_errnum); 210 | 211 | 212 | 213 | /* 214 | * --- [ERROR HANDLING] --- 215 | */ 216 | 217 | extern __thread int mc_errno; 218 | 219 | 220 | // [error codes] 221 | 222 | // 1XX - user errors 223 | #define MC_ERR_PROC_MEM 2100 224 | #define MC_ERR_PROC_MAP 2101 225 | #define MC_ERR_SEEK_ADDR 2102 226 | 227 | // 2XX - internal errors 228 | #define MC_ERR_INTERNAL_INDEX 2200 229 | #define MC_ERR_AREA_IN_OBJ 2201 230 | #define MC_ERR_UNEXPECTED_NULL 2202 231 | #define MC_ERR_CMORE 2203 232 | #define MC_ERR_READ_WRITE 2204 233 | #define MC_ERR_MEMU_TARGET 2205 234 | #define MC_ERR_MEMU_MAP_SZ 2206 235 | #define MC_ERR_MEMU_MAP_GET 2207 236 | #define MC_ERR_PROC_STATUS 2208 237 | #define MC_ERR_PROC_NAV 2209 238 | 239 | // 3XX - environmental errors 240 | #define MC_ERR_MEM 2300 241 | #define MC_ERR_PAGESIZE 2301 242 | #define MC_ERR_KRNCRY_MAJOR 2302 243 | #define MC_ERR_MEMU_OPEN 2303 244 | 245 | 246 | // [error code messages] 247 | 248 | // 1XX - user errors 249 | #define MC_ERR_PROC_MEM_MSG \ 250 | "Could not open /proc//mem for specified pid.\n" 251 | #define MC_ERR_PROC_MAP_MSG \ 252 | "Could not open /proc//maps for specified pid.\n" 253 | #define MC_ERR_SEEK_ADDR_MSG \ 254 | "Could not seek to specified address.\n" 255 | 256 | // 2XX - internal errors 257 | #define MC_ERR_INTERNAL_INDEX_MSG \ 258 | "Internal: Indexing error.\n" 259 | #define MC_ERR_AREA_IN_OBJ_MSG \ 260 | "Internal: Area is not in object when it should be.\n" 261 | #define MC_ERR_UNEXPECTED_NULL_MSG \ 262 | "Internal: Unexpected NULL pointer.\n" 263 | #define MC_ERR_CMORE_MSG \ 264 | "Internal: CMore error. See cm_perror().\n" 265 | #define MC_ERR_READ_WRITE_MSG \ 266 | "Internal: Read/write failed.\n" 267 | #define MC_ERR_MEMU_TARGET_MSG \ 268 | "Internal: Krncry target open failed.\n" 269 | #define MC_ERR_MEMU_MAP_SZ_MSG \ 270 | "Internal: Krncry map size fetch failed..\n" 271 | #define MC_ERR_MEMU_MAP_GET_MSG \ 272 | "Internal: Krncry map transfer failed.\n" 273 | #define MC_ERR_PROC_STATUS_MSG \ 274 | "Internal: Failed to use /proc//status.\n" 275 | #define MC_ERR_PROC_NAV_MSG \ 276 | "Internal: Failed to navigate /proc directories.\n" 277 | 278 | // 3XX - environmental errors 279 | #define MC_ERR_MEM_MSG \ 280 | "Failed to acquire the necessary memory.\n" 281 | #define MC_ERR_PAGESIZE_MSG \ 282 | "Unable to fetch pagesize through sysconf().\n" 283 | #define MC_ERR_KRNCRY_MAJOR_MSG \ 284 | "Could not fetch krncry's major number.\n" 285 | #define MC_ERR_MEMU_OPEN_MSG \ 286 | "Could not open krncry device.\n" 287 | 288 | 289 | #ifdef __cplusplus 290 | } 291 | #endif 292 | 293 | #endif 294 | -------------------------------------------------------------------------------- /src/lib/procfs_iface.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | //system headers 9 | #include 10 | 11 | #include 12 | 13 | //external libraries 14 | #include 15 | 16 | //local headers 17 | #include "procfs_iface.h" 18 | #include "memcry.h" 19 | #include "map.h" 20 | #include "krncry.h" 21 | 22 | 23 | 24 | /* 25 | * --- [INTERNAL] --- 26 | */ 27 | 28 | //build vm_entry from a line in procfs maps 29 | DBG_STATIC DBG_INLINE 30 | void _build_entry(struct vm_entry * entry, const char * line_buf) { 31 | 32 | int mode = 0; 33 | int done = 0; 34 | int column_count = 0; 35 | 36 | char * start_str; 37 | char * end_str; 38 | char * offset_str; 39 | char * file_path_str; 40 | 41 | char perm_chars[] = {'r', 'w', 'x', 's'}; 42 | cm_byte perm_vals[] = {VM_READ, VM_WRITE, VM_EXEC, VM_SHARED}; 43 | 44 | 45 | //save str for start addr 46 | start_str = (char *) line_buf; 47 | 48 | //for every character on the line 49 | for (int i = 0; i < LINE_LEN; ++i) { 50 | 51 | if (done) break; 52 | 53 | switch (mode) { 54 | 55 | case 0: //look for end addr 56 | if (line_buf[i] == '-' || line_buf[i] == 'p') { 57 | ++i; 58 | ++mode; 59 | end_str = (char *) line_buf + i; 60 | } else { 61 | continue; 62 | } 63 | break; 64 | 65 | case 1: //look for permissions & file offset 66 | if (line_buf[i] == ' ') { 67 | 68 | //get permissions 69 | ++i; 70 | ++mode; 71 | //for every char in perms field 72 | for (int j = 0; j < 4; ++j) { 73 | if (*(line_buf + i + j) == perm_chars[j]) { 74 | entry->prot += (krncry_pgprot_t) perm_vals[j]; 75 | } 76 | } //end for 77 | 78 | //get offset 79 | i += 5; 80 | offset_str = (char *) line_buf + i; 81 | 82 | } else { 83 | continue; 84 | } 85 | break; 86 | 87 | case 2: //look for file path 88 | if (line_buf[i] == ' ' || line_buf[i] == '\t') { 89 | ++column_count; 90 | continue; 91 | } 92 | if (column_count <= 2) continue; 93 | file_path_str = (char *) line_buf + i; 94 | done = 1; 95 | 96 | } //end switch 97 | 98 | } //end for every char 99 | 100 | 101 | //fill entry (`prot` already filled inside switch statement) 102 | entry->vm_start = (unsigned long) strtol(start_str, NULL, 16); 103 | entry->vm_end = (unsigned long) strtol(end_str, NULL, 16); 104 | entry->file_off = (unsigned long) strtol(offset_str, NULL, 16); 105 | strncpy(entry->file_path, file_path_str, PATH_MAX); 106 | entry->file_path[strcspn(entry->file_path, "\n")] = '\0'; 107 | 108 | return; 109 | } 110 | 111 | 112 | 113 | /* 114 | * --- [INTERFACE] --- 115 | */ 116 | 117 | int procfs_open(mc_session * session, const int pid) { 118 | 119 | int fd; 120 | char mem_buf[PATH_MAX] = {0}; 121 | 122 | 123 | session->pid = pid; 124 | 125 | //get page size to determine maximum read/write size 126 | session->page_size = sysconf(_SC_PAGESIZE); 127 | if (session->page_size < 0) { 128 | mc_errno = MC_ERR_PAGESIZE; 129 | return -1; 130 | } 131 | 132 | //open procfs mem file 133 | snprintf(mem_buf, PATH_MAX, "/proc/%d/mem", pid); 134 | fd = open(mem_buf, O_RDWR); 135 | if (fd == -1) { 136 | mc_errno = MC_ERR_PROC_MEM; 137 | return -1; 138 | } 139 | session->fd_mem = fd; 140 | 141 | return 0; 142 | } 143 | 144 | 145 | int procfs_close(mc_session * session) { 146 | 147 | //close procfs mem file 148 | close(session->fd_mem); 149 | 150 | return 0; 151 | } 152 | 153 | 154 | int procfs_update_map(const mc_session * session, mc_vm_map * vm_map) { 155 | 156 | int ret; 157 | FILE * fs; 158 | 159 | char map_buf[PATH_MAX] = {0}; 160 | char line_buf[LINE_LEN]; 161 | 162 | struct vm_entry new_entry; 163 | _traverse_state state; 164 | 165 | 166 | //open memory map 167 | snprintf(map_buf, PATH_MAX, "/proc/%d/maps", session->pid); 168 | fs = fopen(map_buf, "r"); 169 | if (fs == NULL) { 170 | mc_errno = MC_ERR_PROC_MAP; 171 | return -1; 172 | } 173 | 174 | //init traverse state for this map 175 | map_init_traverse_state(&state, vm_map); 176 | 177 | //while there are entries left 178 | while (fgets(line_buf, LINE_LEN, fs) != NULL) { 179 | 180 | memset(&new_entry, 0, sizeof(new_entry)); 181 | _build_entry(&new_entry, line_buf); 182 | 183 | ret = map_send_entry(&new_entry, &state, vm_map); 184 | if (ret != 0) return -1; 185 | 186 | } //end while 187 | 188 | 189 | //close memory map 190 | fclose(fs); 191 | 192 | return 0; 193 | } 194 | 195 | 196 | int procfs_read(const mc_session * session, const uintptr_t addr, 197 | cm_byte * buf, const size_t buf_sz) { 198 | 199 | off_t off_ret; 200 | ssize_t read_bytes, read_done, read_left; 201 | 202 | read_done = read_left = 0; 203 | 204 | //seek to address 205 | off_ret = lseek(session->fd_mem, (off_t) addr, SEEK_SET); 206 | if (off_ret == -1) { 207 | mc_errno = MC_ERR_SEEK_ADDR; 208 | return -1; 209 | } 210 | 211 | //read page_size bytes repeatedly until done 212 | do { 213 | 214 | //calc how many bytes left to read 215 | read_left = buf_sz - read_done; 216 | 217 | //read into buffer 218 | read_bytes = read(session->fd_mem, buf + read_done, 219 | read_left > session->page_size 220 | ? session->page_size : read_left); 221 | //if error or EOF before reading len bytes 222 | if (read_bytes == -1 || (read_bytes == 0 223 | && read_done < (ssize_t) buf_sz)) { 224 | 225 | mc_errno = MC_ERR_READ_WRITE; 226 | return -1; 227 | } 228 | read_done += read_bytes; 229 | 230 | } while (read_done < (ssize_t) buf_sz); 231 | 232 | return 0; 233 | } 234 | 235 | 236 | int procfs_write(const mc_session * session, const uintptr_t addr, 237 | const cm_byte * buf, const size_t buf_sz) { 238 | 239 | off_t off_ret; 240 | ssize_t write_bytes, write_done, write_left; 241 | 242 | write_done = write_left = 0; 243 | 244 | //seek to address 245 | off_ret = lseek(session->fd_mem, (off_t) addr, SEEK_SET); 246 | if (off_ret == -1) { 247 | mc_errno = MC_ERR_SEEK_ADDR; 248 | return -1; 249 | } 250 | 251 | //write page_size bytes repeatedly until done 252 | do { 253 | 254 | //calc how many bytes left to write 255 | write_left = buf_sz - write_done; 256 | 257 | //write into buffer 258 | write_bytes = write(session->fd_mem, buf + write_done, 259 | write_left > session->page_size 260 | ? session->page_size : write_left); 261 | //if error or EOF before writing len bytes 262 | if (write_bytes == -1 || (write_bytes == 0 263 | && write_done < (ssize_t) buf_sz)) { 264 | 265 | mc_errno = MC_ERR_READ_WRITE; 266 | return -1; 267 | } 268 | write_done += write_bytes; 269 | 270 | } while (write_done < (ssize_t) buf_sz); 271 | 272 | return 0; 273 | } 274 | -------------------------------------------------------------------------------- /src/lib/procfs_iface.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_IFACE_H 2 | #define PROC_IFACE_H 3 | 4 | //external libraries 5 | #include 6 | 7 | //local headers 8 | #include "memcry.h" 9 | #include "krncry.h" 10 | #include "debug.h" 11 | 12 | 13 | #define LINE_LEN PATH_MAX + 128 14 | 15 | 16 | #ifdef DEBUG 17 | //internal 18 | void _build_entry(struct vm_entry * entry, const char * line_buf); 19 | #endif 20 | 21 | 22 | //interface 23 | int procfs_open(mc_session * session, const int pid); 24 | int procfs_close(mc_session * session); 25 | int procfs_update_map(const mc_session * session, mc_vm_map * vm_map); 26 | int procfs_read(const mc_session * session, const uintptr_t addr, 27 | cm_byte * buf, const size_t buf_sz); 28 | int procfs_write(const mc_session * session, const uintptr_t addr, 29 | const cm_byte * buf, const size_t buf_sz); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/lib/util.c: -------------------------------------------------------------------------------- 1 | //standard libraries 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | //system headers 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | //local headers 16 | #include "memcry.h" 17 | #include "util.h" 18 | #include "debug.h" 19 | 20 | 21 | #define _LINE_LEN PATH_MAX + 128 22 | 23 | 24 | 25 | /* 26 | * --- [INTERNAL] --- 27 | */ 28 | 29 | //convert the first line of /proc/pid/status to a name (comm) 30 | DBG_STATIC DBG_INLINE 31 | void _line_to_name(const char * line_buf, char * name_buf) { 32 | 33 | line_buf += 5; 34 | char * name_str; 35 | 36 | //for every character in the line 37 | for (int i = 0; i < _LINE_LEN; ++i) { 38 | 39 | if (line_buf[i] == ' ' || line_buf[i] == '\t') continue; 40 | name_str = (char *) line_buf + 1; 41 | name_str[strcspn(name_str, "\n")] = '\0'; 42 | break; 43 | } 44 | 45 | strncpy(name_buf, name_str, NAME_MAX); 46 | 47 | return; 48 | } 49 | 50 | 51 | /* 52 | * Use `/proc/pid/status` to get the name of a process. This is 53 | * how utilities like `ps` and `top` fetch process names. 54 | */ 55 | 56 | DBG_STATIC 57 | int _get_status_name(char * name_buf, const pid_t pid) { 58 | 59 | int ret; 60 | char * fret; 61 | 62 | FILE * fs; 63 | 64 | char path_buf[PATH_MAX]; 65 | char line_buf[_LINE_LEN]; 66 | 67 | 68 | //build path 69 | snprintf(path_buf, PATH_MAX, "/proc/%d/status", pid); 70 | 71 | //get name 72 | fs = fopen(path_buf, "r"); 73 | if (fs == NULL) { 74 | mc_errno = MC_ERR_PROC_STATUS; 75 | return -1; 76 | } 77 | 78 | //read top line containing name (comm) of process 79 | fret = fgets(line_buf, _LINE_LEN, fs); 80 | if (fret == NULL) { 81 | fclose(fs); 82 | mc_errno = MC_ERR_PROC_STATUS; 83 | return -1; 84 | } 85 | 86 | //close file stream 87 | ret = fclose(fs); 88 | if (ret == -1) { 89 | mc_errno = MC_ERR_PROC_STATUS; 90 | return -1; 91 | } 92 | 93 | //extract name from line 94 | _line_to_name(line_buf, name_buf); 95 | 96 | //replace trailing newline in the name 97 | name_buf[strcspn(name_buf, "\n")] = '\0'; //replace trailing newline 98 | 99 | return 0; 100 | } 101 | 102 | 103 | 104 | /* 105 | * --- EXTERNAL 106 | */ 107 | 108 | char * mc_pathname_to_basename(const char * pathname) { 109 | 110 | char * basename = strrchr(pathname, (int) '/'); 111 | 112 | if (basename == NULL) return (char *) pathname; 113 | return basename + 1; 114 | } 115 | 116 | 117 | pid_t mc_pid_by_name(const char * comm, cm_vct * pid_vector) { 118 | 119 | int ret; 120 | int first_recorded = 0; 121 | 122 | char name_buf[NAME_MAX]; 123 | 124 | pid_t temp_pid; 125 | pid_t first_pid = -1; 126 | 127 | DIR * ds; 128 | struct dirent * d_ent; 129 | 130 | 131 | 132 | //initialise vector 133 | if (pid_vector != NULL) { 134 | ret = cm_new_vct(pid_vector, sizeof(pid_t)); 135 | if (ret) { 136 | mc_errno = MC_ERR_CMORE; 137 | return -1; 138 | } 139 | } 140 | 141 | //open proc directory 142 | ds = opendir("/proc"); 143 | if (ds == NULL) { 144 | if (pid_vector != NULL) cm_del_vct(pid_vector); 145 | mc_errno = MC_ERR_PROC_NAV; 146 | return -1; 147 | } 148 | 149 | //while entries left 150 | while ((d_ent = readdir(ds)) != NULL) { 151 | 152 | //if the directory belongs to a process 153 | if (d_ent->d_type != DT_DIR || !(isdigit(d_ent->d_name[0]) > 0)) continue; 154 | 155 | //convert dir name to pid_t 156 | temp_pid = (pid_t) strtoul(d_ent->d_name, NULL, 10); 157 | if (errno == ERANGE) { 158 | closedir(ds); 159 | if (pid_vector != NULL) cm_del_vct(pid_vector); 160 | mc_errno = MC_ERR_PROC_NAV; 161 | return -1; 162 | } 163 | 164 | //get the name of this pid 165 | ret = _get_status_name(name_buf, temp_pid); 166 | if (ret) { 167 | closedir(ds); 168 | if (pid_vector != NULL) cm_del_vct(pid_vector); 169 | return -1; 170 | } 171 | 172 | //if found a match 173 | ret = strcmp(name_buf, comm); 174 | if (!ret) { 175 | 176 | //add pid_t to list of potential PIDs if a vector is provided 177 | if (pid_vector != NULL) { 178 | 179 | ret = cm_vct_apd(pid_vector, (cm_byte *) &temp_pid); 180 | if (ret) { 181 | closedir(ds); 182 | cm_del_vct(pid_vector); 183 | mc_errno = MC_ERR_CMORE; 184 | return -1; 185 | } 186 | } 187 | 188 | //save first pid 189 | if (!first_recorded) { 190 | first_pid = temp_pid; 191 | ++first_recorded; 192 | if (pid_vector == NULL) break; 193 | } 194 | 195 | }//end if found process with matching name 196 | 197 | }//end while entries left 198 | 199 | ret = closedir(ds); 200 | if (ret) { 201 | if (pid_vector != NULL) cm_del_vct(pid_vector); 202 | mc_errno = MC_ERR_PROC_NAV; 203 | return -1; 204 | } 205 | 206 | return first_pid; 207 | } 208 | 209 | 210 | int mc_name_by_pid(const pid_t pid, char * name_buf) { 211 | 212 | int ret; 213 | 214 | //get name for this pid 215 | ret = _get_status_name(name_buf, pid); 216 | if (ret) return -1; 217 | 218 | return 0; 219 | } 220 | 221 | 222 | void mc_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out) { 223 | 224 | cm_byte nibble; 225 | int count; 226 | 227 | //for each byte of input 228 | for (int byte_index = 0; byte_index < inp_len; ++byte_index) { 229 | 230 | count = 0; 231 | 232 | //for each nibble of byte 233 | for (int nibble_count = 1; nibble_count >= 0; --nibble_count) { 234 | 235 | //assign nibble 236 | nibble = inp[byte_index]; 237 | if (nibble_count == 1) nibble = nibble >> 4; 238 | nibble = nibble & 0x0F; 239 | 240 | //convert nibble 241 | if (nibble < 0xA) { 242 | out[(byte_index * 2) + count] = 0x30 + nibble; 243 | } else { 244 | out[(byte_index * 2) + count] = 0x61 + nibble - 0xa; 245 | } 246 | 247 | ++count; 248 | 249 | } //end for each nibble 250 | 251 | } //end for each byte 252 | 253 | //add null terminator 254 | out[inp_len * 2] = '\0'; 255 | 256 | return; 257 | } 258 | -------------------------------------------------------------------------------- /src/lib/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | //external libraries 5 | #include 6 | 7 | 8 | #ifdef DEBUG 9 | //internal 10 | void _line_to_name(const char * line_buf, char * name_buf); 11 | int _get_status_name(char * name_buf, const pid_t pid); 12 | #endif 13 | 14 | 15 | //external 16 | char * mc_pathname_to_basename(const char * pathname); 17 | int mc_pid_by_name(const char * comm, cm_vct * pid_vector); 18 | int mc_name_by_pid(const pid_t pid, char * name_buf); 19 | void mc_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/test/Makefile: -------------------------------------------------------------------------------- 1 | .RECIPEPREFIX:=> 2 | 3 | # This makefile takes the following variables: 4 | # 5 | # CC - Compiler. 6 | # BUILD_DIR - Unit test build directory. 7 | # LIB_BIN_DIR - Library artifact directory. 8 | # 9 | # _CFLAGS - Compiler flags. 10 | # _WARN_OPTS - Compiler warnings. 11 | 12 | 13 | #[parameters] 14 | CFLAGS=${_CFLAGS} -fsanitize=address -fsanitize-recover=address 15 | WARN_OPTS+=${_WARN_OPTS} -Wno-unused-variable -Wno-unused-but-set-variable 16 | LDFLAGS=-L${LIB_BIN_DIR} \ 17 | -Wl,-rpath=${LIB_BIN_DIR} -lmcry -lcmore -lcheck -lsubunit -static-libasan 18 | 19 | 20 | #[build constants] 21 | SOURCES_TEST=check_map.c check_procfs_iface.c check_krncry_iface.c \ 22 | check_map_util.c check_util.c iface_helper.c info.c \ 23 | map_helper.c suites.c target_helper.c main.c 24 | OBJECTS_TEST=${SOURCES_TEST:%.c=${BUILD_DIR}/%.o} 25 | TARGET_DIR=${shell pwd}/target 26 | 27 | 28 | #[targets] 29 | TESTS=test 30 | 31 | tests: tgt ${TESTS} 32 | > mkdir -p ${BUILD_DIR} 33 | > mv ${TESTS} ${BUILD_DIR} 34 | 35 | ${TESTS}: ${OBJECTS_TEST} 36 | > ${CC} ${CFLAGS} ${WARN_OPTS} -o $@ $^ ${LDFLAGS} 37 | 38 | ${BUILD_DIR}/%.o: %.c 39 | > ${CC} ${CFLAGS} ${WARN_OPTS} -c $< -o $@ 40 | 41 | tgt: 42 | > $(MAKE) -C ${TARGET_DIR} target CC='${CC}' BUILD_DIR='${BUILD_DIR}' 43 | 44 | clean: 45 | > ${MAKE} -C ${TARGET_DIR} clean BUILD_DIR='${BUILD_DIR}' 46 | > -rm -v ${BUILD_DIR}/${TESTS} 47 | > -rm -v ${OBJECTS_TEST} 48 | -------------------------------------------------------------------------------- /src/test/check_krncry_iface.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | 5 | //system headers 6 | #include 7 | 8 | //external libraries 9 | #include 10 | #include 11 | 12 | //local headers 13 | #include "suites.h" 14 | #include "iface_helper.h" 15 | 16 | //test target headers 17 | #include "../lib/memcry.h" 18 | 19 | 20 | 21 | /* 22 | * [BASIC TEST] 23 | */ 24 | 25 | /* 26 | * NOTE: Unit tests for interfaces are standardised through the iface helper. 27 | */ 28 | 29 | 30 | 31 | /* 32 | * --- [HELPERS] --- 33 | */ 34 | 35 | static void _assert_session(mc_session * se, pid_t pid) { 36 | 37 | ck_assert_int_ne(se->major, -1); 38 | ck_assert_int_eq(se->fd_dev_krncry, pid); 39 | 40 | return; 41 | } 42 | 43 | 44 | 45 | /* 46 | * --- [UNIT TESTS] --- 47 | */ 48 | 49 | //krncry_open() & krncry_close() [no fixture] 50 | START_TEST(test_krncry_mc_open_close) { 51 | 52 | assert_iface_open_close(KRNCRY, _assert_session); 53 | return; 54 | 55 | } END_TEST 56 | 57 | 58 | //krncry_update_map() [no fixture] 59 | START_TEST(test_krncry_mc_update_map) { 60 | 61 | assert_iface_update_map(KRNCRY); 62 | return; 63 | 64 | } END_TEST 65 | 66 | 67 | //krncry_read() & krncry_write() [no fixture] 68 | START_TEST(test_krncry_mc_read_write) { 69 | 70 | assert_iface_read_write(KRNCRY); 71 | return; 72 | 73 | } END_TEST 74 | 75 | 76 | 77 | /* 78 | * --- [SUITE] --- 79 | */ 80 | 81 | Suite * krncry_iface_suite() { 82 | 83 | //test cases 84 | TCase * tc_krncry_mc_open_close; 85 | TCase * tc_krncry_mc_update_map; 86 | TCase * tc_krncry_mc_read_write; 87 | 88 | Suite * s = suite_create("krncry_iface"); 89 | 90 | 91 | //tc_krncry_mc_open_close 92 | tc_krncry_mc_open_close = tcase_create("krncry_mc_open_close"); 93 | tcase_add_test(tc_krncry_mc_open_close, test_krncry_mc_open_close); 94 | 95 | //tc_krncry_mc_update_map 96 | tc_krncry_mc_update_map = tcase_create("krncry_mc_update_map"); 97 | tcase_add_test(tc_krncry_mc_update_map, test_krncry_mc_update_map); 98 | 99 | //tc_krncry_mc_read_write 100 | tc_krncry_mc_read_write = tcase_create("krncry_mc_read_write"); 101 | tcase_add_test(tc_krncry_mc_read_write, test_krncry_mc_read_write); 102 | 103 | 104 | //add test cases to krncry interface test suite 105 | suite_add_tcase(s, tc_krncry_mc_open_close); 106 | suite_add_tcase(s, tc_krncry_mc_update_map); 107 | suite_add_tcase(s, tc_krncry_mc_read_write); 108 | 109 | return s; 110 | } 111 | -------------------------------------------------------------------------------- /src/test/check_map_util.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | 5 | //system headers 6 | #include 7 | 8 | //external libraries 9 | #include 10 | #include 11 | 12 | //local headers 13 | #include "suites.h" 14 | #include "target_helper.h" 15 | 16 | //test target headers 17 | #include "../lib/memcry.h" 18 | 19 | 20 | 21 | /* 22 | * [BASIC TEST] 23 | */ 24 | 25 | 26 | //globals 27 | static mc_vm_map m; 28 | static mc_session s; 29 | static pid_t pid; 30 | 31 | 32 | 33 | /* 34 | * --- [FIXTURES] --- 35 | */ 36 | 37 | //initialise the target 38 | static void _setup_target() { 39 | 40 | int ret; 41 | 42 | 43 | pid = start_target(); 44 | ck_assert_int_ne(pid, -1); 45 | 46 | ret = mc_open(&s, PROCFS, pid); 47 | ck_assert_int_eq(ret, 0); 48 | 49 | mc_new_vm_map(&m); 50 | ret = mc_update_map(&s, &m); 51 | ck_assert_int_eq(ret, 0); 52 | 53 | return; 54 | } 55 | 56 | 57 | //teardown the target 58 | static void _teardown_target() { 59 | 60 | int ret; 61 | 62 | 63 | ret = mc_del_vm_map(&m); 64 | ck_assert_int_eq(ret, 0); 65 | 66 | ret = mc_close(&s); 67 | ck_assert_int_eq(ret, 0); 68 | 69 | end_target(pid); 70 | 71 | return; 72 | } 73 | 74 | 75 | 76 | /* 77 | * --- [UNIT TESTS] --- 78 | */ 79 | 80 | //mc_get_area_off() [target fixture] 81 | START_TEST(test_mc_get_area_off) { 82 | 83 | off_t off; 84 | 85 | mc_vm_obj * o; 86 | cm_lst_node * a_n; 87 | 88 | 89 | //first test: typical offset 90 | o = MC_GET_NODE_OBJ(m.vm_objs.head); 91 | a_n = MC_GET_NODE_PTR(o->last_vm_area_node_ps.head); 92 | 93 | off = mc_get_area_off(a_n, 0x10800); 94 | ck_assert_int_eq(off, 0x800); 95 | 96 | 97 | //second test: address is lower than area's starting address 98 | off = mc_get_area_off(a_n, 0xF800); 99 | ck_assert_int_eq(off, -0x800); 100 | 101 | return; 102 | 103 | } END_TEST 104 | 105 | 106 | //mc_get_obj_off() [target fixture] 107 | START_TEST(test_mc_get_obj_off) { 108 | 109 | off_t off; 110 | 111 | cm_lst_node * o_n; 112 | mc_vm_obj * o; 113 | 114 | 115 | //first test: typical offset 116 | o_n = m.vm_objs.head; 117 | o = MC_GET_NODE_OBJ(o_n); 118 | 119 | off = mc_get_obj_off(o_n, 0x800); 120 | ck_assert_int_eq(off, 0x800); 121 | 122 | 123 | //second test: address is lower than obj's starting address 124 | o->end_addr = o->start_addr = 0x1000; 125 | off = mc_get_obj_off(o_n, 0x800); 126 | ck_assert_int_eq(off, -0x800); 127 | 128 | return; 129 | 130 | } END_TEST 131 | 132 | 133 | //mc_get_area_off_bnd() [target fixture] 134 | START_TEST(test_mc_get_area_off_bnd) { 135 | 136 | off_t off; 137 | 138 | mc_vm_obj * o; 139 | cm_lst_node * a_n; 140 | 141 | 142 | //setup test 143 | o = MC_GET_NODE_OBJ(m.vm_objs.head); 144 | a_n = MC_GET_NODE_PTR(o->last_vm_area_node_ps.head); 145 | 146 | 147 | //first test: typical offset 148 | off = mc_get_area_off_bnd(a_n, 0x10800); 149 | ck_assert_int_eq(off, 0x800); 150 | 151 | 152 | //second test: address is lower than area's starting address 153 | off = mc_get_area_off_bnd(a_n, 0xF800); 154 | ck_assert_int_eq(off, -1); 155 | 156 | return; 157 | 158 | } END_TEST 159 | 160 | 161 | //mc_get_obj_off_bnd() [target fixture] 162 | START_TEST(test_mc_get_obj_off_bnd) { 163 | 164 | off_t off; 165 | 166 | cm_lst_node * o_n; 167 | mc_vm_obj * o; 168 | 169 | 170 | //setup test 171 | o_n = m.vm_objs.head; 172 | o = MC_GET_NODE_OBJ(o_n); 173 | 174 | 175 | //first test: typical offset 176 | o->end_addr = 0x1000; 177 | off = mc_get_obj_off_bnd(o_n, 0x800); 178 | ck_assert_int_eq(off, 0x800); 179 | 180 | 181 | //second test: address is lower than obj's starting address 182 | o->start_addr = 0x1000; 183 | off = mc_get_obj_off_bnd(o_n, 0x800); 184 | ck_assert_int_eq(off, -1); 185 | 186 | return; 187 | 188 | } END_TEST 189 | 190 | 191 | //mc_get_area_by_addr [target fixture] 192 | START_TEST(test_mc_get_area_by_addr) { 193 | 194 | cm_lst_node * ret_n; 195 | off_t off; 196 | 197 | mc_vm_obj * o; 198 | mc_vm_area * a; 199 | cm_lst_node * a_n; 200 | 201 | 202 | //first test: mapped address 203 | a_n = m.vm_areas.head->next->next; 204 | a = MC_GET_NODE_AREA(a_n); 205 | 206 | ret_n = mc_get_area_by_addr(&m, a->start_addr + 0x200, &off); 207 | ck_assert_ptr_eq(ret_n, a_n); 208 | ck_assert_int_eq(off, 0x200); 209 | 210 | 211 | //second test: unmapped address 212 | off = 0x0; 213 | 214 | ret_n = mc_get_area_by_addr(&m, 0x1337, &off); 215 | ck_assert_ptr_null(ret_n); 216 | ck_assert_int_eq(off, 0); 217 | 218 | return; 219 | 220 | } END_TEST 221 | 222 | 223 | //mc_get_obj_by_addr [target fixture] 224 | START_TEST(test_mc_get_obj_by_addr) { 225 | 226 | cm_lst_node * ret_n; 227 | off_t off; 228 | 229 | mc_vm_obj * o; 230 | cm_lst_node * o_n; 231 | 232 | 233 | //first test: mapped address 234 | o_n = m.vm_objs.head->next->next; 235 | o = MC_GET_NODE_OBJ(o_n); 236 | 237 | ret_n = mc_get_obj_by_addr(&m, o->start_addr + 0x200, &off); 238 | ck_assert_ptr_eq(ret_n, o_n); 239 | ck_assert_int_eq(off, 0x200); 240 | 241 | 242 | //second test: unmapped address 243 | off = 0x0; 244 | 245 | ret_n = mc_get_obj_by_addr(&m, 0x1337, &off); 246 | ck_assert_ptr_null(ret_n); 247 | ck_assert_int_eq(off, 0); 248 | 249 | return; 250 | 251 | } END_TEST 252 | 253 | 254 | //mc_get_obj_by_pathname() [target fixture] 255 | START_TEST(test_mc_get_obj_by_pathname) { 256 | 257 | cm_lst_node * ret_n; 258 | 259 | mc_vm_obj * o; 260 | cm_lst_node * o_n; 261 | char * pathname; 262 | 263 | 264 | //first test: pathname exists 265 | o_n = m.vm_objs.head->next->next; 266 | o = MC_GET_NODE_OBJ(o_n); 267 | 268 | pathname = o->pathname; 269 | ret_n = mc_get_obj_by_pathname(&m, pathname); 270 | ck_assert_ptr_eq(ret_n, o_n); 271 | 272 | 273 | //second test: pathname doesn't exist 274 | pathname = "/foo/bar"; 275 | ret_n = mc_get_obj_by_pathname(&m, pathname); 276 | ck_assert_ptr_null(ret_n); 277 | 278 | return; 279 | 280 | } END_TEST 281 | 282 | 283 | //mc_get_obj_by_basename() [target_fixture] 284 | START_TEST(test_mc_get_obj_by_basename) { 285 | 286 | cm_lst_node * ret_n; 287 | 288 | mc_vm_obj * o; 289 | cm_lst_node * o_n; 290 | char * basename; 291 | 292 | 293 | //first test: pathname exists 294 | o_n = m.vm_objs.head->next->next; 295 | o = MC_GET_NODE_OBJ(o_n); 296 | 297 | basename = o->basename; 298 | ret_n = mc_get_obj_by_basename(&m, basename); 299 | ck_assert_ptr_eq(ret_n, o_n); 300 | 301 | 302 | //second test: pathname doesn't exist 303 | basename = "1337"; 304 | ret_n = mc_get_obj_by_basename(&m, basename); 305 | ck_assert_ptr_null(ret_n); 306 | 307 | return; 308 | 309 | } END_TEST 310 | 311 | 312 | //mc_access_to_str() [no fixture] 313 | START_TEST(test_mc_access_to_str) { 314 | 315 | cm_byte accesses[16] = { 316 | 0b0000, 0b0001, 0b0010, 0b0011, 317 | 0b0100, 0b0101, 0b0110, 0b0111, 318 | 0b1000, 0b1001, 0b1010, 0b1011, 319 | 0b1100, 0b1101, 0b1110, 0b1111 320 | }; 321 | 322 | char * access_strings[16] = { 323 | "---p", "r--p", "-w-p", "rw-p", 324 | "--xp", "r-xp", "-wxp", "rwxp", 325 | "---s", "r--s", "-w-s", "rw-s", 326 | "--xs", "r-xs", "-wxs", "rwxs", 327 | }; 328 | 329 | char str_buf[5]; 330 | 331 | 332 | //only test: try every access combination 333 | for (int i = 0; i < 16; ++i) { 334 | 335 | mc_access_to_str(accesses[i], str_buf); 336 | ck_assert_str_eq(str_buf, access_strings[i]); 337 | } 338 | 339 | return; 340 | 341 | } END_TEST 342 | 343 | 344 | 345 | /* 346 | * --- [SUITE] --- 347 | */ 348 | 349 | Suite * map_util_suite() { 350 | 351 | //test cases 352 | TCase * tc_get_area_off; 353 | TCase * tc_get_obj_off; 354 | TCase * tc_get_area_off_bnd; 355 | TCase * tc_get_obj_off_bnd; 356 | TCase * tc_get_area_by_addr; 357 | TCase * tc_get_obj_by_addr; 358 | TCase * tc_get_obj_by_pathname; 359 | TCase * tc_get_obj_by_basename; 360 | TCase * tc_access_to_str; 361 | 362 | Suite * s = suite_create("map_util"); 363 | 364 | 365 | //tc_get_area_off 366 | tc_get_area_off = tcase_create("get_area_off"); 367 | tcase_add_checked_fixture(tc_get_area_off, 368 | _setup_target, _teardown_target); 369 | tcase_add_test(tc_get_area_off, test_mc_get_area_off); 370 | 371 | //tc_get_obj_off 372 | tc_get_obj_off = tcase_create("get_obj_off"); 373 | tcase_add_checked_fixture(tc_get_obj_off, 374 | _setup_target, _teardown_target); 375 | tcase_add_test(tc_get_obj_off, test_mc_get_obj_off); 376 | 377 | //tc_get_area_off_bnd 378 | tc_get_area_off_bnd = tcase_create("get_area_off_bnd"); 379 | tcase_add_checked_fixture(tc_get_area_off_bnd, 380 | _setup_target, _teardown_target); 381 | tcase_add_test(tc_get_area_off_bnd, test_mc_get_area_off_bnd); 382 | 383 | //tc_get_obj_off_bnd 384 | tc_get_obj_off_bnd = tcase_create("get_obj_off_bnd"); 385 | tcase_add_checked_fixture(tc_get_obj_off_bnd, 386 | _setup_target, _teardown_target); 387 | tcase_add_test(tc_get_obj_off_bnd, test_mc_get_obj_off_bnd); 388 | 389 | //tc_get_area_by_addr 390 | tc_get_area_by_addr = tcase_create("get_area_by_addr"); 391 | tcase_add_checked_fixture(tc_get_area_by_addr, 392 | _setup_target, _teardown_target); 393 | tcase_add_test(tc_get_area_by_addr, test_mc_get_area_by_addr); 394 | 395 | //tc_get_area_by_addr 396 | tc_get_obj_by_addr = tcase_create("get_obj_by_addr"); 397 | tcase_add_checked_fixture(tc_get_obj_by_addr, 398 | _setup_target, _teardown_target); 399 | tcase_add_test(tc_get_obj_by_addr, test_mc_get_obj_by_addr); 400 | 401 | //tc_get_obj_by_pathname 402 | tc_get_obj_by_pathname = tcase_create("get_obj_by_pathname"); 403 | tcase_add_checked_fixture(tc_get_obj_by_pathname, 404 | _setup_target, _teardown_target); 405 | tcase_add_test(tc_get_obj_by_pathname, 406 | test_mc_get_obj_by_pathname); 407 | 408 | //tc_get_obj_by_basename 409 | tc_get_obj_by_basename = tcase_create("get_obj_by_basename"); 410 | tcase_add_checked_fixture(tc_get_obj_by_basename, 411 | _setup_target, _teardown_target); 412 | tcase_add_test(tc_get_obj_by_basename, 413 | test_mc_get_obj_by_basename); 414 | 415 | //tc_access_to_str 416 | tc_access_to_str = tcase_create("access_to_str"); 417 | tcase_add_test(tc_access_to_str, test_mc_access_to_str); 418 | 419 | 420 | //add test cases to map util test suite 421 | suite_add_tcase(s, tc_get_area_off); 422 | suite_add_tcase(s, tc_get_obj_off); 423 | suite_add_tcase(s, tc_get_area_off_bnd); 424 | suite_add_tcase(s, tc_get_obj_off_bnd); 425 | suite_add_tcase(s, tc_get_area_by_addr); 426 | suite_add_tcase(s, tc_get_obj_by_addr); 427 | suite_add_tcase(s, tc_get_obj_by_pathname); 428 | suite_add_tcase(s, tc_get_obj_by_basename); 429 | suite_add_tcase(s, tc_access_to_str); 430 | 431 | return s; 432 | } 433 | -------------------------------------------------------------------------------- /src/test/check_procfs_iface.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | 5 | //system headers 6 | #include 7 | 8 | //external libraries 9 | #include 10 | #include 11 | 12 | //local headers 13 | #include "suites.h" 14 | #include "iface_helper.h" 15 | 16 | //test target headers 17 | #include "../lib/memcry.h" 18 | 19 | 20 | 21 | /* 22 | * [BASIC TEST] 23 | */ 24 | 25 | /* 26 | * NOTE: Unit tests for interfaces are standardised through the iface helper. 27 | */ 28 | 29 | 30 | 31 | /* 32 | * --- [HELPERS] --- 33 | */ 34 | 35 | static void _assert_session(mc_session * se, pid_t pid) { 36 | 37 | ck_assert_int_ne(se->fd_mem, -1); 38 | ck_assert_int_eq(se->pid, pid); 39 | 40 | return; 41 | } 42 | 43 | 44 | 45 | /* 46 | * --- [UNIT TESTS] --- 47 | */ 48 | 49 | //procfs_open() & procfs_close() [no fixture] 50 | START_TEST(test_procfs_mc_open_close) { 51 | 52 | assert_iface_open_close(PROCFS, _assert_session); 53 | return; 54 | 55 | } END_TEST 56 | 57 | 58 | //procfs_update_map() [no fixture] 59 | START_TEST(test_procfs_mc_update_map) { 60 | 61 | assert_iface_update_map(PROCFS); 62 | return; 63 | 64 | } END_TEST 65 | 66 | 67 | //procfs_read() & procfs_write() [no fixture] 68 | START_TEST(test_procfs_mc_read_write) { 69 | 70 | assert_iface_read_write(PROCFS); 71 | return; 72 | 73 | } END_TEST 74 | 75 | 76 | 77 | /* 78 | * --- [SUITE] --- 79 | */ 80 | 81 | Suite * procfs_iface_suite() { 82 | 83 | //test cases 84 | TCase * tc_procfs_mc_open_close; 85 | TCase * tc_procfs_mc_update_map; 86 | TCase * tc_procfs_mc_read_write; 87 | 88 | Suite * s = suite_create("procfs_iface"); 89 | 90 | 91 | //tc_procfs_mc_open_close 92 | tc_procfs_mc_open_close = tcase_create("procfs_mc_open_close"); 93 | tcase_add_test(tc_procfs_mc_open_close, test_procfs_mc_open_close); 94 | 95 | //tc_procfs_mc_update_map 96 | tc_procfs_mc_update_map = tcase_create("procfs_mc_update_map"); 97 | tcase_add_test(tc_procfs_mc_update_map, test_procfs_mc_update_map); 98 | 99 | //tc_procfs_mc_read_write 100 | tc_procfs_mc_read_write = tcase_create("procfs_mc_read_write"); 101 | tcase_add_test(tc_procfs_mc_read_write, test_procfs_mc_read_write); 102 | 103 | 104 | //add test cases to procfs interface test suite 105 | suite_add_tcase(s, tc_procfs_mc_open_close); 106 | suite_add_tcase(s, tc_procfs_mc_update_map); 107 | suite_add_tcase(s, tc_procfs_mc_read_write); 108 | 109 | return s; 110 | } 111 | -------------------------------------------------------------------------------- /src/test/check_util.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | #include 5 | 6 | //system headers 7 | #include 8 | 9 | #include 10 | 11 | //external libraries 12 | #include 13 | #include 14 | 15 | //local headers 16 | #include "suites.h" 17 | #include "target_helper.h" 18 | 19 | //test target headers 20 | #include "../lib/memcry.h" 21 | 22 | 23 | 24 | /* 25 | * [BASIC TEST] 26 | */ 27 | 28 | 29 | 30 | //globals 31 | static mc_session s; 32 | static pid_t pid; 33 | 34 | 35 | 36 | /* 37 | * --- [FIXTURES] --- 38 | */ 39 | 40 | //initialise the target 41 | static void _setup_target() { 42 | 43 | int ret; 44 | 45 | ret = clean_targets(); 46 | ck_assert_int_eq(ret, 0); 47 | 48 | pid = start_target(); 49 | ck_assert_int_ne(pid, -1); 50 | 51 | ret = mc_open(&s, PROCFS, pid); 52 | ck_assert_int_eq(ret, 0); 53 | 54 | return; 55 | } 56 | 57 | 58 | //teardown the target 59 | static void _teardown_target() { 60 | 61 | int ret; 62 | 63 | 64 | ret = mc_close(&s); 65 | ck_assert_int_eq(ret, 0); 66 | 67 | end_target(pid); 68 | 69 | return; 70 | } 71 | 72 | 73 | 74 | /* 75 | * --- [UNIT TESTS] --- 76 | */ 77 | 78 | //mc_pathname_to_basename() [no fixture] 79 | START_TEST(test_mc_pathname_to_basename) { 80 | 81 | char * pathname, * basename; 82 | 83 | 84 | //first test: pathname is a path 85 | pathname = "/foo/bar"; 86 | basename = mc_pathname_to_basename(pathname); 87 | 88 | ck_assert_str_eq(basename, "bar"); 89 | 90 | 91 | //second test: basename is not a path 92 | pathname = "baz"; 93 | basename = mc_pathname_to_basename(pathname); 94 | 95 | ck_assert_str_eq(basename, "baz"); 96 | 97 | return; 98 | 99 | } END_TEST 100 | 101 | 102 | //mc_pid_by_name() [target fixture] 103 | START_TEST(test_mc_pid_by_name) { 104 | 105 | pid_t pid; 106 | cm_vct v; 107 | 108 | 109 | //first test: target exists, provide vector 110 | pid = mc_pid_by_name(TARGET_NAME, &v); 111 | 112 | ck_assert_int_ne(pid, -1); 113 | ck_assert_int_eq(v.len, 1); 114 | 115 | cm_del_vct(&v); 116 | 117 | 118 | //second test: target exists, do not provide vector 119 | pid = mc_pid_by_name(TARGET_NAME, NULL); 120 | 121 | ck_assert_int_ne(pid, -1); 122 | 123 | 124 | //second test: target does not exist 125 | pid = mc_pid_by_name("foo", &v); 126 | 127 | ck_assert_int_eq(pid, -1); 128 | ck_assert_int_eq(v.len, 0); 129 | 130 | cm_del_vct(&v); 131 | 132 | return; 133 | 134 | } END_TEST 135 | 136 | 137 | //mc_name_by_pid() [no fixture] 138 | START_TEST(test_mc_name_by_pid) { 139 | 140 | int ret; 141 | 142 | pid_t pid; 143 | char name_buf[NAME_MAX]; 144 | 145 | 146 | //setup test 147 | pid = start_target(); 148 | ck_assert_int_ne(pid, -1); 149 | 150 | 151 | //first test: target exists 152 | ret = mc_name_by_pid(pid, name_buf); 153 | ck_assert_int_eq(ret, 0); 154 | ck_assert_str_eq(name_buf, TARGET_NAME); 155 | 156 | 157 | //second test: target does not exist 158 | memset(name_buf, 0, NAME_MAX); 159 | ret = mc_name_by_pid((int) (pow(2, 32)) - 1, name_buf); 160 | ck_assert_int_eq(ret, -1); 161 | ck_assert_str_eq(name_buf, "\0"); 162 | 163 | 164 | //cleanup 165 | end_target(pid); 166 | 167 | return; 168 | 169 | } END_TEST 170 | 171 | 172 | //mc_bytes_to_hex() [no fixture] 173 | START_TEST(test_mc_bytes_to_hex) { 174 | 175 | int ret; 176 | 177 | 178 | //only test: convert ascii "foo" to hex representation 179 | cm_byte str[4] = "foo"; 180 | char hex_str[8] = {0}; 181 | 182 | mc_bytes_to_hex(str, 3, hex_str); 183 | ret = memcmp(hex_str, "666f6f", 6); 184 | ck_assert_int_eq(ret, 0); 185 | 186 | return; 187 | 188 | } END_TEST 189 | 190 | 191 | 192 | /* 193 | * --- [SUITE] --- 194 | */ 195 | 196 | Suite * util_suite() { 197 | 198 | //test cases 199 | TCase * tc_pathname_to_basename; 200 | TCase * tc_pid_by_name; 201 | TCase * tc_name_by_pid; 202 | TCase * tc_bytes_to_hex; 203 | 204 | Suite * s = suite_create("util"); 205 | 206 | 207 | //tc_pathname_to_basename 208 | tc_pathname_to_basename = tcase_create("pathname_to_basename"); 209 | tcase_add_test(tc_pathname_to_basename, test_mc_pathname_to_basename); 210 | 211 | //tc_pid_by_name 212 | tc_pid_by_name = tcase_create("pid_by_name"); 213 | tcase_add_checked_fixture(tc_pid_by_name, _setup_target, _teardown_target); 214 | tcase_add_test(tc_pid_by_name, test_mc_pid_by_name); 215 | 216 | //tc_name_by_pid 217 | tc_name_by_pid = tcase_create("name_by_pid"); 218 | tcase_add_checked_fixture(tc_name_by_pid, _setup_target, _teardown_target); 219 | tcase_add_test(tc_name_by_pid, test_mc_name_by_pid); 220 | 221 | //tc_bytes_to_hex 222 | tc_bytes_to_hex = tcase_create("bytes_to_hex"); 223 | tcase_add_test(tc_bytes_to_hex, test_mc_bytes_to_hex); 224 | 225 | 226 | //add test cases to util test suite 227 | suite_add_tcase(s, tc_pathname_to_basename); 228 | suite_add_tcase(s, tc_pid_by_name); 229 | suite_add_tcase(s, tc_name_by_pid); 230 | suite_add_tcase(s, tc_bytes_to_hex); 231 | 232 | return s; 233 | } 234 | -------------------------------------------------------------------------------- /src/test/iface_helper.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | #include 5 | 6 | //system headers 7 | #include 8 | 9 | //external libraries 10 | #include 11 | #include 12 | 13 | //local headers 14 | #include "info.h" 15 | #include "iface_helper.h" 16 | #include "target_helper.h" 17 | 18 | //test target headers 19 | #include "../lib/memcry.h" 20 | #include "../lib/procfs_iface.h" 21 | 22 | 23 | 24 | /* 25 | * NOTE: Interface helper is a generic interface tester. It requires the 26 | * test suite of an interface to implement a custom `assert_session()` 27 | * function that verifies the validity of an open session. 28 | */ 29 | 30 | 31 | //get address for testing a read / write in a PIE process 32 | static inline uintptr_t _get_addr(mc_vm_map * m, bool is_last_area, 33 | int obj_index, int area_index, off_t off) { 34 | 35 | int ret; 36 | 37 | mc_vm_area * a; 38 | cm_lst_node * a_p; 39 | mc_vm_obj * o; 40 | cm_lst_node * o_p; 41 | 42 | 43 | //get relevant object 44 | o_p = cm_lst_get_n(&m->vm_objs, obj_index); 45 | ck_assert_ptr_nonnull(o_p); 46 | o = MC_GET_NODE_OBJ(o_p); 47 | 48 | //get relevant area 49 | if (is_last_area == true) { 50 | ret = cm_lst_get(&o->last_vm_area_node_ps, area_index, &a_p); 51 | } else { 52 | ret = cm_lst_get(&o->vm_area_node_ps, area_index, &a_p); 53 | } 54 | ck_assert_int_eq(ret, 0); 55 | a = MC_GET_NODE_AREA(a_p); 56 | 57 | return (a->start_addr + off); 58 | } 59 | 60 | 61 | //assert open() and close() methods of an interface 62 | void assert_iface_open_close(enum mc_iface_type iface, 63 | void (* assert_session)(mc_session *, pid_t)) { 64 | 65 | int ret; 66 | 67 | pid_t pid; 68 | mc_session s; 69 | 70 | 71 | //kill existing targets 72 | ret = clean_targets(); 73 | ck_assert_int_eq(ret, 0); 74 | 75 | //setup tests 76 | pid = start_target(); 77 | ck_assert_int_ne(pid, -1); 78 | 79 | 80 | //first test: open the target & close the target 81 | ret = mc_open(&s, iface, pid); 82 | ck_assert_int_eq(ret, 0); 83 | 84 | assert_session(&s, pid); 85 | 86 | ret = mc_close(&s); 87 | ck_assert_int_eq(ret, 0); 88 | 89 | 90 | //second test: re-attach to existing target and 91 | // target exits before session is closed 92 | ret = mc_open(&s, iface, pid); 93 | ck_assert_int_eq(ret, 0); 94 | 95 | assert_session(&s, pid); 96 | end_target(pid); 97 | 98 | ret = mc_close(&s); 99 | ck_assert_int_eq(ret, 0); 100 | 101 | return; 102 | } 103 | 104 | 105 | //procfs_update_map() [no fixture] 106 | void assert_iface_update_map(enum mc_iface_type iface) { 107 | 108 | int ret; 109 | 110 | pid_t pid; 111 | mc_session s; 112 | mc_vm_map m; 113 | 114 | 115 | //kill existing targets 116 | ret = clean_targets(); 117 | ck_assert_int_eq(ret, 0); 118 | 119 | //setup tests 120 | pid = start_target(); 121 | ck_assert_int_ne(pid, -1); 122 | 123 | ret = mc_open(&s, iface, pid); 124 | ck_assert_int_eq(ret, 0); 125 | 126 | mc_new_vm_map(&m); 127 | 128 | 129 | //first test: update empty map 130 | ret = mc_update_map(&s, &m); 131 | ck_assert_int_eq(ret, 0); 132 | 133 | assert_target_map(&m); 134 | 135 | 136 | //second test: update filled map (map new areas) 137 | change_target_map(pid); 138 | 139 | ret = mc_update_map(&s, &m); 140 | ck_assert_int_eq(ret, 0); 141 | 142 | assert_target_map(&m); 143 | 144 | 145 | //third test: update filled map (unmap old areas) 146 | change_target_map(pid); 147 | 148 | ret = mc_update_map(&s, &m); 149 | ck_assert_int_eq(ret, 0); 150 | 151 | assert_target_map(&m); 152 | 153 | 154 | //fourth test: process exited 155 | end_target(pid); 156 | 157 | ret = mc_update_map(&s, &m); 158 | ck_assert_int_eq(ret, -1); 159 | 160 | 161 | //cleanup 162 | ret = mc_close(&s); 163 | ck_assert_int_eq(ret, 0); 164 | 165 | ret = mc_del_vm_map(&m); 166 | ck_assert_int_eq(ret, 0); 167 | 168 | return; 169 | } 170 | 171 | 172 | //procfs_read() & procfs_write() [no fixture] 173 | void assert_iface_read_write(enum mc_iface_type iface) { 174 | 175 | /* 176 | * NOTE: These tests work with hardcoded offsets extracted from 177 | * a compiled `unit_target` binary with rizin. If you find 178 | * that these tests are failing for you, verify these 179 | * offsets are correct with gdb & `/proc//maps` 180 | */ 181 | 182 | int ret; 183 | 184 | cm_byte rw_buf[16]; 185 | uintptr_t tgt_buf_addr; 186 | 187 | pid_t pid; 188 | mc_session s; 189 | mc_vm_map m; 190 | 191 | const char * w_buf = IFACE_W_BUF_STR; 192 | 193 | 194 | //kill existing targets 195 | ret = clean_targets(); 196 | ck_assert_int_eq(ret, 0); 197 | 198 | //setup tests 199 | pid = start_target(); 200 | ck_assert_int_ne(pid, -1); 201 | 202 | ret = mc_open(&s, iface, pid); 203 | ck_assert_int_eq(ret, 0); 204 | 205 | mc_new_vm_map(&m); 206 | ret = mc_update_map(&s, &m); 207 | ck_assert_int_eq(ret, 0); 208 | 209 | 210 | //first test: read & write predefined rw- segment, seek & no seek 211 | tgt_buf_addr = _get_addr(&m, false, IFACE_RW_BUF_OBJ_INDEX, 212 | IFACE_RW_BUF_AREA_INDEX, IFACE_RW_BUF_OFF); 213 | 214 | //read foreign buffer 215 | ret = mc_read(&s, tgt_buf_addr, rw_buf, TARGET_BUF_SZ); 216 | ck_assert_int_eq(ret, 0); 217 | 218 | //check foreign buffer was read correctly 219 | ret = strncmp((char *) rw_buf, IFACE_RW_BUF_STR, TARGET_BUF_SZ); 220 | ck_assert_int_eq(ret, 0); 221 | 222 | 223 | //write to foreign buffer (first half) 224 | ret = mc_write(&s, tgt_buf_addr, (cm_byte *) w_buf, TARGET_BUF_SZ); 225 | ck_assert_int_eq(ret, 0); 226 | 227 | //re-read foreign buffer 228 | ret = mc_read(&s, tgt_buf_addr, rw_buf, TARGET_BUF_SZ); 229 | ck_assert_int_eq(ret, 0); 230 | 231 | //check the write was performed correctly 232 | ret = strncmp((char *) rw_buf, IFACE_W_BUF_STR, TARGET_BUF_SZ); 233 | ck_assert_int_eq(ret, 0); 234 | 235 | 236 | 237 | //second test: read & write to region with no read & write permissions 238 | memset(rw_buf, 0, 16); 239 | tgt_buf_addr = _get_addr(&m, true, IFACE_NONE_OBJ_INDEX, 240 | IFACE_NONE_AREA_INDEX, IFACE_NONE_OFF); 241 | 242 | //write to foreign buffer 243 | ret = mc_write(&s, tgt_buf_addr, (cm_byte *) w_buf, TARGET_BUF_SZ); 244 | INFO_PRINT("[%s][no perm]: returned %d\n", 245 | get_iface_name(iface), ret); 246 | 247 | //read foreign buffer 248 | ret = mc_read(&s, tgt_buf_addr, rw_buf, TARGET_BUF_SZ); 249 | INFO_PRINT("[%s][no perm]: returned %d\n", 250 | get_iface_name(iface), ret); 251 | 252 | //check if write succeeded 253 | ret = strncmp((char *) rw_buf, IFACE_W_BUF_STR, TARGET_BUF_SZ); 254 | INFO_PRINT("[%s][no perm]: returned %d\n", 255 | get_iface_name(iface), ret); 256 | 257 | 258 | 259 | //third test: read & write to unmapped memory 260 | memset(rw_buf, 0, 16); 261 | tgt_buf_addr = 0x1337; 262 | 263 | //write to foreign buffer 264 | ret = mc_write(&s, tgt_buf_addr, (cm_byte *) w_buf, TARGET_BUF_SZ); 265 | INFO_PRINT("[%s][unmapped]: returned %d\n", 266 | get_iface_name(iface), ret); 267 | 268 | //read foreign buffer 269 | ret = mc_read(&s, tgt_buf_addr, rw_buf, TARGET_BUF_SZ); 270 | INFO_PRINT("[%s][unmapped]: returned %d\n", 271 | get_iface_name(iface), ret); 272 | 273 | ret = strncmp((char *) rw_buf, IFACE_W_BUF_STR, TARGET_BUF_SZ); 274 | INFO_PRINT("[%s][unmapped]: returned %d\n", 275 | get_iface_name(iface), ret); 276 | 277 | 278 | //cleanup 279 | ret = mc_close(&s); 280 | ck_assert_int_eq(ret, 0); 281 | 282 | ret = mc_del_vm_map(&m); 283 | ck_assert_int_eq(ret, 0); 284 | 285 | end_target(pid); 286 | 287 | return; 288 | 289 | } END_TEST 290 | -------------------------------------------------------------------------------- /src/test/iface_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef IFACE_HELPER_H 2 | #define IFACE_HELPER_H 3 | 4 | //standard library 5 | #include 6 | 7 | //system headers 8 | #include 9 | 10 | //external libraries 11 | #include 12 | 13 | //test target headers 14 | #include "../lib/memcry.h" 15 | 16 | 17 | //map helper functions 18 | void assert_iface_open_close(enum mc_iface_type iface, 19 | void (* assert_session)(mc_session *, pid_t)); 20 | void assert_iface_update_map(enum mc_iface_type iface); 21 | void assert_iface_read_write(enum mc_iface_type iface); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/test/info.c: -------------------------------------------------------------------------------- 1 | //local includes 2 | #include "../lib/memcry.h" 3 | 4 | 5 | //mc_iface_type names 6 | static char * _iface_names[2] = { 7 | "PROCFS", 8 | "KRNCRY" 9 | }; 10 | 11 | 12 | //convert enum to name 13 | char * get_iface_name(enum mc_iface_type iface) { 14 | 15 | return _iface_names[(int) iface]; 16 | } 17 | -------------------------------------------------------------------------------- /src/test/info.h: -------------------------------------------------------------------------------- 1 | #ifndef INFO_H 2 | #define INFO_H 3 | 4 | //standard library 5 | #include 6 | 7 | //local includes 8 | #include "../lib/memcry.h" 9 | 10 | 11 | #define INFO_PRINT(format, ...)\ 12 | printf("[INFO]: " format, ##__VA_ARGS__) 13 | 14 | 15 | char * get_iface_name(enum mc_iface_type iface); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/test/main.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | 5 | //system headers 6 | #include 7 | #include 8 | 9 | //external libraries 10 | #include 11 | #include 12 | 13 | //local headers 14 | #include "suites.h" 15 | 16 | 17 | //manipulated by debug.sh; see `suites.h` 18 | bool _DEBUG_ACTIVE = false; 19 | 20 | //tests bitmask 21 | #define MAP_TEST 0x1 22 | #define PROCFS_TEST 0x2 23 | #define KRNCRY_TEST 0x4 24 | #define MAP_UTIL_TEST 0x8 25 | #define UTIL_TEST 0x10 26 | 27 | 28 | //determine which tests to run 29 | static cm_byte _get_test_mode(int argc, char ** argv) { 30 | 31 | const struct option long_opts[] = { 32 | {"map", no_argument, NULL, 'm'}, 33 | {"procfs", no_argument, NULL, 'p'}, 34 | {"krncry", no_argument, NULL, 'k'}, 35 | {"map-util", no_argument, NULL, 'n'}, 36 | {"util", no_argument, NULL, 'u'}, 37 | {0,0,0,0} 38 | }; 39 | 40 | int opt; 41 | cm_byte test_mask = 0; 42 | 43 | 44 | while((opt = getopt_long(argc, argv, "mpknu", long_opts, NULL)) != -1 45 | && opt != 0) { 46 | 47 | //determine parsed argument 48 | switch (opt) { 49 | 50 | case 'm': 51 | test_mask |= MAP_TEST; 52 | break; 53 | 54 | case 'p': 55 | test_mask |= PROCFS_TEST; 56 | break; 57 | 58 | case 'k': 59 | test_mask |= KRNCRY_TEST; 60 | break; 61 | 62 | case 'n': 63 | test_mask |= MAP_UTIL_TEST; 64 | break; 65 | 66 | case 'u': 67 | test_mask |= UTIL_TEST; 68 | break; 69 | } 70 | } 71 | 72 | return test_mask; 73 | } 74 | 75 | 76 | //run unit tests 77 | static void _run_unit_tests(cm_byte test_mask) { 78 | 79 | Suite * s_fake; 80 | Suite * s_map; 81 | Suite * s_procfs_iface; 82 | Suite * s_krncry_iface; 83 | Suite * s_map_util; 84 | Suite * s_util; 85 | 86 | SRunner * sr; 87 | 88 | 89 | //initialise test suites 90 | s_fake = fake_suite(); 91 | if (test_mask & MAP_TEST) s_map = map_suite(); 92 | if (test_mask & PROCFS_TEST) s_procfs_iface = procfs_iface_suite(); 93 | if (test_mask & KRNCRY_TEST) s_krncry_iface = krncry_iface_suite(); 94 | if (test_mask & MAP_UTIL_TEST) s_map_util = map_util_suite(); 95 | if (test_mask & UTIL_TEST) s_util = util_suite(); 96 | 97 | //create suite runner 98 | sr = srunner_create(s_fake); 99 | if (test_mask & MAP_TEST) srunner_add_suite(sr, s_map); 100 | if (test_mask & PROCFS_TEST) srunner_add_suite(sr, s_procfs_iface); 101 | if (test_mask & KRNCRY_TEST) srunner_add_suite(sr, s_krncry_iface); 102 | if (test_mask & MAP_UTIL_TEST) srunner_add_suite(sr, s_map_util); 103 | if (test_mask & UTIL_TEST) srunner_add_suite(sr, s_util); 104 | 105 | //run tests 106 | srunner_run_all(sr, CK_VERBOSE); 107 | 108 | //cleanup 109 | srunner_free(sr); 110 | 111 | return; 112 | } 113 | 114 | 115 | //dispatch tests 116 | int main(int argc, char ** argv) { 117 | 118 | cm_byte test_mask = _get_test_mode(argc, argv); 119 | _run_unit_tests(test_mask); 120 | 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /src/test/map_helper.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | 5 | //system headers 6 | #include 7 | 8 | #include 9 | 10 | //external libraries 11 | #include 12 | #include 13 | 14 | //local headers 15 | #include "map_helper.h" 16 | #include "suites.h" 17 | 18 | //test target headers 19 | #include "../lib/memcry.h" 20 | #include "../lib/map.h" 21 | 22 | 23 | //initialise a cm_lst_node stub wrapper 24 | void create_lst_wrapper(cm_lst_node * node, void * data) { 25 | 26 | node->data = data; 27 | node->next = node->prev = NULL; 28 | 29 | return; 30 | } 31 | 32 | 33 | //assert the length of a list, also works as an integration test for CMore 34 | void assert_lst_len(cm_lst * list, int len) { 35 | 36 | ck_assert_int_eq(list->len, len); 37 | 38 | //if length is zero (0), ensure head is null 39 | if (len == 0) { 40 | ck_assert_ptr_null(list->head); 41 | return; 42 | } 43 | 44 | //if length is one (1), ensure head is not null 45 | ck_assert_ptr_nonnull(list->head); 46 | cm_lst_node * iter = list->head; 47 | 48 | if (len == 1) { 49 | ck_assert_ptr_null(iter->next); 50 | ck_assert_ptr_null(iter->prev); 51 | return; 52 | } 53 | 54 | //if length is greater than 1 (1), iterate over nodes to ensure length 55 | ck_assert_ptr_nonnull(iter->next); 56 | iter = iter->next; 57 | 58 | for (int i = 1; i < len; ++i) { 59 | ck_assert(iter != list->head); 60 | iter = iter->next; 61 | } 62 | 63 | return; 64 | } 65 | 66 | 67 | //properly assert a potentially NULL string pair 68 | void assert_names(char * a, char * b) { 69 | 70 | //determine comparison type 71 | int cmp_type = (a == NULL ? 0 : 1) + (b == NULL ? 0 : 2); 72 | 73 | 74 | switch (cmp_type) { 75 | 76 | case 0: 77 | ck_assert_ptr_eq(a, b); 78 | break; 79 | case 1: 80 | ck_assert(*a == '\0' && b == NULL); 81 | break; 82 | case 2: 83 | ck_assert(a == NULL && *b == '\0'); 84 | break; 85 | case 3: 86 | ck_assert_str_eq(a, b); 87 | break; 88 | 89 | } //end switch 90 | 91 | return; 92 | } 93 | 94 | 95 | //basic assertion of state for a mc_vm_map 96 | void assert_vm_map(mc_vm_map * map, int vm_areas_len, int vm_objs_len, 97 | int vm_areas_unmapped_len, int vm_objs_unmapped_len, 98 | int next_id_area, int next_id_obj) { 99 | 100 | //check mapped lists 101 | assert_lst_len(&map->vm_areas, vm_areas_len); 102 | assert_lst_len(&map->vm_objs, vm_objs_len); 103 | 104 | //check unmapped lists 105 | assert_lst_len(&map->vm_areas_unmapped, vm_areas_unmapped_len); 106 | assert_lst_len(&map->vm_objs_unmapped, vm_objs_unmapped_len); 107 | 108 | //check next IDs 109 | ck_assert_int_eq(map->next_id_area, next_id_area); 110 | ck_assert_int_eq(map->next_id_obj, next_id_obj); 111 | 112 | return; 113 | } 114 | 115 | 116 | /* 117 | * NOTE: The mapped area & object lists store areas and objects directly. 118 | * Unmapped area & object lists store unmapped nodes instead, which 119 | * means an additional pointer dereference is required. 120 | */ 121 | 122 | //assert the state of all [unmapped] objects inside a mc_vm_map 123 | void assert_vm_map_objs(cm_lst * lst, struct obj_check * obj_checks, 124 | int start_index, int len, bool mapped) { 125 | 126 | int ret; 127 | 128 | cm_lst_node * node; 129 | mc_vm_obj * obj; 130 | 131 | 132 | for (int i = 0; i < len; ++i) { 133 | 134 | if (!mapped) { 135 | ret = cm_lst_get(lst, start_index + i, &node); 136 | ck_assert_int_eq(ret, 0); 137 | obj = MC_GET_NODE_OBJ(node); 138 | 139 | } else { 140 | obj = cm_lst_get_p(lst, start_index + i); 141 | ck_assert_ptr_nonnull(obj); 142 | } 143 | 144 | assert_names(obj->basename, obj_checks[i].basename); 145 | ck_assert_int_eq(obj->start_addr, obj_checks[i].start_addr); 146 | ck_assert_int_eq(obj->end_addr, obj_checks[i].end_addr); 147 | } 148 | 149 | return; 150 | } 151 | 152 | 153 | //assert only pathnames, not mapped address ranges 154 | void assert_vm_map_objs_aslr(cm_lst * lst, char (* basenames)[NAME_MAX], 155 | int start_index, int len, bool mapped) { 156 | 157 | int ret; 158 | 159 | cm_lst_node * node; 160 | mc_vm_obj * obj; 161 | 162 | 163 | for (int i = 0; i < len; ++i) { 164 | 165 | if (!mapped) { 166 | ret = cm_lst_get(lst, start_index + i, &node); 167 | ck_assert_int_eq(ret, 0); 168 | obj = MC_GET_NODE_OBJ(node); 169 | 170 | } else { 171 | obj = cm_lst_get_p(lst, start_index + i); 172 | ck_assert_ptr_nonnull(obj); 173 | } 174 | 175 | assert_names(obj->basename, basenames[i]); 176 | } 177 | 178 | return; 179 | } 180 | 181 | 182 | //assert the state of all [unmapped] memory areas inside a mc_vm_map 183 | void assert_vm_map_areas(cm_lst * lst, struct area_check * area_checks, 184 | int start_index, int len, bool mapped) { 185 | 186 | int ret; 187 | 188 | cm_lst_node * node; 189 | mc_vm_area * area; 190 | 191 | for (int i = 0; i < len; ++i) { 192 | 193 | if (!mapped) { 194 | ret = cm_lst_get(lst, start_index + i, &node); 195 | ck_assert_int_eq(ret, 0); 196 | area = MC_GET_NODE_AREA(node); 197 | 198 | } else { 199 | area = cm_lst_get_p(lst, start_index + i); 200 | ck_assert_ptr_nonnull(area); 201 | } 202 | 203 | assert_names(area->basename, area_checks[i].basename); 204 | ck_assert_int_eq(area->start_addr, area_checks[i].start_addr); 205 | ck_assert_int_eq(area->end_addr, area_checks[i].end_addr); 206 | } 207 | 208 | return; 209 | } 210 | 211 | 212 | //assert only pathnames, not mapped address ranges 213 | void assert_vm_map_areas_aslr(cm_lst * lst, char (* basenames)[NAME_MAX], 214 | int start_index, int len, bool mapped) { 215 | 216 | int ret; 217 | 218 | cm_lst_node * node; 219 | mc_vm_area * area; 220 | 221 | for (int i = 0; i < len; ++i) { 222 | 223 | if (!mapped) { 224 | ret = cm_lst_get(lst, start_index + i, &node); 225 | ck_assert_int_eq(ret, 0); 226 | area = MC_GET_NODE_AREA(node); 227 | 228 | } else { 229 | area = cm_lst_get_p(lst, start_index + i); 230 | ck_assert_ptr_nonnull(area); 231 | } 232 | 233 | assert_names(area->basename, basenames[i]); 234 | } 235 | 236 | return; 237 | } 238 | 239 | 240 | void assert_vm_obj(mc_vm_obj * obj, char * pathname, char * basename, 241 | uintptr_t start_addr, uintptr_t end_addr, int vm_areas_len, 242 | int last_vm_areas_len, int id, bool mapped) { 243 | 244 | //check names 245 | assert_names(obj->pathname, pathname); 246 | assert_names(obj->basename, basename); 247 | 248 | //check addresses 249 | ck_assert_int_eq(obj->start_addr, start_addr); 250 | ck_assert_int_eq(obj->end_addr, end_addr); 251 | 252 | //check area node lists are initialised 253 | assert_lst_len(&obj->vm_area_node_ps, vm_areas_len); 254 | assert_lst_len(&obj->last_vm_area_node_ps, last_vm_areas_len); 255 | 256 | //check the object ID is correctly set 257 | ck_assert_int_eq(obj->id, id); 258 | 259 | //check the object is set as mapped 260 | ck_assert(obj->mapped == mapped); 261 | 262 | return; 263 | } 264 | 265 | 266 | /* 267 | * Check state of the object by checking the starting addresses of each of 268 | * its constituent areas. 269 | */ 270 | 271 | void assert_vm_obj_list(cm_lst * outer_node_lst, 272 | uintptr_t * start_addrs, int start_addrs_len) { 273 | 274 | mc_vm_area * area; 275 | cm_lst_node * area_node, * iter_node; 276 | 277 | 278 | //setup iteration 279 | iter_node = outer_node_lst->head; 280 | 281 | //if provided lst is empty, return 282 | if (outer_node_lst->len == 0 && outer_node_lst->head == NULL) return; 283 | 284 | //otherwise iterate over area starting addresses 285 | for (int i = 0; i < start_addrs_len; ++i) { 286 | 287 | //check starting address 288 | area_node = MC_GET_NODE_PTR(iter_node); 289 | area = MC_GET_NODE_AREA(area_node); 290 | ck_assert_int_eq(area->start_addr, start_addrs[i]); 291 | 292 | //advance iteration 293 | iter_node = iter_node->next; 294 | } 295 | } 296 | 297 | 298 | void assert_vm_area(mc_vm_area * area, char * pathname, char * basename, 299 | uintptr_t start_addr, uintptr_t end_addr, 300 | cm_byte access, cm_lst_node * obj_node_p, 301 | cm_lst_node * last_obj_node_p, int id, bool mapped) { 302 | 303 | //check names 304 | assert_names(area->pathname, pathname); 305 | assert_names(area->basename, basename); 306 | 307 | //check addresses 308 | ck_assert_int_eq(area->start_addr, start_addr); 309 | ck_assert_int_eq(area->end_addr, end_addr); 310 | 311 | //check access 312 | ck_assert(area->access == access); 313 | 314 | //check object pointers 315 | ck_assert_ptr_eq(area->obj_node_p, obj_node_p); 316 | ck_assert_ptr_eq(area->last_obj_node_p, last_obj_node_p); 317 | 318 | //check the area ID is correctly set 319 | ck_assert_int_eq(area->id, id); 320 | 321 | //check the area is mapped 322 | ck_assert(area->mapped == mapped); 323 | 324 | return; 325 | } 326 | -------------------------------------------------------------------------------- /src/test/map_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_HELPER_H 2 | #define MAP_HELPER_H 3 | 4 | //standard library 5 | #include 6 | 7 | //system headers 8 | #include 9 | 10 | #include 11 | 12 | //external libraries 13 | #include 14 | 15 | //test target headers 16 | #include "../lib/memcry.h" 17 | 18 | 19 | //map test structures 20 | struct obj_check { 21 | 22 | char basename[NAME_MAX]; 23 | uintptr_t start_addr; 24 | uintptr_t end_addr; 25 | }; 26 | 27 | 28 | struct area_check { 29 | 30 | char basename[NAME_MAX]; 31 | uintptr_t start_addr; 32 | uintptr_t end_addr; 33 | }; 34 | 35 | 36 | //map helper functions 37 | void create_lst_wrapper(cm_lst_node * node, void * data); 38 | void assert_lst_len(cm_lst * list, int len); 39 | 40 | void assert_names(char * name_1, char * name_2); 41 | void assert_vm_map(mc_vm_map * map, int vm_areas_len, int vm_objs_len, 42 | int vm_areas_unmapped_len, int vm_objs_unmapped_len, 43 | int next_id_area, int next_id_obj); 44 | 45 | void assert_vm_map_objs(cm_lst * lst, struct obj_check * obj_checks, 46 | int start_index, int len, bool mapped); 47 | void assert_vm_map_objs_aslr(cm_lst * lst, char (* basenames)[NAME_MAX], 48 | int start_index, int len, bool mapped); 49 | 50 | void assert_vm_map_areas(cm_lst * lst, struct area_check * area_checks, 51 | int start_index, int len, bool mapped); 52 | void assert_vm_map_areas_aslr(cm_lst * lst, char (* basenames)[NAME_MAX], 53 | int start_index, int len, bool mapped); 54 | 55 | void assert_vm_obj(mc_vm_obj * obj, char * pathname, char * basename, 56 | uintptr_t start_addr, uintptr_t end_addr, int vm_areas_len, 57 | int last_vm_areas_len, int id, bool mapped); 58 | void assert_vm_obj_list(cm_lst * outer_node_lst, 59 | uintptr_t * start_addrs, int start_addrs_len); 60 | 61 | void assert_vm_area(mc_vm_area * area, char * pathname, char * basename, 62 | uintptr_t start_addr, uintptr_t end_addr, 63 | cm_byte access, cm_lst_node * obj_node_p, 64 | cm_lst_node * last_obj_node_p, int id, bool mapped); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/test/suites.c: -------------------------------------------------------------------------------- 1 | //external libraries 2 | #include 3 | 4 | 5 | 6 | /* 7 | * `libcheck` requires that a test suite is constructed with at least 8 | * one suite. However, all tests should be optional and be selected 9 | * through command-line arguments. To circumvent this limitation, the 10 | * test runner is constructed with a fake_suite. 11 | */ 12 | 13 | Suite * fake_suite() { 14 | 15 | //create a placeholder suite 16 | Suite * s = suite_create("fake suite"); 17 | 18 | return s; 19 | } 20 | -------------------------------------------------------------------------------- /src/test/suites.h: -------------------------------------------------------------------------------- 1 | #ifndef SUITES_H 2 | #define SUITES_H 3 | 4 | //standard library 5 | #include 6 | 7 | //external libraries 8 | #include 9 | 10 | 11 | //modify behaviour a debugger breaks 12 | extern bool _DEBUG_ACTIVE; 13 | 14 | 15 | //unit test suites 16 | Suite * fake_suite(); 17 | Suite * krncry_iface_suite(); 18 | Suite * procfs_iface_suite(); 19 | Suite * map_suite(); 20 | Suite * map_util_suite(); 21 | Suite * util_suite(); 22 | 23 | 24 | //other tests 25 | //TODO 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/test/target/Makefile: -------------------------------------------------------------------------------- 1 | .RECIPEPREFIX:=> 2 | 3 | # This makefile takes the following variables: 4 | # 5 | # CC - Compiler. 6 | # BUILD_DIR - Unit test build directory. 7 | 8 | 9 | CFLAGS=-O0 -ggdb 10 | WARN_OPTS=-Wno-unused-variable -Wno-unused-but-set-variable 11 | LDFLAGS=-L${LIB_BIN_DIR} -Wl,-rpath=${LIB_BIN_DIR} 12 | 13 | SOURCES_TARGET=unit_target.c 14 | OBJECTS_TARGET=${SOURCES_TARGET:%.c=${BUILD_DIR}/%.o} 15 | 16 | TARGET=unit_target 17 | 18 | target: ${TARGET} 19 | > mkdir -p ${BUILD_DIR} 20 | > mv ${TARGET} ${BUILD_DIR} 21 | 22 | ${TARGET}: ${OBJECTS_TARGET} 23 | > ${CC} ${CFLAGS} ${WARN_OPTS} -o $@ $^ ${LDFLAGS} 24 | 25 | ${BUILD_DIR}/%.o: %.c 26 | > ${CC} ${CFLAGS} ${WARN_OPTS} -c $< -o $@ 27 | 28 | clean: 29 | > -rm -v ${BUILD_DIR}/${TARGET} 30 | > -rm -v ${OBJECTS_TARGET} 31 | -------------------------------------------------------------------------------- /src/test/target/unit_target.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | //system headers 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | //local headers 17 | #include "../target_helper.h" 18 | 19 | 20 | /* 21 | * Unit target is tied directly to `../target_helper.{c,h}`. Changing this 22 | * target likely necessitates also changing the target helper. 23 | */ 24 | 25 | /* 26 | * This program sleeps indefinitely, awaiting a signal from its parent, 27 | * which will cause it to dlopen() an additional library and hence change 28 | * its memory map. This is done from inside unit tests. 29 | */ 30 | 31 | 32 | //globals 33 | pid_t parent_pid; 34 | void * libelf; 35 | enum target_map_state state = UNCHANGED; 36 | 37 | /* 38 | * These buffers are used in read/write tests for interfaces. 39 | */ 40 | 41 | char rw_buf[TARGET_BUF_SZ] = IFACE_RW_BUF_STR; 42 | 43 | 44 | //unit test signal handler 45 | void sigusr1_handler() { 46 | 47 | if (state == UNCHANGED) { 48 | libelf = dlopen("libelf.so.1", RTLD_LAZY); 49 | state = MAPPED; 50 | 51 | } else if (state == MAPPED) { 52 | dlclose(libelf); 53 | state = UNMAPPED; 54 | } 55 | 56 | kill(parent_pid, SIGUSR1); 57 | 58 | return; 59 | } 60 | 61 | 62 | int main(int argc, char ** argv) { 63 | 64 | int ch; 65 | void * protected_area; 66 | 67 | //check correct number of args is provided (quiet -Wunused-parameter) 68 | if (argc != 2) return -1; 69 | 70 | //recover parent pid 71 | parent_pid = atoi(argv[1]); 72 | 73 | //map an area that can't be accessed 74 | protected_area = mmap((void *) 0x10000, 0x1000, PROT_NONE, 75 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 76 | 77 | //register unit test handler 78 | signal(SIGUSR1, sigusr1_handler); 79 | 80 | //signal parent that initialisation is finished 81 | kill(parent_pid, SIGUSR1); 82 | 83 | for (int i = 0; ++i; ) { 84 | 85 | //sleep for 10ms to not hoard the CPU 86 | usleep(100000); 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /src/test/target_helper.c: -------------------------------------------------------------------------------- 1 | //standard library 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | //system headers 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | //external libraries 16 | #include 17 | 18 | //local headers 19 | #include "target_helper.h" 20 | #include "map_helper.h" 21 | #include "suites.h" 22 | 23 | //test target headers 24 | #include "../lib/memcry.h" 25 | 26 | 27 | //globals 28 | static enum target_map_state target_state; 29 | 30 | char areas_unchanged[TARGET_AREAS_UNCHANGED][NAME_MAX] = { 31 | "", 32 | "unit_target", 33 | "unit_target", 34 | "unit_target", 35 | "unit_target", 36 | "unit_target", 37 | "", 38 | "libc.so.6", 39 | "libc.so.6", 40 | "libc.so.6", 41 | "libc.so.6", 42 | "libc.so.6", 43 | "", 44 | "", 45 | "ld-linux-x86-64.so.2", 46 | "ld-linux-x86-64.so.2", 47 | "ld-linux-x86-64.so.2", 48 | "ld-linux-x86-64.so.2", 49 | "ld-linux-x86-64.so.2", 50 | "[stack]", 51 | "[vvar]", 52 | "[vdso]" 53 | }; 54 | 55 | char objs_unchanged[TARGET_OBJS_UNCHANGED][NAME_MAX] = { 56 | "0x0", 57 | "unit_target", 58 | "libc.so.6", 59 | "ld-linux-x86-64.so.2", 60 | "[stack]", 61 | "[vvar]", 62 | "[vdso]" 63 | }; 64 | 65 | 66 | char areas_mapped[TARGET_AREAS_MAPPED][NAME_MAX] = { 67 | "", 68 | "unit_target", 69 | "unit_target", 70 | "unit_target", 71 | "unit_target", 72 | "unit_target", 73 | "[heap]", 74 | "libz.so.1.2.13", 75 | "libz.so.1.2.13", 76 | "libz.so.1.2.13", 77 | "libz.so.1.2.13", 78 | "libz.so.1.2.13", 79 | "libelf-0.188.so", 80 | "libelf-0.188.so", 81 | "libelf-0.188.so", 82 | "libelf-0.188.so", 83 | "libelf-0.188.so", 84 | "", 85 | "libc.so.6", 86 | "libc.so.6", 87 | "libc.so.6", 88 | "libc.so.6", 89 | "libc.so.6", 90 | "", 91 | "", 92 | "ld-linux-x86-64.so.2", 93 | "ld-linux-x86-64.so.2", 94 | "ld-linux-x86-64.so.2", 95 | "ld-linux-x86-64.so.2", 96 | "ld-linux-x86-64.so.2", 97 | "[stack]", 98 | "[vvar]", 99 | "[vdso]" 100 | }; 101 | 102 | char objs_mapped[TARGET_OBJS_MAPPED][NAME_MAX] = { 103 | "0x0", 104 | "unit_target", 105 | "[heap]", 106 | "libz.so.1.2.13", 107 | "libelf-0.188.so", 108 | "libc.so.6", 109 | "ld-linux-x86-64.so.2", 110 | "[stack]", 111 | "[vvar]", 112 | "[vdso]" 113 | }; 114 | 115 | 116 | char areas_unmapped[TARGET_AREAS_UNMAPPED][NAME_MAX] = { 117 | "", 118 | "unit_target", 119 | "unit_target", 120 | "unit_target", 121 | "unit_target", 122 | "unit_target", 123 | "[heap]", 124 | "", 125 | "libc.so.6", 126 | "libc.so.6", 127 | "libc.so.6", 128 | "libc.so.6", 129 | "libc.so.6", 130 | "", 131 | "", 132 | "ld-linux-x86-64.so.2", 133 | "ld-linux-x86-64.so.2", 134 | "ld-linux-x86-64.so.2", 135 | "ld-linux-x86-64.so.2", 136 | "ld-linux-x86-64.so.2", 137 | "[stack]", 138 | "[vvar]", 139 | "[vdso]" 140 | }; 141 | 142 | char objs_unmapped[TARGET_OBJS_UNMAPPED][NAME_MAX] = { 143 | "0x0", 144 | "unit_target", 145 | "[heap]", 146 | "libc.so.6", 147 | "ld-linux-x86-64.so.2", 148 | "[stack]", 149 | "[vvar]", 150 | "[vdso]" 151 | }; 152 | 153 | 154 | //signal handlers 155 | static void _sigusr1_handler() { 156 | 157 | //printf(" received signal from unit_target.\n"); 158 | 159 | if (target_state == UNINIT) { 160 | target_state = UNCHANGED; 161 | 162 | } else if (target_state == UNCHANGED) { 163 | target_state = MAPPED; 164 | 165 | } else if (target_state == MAPPED) { 166 | target_state = UNMAPPED; 167 | } 168 | 169 | return; 170 | } 171 | 172 | 173 | //helpers 174 | int clean_targets() { 175 | 176 | int ret; 177 | char command_buf[TARGET_KILL_CMD_LEN]; 178 | 179 | 180 | //build command string 181 | snprintf(command_buf, TARGET_KILL_CMD_LEN, 182 | TARGET_KILL_CMD_FMT, TARGET_NAME); 183 | 184 | //use system() to kill all existing targets 185 | ret = system(command_buf); 186 | if (ret == -1) return -1; 187 | 188 | return 0; 189 | } 190 | 191 | 192 | pid_t start_target() { 193 | 194 | int ret; 195 | __sighandler_t ret_s; 196 | 197 | pid_t target_pid, parent_pid; 198 | char pid_buf[8]; 199 | 200 | char * argv[3] = {TARGET_NAME, 0, 0}; 201 | enum target_map_state old_state; 202 | 203 | 204 | //setup initial state 205 | target_state = old_state = UNINIT; 206 | 207 | //get current pid to pass to target 208 | parent_pid = getpid(); 209 | snprintf(pid_buf, 8, "%d", parent_pid); 210 | argv[1] = pid_buf; 211 | 212 | //register signal handler 213 | ret_s = signal(SIGUSR1, _sigusr1_handler); 214 | ck_assert(ret_s != SIG_ERR); 215 | 216 | //fork a new process 217 | target_pid = fork(); 218 | ck_assert_int_ne(target_pid, -1); 219 | 220 | //change image to target in child 221 | if (target_pid == 0) { 222 | ret = execve(TARGET_NAME, argv, NULL); 223 | ck_assert_int_ne(ret, -1); 224 | 225 | //parent waits for child to complete initialisation 226 | } else { 227 | while (target_state == old_state) {} 228 | } 229 | 230 | return target_pid; 231 | } 232 | 233 | 234 | void end_target(pid_t pid) { 235 | 236 | int ret; 237 | __sighandler_t ret_s; 238 | 239 | pid_t ret_p; 240 | 241 | 242 | //unregister signal handler 243 | ret_s = signal(SIGUSR1, SIG_DFL); 244 | 245 | //terminate target process 246 | ret = kill(pid, SIGTERM); 247 | ck_assert_int_eq(ret, 0); 248 | 249 | //wait for it to terminate 250 | ret_p = waitpid(pid, NULL, 0); 251 | ck_assert_int_eq(ret_p, pid); 252 | 253 | return; 254 | } 255 | 256 | 257 | void change_target_map(pid_t pid) { 258 | 259 | int ret; 260 | __sighandler_t ret_s; 261 | 262 | enum target_map_state old_state = target_state; 263 | 264 | 265 | //assert the target hasn't performed all map transformations already 266 | ck_assert(target_state != UNMAPPED); 267 | 268 | //send SIGUSR1 to the target process to request a change in its memory map 269 | ret = kill(pid, SIGUSR1); 270 | ck_assert_int_eq(ret, 0); 271 | 272 | //busy-wait for target to change its memory map 273 | if (_DEBUG_ACTIVE) { 274 | if (target_state == MAPPED) target_state = UNMAPPED; 275 | if (target_state == UNCHANGED) target_state = MAPPED; 276 | } else { 277 | while (target_state == old_state) {} 278 | } 279 | 280 | return; 281 | } 282 | 283 | 284 | void assert_target_map(mc_vm_map * map) { 285 | 286 | switch(target_state) { 287 | 288 | case UNCHANGED: 289 | assert_vm_map_areas_aslr(&map->vm_areas, areas_unchanged, 290 | 0, TARGET_AREAS_UNCHANGED, true); 291 | assert_vm_map_objs_aslr(&map->vm_objs, objs_unchanged, 292 | 0, TARGET_OBJS_UNCHANGED, true); 293 | break; 294 | 295 | case MAPPED: 296 | assert_vm_map_areas_aslr(&map->vm_areas, areas_mapped, 297 | 0, TARGET_AREAS_MAPPED, true); 298 | assert_vm_map_objs_aslr(&map->vm_objs, objs_mapped, 299 | 0, TARGET_OBJS_MAPPED, true); 300 | break; 301 | 302 | case UNMAPPED: 303 | assert_vm_map_areas_aslr(&map->vm_areas, areas_unmapped, 304 | 0, TARGET_AREAS_UNMAPPED, true); 305 | assert_vm_map_objs_aslr(&map->vm_objs, objs_unmapped, 306 | 0, TARGET_OBJS_UNMAPPED, true); 307 | break; 308 | 309 | default: 310 | ck_assert(false); //invalid state 311 | 312 | } //end switch 313 | 314 | return; 315 | } 316 | -------------------------------------------------------------------------------- /src/test/target_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef TARGET_HELPER_H 2 | #define TARGET_HELPER_H 3 | 4 | //system headers 5 | #include 6 | 7 | //test target headers 8 | #include "../lib/memcry.h" 9 | 10 | 11 | /* 12 | * The target helper is directly tied to `target/unit_target.c`, changing 13 | * the target helper means you likely have to change the unit target and 14 | * vice versa. 15 | */ 16 | 17 | 18 | #define TARGET_AREAS_UNCHANGED 22 19 | extern char areas_unchanged[TARGET_AREAS_UNCHANGED][NAME_MAX]; 20 | 21 | #define TARGET_OBJS_UNCHANGED 7 22 | extern char objs_unchanged[TARGET_OBJS_UNCHANGED][NAME_MAX]; 23 | 24 | #define TARGET_AREAS_MAPPED 33 25 | extern char areas_mapped[TARGET_AREAS_MAPPED][NAME_MAX]; 26 | 27 | #define TARGET_OBJS_MAPPED 10 28 | extern char objs_mapped[TARGET_OBJS_MAPPED][NAME_MAX]; 29 | 30 | #define TARGET_AREAS_UNMAPPED 23 31 | extern char areas_unmapped[TARGET_AREAS_UNMAPPED][NAME_MAX]; 32 | 33 | #define TARGET_OBJS_UNMAPPED 8 34 | extern char objs_unmapped[TARGET_OBJS_UNMAPPED][NAME_MAX]; 35 | 36 | 37 | 38 | //the state of the target's memory map 39 | enum target_map_state { 40 | UNINIT, //waiting for child to start 41 | UNCHANGED, //default state 42 | MAPPED, //new areas mapped 43 | UNMAPPED //newly mapped areas now unmapped 44 | }; 45 | 46 | 47 | //target metadata 48 | #define TARGET_NAME "unit_target" 49 | 50 | #define TARGET_KILL_CMD_FMT "kill $(pidof %s) > /dev/null 2> /dev/null" 51 | #define TARGET_KILL_CMD_LEN NAME_MAX + 64 52 | 53 | #define TARGET_BUF_SZ 16 /* must be even */ 54 | #define IFACE_RW_BUF_STR "read & write me " 55 | #define IFACE_W_BUF_STR "buffer written " 56 | 57 | #define IFACE_RW_BUF_OBJ_INDEX 1 58 | #define IFACE_RW_BUF_AREA_INDEX 4 59 | #define IFACE_RW_BUF_OFF 0x60 60 | 61 | #define IFACE_NONE_OBJ_INDEX 0 62 | #define IFACE_NONE_AREA_INDEX 0 63 | #define IFACE_NONE_OFF 0x0 64 | 65 | 66 | //target helpers 67 | int clean_targets(); 68 | pid_t start_target(); 69 | void end_target(pid_t pid); 70 | void change_target_map(pid_t pid); 71 | void assert_target_map(mc_vm_map * map); 72 | 73 | #endif 74 | --------------------------------------------------------------------------------