Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
d9c44f1
fix incorrect last object in updated maps (untested)
vykt Nov 3, 2024
e18b08a
start testing & refactor
vykt Dec 19, 2024
8847359
add unit tests
vykt Dec 22, 2024
47ba8a1
more tests
vykt Dec 24, 2024
82f78c7
add temporary note to README
vykt Dec 26, 2024
18394c8
add more unit tests
vykt Dec 27, 2024
c272094
merge local changes with remote readme update
vykt Dec 27, 2024
351f9d9
finish internal unit tests (not run)
vykt Dec 29, 2024
7aa955f
finish map unit tests
vykt Dec 30, 2024
8b4f751
fix main header
vykt Dec 30, 2024
b1fbc8d
add local changes (unstable)
Feb 1, 2025
3169707
add map util unit tests
Feb 2, 2025
440488e
finish untested suite
Feb 2, 2025
ee45323
Make test suite compile & fix build system
Feb 7, 2025
9b805bd
Change formatting & debug tests
Feb 14, 2025
e1ba879
Bugfix map updates & fix unit tests
Feb 17, 2025
47265c3
Fix assertions of unmapped nodes
Feb 18, 2025
e31571c
Add GDB scripts & make map tests pass
Feb 25, 2025
9fe8e98
Make map tests pass
Feb 25, 2025
bd913c0
Make most tests pass
Feb 27, 2025
1defd2a
Transfer WIP files
Mar 2, 2025
0391926
prepare release
Mar 2, 2025
4cafd9c
Cleanup repo
Mar 2, 2025
12c840f
fix install script
Mar 2, 2025
94071ba
add package make target
Mar 2, 2025
225969f
Cleanup repo
Mar 2, 2025
3c28009
add 'media' folder
Mar 2, 2025
d05c36c
Restructure media
Mar 2, 2025
4634397
Fix README.md
Mar 2, 2025
a4f39a6
Remove notes from README.md
Mar 2, 2025
e448d42
Fix tabs in README.md
Mar 2, 2025
51dabda
Fix README.md indents (again)
Mar 2, 2025
435c95c
fix comment
Mar 6, 2025
a5bb1b2
Remove documentation remnants from Makefile.
Mar 8, 2025
a8adea5
Remove unnecessary CC inter-Makefile parameter for clean targets
Mar 8, 2025
17a17f3
Cleanup makefile
Mar 8, 2025
0400069
Cleanup testing Makefiles
Mar 8, 2025
d3628ed
Cleanup target makefile v2
Mar 8, 2025
24dca83
uwu
Mar 8, 2025
4951a28
Fix clean targets
Mar 9, 2025
53963bd
Rename gdb script file
Mar 14, 2025
531b921
Save 4.2Kb per mc_vm_obj
Mar 17, 2025
ced8fc0
Zero map structures in ctors
Mar 18, 2025
3c3ebc8
Make map_util function names more compact
Mar 28, 2025
a8ad0c3
Add access -> string function
May 16, 2025
3e3f737
Update README.md
vykt Jun 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 71 additions & 41 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,63 +1,93 @@
.RECIPEPREFIX:=>

#TODO [set as required] TODO

#[set as required]
INSTALL_DIR=/usr/local/lib
INCLUDE_INSTALL_DIR=/usr/local/include
LD_DIR=/etc/ld.so.conf.d

CC=gcc
CFLAGS=-ggdb -Wall -fPIC
INCLUDE=-lcmore
CFLAGS=
CFLAGS_TEST=-ggdb3 -O0
WARN_OPTS=-Wall -Wextra
LDFLAGS=-lcmore

LIB_DIR="./src/lib"
TEST_DIR="./src/test"
TGT_DIR="./src/tgt"

SRC_DIR=$(shell pwd)/src
BUILD_DIR=$(shell pwd)/build
DOC_DIR=$(shell pwd)/doc
#[build constants]
LIB_DIR=./src/lib
TEST_DIR=./src/test
BUILD_DIR=${shell pwd}/build
PACKAGE_DIR=./package

