什么是audit? audit可以用来干什么?
什么是audit
The Linux Audit Subsystem is a system to Collect information regarding events occurring on the system(s) ,Kernel events (syscall events), User events (audit-enabled programs) syslog记录的信息有限,主要目的是软件调试,跟踪和打印软件的运行状态,而audit的目的则不同,它是linux安全体系的重要组成部分,是一种“被动”的防御体系。在内核里有内核审计模块,记录系统中的各种动作和事件,比如系统调用,文件修改,执行的程序,系统登入登出和记录所有系统中所有的事件,它的主要目的是方便管理员根据日记审计系统是否允许有异常,是否有入侵等等,说穿了就是把和系统安全有关的事件记录下来。
(资料图片仅供参考)
audit可以用来干什么
Watching file accessMonitoring system callsRecording commands run by a userRecording security eventsMonitoring network access
怎么开启audit
首先内核需要打开CONFIG_AUDIT的配置,在打开了配置重新编译内核后,audit功能默认是关闭的,有两种方法在使能audit: 1)cmdline中加入audit= 1参数,如果这个参数设置为1,而且auditd没有运行,则审计日志会被写到/var/log/messages中。 2)使用守护进程auditd
auditd是如何工作的
下面是auditd整体的框架图: 从这个图大概就能看出audit是如何工作的,可以看到 audit 是内核中的一个模块,内核的运行情况都会记录在 audit 中,当然这个记录的规则是由超级用户来设置的。audit.rules 是 audit 的规则文件,auditctl程序负责将规则写入audit模块的过滤器中,过滤后的数据都会传送到 auditd 中,然后再由 auditd 进行其它操作。auditd.conf 是 auditd 的配置文件,确定 auditd 是如何启动的,日志文件放在哪里等等。auditd 收到的数据后会有两个去处。默认的是将日志保存在 audit.log 文件中,默认路径/var/log/audit/audit.log。另一个通过 audispd 将日志进行分发。 简单的使用: auditd auditctl -R /etc/audit/rules.d/audit.rules cat /var/log/audit/audit.log 就可以查看audit记录的信息
auditd对应的源码在:audit-2.7.1/src/auditd.c,其中audit-2.7.1/lib 对应着库的源码,简单的浏览一下源码就能知道auditd每个选项的作用和它的主要工作 -s指定启动时的audit工作状态,可选的状态有:startup_disable,startup_enable,startup_nochange,如果未指定,默认为 enum startup_state opt_startup = startup_enable;
auditctl对应的源码在:audit-2.7.1/src/auditctl.c,简单浏览一下源码就能知道auditctl每个选择的作用和程序的主要原理。 auditctl的工作流程大概如下:
int main(int argc, char *argv[]){if ((argc == 3) && (strcmp(argv[1], "-R") == 0)) {fd = audit_open();if (is_ready(fd) == 0)return 0;fileopt(argv[2])}}
其中需要特别注意的是is_ready(),如果-R选项,也就是从文件中读取规则,则audit系统必须要先处于enable状态,is_ready()的实现如下:
static int is_ready(int fd) { if (audit_is_enabled(fd) == 2) { //规则不可改变 audit_msg(LOG_ERR, "The audit system is in immutable mode," " no rule changes allowed"); return 0; } else if (errno == ECONNREFUSED) { //audit处于disable状态 audit_msg(LOG_ERR, "The audit system is disabled"); return 0; } return 1; }
接着上面的分析,fileopt()打开规则文件:
static int fileopt(const char *file){rc = open(file, O_RDONLY);if (rc < 0) {... //规则文件不存在,返回错误}... //接下来判读规则文件的访问权限,当前进程是否是root,**规则文件是否全局可写**,是否是常规文件while (get_line(f, buf)) { //循环读取文件中的每一行preprocess(buf);ptr = audit_strsplit(buf); //去除行首的空白if (ptr == NULL) //空行标志着规则文件的结束break;if (ptr[0] == "#") { //#开头为注释,直接跳过,读取下一行lineno++;continue;}reset_vars();//复位变量,也就是说规则是以行为单位的,行之间无关联rc = setopt(i, lineno, fields); //分析每一行的选项,分析的结果放在rule_new中if (rc != -3) {handle_request(rc);//如果规则没错,这执行存放在rule_new中的分析结果}lineno++;}//while}
需要说明的选项是: -e: 0表示disable,1表示enable,2表示设置规则不可变 -f:当audit反应严重错误是应该采取的动作,0=silent啥都不干, 1=printk 打印错误,2=panic
配置文件详解
/etc/audit/auditd.conf存放在配置文件,决定auditd程序的行为,对其中一些重要的选项进行说明:
log_file
指定log文件存放的位置
max_log_file
指定单个log文件的最大大小,单位是Mbyte
max_log_file_action
当log文件达到max_log_file设定的大小时执行的动作,可选的动作 有:ignore/syslog/suspend/rotate, ignore表示忽略max_log_file设置的限制,继续写log文件,syslog表示会向syslog中写入一条warning,suspend表示auditd不再写log文件,但是auditd继续运行,rotate表示分多个log文件,一个log文件达到上限后在创建一个新的不同名字的log文件,后面会继续讲解该选项。
space_left
表示log_file 文件所在的分区空闲空间少于这个设定的值时,触发相应的动作,单位是Mbyte
space_left_action 指定space_left触发后执行的动作,可选的选项有:ignore/syslog/suspend/single/halt,前面三个选项与max_log_file_action相似,single表示audit进程会将系统模式变为单用户模式,halt表示audit进程将会触发系统关机admin_space_left 系统管理员用户对于的空间,space_left讲的是普通用户admin_space_left_action 与space_left_action类似,但是对于系统管理员disk_full_action 磁盘满了之后应该采取的动作disk_error_action 磁盘写错误之后应该采取的动作flush 表示日志文件的刷新方式,可选的选项有:NONE、INCREMENTAL、DATA和SYNC,如果设置为NONE,则不需要做特殊努力来将数据刷新到日志文件中。如果设置为INCREMENTAL,则用freq选项的值确定多长时间发生一次向磁盘的刷新。如果设置为DATA,则审计数据和日志文件一直是同步的。如果设置为SYNC,则每次写到日志文件时,数据和元数据是同步的。num_logs 表示保留日志文件的最大个数,只有在max_log_file_action=rotate时该选项该有意义,必须是0~99之间的数。如果设置为小于2,则不会循环日志。如果递增了日志文件的数目,就可能有必要递增/etc/audit/audit.rules中的内核backlog设置值,以便留出日志循环的时间。如果没有设置num_logs值,它就默认为0,意味着从来不循环日志文件。当达到指定文件容量后会循环日志文件,但是只会保存一定数目的老文件,这个数目由num_logs参数指定。老文件的文件名将为audit.log.N,其中 N是一个数字。这个数字越大,则文件越老。
规则文件详解
先来一种整体的架构图: 内核audit模块定义了user,task,exit钩子,每个钩子对应一张表,当内核路径调用对应钩子时,对比钩子对应表中的规则,如果条件符合,则打印log到audit子系统,exclude用于auditd取audit log时进行过滤,过滤掉不感兴趣的event。
//task,user,exit钩子Kernel: if (audit_enabled) { struct audit_buffer *ab; uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current)); unsigned int sessionid = audit_get_sessionid(current); ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_KERNEL_OTHER); if (!ab) return; audit_log_format(ab, "auid=%u ses=%u" ,loginuid, sessionid); audit_log_task_context(ab); audit_log_format(ab, " comm="); audit_log_untrustedstring(ab, comm); audit_log_end(ab);}//用户空间也可产生audit消息,此时对应user钩子User space:char buf[4096], *acct;int fd = audit_open();// acct is untrusted string and must be encodedacct = audit_encode_nv_string("acct", pamh->user, 0);snprintf(buf, sizeof(buf), "op=change-password sauid=%d %s", audit_getloginuid(), acct);audit_log_user_message(fd, AUDIT_USER_CHAUTHTOK, buf, NULL, NULL, NULL, 0);free(acct);close(fd);
audit_open()和audit_log_user_message()函数在audit lib中定义,用户空间和audit内核空间是通过netlink进行数据交换的。 auid是Audit User IDentity的缩写,下面一段话有助于理解它的作用: Clearly, we first need a way to track a user, be it an actual person or a system user, in such a way that we won’t lose track e.g. by a “sudo” or “su”. The way this is done is by setting an additional UID, the AUID, which is different from the “normal” UID a user has, and is supposed to remain unchanged, whatever the user does.
4294967295 is just (unsigned long) -1. -1 means that loginuid was not set. This is normal behavior for processes that were not spawned by any login process (e.g. for daemons). loginuid is -1 by default; pam_loginuid module changes it to your user id whenever you login (in a tty/in DM/via ssh), and this value is preserved by child processes.
pam_loginuid 是pam的一个库,通过调用audit_setloginuid()函数来修改:
int audit_setloginuid(uid_t uid) { char loginuid[16]; int o, count, rc = 0; errno = 0; count = snprintf(loginuid, sizeof(loginuid), "%u", uid); o = open("/proc/self/loginuid", O_NOFOLLOW|O_WRONLY|O_TRUNC); if (o >= 0) { int block, offset = 0; while (count > 0) { block = write(o, &loginuid[offset], (unsigned)count); if (block < 0) { if (errno == EINTR) continue; printf("Error writing loginuid\n"); close(o); return 1; } offset += block; count -= block; } close(o); } else { printf("Error opening /proc/self/loginuid\n"); rc = 1; } return rc; }
当然可能有人会说黑客可以篡改这个值,kernel也是有响应的机制来防止auid设置了之后是不可修改的:
static int audit_set_loginuid_perm(kuid_t loginuid) { /* if we are unset, we don"t need privs */ if (!audit_loginuid_set(current)) return 0; /* if AUDIT_FEATURE_LOGINUID_IMMUTABLE means never ever allow a change*/ if (is_audit_feature_set(AUDIT_FEATURE_LOGINUID_IMMUTABLE)) return -EPERM; /* it is set, you need permission */ if (!capable(CAP_AUDIT_CONTROL)) return -EPERM; /* reject if this is not an unset and we don"t allow that */ if (is_audit_feature_set(AUDIT_FEATURE_ONLY_UNSET_LOGINUID) && uid_valid(loginuid)) return -EPERM; return 0; }
每个钩子都有一张表,表中每一行存放一条规则,规则由一系列条件组成,在决定是否打印log到audit子系统时,依次比较每一条规则,如果规则满足,则停止比较,产生audit log,如果所有的规则都不满足,则log丢弃。 auditctl工具就是向对应的list(task,user,exit)中添加,删除规则。 注意user都是从用户空间写入audit log的,user中的规则主要是过滤从用户空间写入的audit log。 规则可以存放在文件中,由auditctl -R /etc/audit/rules.d/audit.rules加载规则文件。
There are three types of Audit rules that can be specified Control rules— allow the Audit system’s behavior and some of its configuration to be modified.
File system rules— also known as file watches, allow the auditing of access to a particular file or a directory.
auditctl -w path_to_file -p permissions -k key_name
path_to_fileis the file or directory that is audited. permissions are the permissions that are logged. permissionscan be one or a combination of r(read), w(write), x(execute), and a(attribute change). key_nameis an optional string that helps you identify which rule(s) generated a particular log entry. System call rules— allow logging of system calls that any specified program makes.
auditctl -a action,filter -S system_call -F field=value -k key_name
-a [list,action|action,list]Append rule to the end of list with action. Please note the comma separating the two values. Omitting it will cause errors. The fields may be in either order. It could be list,action or action,list. The following describes the valid list names: taskAdd a rule to the per task list. This rule list is used only at the time a task is created – when fork() or clone() are called by the parent task. When using this list, you should only use fields that are known at task creation time, such as the uid, gid, etc. exitAdd a rule to the syscall exit list. This list is used upon exit from a system call to determine if an audit event should be created. userAdd a rule to the user message filter list. This list is used by the kernel to filter events originating in user space before relaying them to the audit daemon. It should be noted that the only fields that are valid are: uid, auid, gid, pid, subj_user, subj_role, subj_type, subj_sen, and subj_clr. All other fields will be treated as non-matching. excludeAdd a rule to the event type exclusion filter list. This list is used to filter events that you do not want to see. For example, if you do not want to see any avc messages, you would using this list to record that. The message type that you do not wish to see is given with the msgtype field. The following describes the valid actions for the rule:
neverNo audit records will be generated. This can be used to suppress event generation. In general, you want suppressions at the top of the list instead of the bottom. This is because the event triggers on the first matching rule. alwaysAllocate an audit context, always fill it in at syscall entry time, and always write out a record at syscall exit time. Replacing -a with -A in the above command will insert the rule at the top instead of at the bottom. action and filter specify when a certain event is logged. action can be either always or never. filter specifies which kernel rule-matching filter is applied to the event. The rule-matching filter can be one of the following: task, exit, user, and exclude. action,filter will be always,exit in most cases, which tells auditctl that you want to audit this system call when it exits. system_call specifies the system call by its name. Several system calls can be grouped into one rule, each specified after a -S option. The word all may also be used. You can use the sudo ausyscall --dump command to view a list of all system calls along with their numbers. field=value specifies additional options that modify the rule to match events based on a specified architecture, user ID, process ID, path, and others. key_name is an optional string that helps you identify later which rule or a set of rules generated a particular log entry. You can also define a filesystem rule using the system call rule syntax. For example, the following rule:
sudo auditctl -a always,exit -F path=/etc/hosts -F perm=wa -k hosts_file_change does the same job as the filesystem rule we saw in the earlier section:
sudo auditctl -w /etc/hosts -p wa -k hosts_file_change
可参考:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security_guide/sec-defining_audit_rules_and_controls
https://www.digitalocean.com/community/tutorials/how-to-write-custom-system-audit-rules-on-centos-7
https://linux.die.net/man/8/auditctl
规则源代码分析
规则分析 分析的入口是:src/auditctl.c static int setopt(int count, int lineno, char *vars[]) 分析之后的数据为:
struct audit_rule_data {__u32flags;/* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */__u32action;/* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */__u32field_count;__u32mask[AUDIT_BITMASK_SIZE]; /* syscall(s) affected */__u32fields[AUDIT_MAX_FIELDS];__u32values[AUDIT_MAX_FIELDS];__u32fieldflags[AUDIT_MAX_FIELDS];__u32buflen;/* total length of string fields */charbuf[0];/* string fields buffer */};
通过以下接口将规则数据写入内核:
rc = audit_add_rule_data(fd, rule_new, add, action);rc = audit_delete_rule_data(fd, rule_new, del, action);int audit_add_rule_data(int fd, struct audit_rule_data *rule, int flags, int action){int rc;rule->flags = flags;rule->action = action;rc = audit_send(fd, AUDIT_ADD_RULE, rule, sizeof(struct audit_rule_data) + rule->buflen);if (rc < 0)audit_msg(audit_priority(errno),"Error sending add rule data request (%s)",errno == EEXIST ? "Rule exists" : strerror(-rc));return rc;}
可以看出是通过netlink和内核进行通信的。 具体的分析过程不讲,感兴趣的可以自己去看源码,这里仅简单说明以下-F选项:
int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair, int flags){field = audit_name_to_field(f);rule->fields[rule->field_count] = field;case AUDIT_ARCH:if (_audit_syscalladded) return -EAU_ARCHMISPLACED;if (!(op == AUDIT_NOT_EQUAL || op == AUDIT_EQUAL))return -EAU_OPEQNOTEQ;if (isdigit((char)*(v))) {int machine;errno = 0;_audit_elf = strtoul(v, NULL, 0);if (errno) return -EAU_ELFUNKNOWN;// Make sure we have a valid mappingmachine = audit_elf_to_machine(_audit_elf);if (machine < 0)return -EAU_ELFUNKNOWN;}else {const char *arch=v;unsigned int machine, elf;machine = audit_determine_machine(arch);/* OK, we have the machine type, now convert to elf. */elf = audit_machine_to_elf(machine);if (elf == 0)return -EAU_ELFUNKNOWN;_audit_elf = elf;}rule->values[rule->field_count] = _audit_elf; _audit_archadded = 1;break;rule->fieldflags[rule->field_count] = op;}//field名字和对应的field num之间的转换static const char field_strings[] = "a0\0a1\0a2\0a3\0arch\0auid\0devmajor\0devminor\0dir\0egid\0""euid\0exe\0exit\0field_compare\0filetype\0fsgid\0fstype\0fsuid\0gid\0inode\0""key\0loginuid\0msgtype\0obj_gid\0obj_lev_high\0obj_lev_low\0obj_role\0obj_type\0obj_uid\0obj_user\0""path\0perm\0pers\0pid\0ppid\0sessionid\0sgid\0subj_clr\0subj_role\0subj_sen\0""subj_type\0subj_user\0success\0suid\0uid";
#define AUDIT_MAX_FIELDS 64, 最多支持64个field。
//kernel/audit.c static int __net_init audit_net_init(struct net *net) { struct netlink_kernel_cfg cfg = { .input = audit_receive, .bind = audit_bind, .flags = NL_CFG_F_NONROOT_RECV, .groups = AUDIT_NLGRP_MAX, }; struct audit_net *aunet = net_generic(net, audit_net_id); aunet->nlsk = netlink_kernel_create(net, NETLINK_AUDIT, &cfg); if (aunet->nlsk == NULL) { audit_panic("cannot initialize netlink socket in namespace"); return -ENOMEM; } aunet->nlsk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; return 0; } /* Receive messages from netlink socket. */ static void audit_receive(struct sk_buff *skb) { mutex_lock(&audit_cmd_mutex); audit_receive_skb(skb); mutex_unlock(&audit_cmd_mutex); } static void audit_receive_skb(struct sk_buff *skb) { struct nlmsghdr *nlh; /* * len MUST be signed for nlmsg_next to be able to dec it below 0 * if the nlmsg_len was not aligned */ int len; int err; nlh = nlmsg_hdr(skb); len = skb->len; while (nlmsg_ok(nlh, len)) { err = audit_receive_msg(skb, nlh); /* if err or if this message says it wants a response */ if (err || (nlh->nlmsg_flags & NLM_F_ACK)) netlink_ack(skb, nlh, err); nlh = nlmsg_next(nlh, &len); } } static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { ... case AUDIT_ADD_RULE: case AUDIT_DEL_RULE: if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) return -EINVAL; if (audit_enabled == AUDIT_LOCKED) { audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE); audit_log_format(ab, " audit_enabled=%d res=0", audit_enabled); audit_log_end(ab); return -EPERM; } err = audit_rule_change(msg_type, NETLINK_CB(skb).portid, seq, data, nlmsg_len(nlh)); break; ... }//kernel/auditfilter.c int audit_rule_change(int type, __u32 portid, int seq, void *data, size_t datasz) { int err = 0; struct audit_entry *entry; entry = audit_data_to_entry(data, datasz); if (IS_ERR(entry)) return PTR_ERR(entry); switch (type) { case AUDIT_ADD_RULE: err = audit_add_rule(entry); audit_log_rule_change("add_rule", &entry->rule, !err); break; case AUDIT_DEL_RULE: err = audit_del_rule(entry); audit_log_rule_change("remove_rule", &entry->rule, !err); break; default: err = -EINVAL; WARN_ON(1); } ...}//kernel/auditsc.c void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4) { struct task_struct *tsk = current; struct audit_context *context = tsk->audit_context; enum audit_state state; if (!context) return; BUG_ON(context->in_syscall || context->name_count); if (!audit_enabled) return; context->arch = syscall_get_arch(); context->major = major; context->argv[0] = a1; context->argv[1] = a2; context->argv[2] = a3; context->argv[3] = a4; state = context->state; context->dummy = !audit_n_rules; if (!context->dummy && state == AUDIT_BUILD_CONTEXT) { context->prio = 0; state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]); //比较规则 } if (state == AUDIT_DISABLED) //如果规则不匹配,则返回 return; context->serial = 0; context->ctime = CURRENT_TIME; context->in_syscall = 1; //否则设置in_syscall 标志位 context->current_state = state; context->ppid = 0; } void __audit_syscall_exit(int success, long return_code) { struct task_struct *tsk = current; struct audit_context *context; if (success) success = AUDITSC_SUCCESS; else success = AUDITSC_FAILURE; context = audit_take_context(tsk, success, return_code); if (!context) return; if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT) //如果in_syscall 为1,也就是规则满足 audit_log_exit(context, tsk); //打印审计日志...} static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) { int i, call_panic = 0; struct audit_buffer *ab; struct audit_aux_data *aux; struct audit_names *n; /* tsk == current */ context->personality = tsk->personality; ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL); if (!ab) return; /* audit_panic has been called */ audit_log_format(ab, "arch=%x syscall=%d", context->arch, context->major); if (context->personality != PER_LINUX) audit_log_format(ab, " per=%lx", context->personality); if (context->return_valid) audit_log_format(ab, " success=%s exit=%ld", (context->return_valid==AUDITSC_SUCCESS)?"yes":"no", context->return_code); audit_log_format(ab, " a0=%lx a1=%lx a2=%lx a3=%lx items=%d", context->argv[0], context->argv[1], context->argv[2], context->argv[3], context->name_count); audit_log_task_info(ab, tsk); audit_log_key(ab, context->filterkey); audit_log_end(ab);}
性能开销
关于这方面的资料挺少,有人说性能会降低一半,所以制定规则的时候需要特别小心的评估: Auditing system calls results in high logging activity, which in turn puts a heavy load on the kernel. With a kernel less responsive than usual, the system’s backlog and rate limits might well be exceeded. Carefully evaluate which system calls to include in your audit rule set and adjust the log settings accordingly. See Section 33.2, “Configuring the Audit Daemon” for details on how to tweak the relevant settings.
参考资料: https://doc.opensuse.org/documentation/leap/security/html/book.security/cha.audit.comp.html https://www.suse.com/documentation/sled11/book_security/data/sec_audit_auditd.html https://www.ibm.com/developerworks/cn/linux/l-lo-use-space-audit-tool/index.html https://blog.csdn.net/qwertyupoiuytr/article/details/58278349 https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security_guide/sec-defining_audit_rules_and_controls https://github.com/linux-audit/audit-documentation/wiki
标签:
相关推荐:
最新新闻:
- 什么是audit? audit可以用来干什么?
- 英伟达开发板中的编译系统 能否在ZC706的板子上执行?|速读
- 【时快讯】如何禁用Windows期自动更新?禁用方法步骤
- Linux命令之restore命令 使用语法及参数说明
- 女UP主穿紧身衣装奥特曼跳舞?谁当直女的动态图-世界聚看点
- 外媒:今年12月PS会免比去年强得多 游戏数量翻倍-当前信息
- ICMP是什么意思?ICMP的详解 今日播报
- 什么是黑苹果系统?黑苹果Mac系统安装教程
- 今热点:【音频】syntax How to Write Custom Syntax
- 天天信息:小岛秀夫:《死亡搁浅2》游戏封面参考了一代设计
- 【世界快播报】外网热议:《艾尔登法环》哪个角色的身世最悲惨?
- 还能这样玩?Twitch主播手脚并用双开《艾尔登法环》
- 《天命奇御:归途》正式发售! 5折优惠价24.5元畅游江湖_天天速看
- 每日速看!install安装命令的常见用法 install有哪些优点?
- 定位赛10连胜是什么段位?LOL S5定位赛胜率高为什么没有上段?
- 玩家根据宝可梦游戏发售规律推测 Switch2|Pro或2023年推出
- 【环球新视野】暴风影音如何倍速播放?暴风影音倍速播放的方法
- 死或生5最后一战怎么反击?死或生5最后一战反击技巧攻略
- 全球观热点:HTC One X Recovery一键刷入步骤介绍 一键刷入的注意事项
- 《天命奇御:归途》正式发售 优惠价24.5元畅游江湖|今日关注
- 焦点滚动:曝斯柯达打算退出中国市场,曾靠“贴牌大众”爆火,下一站发力印度
- 通信行程卡下线,其存储的个人信息可以挪作他用吗?
- 第二代骁龙8赋能Xiaomi 13系列年度旗舰,实现高端探索新突破:世界资讯
- 全球观速讯丨《人中之龙维新!极》公布全新支线剧情、照功能等游戏要素
- 日本知名动画歌手水木一郎因肺癌去世 享年74岁 热头条
- 《云·原神》PC平台公测正式开启,可实现低配置高画质
- 热议:小米1TB移动固态硬盘发布:众筹649元 读写超2000MB/s
- 环球今热点:谐波减速机优点是什么?谐波传动减速器的优点
- 怎么用电脑摄像头录像?使用电脑摄像头录像详细步骤
- 三阶魔方还原公式是什么?三阶魔方的还原公式:环球今日报
- Visio绘图文件阅读器 VSD Viewer版功能介绍
- MetroModernUI库应用实例 MetroModernUI库安装流程_天天速看
- 联想V480EI Capitan完美驱动教程 安装黑苹果的步骤及注意事项|全球百事通
- 诺亚舟np360学习机怎么样?诺亚舟np360性能介绍_世界热推荐
- c语言编程题中华文本库 计算机考试二级C语言上机试题|当前速看
- nginx简单介绍 tomcat与nginx、apache的区别是什么?
- 环球观热点:小米与华为手机哪个好些?小米、华为与手机销量有关的对比
- Slony到底是什么?关于VMware Desktone中的Slony和数据库
- 视觉效果艺术家在“NVIDIA Studio 创意加速”中分享电影幕后制作的故事
- 操作系统有哪些类型?操作系统各自的特点
- 天天微速讯:htc是什么牌子?htc款式推荐
- arp防火墙哪个最好?金山arp防火墙的介绍 世界关注
- 三星9050好不好?三星9050如何刷机?
- 通讯!跳跃忍者怎么玩?若吃完所有能量球最多能保留多少能量?
- 纯甲类功放是什么意思?纯甲类功放价格参考及其介绍 世界今亮点
- 极米投影斩获五项国际大奖:画面自适应技术获日本VGP2023技术奖
- 联想S890支持什么视频格式?联想S890支持RMVB播放吗?-环球关注
- 全球热讯:百度云盒怎么预约购买?电视应用购买教程
- 辽源市谷歌高清卫星地图离线包如何下载?:视焦点讯
- 洛谷 P8400 Cupcake Party 计算机科学教育新生态 :视焦点讯
- 天天精选!ic卡消费管理系统 4.人脸消费管理系统的优势有哪些?
- MetroModernUI库应用实例 MetroModernUI库安装流程_天天速看
- Matlab中max函数 max函数式是求数组的最大元素
- 洛谷 P8400 Cupcake Party 计算机科学教育新生态 :视焦点讯
- Windows Live应用怎么添加到网站或博客?
- 辽源市谷歌高清卫星地图离线包如何下载?:视焦点讯
- c语言编程题中华文本库 计算机考试二级C语言上机试题|当前速看
- 全球聚焦:希捷宣布全球大裁员 股价在盘后大涨9%至26.26美元
- 环球观察:样本容量怎么确定?影响样本容量n的因素
- 现在进行时怎么使用?现在进行时和一般现在时的将来时态_微速讯
- Slony到底是什么?关于VMware Desktone中的Slony和数据库
- 世界快看点丨信息的价值是什么?信息的英文名Information
- 图片的格式怎么改?教你快速转格式的方法
- 联想V480EI Capitan完美驱动教程 安装黑苹果的步骤及注意事项|全球百事通
- 三阶魔方还原公式是什么?三阶魔方的还原公式:环球今日报
- 排名精灵怎么样?排名精灵功能评测
- 钛备份怎么用?钛备份怎么还原?
- 怎么用魔影工厂转换视频?魔影工厂怎么识别爱奇艺的视频?
- connectify怎么设置?connectify如何设置使用教程
- Win7系统中WmiPrvSE是什么进程?如何禁止Wmiprvse.exe进程?
- Anyview阅读APP如何使用?Anyview阅读使用图文教程
- win7使用过程中出现错误1079故障的原因及解决方法
- 网页中Flash如何下载?网页中Flash下载方法
- 网络老是掉线怎么回事?wifi一段时间断开怎么办?
- win10怎么升级正版?win10升级正版步骤
- qq远程控制鼠标点不动怎么?qq远程控制鼠标点不动解决方法
- 跨显卡双屏显示怎么设置?跨显卡双屏显示设置问题
- 如何通过Logoup 3D制作立方体?Logoup 3D制作立方体操作步骤
- 视频万能驱动怎么安装?视频万能驱动安装方法
- 怎么给手机版的WPS文档加密?手机版的WPS文档加密方法
- Adblock浏览器怎么用?Adbloc去广告浏览器下载
- win10 Build 9865怎么更新升级?win10 Build 9865更新升级方法
- qq授权管理在哪里?qq授权管理介绍
- 腾讯防沉迷如何通过官方修改?腾讯防沉迷修改方法
- 电脑断网掉线怎么办?电脑断网掉线的解决方法