~/.gdbinit
нужно записать полезную функцию:define xxd
dump binary memory dump.bin $arg0 $arg0+$arg1
shell xxd dump.bin
end
0x413000
. Этот скрипт проверяет почту и ключ:activated
до activ
, чтобы размер образа остался тем же. К счастью, проверки хэш-суммы нет. Образ назовем lunix_broken_activation.iso.sudo qemu-system-x86_64 lunix_broken_activation.iso -enable-kvm
/dev/activate
, а значит, логику проверки нужно искать где-то в недрах ядра.email|key
./dev/mem
, /dev/vcs
, /dev/activate
и т.д. регистрируются с помощью функции register_chrdev
:int register_chrdev (unsigned int major,
const char * name,
const struct fops);
name
— имя, а структура fops
содержит указатели на функции драйвера:struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
loff_t *);
};
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
эталонный Minimal Linux -> известный адрес register_chrdev -> сигнатура ->
искомый адрес register_chrdev в Lunix
sudo apt install wget make gawk gcc bc bison flex xorriso libelf-dev libssl-dev
git clone https://github.com/ivandavidov/minimal
cd minimal/src
02_build_kernel.sh
:# Disable debug symbols in kernel => smaller kernel binary.
sed -i "s/^CONFIG_DEBUG_KERNEL.*/\\# CONFIG_DEBUG_KERNEL is not set/" .config
echo "CONFIG_GDB_SCRIPTS=y" >> .config
./build_minimal_linux_live.sh
kernel.xz
и образ ФС rootfs.xz
. Переименуем их в kernel.minimal.xz
, rootfs.minimal.xz
.extract-vmlinux kernel.minimal.xz > vmlinux.minimal
kernel.minimal.xz
, rootfs.minimal.xz
, vmlinux.minimal
.vmlinux.lunix
, про kernel.xz
, rootfs.xz
забываем, сейчас расскажу почему."APPEND vga=normal"
и заменим на "APPEND nokaslr\x20\x20\x20"
. sudo qemu-system-x86_64 -kernel kernel.minimal.xz -initrd rootfs.minimal.xz -append nokaslr -s
sudo gdb vmlinux.minimal
(gdb) target remote localhost:1234
register_chrdev
в списке функций:__register_chrdev
. 0xffffffff811c9785 <+101>: shl $0x14,%esi
0xffffffff811c9788 <+104>: or %r12d,%esi
lunix
есть только одна функция, которая содержит 0xc1, 0xe6, 0x14, 0x44, 0x09, 0xe6
.__register_chrdev
адрес 0xffffffff811c9720
, это сегмент .text
. Там и будем искать.sudo qemu-system-x86_64 lunix_nokaslr.iso -s -enable-kvm
sudo gdb vmlinux.lunix
(gdb) target remote localhost:1234
.text
:0xffffffff81000000 - 0xffffffff81600b91
, ищем 0xc1, 0xe6, 0x14, 0x44, 0x09, 0xe6
:0xffffffff810dc643
. Но это только часть функции, посмотрим, что выше:0xffffffff810dc5d0
(потому что retq
— это выход из соседней функции).register_chrdev
такой:int register_chrdev (unsigned int major,
const char * name,
const struct fops);
fops
.0xffffffff810dc5d0
. Он сработает несколько раз. Это просыпаются устройства mem, vcs, cpu/msr, cpu/cpuid
, а сразу за ними и activate
.rcx
. А указатель на fops
— в r8
:struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
loff_t *);
};
write
— 0xffffffff811f068f
.vmlinux.lunix
и посмотрим, что внутри у функции write.sub_FFFFFFFF811F0413
, которая начинается так:0xffffffff81829ce0
обнаруживается таблица для sha256:sub_FFFFFFFF811F0413
= sha256. Байты, хэш которых нужно получить, передаются через $sp+0x50+var49
, а результат сохраняется по адресу $sp+0x50+var48
. Кстати, var49=-0x49
, var48=-0x48
, так что $sp+0x50+var49 = $sp+0x7
, $sp+0x50+var48 = $sp+0x8
.0xffffffff811f0748 call sub_FFFFFFFF811F0413
и на инструкцию 0xffffffff811f074d xor ecx, ecx
, которая сразу за функцией. Вводим почту test@mail.ru
, пароль 1234-5678-0912-3456
.>>> import hashlib
>>> hashlib.sha256(b"t").digest().hex()
'e3b98a4da31a127d4bde6e43033f66ba274cab0eb7eb1c70ec41402bf6273dd8'
>>>
0xEC
, то сохраняется остаток от деления на 0xEC
:import hashlib
def get_email_hash(email):
h = [0]*32
for sym in email:
sha256 = hashlib.sha256(sym.encode()).digest()
for i in range(32):
s = h[i] + sha256[i]
if s <= 0xEC:
h[i] = s
else:
h[i] = s % 0xEC
return h
0xffffffff81c82f80
. Давайте посмотрим, какой будет хэш от почты test@mail.ru
.ffffffff811f0786 dec r13d
(это выход из цикла):>>> get_email_hash('test@mail.ru')
2b902daf5cc483159b0a2f7ed6b593d1d56216a61eab53c8e4b9b9341fb14880
0xFFFFFFFF811F0943 imul eax, r12d
0xFFFFFFFF811F0947 cdq
0xFFFFFFFF811F0948 idiv r10d
eax
и r12d
байты хэша, они перемножаются, а потом берется остаток от деления на 9.def keygen(email):
email_hash = get_email_hash(email)
pairs = [(0x00, 0x1c), (0x1f, 0x03), (0x01, 0x1d), (0x1e, 0x02),
(0x04, 0x18), (0x1b, 0x07), (0x05, 0x19), (0x1a, 0x06),
(0x08, 0x14), (0x17, 0x0b), (0x09, 0x15), (0x16, 0x0a),
(0x0c, 0x10), (0x13, 0x0f), (0x0d, 0x11), (0x12, 0x0e)]
key = []
for pair in pairs:
i = pair[0]
j = pair[1]
key.append((email_hash[i] * email_hash[j])%9)
return [''.join(map(str, key[i:i+4])) for i in range(0, 16, 4)]
>>> import lunix
>>> lunix.keygen("m.gayanov@gmail.com")
['0456', '3530', '0401', '2703']
К сожалению, не доступен сервер mySQL