INSTALL_DIR=/usr/local
LD_DIR=/etc/ld.so.conf.d

#[installation constants]
SHARED=libmcry.so
STATIC=libmcry.a
HEADER=memcry.h


#[set build options]
ifeq ($(build),debug)
CFLAGS += -O0
CFLAGS += -O0 -ggdb3 -fsanitize=address -DDEBUG
CFLAGS_TEST += -DDEBUG
LDFLAGS += -static-libasan
else
CFLAGS += -O2
CFLAGS += -O3 -flto
endif


#[process targets]
all: lib test tgt
#[set static analysis options]
ifeq ($(fanalyzer),true)
CFLAGS += -fanalyzer
endif

install:
> cp ${BUILD_DIR}/lib/liblain.so ${INSTALL_DIR}/lib
> mkdir -pv ${INSTALL_DIR}/include
> cp ${SRC_DIR}/lib/liblain.h ${INSTALL_DIR}/include
> mkdir -pv ${INSTALL_DIR}/share/man
> cp -R ${DOC_DIR}/roff/* ${INSTALL_DIR}/share/man
> echo "${INSTALL_DIR}/lib" > ${LD_DIR}/90lain.conf
> ldconfig

install_doc:
> mkdir -pv ${INSTALL_DIR}/share/doc/liblain
> cp ${DOC_DIR}/md/* ${INSTALL_DIR}/share/doc/liblain
#[process targets]
.PHONY prepare:
> mkdir -p ${BUILD_DIR}/test ${BUILD_DIR}/lib ${PACKAGE_DIR}

uninstall:
> rm -vf ${INSTALL_DIR}/lib/liblain.so ${INSTALL_DIR}/include/liblain.h \
${INSTALL_DIR}/share/man/man3/liblain_* ${INSTALL_DIR}/share/doc/liblain/*
> rmdir ${INSTALL_DIR}/share/doc/liblain
> rm ${LD_DIR}/90lain.conf
> ldconfig
test: shared
> $(MAKE) -C ${TEST_DIR} tests CC='${CC}' _CFLAGS='${CFLAGS_TEST}' \
_WARN_OPTS='${WARN_OPTS}' \
BUILD_DIR='${BUILD_DIR}/test' \
LIB_BIN_DIR='${BUILD_DIR}/lib'

tgt:
> $(MAKE) -C ${TGT_DIR} tgt CC='${CC}' BUILD_DIR='${BUILD_DIR}'
all: shared static

test: lib
> $(MAKE) -C ${TEST_DIR} test CC='${CC}' BUILD_DIR='${BUILD_DIR}'
shared:
> $(MAKE) -C ${LIB_DIR} shared CC='${CC}' _CFLAGS='${CFLAGS} -fPIC' \
_WARN_OPTS='${WARN_OPTS}' \
_LDFLAGS='${LDFLAGS}' \
BUILD_DIR='${BUILD_DIR}/lib'

lib:
> $(MAKE) -C ${LIB_DIR} lib CC='${CC}' CFLAGS='${CFLAGS}' INCLUDE='${INCLUDE}' BUILD_DIR='${BUILD_DIR}'
static:
> $(MAKE) -C ${LIB_DIR} static CC='${CC}' _CFLAGS='${CFLAGS}' \
_WARN_OPTS='${WARN_OPTS}' \
_LDFLAGS='${LDFLAGS}' \
BUILD_DIR='${BUILD_DIR}/lib'

clean:
> $(MAKE) -C ${TEST_DIR} clean_all CC='${CC}' BUILD_DIR='${BUILD_DIR}'
> $(MAKE) -C ${LIB_DIR} clean_all CC='${CC}' CFLAGS='${CFLAGS}' BUILD_DIR='${BUILD_DIR}'
> ${MAKE} -C ${TGT_DIR} clean_all CC='${CC}' BUILD_DIR='${BUILD_DIR}'
> $(MAKE) -C ${TEST_DIR} clean BUILD_DIR='${BUILD_DIR}/test'
> $(MAKE) -C ${LIB_DIR} clean BUILD_DIR='${BUILD_DIR}/lib'
> -rm ${PACKAGE_DIR}/*

install:
> mkdir -pv ${INSTALL_DIR}
> cp -v ${BUILD_DIR}/lib/${SHARED} ${INSTALL_DIR}
> cp -v ${BUILD_DIR}/lib/${STATIC} ${INSTALL_DIR}
> mkdir -pv ${INCLUDE_INSTALL_DIR}
> cp -v ${LIB_DIR}/${HEADER} ${INCLUDE_INSTALL_DIR}
> echo "${INSTALL_DIR}" > ${LD_DIR}/90memcry.conf
> ldconfig

uninstall:
> -rm -v ${INSTALL_DIR}/{${SHARED},${STATIC}}
> -rm -v ${INCLUDE_INSTALL_DIR}/${HEADER}
> -rm ${LD_DIR}/90memcry.conf
> ldconfig

package: all
> -cp ${BUILD_DIR}/lib/${SHARED} ${PACKAGE_DIR}
> -cp ${BUILD_DIR}/lib/${STATIC} ${PACKAGE_DIR}
> -cp ${LIB_DIR}/memcry.h ${PACKAGE_DIR}
> -tar cvjf ${PACKAGE_DIR}/memcry.tar.bz2 ${PACKAGE_DIR}/*
191 changes: 134 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,165 @@
# liblain
# MemCry

<p align="center">
<img src="liblain.png" width="150" height="150">
<img src="media/memcry.png" width="150" height="150">
</p>


### ABOUT:

The Lain library (<b>liblain</b>) provides a programmatic interface to the memory and memory maps of processes on Linux. Liblain can be used for:
**The MemCry Library provides**:

- Code injection
- Memory analysis
- Anti-cheat bypass (see [lain.ko](https://github.com/vykt/lain.ko))
- Graph-like data structure (*map*) for representing the memory map of a target process.
- The ability to update the *map* as the target's memory mappings change without invalidating pointers to said map.
- Tracking of (assumed) ownership of unnamed *vm_area*s.
- Support for **multiple interfaces** for acquiring the memory maps, reading and writing memory.
- Multiple convenient utilities.

<b>liblain</b> offers both a procfs and a [lain.ko](https://github.com/vykt/lain.ko) LKM backend. Both interfaces provide identical functionality and are interchangable.
**Planned**:

<b>liblain</b> stores both virtual memory areas and backing objects in nodes traversable as lists or trees. The use of nodes for storage means the internal memory map can be updated without invalidating any pointers. This makes development of complex tools much easier.
- An interface for operating on coredumps instead of live processes.

In addition to a memory interface <b>liblain</b> also provides several utilities including:
<p align="center">
<img src="media/overview.png">
</p>

- Same method for resolving PID as ps/top.
- Fast address -> VM area search.
See the example below. Feel free to contact me on discord: *@vykt* or email: *vykt[at]disroot[dot]org*

---

### DEPENDENCIES:

<b>liblain</b> requires [libcmore](https://github.com/vykt/libcmore).
If you're not using a packaged release, you'll need to install:

- [CMore](https://github.com/vykt/cmore) - Data structures for C.

### INSTALLATION:

Fetch the repo:
```
$ git clone https://github.com/vykt/liblain
```
### EXAMPLE:

Build:
```
$ make lib
```
```c
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>

Install:
```
# make install
```
#include <memcry.h>
#include <cmore.h>

Install additional markdown documentation:
```
# make install_doc
```

To uninstall:
```
# make uninstall
```
int main() {

---
int ret;


### LINKING:
/*
* First, find the PID of the target based on the target's name. You can
* optionally pass a pointer to an uninitialised CMore vector if you want
* to find PIDs of multiple processes with the same name.
*/
pid_t pid;
pid = mc_pid_by_name("target_name", NULL);

