360已经开始抢占linux市场, 于是推出了360 for linux版本。 楼主分析的是pc版本,android版本底层功能应该类似。安装360后, 你会发现即使是root也kill不掉start360进程,360使用的所有文件也删不掉。 原因是它加载了两个内核保护模块360safe.ko和immu.ko, 360safe.ko用来保护其进程不被杀掉, immu.ko保护其文件不被删除掉。
欢迎访问我的github: [https://github.com/cloudsec]
360safe.ko
.text.unlikely:0000000000000046 unkill_get_signal_from_task proc near ; CODE XREF: unkill_init+3F p
.text.unlikely:0000000000000046 ; unkill_exit+3F p
.text.unlikely:0000000000000046 ; DATA XREF: ...
.text.unlikely:0000000000000046 call __fentry__
.text.unlikely:000000000000004B push rbp
.text.unlikely:000000000000004C mov rbp, rsp
.text.unlikely:000000000000004F push r12
.text.unlikely:0000000000000051 push rbx
.text.unlikely:0000000000000052 mov rbx, rdi
.text.unlikely:0000000000000055 mov rdi, offset aGet_files_stru ; "get_files_struct"
.text.unlikely:000000000000005C call kallsyms_lookup_name
.text.unlikely:0000000000000061 mov rdi, offset aPut_files_stru ; "put_files_struct"
.text.unlikely:0000000000000068 mov cs:unkill_get_files_struct, rax
.text.unlikely:000000000000006F call kallsyms_lookup_name
.text.unlikely:0000000000000074 mov rdi, rbx
.text.unlikely:0000000000000077 mov cs:unkill_put_files_struct, rax
.text.unlikely:000000000000007E call cs:unkill_get_files_struct
.text.unlikely:0000000000000084 test rax, rax
.text.unlikely:0000000000000087 mov r12, rax
.text.unlikely:000000000000008A jz short loc_D0
.text.unlikely:000000000000008C xor edx, edx
.text.unlikely:000000000000008E
.text.unlikely:000000000000008E loc_8E: ; CODE XREF: unkill_get_signal_from_task+78 j
.text.unlikely:000000000000008E mov rcx, [rbx+rdx*8]
.text.unlikely:0000000000000092 test rcx, rcx
.text.unlikely:0000000000000095 jz short loc_B4
.text.unlikely:0000000000000097 cmp r12, rcx
.text.unlikely:000000000000009A jnz short loc_B4
.text.unlikely:000000000000009C mov rbx, [rbx+rdx*8+10h]
.text.unlikely:00000000000000A1 mov rdi, offset aSignalAddrLx ; "signal addr %lx\n"
.text.unlikely:00000000000000A8 xor eax, eax
.text.unlikely:00000000000000AA mov rsi, rbx
.text.unlikely:00000000000000AD call printk
.text.unlikely:00000000000000B2 jmp short loc_C2
.text.unlikely:00000000000000B4 ; ---------------------------------------------------------------------------
.text.unlikely:00000000000000B4
.text.unlikely:00000000000000B4 loc_B4: ; CODE XREF: unkill_get_signal_from_task+4F j
.text.unlikely:00000000000000B4 ; unkill_get_signal_from_task+54 j
.text.unlikely:00000000000000B4 inc rdx
.text.unlikely:00000000000000B7 cmp rdx, 31Ch
.text.unlikely:00000000000000BE jnz short loc_8E
.text.unlikely:00000000000000C0 xor ebx, ebx
.text.unlikely:00000000000000C2
.text.unlikely:00000000000000C2 loc_C2: ; CODE XREF: unkill_get_signal_from_task+6C j
.text.unlikely:00000000000000C2 mov rdi, r12
.text.unlikely:00000000000000C5 call cs:unkill_put_files_struct
.text.unlikely:00000000000000CB mov rax, rbx
.text.unlikely:00000000000000CE jmp short loc_D2
.text.unlikely:00000000000000D0 ; ---------------------------------------------------------------------------
.text.unlikely:00000000000000D0
.text.unlikely:00000000000000D0 loc_D0: ; CODE XREF: unkill_get_signal_from_task+44 j
.text.unlikely:00000000000000D0 xor eax, eax
.text.unlikely:00000000000000D2
.text.unlikely:00000000000000D2 loc_D2: ; CODE XREF: unkill_get_signal_from_task+88 j
.text.unlikely:00000000000000D2 pop rbx
.text.unlikely:00000000000000D3 pop r12
.text.unlikely:00000000000000D5 pop rbp
.text.unlikely:00000000000000D6 retn
.text.unlikely:00000000000000D6 unkill_get_signal_from_task endp
还原为c源码:
void *unkill_get_signal_from_task(struct task_struct *tsk)
{
void *tmp_addr;
long idx;
unkill_get_files_struct=kallsyms_lookup_name(aGet_files_stru);
unkill_put_files_struct=kallsyms_lookup_name(aPut_files_stru);
tmp_addr=unkill_get_files_struct(tsk);
if (!tmp_addr) {
goto out;
}
for (idx = 0; idx <= 0x31c; idx++) {
if (*(tsk + idx * 8) == 0)
continue;
if (tmp_addr = *(tsk + idx * 8)) {
printk("signal addr %lx\n", *(tsk + idx * 8 + 0x10));
unkill_put_files_struct(tmp_addr);
return *(tsk + idx * 8 + 0x10);
}
}
out:
return NULL;
}
.text:0000000000000000 public unkill_get_flags_from_sig_task
.text:0000000000000000 unkill_get_flags_from_sig_task proc near ; CODE XREF: unkill_init+4F p
.text:0000000000000000 ; unkill_exit+4F p
.text:0000000000000000 ; DATA XREF: ...
.text:0000000000000000 call __fentry__
.text:0000000000000005 push rbp
.text:0000000000000006 xor eax, eax
.text:0000000000000008 mov rbp, rsp
.text:000000000000000B push rbx
.text:000000000000000C nop dword ptr [rax+00h]
.text:0000000000000010
.text:0000000000000010 loc_10: ; CODE XREF: unkill_get_flags_from_sig_task+25 j
.text:0000000000000010 mov edx, [rdi+rax*4]
.text:0000000000000013 test edx, edx
.text:0000000000000015 jz short loc_1B
.text:0000000000000017 cmp edx, esi
.text:0000000000000019 jz short loc_2F
.text:000000000000001B
.text:000000000000001B loc_1B: ; CODE XREF: unkill_get_flags_from_sig_task+15 j
.text:000000000000001B add rax, 1
.text:000000000000001F cmp rax, 110h
.text:0000000000000025 jnz short loc_10
.text:0000000000000027 xor ebx, ebx
.text:0000000000000029
.text:0000000000000029 loc_29: ; CODE XREF: unkill_get_flags_from_sig_task+44 j
.text:0000000000000029 mov rax, rbx
.text:000000000000002C pop rbx
.text:000000000000002D pop rbp
.text:000000000000002E retn
.text:000000000000002F ; ---------------------------------------------------------------------------
.text:000000000000002F
.text:000000000000002F loc_2F: ; CODE XREF: unkill_get_flags_from_sig_task+19 j
.text:000000000000002F lea rbx, [rdi+rax*4+34h]
.text:0000000000000034 mov rdi, offset aFlagsAddrX ; "flags addr %x\n"
.text:000000000000003B xor eax, eax
.text:000000000000003D mov esi, ebx
.text:000000000000003F call printk
.text:0000000000000044 jmp short loc_29
.text:0000000000000044 unkill_get_flags_from_sig_task endp
还原c源码:
void *unkill_get_flags_from_sig_task(struct sighand_struct *sig, struct task_struct *tsk){
int idx;
for (idx = 0; idx <= 0x110; idx++)) {
if (*(sig + idx * 4) == (unsigned int *)tsk) {
printk("flags addr %x\n", *(sig + idx * 4 + 0x34));
return *(sig + idx * 4 + 0x34);
}
}
return NULL;
}
进程不被杀掉的主要原因是修改了其保护进程的signal处理函数, 对SIG_SEGV进行了忽略。
immu.ko同理, 把其保护文件的inode->flags设置为一个不存在的模式,vfs不认识,所以对其进行忽略。
360safe.ko
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
void const *aGet_files_stru="get_files_struct";
void const *aPut_files_stru="put_files_struct";
void *unkill_get_files_struct;
void *unkill_put_files_struct;
void const *aUnKillablePidU="un killable pid %u\n";
void const *aUnKillablePi="un killable pid %u done!\n";
void const *aKillablePidU="killable pid %u\n";
void const *aKillablePidUDo="killable pid %u done!\n"
pidt_t g_pid;
void *unkill_get_flags_from_sig_task(struct sighand_struct *sig, struct task_struct *tsk){
int idx;
for (idx = 0; idx <= 0x110; idx++)) {
if (*(sig + idx * 4) == (unsigned int *)tsk) {
printk("flags addr %x\n", *(sig + idx * 4 + 0x34));
return *(sig + idx * 4 + 0x34);
}
}
return NULL;
}
void *unkill_get_signal_from_task(struct task_struct *tsk)
{
void *tmp_addr;
long idx;
unkill_get_files_struct=kallsyms_lookup_name(aGet_files_stru);
unkill_put_files_struct=kallsyms_lookup_name(aPut_files_stru);
tmp_addr=unkill_get_files_struct(tsk);
if (!tmp_addr) {
goto out;
}
for (idx = 0; idx <= 0x31c; idx++) {
if (*(tsk + idx * 8) == 0)
continue;
if (tmp_addr = *(tsk + idx * 8)) {
printk("signal addr %lx\n", *(tsk + idx * 8 + 0x10));
unkill_put_files_struct(tmp_addr);
return *(tsk + idx * 8 + 0x10);
}
}
out:
return NULL;
}
int unkill_init(void)
{
struct pid *tmp_pid;
struct task_struct *tmp_tsk;
void *tmp_addr;
printk(aUnKillablePidU, g_pid);
tmp_pid=find_get_pid(g_pid);
if (!tmp_pid)
goto out;
tmp_tsk=pid_task(tmp_pid, 0);
if (!tmp_tsk)
goto out;
tmp_addr=unkill_get_signal_from_task(tmp_tsk);
if (!tmp_addr)
goto out;
tmp_addr=unkill_get_flags_from_sig_task(tmp_addr, tmp_tsk);
if (!tmp_addr)
goto out;
if (*(tmp_addr) | 0x40))
printk(aUnKillablePi, g_pid);
out:
return 0;
}
int unkill_exit(void)
{
struct pid *tmp_pid;
struct task_struct *tmp_tsk;
void *tmp_addr;
printk(aKillablePidU, g_pid);
tmp_pid=find_get_pid(g_pid);
if (!tmp_pid)
goto out;
tmp_tsk=pid_task(tmp_pid, 0);
if (!tmp_tsk)
goto out;
tmp_addr=unkill_get_signal_from_task(tmp_tsk);
if (!tmp_addr)
goto out;
tmp_addr=unkill_get_flags_from_sig_task(tmp_addr, tmp_tsk);
if (!tmp_addr)
goto out;
if (*(tmp_addr) & 0FFFFFFBFh)
printk(aKillablePidUDo, g_pid);
out:
return 0;
}
immu.ko
/*
* immu.ko - 360 for linux kernel module.
*
* disassemberd by wzt
*
* hooked_syms:
* 0 &hooked_syms
* 8 addr + 32
* hooked_syms
* 0 | 8 | 16 |20 |28 | 32 | 40 |
* orig_addr|orig_op1|orig_op2|op2|op1|orig_hooked_syms|hooked_syms|
*
* *orig_addr ->rbp - 56
* *(orig_addr + 8) ->rbp - 48
* 0B848 ->rbp - 44
* 0E0FF0000 ->rbp - 36
* gs:28h ->rbp - 32
* rbx
* r12
* r13
* rbp ->rbp
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
int os_index = -1;
char *os_version;
const char *unk_75D = "/";
void *unlocked_ioctl;
void *compat_ioctl;
char *vesions[] = {"3.7", "3.8", "3,9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15", "3.16", "3.17"};
char *g_files[] = {"/etc/360safe/360safe.xml",
"/etc/360safe/7z.so",
"/etc/360safe/qex/MacroDef.enc",
"/etc/360safe/qex/patt.enc",
"/etc/360safe/qex/qex.vdb.enc",
"/etc/360safe/Rar29.so",
"/opt/360safeforlinux/start360"};
int hijack_start(void *orig_addr, void *new_addr)
{
long gs_v;
int op1=0x0E0FF0000;
long op2=0x0B848;
long orig_op2=*(long *)(orig_addr + 8);
long orig_op1=*(long *)(orig_addr);
void *addr;
void *tmp;
void *orig_hooked_syms;
asm("movq %gs:0x28 %0":"=(&gs_v)":);
addr=*(void *)kmalloc_caches();
tmp=addr;
tmp = tmp & 0x0FFFFFFFFFFFEFFFF;
*(void *)(0x15ec)(tmp);
*(long *)(&op2 + 2) = new_addr;
*(long *)orig_addr = *(long *)&op2;
*(int *)(orig_addr + 8) = *(int *)&op1;
*(void *)(0x15ec)(addr);
addr=kmem_cache_alloc_trace(kmalloc_caches[0x30], 0x30, 0xd0);
if (!addr) {
out;
}
*(long *)(&op2 + 2) = new_addr;
*(long *)addr = orig_addr;
*(long *)(addr + 8) = orig_op1;
*(int *)(addr + 16) = orig_op2;
*(long *)(addr + 20) = *(long *)&op2;
*(int *)(addr + 28) = *(int *)&op1;
orig_hooked_syms = hooked_syms;
*(long *)(hooked_syms + 8) = addr + 32;
hooked_syms = addr + 32;
*(long *)(addr + 32) = orig_hooked_syms;
*(long *)(addr + 40) = hooked_syms;
out:
long tmp1;
asm("movq %gs:0x28 %0":"=(&tmp1)":);
if (tmp1 != gs_v)
__stack_chk_fail();
return gs_v;
}
void *hijack_pause(void *orig_addr)
{
void *tmp_hooked_syms = hooked_syms;
void *tmp_addr = (void *)(*(long *)(tmp_hooked_syms - 32));
void *addr, *addr1;
loc_4A8:
if ((long *)tmp_hooked_syms == *(long *)hooked_syms)
return tmp_hooked_syms;
if (tmp_addr != orig_addr) {
if (*(long *)tmp_hooked_syms != *(long *)hooked_syms) {
tmp_hooked_syms = *(long *)hooked_syms - 32;
goto loc_4A8;
}
addr = kmalloc_caches;
addr1 = addr;
addr1 = addr1 & 0xFFFFFFFFFFFEFFFF;
*(void *)(0x15ec)(tmp);
*(long *)orig_addr = *(long *)(tmp_hooked_syms + 8);
*(long *)(orig_addr + 8)= *(long *)(tmp_hooked_syms + 16);
*(void *)(0x15ec)(addr);
goto loc_42D;
}
void *hijack_resume(void *orig_addr)
{
void *tmp_hooked_syms = hooked_syms;
void *tmp_addr = (void *)(*(long *)(tmp_hooked_syms - 32));
void *addr, *addr1;
loc_42D:
if ((long *)tmp_hooked_syms == *(long *)hooked_syms)
return tmp_hooked_syms;
if (tmp_addr != orig_addr) {
if (*(long *)tmp_hooked_syms != *(long *)hooked_syms) {
tmp_hooked_syms = *(long *)hooked_syms - 32;
goto loc_42D;
}
addr = kmalloc_caches;
addr1 = addr;
addr1 = addr1 & 0xFFFFFFFFFFFEFFFF;
*(void *)(0x15ec)(tmp);
*(long *)orig_addr = *(long *)(tmp_hooked_syms + 20);
*(long *)(orig_addr + 8)= *(long *)(tmp_hooked_syms + 28);
*(void *)(0x15ec)(addr);
goto loc_42D;
}
void hijack_stop(void *orig_addr)
{
void *tmp_hooked_syms = hooked_syms;
void *tmp_addr = (void *)(*(long *)(tmp_hooked_syms - 32));
void *addr, *addr1;
if ((long *)tmp_hooked_syms == *(long *)hooked_syms)
return tmp_hooked_syms;
loc_42D:
if (tmp_addr != orig_addr) {
if (*(long *)tmp_hooked_syms != *(long *)hooked_syms) {
tmp_hooked_syms = *(long *)hooked_syms - 32;
goto loc_42D;
}
}
addr = kmalloc_caches;
addr1 = addr;
addr1 = addr1 & 0xFFFFFFFFFFFEFFFF;
*(void *)(0x15ec)(tmp);
*(long *)orig_addr = *(long *)(tmp_hooked_syms + 8);
*(long *)(orig_addr + 8)= *(long *)(tmp_hooked_syms + 16);
*(void *)(0x15ec)(addr);
long v1,v2;
v1 = *(long *)(tmp_hooked_syms + 0x28)
v2 = *(long *)(tmp_hooked_syms + 0x20)
*(long *)(v2 + 8) = v1;
*(long *)v1 = v2;
*(long *)(tmp_hooked_syms + 0x20) = 0xDEAD000000100100;
*(long *)(tmp_hooked_syms + 0x28) = 0xDEAD000000100200;
kfree(tmp_hooked_syms);
}
void *n_unlocked_ioctl(void *addr, long cmd, long arg)
{
void *tmp_addr = addr;
long tmp_cmd = cmd, tmp_arg = arg;
long ret;
int flag;
hijack_pasue(unlocked_ioctl);
ret = unlokced_ioctl(addr, cmd, arg);
hijack_resume(unlocked_ioctl);
if ( *(long *)(*(long *)(addr + 0x18) + 0x30) == NULL)
return cmd;
flag = *(long *)(*(long *)(*(long *)(addr + 0x18) + 0x30) + 0xc);
if (!(flag & 0x1000000))
return cmd;
flag = flag | 0x8;
*(long *)(*(long *)(*(long *)(addr + 0x18) + 0x30) + 0xc) = flag;
return cmd;
}
void *n_compat_ioctl(void *addr, long cmd, long arg)
{
void *tmp_addr = addr;
long tmp_cmd = cmd, tmp_arg = arg;
long ret;
int flag;
hijack_pasue(compat_ioctl);
ret = compat_ioctl(addr, cmd, arg);
hijack_resume(compat_ioctl);
if ( *(long *)(*(long *)(addr + 0x18) + 0x30) == NULL)
return cmd;
flag = *(long *)(*(long *)(*(long *)(addr + 0x18) + 0x30) + 0xc);
if (!(flag & 0x1000000))
return cmd;
flag = flag | 0x8;
*(long *)(*(long *)(*(long *)(addr + 0x18) + 0x30) + 0xc) = flag;
return cmd;
}
void immutable_file(const char *file_name)
{
void *addr;
long flag;
addr = filp_open(file_name, 0, 0);
if (addr < 4096)
return ;
flag = *(long *)(*(long *)(*(long *)(addr + 0x18) + 0x30) + 0xc);
flag = flag | 0x1000008;
*(long *)(*(long *)(*(long *)(addr + 0x18) + 0x30) + 0xc) = flag;
}
void unimmutable_file(const char *file_name)
{
void *addr;
long flag;
addr = filp_open(file_name, 0, 0);
if (addr < 4096)
return ;
flag = *(long *)(*(long *)(*(long *)(addr + 0x18) + 0x30) + 0xc);
flag = flag & 0xFEFFFFF7;
*(long *)(*(long *)(*(long *)(addr + 0x18) + 0x30) + 0xc) = flag;
}
int immu_get_os_version_index(void)
{
int idx;
if (!os_version)
return -1;
for (idx = 0; idx <= 0xa; idx++) {
if (!strncmp(vesion[idx * 8 ], os_version, strlen(version[idx * 8]))
return idx;
}
return -1;
}
void *immu_get_fileo_from_filep(void *file)
{
return (void *)(file + 0x28);
}
int immu_get_unlockedioctl_offset(void)
{
if (os_index < 0)
return -1;
if (os_index <= 7)
return 8;
if (os_index < 9)
return 10;
return -1;
}
void *immu_get_ioctlp_from_fileo(void *file, int flag)
{
void *addr;
int idx
if (os_index < 0)
return NULL;
if (os_index <= 7)
idx = 8;
if (os_index < 9)
idx = 10;
if (flag <= 1) {
return (long)(addr + idx*8);
}
else {
return (long)(addr + idx*8);
}
return NULL;
}
void *disable_wp(void)
{
void *addr;
addr=*(void *)kmalloc_caches();
tmp=addr;
tmp = tmp & 0x0FFFFFFFFFFFEFFFF;
*(void *)(0x15ec)(tmp);
return addr;
}
void restore_wp(void *addr)
{
*(void *)(0x15ec)(addr);
}
void *get_vfs_ioctl(const char *file_name, int flag)
{
void *file, *addr;
file = filp_open(file_name, 0, 0);
if (file < 4096)
return NULL;
addr = *(long *)(file + 0x28);
if (os_index < 0) {
filp_close(file, 0);
return NULL;
}
if (os_index <= 7) {
if (flag <= 1) {
filp_close(file, 0);
return (long)(addr + 8*8);
}
else {
filp_close(file, 0);
return (long)(addr + 9*8);
}
}
return NULL;
}
int init_module(void)
{
long idx;
os_index = immu_get_os_version_index;
if (os_index < 0)
return -1;
unlocked_ioctl = get_vfs_ioctl(unk_75D, 0);
if (unlocked_ioctl) {
hijack_start(n_unlocked_ioctl);
}
compat_ioctl=get_vfs_ioctl(unk_75D, 1);
if (compat_ioctl)
hijack_start(n_compat_ioctl);
}
for (idx = 0; idx <= 0xd0; idx += 8) {
immutable_file(g_files[idx]);
}
return 0;
}
void immu_exit(void)
{
long idx;
if (os_index < 0)
return ;
if (unlocked_ioctl) {
hijack_stop(unlocked_ioctl);
}
if (compat_ioctl) {
hijack_stop(compat_ioctl);
}
for (idx = 0; idx <= 0xd0; idx += 8) {
unimmutable_file(g_files[idx]);
}
}
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.