├── 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 |
--------------------------------------------------------------------------------