Ensure your linker searches for liblain in the install directory (adjust as required):
```
# echo "/usr/local/lib" > /etc/ld.so.conf.d/liblain.conf
```

Include `<liblain.h\>` in your sources:
```
#include <liblain.h>
```
/*
* Open a session on your target. For the procfs interface, this will
* open file descriptors on /proc/pid/{mem,maps}
*/
mc_session s;
ret = mc_open(&s, PROCFS, pid);
if (ret != 0) {
/* on error, a perror() function is provided */
mc_perror("[error]");
}

Ask your compiler to link liblain:
```
$ gcc -o test test.c -llain
```

---
/*
* Read the target's memory map for the first time.
*/
mc_vm_map m;
ret = mc_update_map(&s, &m);


/*
* Find the "libfoo.so" object in the target.
*/
cm_lst_node * libfoo_node = NULL;
libfoo_node = mc_get_obj_node_by_basename(&m, "libfoo.so");
if (libfoo_node == NULL) {/*...*/}


/*
* Print libfoo.so's starting address.
*/
mc_vm_obj * libfoo_obj = MC_GET_NODE_OBJ(libfoo_node);
printf("libfoo.so start addr: 0x%lx, end addr: 0x%lx\n",
libfoo_obj->start_addr, libfoo_obj->end_addr);


### DOCUMENTATION:
/*
* Print the full path of the object after libfoo.so.
*/
cm_lst_node * next_node = libfoo_node->next;
mc_vm_obj * next_obj = MC_GET_NODE_OBJ(next_node);
printf("after libfoo.so: %s\n", next_obj->pathname);

See `./doc/md` for markdown documentation. After installing <b>liblain</b> the following manpages are available:

| Manpage | Description |
| --------------- | --------------------------- |
| `liblain_error` | Error handling |
| `liblain_map` | Memory map data structure |
| `liblain_iface` | Using interfaces |
| `liblain_util` | Utilities |
/*
* Get the first area of libfoo.so. The object of libfoo (libfoo_obj)
* stores pointers to area nodes.
*/
cm_lst_node * area_node_p = libfoo_obj->vm_area_node_ps.head;
cm_lst_node * area_node = MC_GET_NODE_PTR(area_node_p);
mc_vm_area * area = MC_GET_NODE_AREA(area_node);
printf("is first area writable?: %d\n, area->access & MC_ACCESS_WRITE);


/*
* Get the next area and print its address range.
*/
mc_vm_area * next_area = MC_GET_NODE_AREA(area_node->next);
printf("next area start addr: 0x%lx, end addr: 0x%lx\n",
next_area->start_addr, next_area->end_addr);


/*
* The target's (OS-wide) memory map may have been updated; we should update
* our local map.
*/
ret = mc_update_map(&s, &m);
if (ret != 0) {/*...*/}


/*
* Check if libfoo.so is still mapped. If not, fetch the next mapped
* object. Even if libfoo.so and its constituent areas have been unmapped,
* their nodes and object pointers will remain valid.
*/
cm_lst_node * iter_node;
mc_vm_obj * iter_obj;
if (libfoo_obj->mapped == false) {
iter_node = libfoo_node->next;
while (iter_node != m.vm_objs.head) {
iter_obj = MC_GET_NODE_OBJ(iter_node);
if (iter_obj->mapped == true) break;
}
}


/*
* Clean up unmapped objects & areas. This will cause `libfoo_node` and
* `libfoo_obj` pointers to become invalid.
*/
ret = mc_map_clean_unmapped(&m);
if (ret != 0) {/*...*/}


/*
* Clean up and exit.
*/
ret = mc_close(&s);
if (ret != 0) {/*...*/}

ret = mc_del_vm_map(&m);
if (ret != 0) {/*...*/}

return 0;
}
```
Loading