wazuh-modules-sca-scan

news2024/12/22 2:01:26

 sca模块主函数wm_sca_main -> wm_sca_start

 检查policy文件中的每一个项目wm_sca_check_policy

static int wm_sca_check_policy(const cJSON * const policy, const cJSON * const checks, OSHash *global_check_list)
{
    if(!policy) {
        return 1;
    }

    const cJSON * const id = cJSON_GetObjectItem(policy, "id");
    if(!id) {
        mwarn("Field 'id' not found in policy header.");
        return 1;
    }

    if(!id->valuestring){
        mwarn("Invalid format for field 'id'");
        return 1;
    }

    char *coincident_policy_file;
    if((coincident_policy_file = OSHash_Get(global_check_list,id->valuestring)), coincident_policy_file) {
        mwarn("Found duplicated policy ID: %s. File '%s' contains the same ID.", id->valuestring, coincident_policy_file);
        return 1;
    }

    const cJSON * const name = cJSON_GetObjectItem(policy, "name");
    if(!name) {
        mwarn("Field 'name' not found in policy header.");
        return 1;
    }

    if(!name->valuestring){
        mwarn("Invalid format for field 'name'");
        return 1;
    }

    const cJSON * const file = cJSON_GetObjectItem(policy, "file");
    if(!file) {
        mwarn("Field 'file' not found in policy header.");
        return 1;
    }

    if(!file->valuestring){
        mwarn("Invalid format for field 'file'");
        return 1;
    }

    const cJSON * const description = cJSON_GetObjectItem(policy, "description");
    if(!description) {
        mwarn("Field 'description' not found in policy header.");
        return 1;
    }

    const cJSON * const regex_type = cJSON_GetObjectItem(policy, "regex_type");
    if(!regex_type) {
        mdebug1("Field 'regex_type' not found in policy header. The OS_REGEX engine shall be used.");
    }

    if(!description->valuestring) {
        mwarn("Invalid format for field 'description'");
        return 1;
    }

    // Check for policy rules with duplicated IDs */
    if (!checks) {
        mwarn("Section 'checks' not found.");
        return 1;
    }

    int *read_id;
    os_calloc(1, sizeof(int), read_id);
    read_id[0] = 0;

    const cJSON *check;
    cJSON_ArrayForEach(check, checks) {
        const cJSON * const check_id = cJSON_GetObjectItem(check, "id");
        if (check_id == NULL) {
            mwarn("Check ID not found.");
            free(read_id);
            return 1;
        }

        if (check_id->valueint <= 0) {
            // Invalid ID
            mwarn("Invalid check ID: %d", check_id->valueint);
            free(read_id);
            return 1;
        }

        char *coincident_policy;
        char *key_id;
        size_t key_length = snprintf(NULL, 0, "%d", check_id->valueint);
        os_malloc(key_length + 1, key_id);
        snprintf(key_id, key_length + 1, "%d", check_id->valueint);

        if((coincident_policy = (char *)OSHash_Get(global_check_list, key_id)), coincident_policy){
            // Invalid ID
            mwarn("Found duplicated check ID: %d. First appearance at policy '%s'", check_id->valueint, coincident_policy);
            os_free(key_id);
            os_free(read_id);
            return 1;
        }
        os_free(key_id);

        int i;
        for (i = 0; read_id[i] != 0; i++) {
            if (check_id->valueint == read_id[i]) {
                // Duplicated ID
                mwarn("Found duplicated check ID: %d", check_id->valueint);
                free(read_id);
                return 1;
            }
        }

        os_realloc(read_id, sizeof(int) * (i + 2), read_id);
        read_id[i] = check_id->valueint;
        read_id[i + 1] = 0;

        const cJSON * const rules = cJSON_GetObjectItem(check, "rules");

        if (rules == NULL) {
            mwarn("Invalid check %d: no rules found.", check_id->valueint);
            free(read_id);
            return 1;
        }

        int rules_n = 0;
        const cJSON *rule;
        cJSON_ArrayForEach(rule, rules) {
            if (!rule->valuestring) {
                mwarn("Invalid check %d: Empty rule.", check_id->valueint);
                free(read_id);
                return 1;
            }

            char *valuestring_ref = rule->valuestring;
            valuestring_ref += 4 * (!strncmp(valuestring_ref, "NOT ", 4) || !strncmp(valuestring_ref, "not ", 4));

            switch (*valuestring_ref) {
#ifdef WIN32
                case 'r':
#endif
                case 'f':
                case 'd':
                case 'p':
                case 'c':
                    break;
                case '\0':
                    mwarn("Invalid check %d: Empty rule.", check_id->valueint);
                    free(read_id);
                    return 1;
                default:
                    mwarn("Invalid check %d: Invalid rule format.", check_id->valueint);
                    free(read_id);
                    return 1;
            }

            rules_n++;
            if (rules_n > 255) {
                free(read_id);
                mwarn("Invalid check %d: Maximum number of rules is 255.", check_id->valueint);
                return 1;
            }
        }

        if (rules_n == 0) {
            mwarn("Invalid check %d: no rules found.", check_id->valueint);
            free(read_id);
            return 1;
        }

    }

    char *policy_file = NULL;
    os_strdup(file->valuestring, policy_file);
    const int id_add_retval = OSHash_Add(global_check_list, id->valuestring, policy_file);
    if (id_add_retval == 0){
        os_free(policy_file);
        os_free(read_id);
        merror_exit("(1102): Could not acquire memory");
    }

    if (id_add_retval == 1){
        merror("Error validating duplicated ID. Policy %s in file %s is duplicated", id->valuestring, policy_file);
        os_free(policy_file);
        os_free(read_id);
        return 1;
    }

    int i;
    for (i = 0; read_id[i] != 0; ++i) {
        char *policy_id = NULL;
        os_strdup(id->valuestring, policy_id);
        const int check_add_retval = OSHash_Numeric_Add_ex(global_check_list, read_id[i], policy_id);
        if (check_add_retval == 0){
            os_free(policy_id);
            os_free(read_id);
            merror_exit("(1102): Could not acquire memory");
        }

        if (check_add_retval == 1){
            merror("Error validating duplicated ID. Check %s in policy %s is duplicated", id->valuestring, policy_id);
            os_free(policy_id);
            os_free(read_id);
            return 1;
        }
    }

    os_free(read_id);
    return 0;
}

policy文件中的具体rules项目,其中规则之一:

# 1.1.1.3 udf: filesystem
  - id: 6002
    title: "Ensure mounting of udf filesystems is disabled"
    description: "The udf filesystem type is the universal disk format used to implement ISO/IEC 13346 and ECMA-167 specifications. This is an open vendor filesystem type for data storage on a broad range of media. This filesystem type is necessary to support writing DVDs and newer optical disc formats."
    rationale: "Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it."
    remediation: "Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: install udf /bin/true. Run the following command to unload the udf module: rmmod udf"
    compliance:
      - cis: ["1.1.1.3"]
      - cis_csc: ["5.1"]
      - pci_dss: ["2.2.5"]
      - tsc: ["CC6.3"]
    references:
      - AJ Lewis, "LVM HOWTO", https://tldp.org/HOWTO/LVM-HOWTO/
    condition: all
    rules:
      - 'c:modprobe -n -v udf -> r:install /bin/true|Module udf not found'
      - 'not c:lsmod -> r:udf'

rules中的每一项是r (读取), f,d,p,c,not,NOT开头  "->"表示前一个动作之后的接着的下一个动作,或者条件

 sca扫描核心函数

/*
Rules that match always return 1, and the other way arround.

Rule aggregators logic:

##########################################################

ALL:
    r_1 -f -> r:123
    ...
    r_n -f -> r:234

For an ALL to succeed, every rule shall return 1, in other words,

               |  = n -> ALL = RETURN_FOUND
SUM(r_i, 0, n) |
               | != n -> ALL = RETURN_NOT_FOUND

##########################################################

ANY:
    r_1 -f -> r:123
    ...
    r_n -f -> r:234

For an ANY to succeed, a rule shall return 1, in other words,

               | > 0 -> ANY = RETURN_FOUND
SUM(r_i, 0, n) |
               | = 0 -> ANY = RETURN_NOT_FOUND

##########################################################

NONE:
    r_1 -f -> r:123
    ...
    r_n -f -> r:234

For a NONE to succeed, all rules shall return RETURN_NOT_FOUND, in other words,

               |  > 0 -> NONE = RETURN_NOT_FOUND
SUM(r_i, 0, n) |
               |  = 0 -> NONE = RETURN_FOUND

##########################################################

ANY and NONE aggregators are complementary.

*/

static int wm_sca_do_scan(cJSON * checks,
                          OSStore * vars,
                          wm_sca_t * data,
                          int id,
                          cJSON * policy,
                          int requirements_scan,
                          int cis_db_index,
                          unsigned int remote_policy,
                          int first_scan,
                          int * checks_number,
                          char ** sorted_variables,
                          char * policy_engine)
{
    int type = 0;
    char buf[OS_SIZE_1024 + 2];
    char final_file[2048 + 1];
    char *reason = NULL;

    int ret_val = 0;
    OSList *p_list = NULL;

    /* Initialize variables */
    memset(buf, '\0', sizeof(buf));
    memset(final_file, '\0', sizeof(final_file));

    int check_count = 0;
    cJSON *check = NULL;
    cJSON_ArrayForEach(check, checks) {
        char _check_id_str[50];
        if (requirements_scan) {
            snprintf(_check_id_str, sizeof(_check_id_str), "Requirements check");
        } else {
            const cJSON * const c_id = cJSON_GetObjectItem(check, "id");
            if (!c_id || !c_id->valueint) {
                merror("Skipping check. Check ID is invalid. Offending check number: %d", check_count);
                ret_val = 1;
                continue;
            }
            snprintf(_check_id_str, sizeof(_check_id_str), "id: %d", c_id->valueint);
        }

        const cJSON * const c_title = cJSON_GetObjectItem(check, "title");
        if (!c_title || !c_title->valuestring) {
            merror("Skipping check with %s: Check name is invalid.", _check_id_str);
            if (requirements_scan) {
                ret_val = 1;
                goto clean_return;
            }
            continue;
        }

        const cJSON * const c_condition = cJSON_GetObjectItem(check, "condition");
        if (!c_condition || !c_condition->valuestring) {
            merror("Skipping check '%s: %s': Check condition not found.", _check_id_str, c_title->valuestring);
            if (requirements_scan) {
                ret_val = 1;
                goto clean_return;
            }
            continue;
        }

        int condition = 0;
        wm_sca_set_condition(c_condition->valuestring, &condition);

        if (condition == WM_SCA_COND_INV) {
            merror("Skipping check '%s: %s': Check condition (%s) is invalid.",_check_id_str, c_title->valuestring, c_condition->valuestring);
            if (requirements_scan) {
                ret_val = 1;
                goto clean_return;
            }
            continue;
        }

        int g_found = RETURN_NOT_FOUND;
        if ((condition & WM_SCA_COND_ANY) || (condition & WM_SCA_COND_NON)) {
            /* aggregators ANY and NONE break by matching, so they shall return NOT_FOUND if they never break */
            g_found = RETURN_NOT_FOUND;
        } else if (condition & WM_SCA_COND_ALL) {
            /* aggregator ALL breaks the moment a rule does not match. If it doesn't break, all rules have matched */
            g_found = RETURN_FOUND;
        }

        mdebug1("Beginning evaluation of check %s '%s'", _check_id_str, c_title->valuestring);
        mdebug1("Rule aggregation strategy for this check is '%s'", c_condition->valuestring);
        mdebug2("Initial rule-aggregator value por this type of rule is '%d'",  g_found);
        mdebug1("Beginning rules evaluation.");

        const cJSON *const rules = cJSON_GetObjectItem(check, "rules");
        if (!rules) {
            merror("Skipping check %s '%s': No rules found.", _check_id_str, c_title->valuestring);
            if (requirements_scan) {
                ret_val = 1;
                goto clean_return;
            }
            continue;
        }

        w_expression_t * regex_engine = NULL;
        cJSON * engine = cJSON_GetObjectItem(check, "regex_type");
        if (engine) {
            if (strcmp(PCRE2_STR, cJSON_GetStringValue(engine)) == 0) {
                w_calloc_expression_t(&regex_engine, EXP_TYPE_PCRE2);
            } else {
                w_calloc_expression_t(&regex_engine, EXP_TYPE_OSREGEX);
            }
        } else {
            if(strcmp(PCRE2_STR, policy_engine) == 0) {
                w_calloc_expression_t(&regex_engine, EXP_TYPE_PCRE2);
            } else {
                w_calloc_expression_t(&regex_engine, EXP_TYPE_OSREGEX);
            }
        }
        mdebug1("SCA will use '%s' engine to check the rules.", w_expression_get_regex_type(regex_engine));

        char *rule_cp = NULL;
        const cJSON *rule_ref;
        cJSON_ArrayForEach(rule_ref, rules) {
            /* this free is responsible of freeing the copy of the previous rule if
            the loop 'continues', i.e, does not reach the end of its block. */
            os_free(rule_cp);

            if(!rule_ref->valuestring) {
                mdebug1("Field 'rule' must be a string.");
                ret_val = 1;
                os_free(regex_engine);
                goto clean_return;
            }

            mdebug1("Considering rule: '%s'", rule_ref->valuestring);

            os_strdup(rule_ref->valuestring, rule_cp);
            char *rule_cp_ref = NULL;

        #ifdef WIN32
            char expanded_rule[2048] = {0};
            ExpandEnvironmentStrings(rule_cp, expanded_rule, 2048);
            rule_cp_ref = expanded_rule;
            mdebug2("Rule after variable expansion: '%s'", rule_cp_ref);
        #else
            rule_cp_ref = rule_cp;
        #endif

            int rule_is_negated = 0;
            if (rule_cp_ref &&
                    (strncmp(rule_cp_ref, "NOT ", 4) == 0 ||
                     strncmp(rule_cp_ref, "not ", 4) == 0))
            {
                mdebug2("Rule is negated.");
                rule_is_negated = 1;
                rule_cp_ref += 4;
            }

            /* Get value to look for. char *value is a reference
            to rule_cp memory. Do not release value!  */
            char *value = wm_sca_get_value(rule_cp_ref, &type);

            if (value == NULL) {
                merror("Invalid rule: '%s'. Skipping policy.", rule_ref->valuestring);
                os_free(rule_cp);
                ret_val = 1;
                os_free(regex_engine);
                goto clean_return;
            }

            int found = RETURN_NOT_FOUND;
            if (type == WM_SCA_TYPE_FILE) {
                /* Check files */
                char *pattern = wm_sca_get_pattern(value);
                char *rule_location = NULL;
                char *aux = NULL;

                os_strdup(value, rule_location);

                /* If any, replace the variables by their respective values */
                if (sorted_variables) {
                    int i = 0;
                    for (i = 0; sorted_variables[i]; i++) {
                        if (strstr(rule_location, sorted_variables[i])) {
                            mdebug2("Variable '%s' found at rule '%s'. Replacing it.", sorted_variables[i], rule_location);
                            aux = wstr_replace(rule_location, sorted_variables[i], OSStore_Get(vars, sorted_variables[i]));
                            os_free(rule_location);
                            rule_location = aux;
                            if (!rule_location) {
                                merror("Invalid variable replacement: '%s'. Skipping check.", sorted_variables[i]);
                                break;
                            }
                            mdebug2("Variable replaced: '%s'", rule_location);
                        }
                    }
                }

                if (!rule_location) {
                    continue;
                }
                const int result = wm_sca_check_file_list(rule_location, pattern, &reason, regex_engine);
                if (result == RETURN_FOUND || result == RETURN_INVALID) {
                    found = result;
                }

                char _b_msg[OS_SIZE_1024 + 1];
                _b_msg[OS_SIZE_1024] = '\0';
                snprintf(_b_msg, OS_SIZE_1024, " File: %s", rule_location);
                append_msg_to_vm_scat(data, _b_msg);
                os_free(rule_location);

            } else if (type == WM_SCA_TYPE_COMMAND) {
                /* Check command output */
                char *pattern = wm_sca_get_pattern(value);
                char *rule_location = NULL;
                char *aux = NULL;

                os_strdup(value, rule_location);

                if (!data->remote_commands && remote_policy) {
                    mwarn("Ignoring check for policy '%s'. The internal option 'sca.remote_commands' is disabled.", cJSON_GetObjectItem(policy, "name")->valuestring);
                    if (reason == NULL) {
                        os_malloc(snprintf(NULL, 0, "Ignoring check for running command '%s'. The internal option 'sca.remote_commands' is disabled", rule_location) + 1, reason);
                        sprintf(reason, "Ignoring check for running command '%s'. The internal option 'sca.remote_commands' is disabled", rule_location);
                    }
                    found = RETURN_INVALID;

                } else {
                    /* If any, replace the variables by their respective values */
                    if (sorted_variables) {
                        int i = 0;
                        for (i = 0; sorted_variables[i]; i++) {
                            if (strstr(rule_location, sorted_variables[i])) {
                                mdebug2("Variable '%s' found at rule '%s'. Replacing it.", sorted_variables[i], rule_location);
                                aux = wstr_replace(rule_location, sorted_variables[i], OSStore_Get(vars, sorted_variables[i]));
                                os_free(rule_location);
                                rule_location = aux;
                                if (!rule_location) {
                                    merror("Invalid variable: '%s'. Skipping check.", sorted_variables[i]);
                                    break;
                                }
                                mdebug2("Variable replaced: '%s'", rule_location);
                            }
                        }
                    }

                    if (!rule_location) {
                        continue;
                    }

                    mdebug2("Running command: '%s'", rule_location);
                    const int val = wm_sca_read_command(rule_location, pattern, data, &reason, regex_engine);
                    if (val == RETURN_FOUND) {
                        mdebug2("Command output matched.");
                        found = RETURN_FOUND;
                    } else if (val == RETURN_INVALID){
                        mdebug2("Command output did not match.");
                        found = RETURN_INVALID;
                    }
                }

                char _b_msg[OS_SIZE_1024 + 1];
                _b_msg[OS_SIZE_1024] = '\0';
                snprintf(_b_msg, OS_SIZE_1024, " Command: %s", rule_location);
                append_msg_to_vm_scat(data, _b_msg);
                os_free(rule_location);

            } else if (type == WM_SCA_TYPE_DIR) {
                /* Check directory */
                mdebug2("Processing directory rule '%s'", value);
                char * const file = wm_sca_get_pattern(value);
                char *rule_location = NULL;
                char *aux = NULL;

                os_strdup(value, rule_location);

                /* If any, replace the variables by their respective values */
                if (sorted_variables) {
                    int i = 0;
                    for (i = 0; sorted_variables[i]; i++) {
                        if (strstr(rule_location, sorted_variables[i])) {
                            mdebug2("Variable '%s' found at rule '%s'. Replacing it.", sorted_variables[i], rule_location);
                            aux = wstr_replace(rule_location, sorted_variables[i], OSStore_Get(vars, sorted_variables[i]));
                            os_free(rule_location);
                            rule_location = aux;
                            if (!rule_location) {
                                merror("Invalid variable: '%s'. Skipping check.", sorted_variables[i]);
                                break;
                            }
                            mdebug2("Variable replaced: '%s'", rule_location);
                        }
                    }
                }

                if (!rule_location) {
                    continue;
                }

                char * const pattern = wm_sca_get_pattern(file);
                found = wm_sca_check_dir_list(data, rule_location, file, pattern, &reason, regex_engine);
                mdebug2("Check directory rule result: %d", found);
                os_free(rule_location);

            } else if (type == WM_SCA_TYPE_PROCESS) {
                /* Check process existence */
                if (!p_list) {
                    /* Lazy evaluation */
                    p_list = w_os_get_process_list();
                }

                mdebug2("Checking process: '%s'", value);
                if (wm_sca_check_process_is_running(p_list, value, &reason, regex_engine)) {
                    mdebug2("Process found.");
                    found = RETURN_FOUND;
                } else {
                    mdebug2("Process not found.");
                }

                char _b_msg[OS_SIZE_1024 + 1];
                _b_msg[OS_SIZE_1024] = '\0';
                snprintf(_b_msg, OS_SIZE_1024, " Process: %s", value);
                append_msg_to_vm_scat(data, _b_msg);
            }
        #ifdef WIN32
            else if (type == WM_SCA_TYPE_REGISTRY) {
                /* Check windows registry */
                char * const entry = wm_sca_get_pattern(value);
                char * const pattern = wm_sca_get_pattern(entry);
                found = wm_sca_is_registry(value, entry, pattern, &reason, regex_engine);

                char _b_msg[OS_SIZE_1024 + 1];
                _b_msg[OS_SIZE_1024] = '\0';
                snprintf(_b_msg, OS_SIZE_1024, " Registry: %s", value);
                append_msg_to_vm_scat(data, _b_msg);
            }
        #endif

            /* Rule result processing */

            if (found != RETURN_INVALID) {
                found = rule_is_negated ^ found;
            }

            mdebug1("Result for rule '%s': %d", rule_ref->valuestring, found);

            if (((condition & WM_SCA_COND_ALL) && found == RETURN_NOT_FOUND) ||
                ((condition & WM_SCA_COND_ANY) && found == RETURN_FOUND) ||
                ((condition & WM_SCA_COND_NON) && found == RETURN_FOUND))
            {
                g_found = found;
                mdebug1("Breaking from rule aggregator '%s' with found = %d", c_condition->valuestring, g_found);
                break;
            }

            if (found == RETURN_INVALID) {
                /* Rules that agreggate by ANY are the only that can success after an INVALID
                On the other hand ALL and NONE agregators can fail after an INVALID. */
                g_found = found;
                mdebug1("Rule evaluation returned INVALID. Continuing.");
            }
        }

        if ((condition & WM_SCA_COND_NON) && g_found != RETURN_INVALID) {
            g_found = !g_found;
        }

        mdebug1("Result for check %s '%s' -> %d", _check_id_str, c_title->valuestring, g_found);

        if (g_found != RETURN_INVALID) {
            os_free(reason);
        }

        /* if the loop breaks, rule_cp shall be released.
            Also frees the the memory reserved on the last iteration */
        os_free(rule_cp);

        /* Determine if requirements are satisfied */
        if (requirements_scan) {
            /*  return value for requirement scans is the inverse of the result,
                unless the result is INVALID */
            ret_val = g_found == RETURN_INVALID ? 1 : !g_found;
            int i;
            for (i=0; data->alert_msg[i]; i++){
                free(data->alert_msg[i]);
                data->alert_msg[i] = NULL;
            }
            w_free_expression_t(&regex_engine);
            goto clean_return;
        }

        /* Event construction */
        const char failed[] = "failed";
        const char passed[] = "passed";
        const char invalid[] = ""; //NOT AN ERROR!
        const char *message_ref = NULL;

        if (g_found == RETURN_NOT_FOUND) {
            wm_sca_summary_increment_failed();
            message_ref = failed;
        } else if (g_found == RETURN_FOUND) {
            wm_sca_summary_increment_passed();
            message_ref = passed;
        } else {
            wm_sca_summary_increment_invalid();
            message_ref = invalid;

            if (reason == NULL) {
                os_malloc(snprintf(NULL, 0, "Unknown reason") + 1, reason);
                sprintf(reason, "Unknown reason");
                mdebug1("A check returned INVALID for an unknown reason.");
            }
        }

        cJSON *event = wm_sca_build_event(check, policy, data->alert_msg, id, message_ref, reason);
        if (event) {
            /* Alert if necessary */
            if(!cis_db_for_hash[cis_db_index].elem[check_count]) {
                os_realloc(cis_db_for_hash[cis_db_index].elem, sizeof(cis_db_info_t *) * (check_count + 2), cis_db_for_hash[cis_db_index].elem);
                cis_db_for_hash[cis_db_index].elem[check_count] = NULL;
                cis_db_for_hash[cis_db_index].elem[check_count + 1] = NULL;
            }

            if (wm_sca_check_hash(cis_db[cis_db_index], message_ref, check, event, check_count, cis_db_index) && !first_scan) {
                wm_sca_send_event_check(data,event);
            }

            check_count++;

            cJSON_Delete(event);
        } else {
            merror("Error constructing event for check: %s. Set debug mode for more information.", c_title->valuestring);
            ret_val = 1;
        }

        int i;
        for (i=0; data->alert_msg[i]; i++){
            free(data->alert_msg[i]);
            data->alert_msg[i] = NULL;
        }

        os_free(reason);
        w_free_expression_t(&regex_engine);
    }

    *checks_number = check_count;

/* Clean up memory */
clean_return:
    os_free(reason);
    w_del_plist(p_list);

    return ret_val;
}

迭代每一个检查项cJSON_ArrayForEach(check, checks)

/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2263533.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp 自定义图标03

插入工程&#xff0c;修改名称文件内容 编译运行

在Windows本地用网页查看编辑服务器上的 jupyter notebook

​ Motivation: jupyter notebook 可以存中间变量&#xff0c;方便我调整代码&#xff0c;但是怎么用服务器的GPU并在网页上查看编辑呢&#xff1f; 参考 https://zhuanlan.zhihu.com/p/440080687 服务端(Ubuntu)&#xff1a; 激活环境 source activate my_env安装notebook …

【YOLO 项目实战】(11)YOLO8 数据集与模型训练

欢迎关注『youcans动手学模型』系列 本专栏内容和资源同步到 GitHub/youcans 【YOLO 项目实战】&#xff08;1&#xff09;YOLO5 环境配置与检测 【YOLO 项目实战】&#xff08;10&#xff09;YOLO8 环境配置与推理检测 【YOLO 项目实战】&#xff08;11&#xff09;YOLO8 数据…

Ubuntu22.04上安装esp-idf

一、安装准备# 建议使用Ubuntu 20.04 或 Ubuntu 22.04 操作系统 为了在 Ubuntu 22.04 中使用 esp-idf&#xff0c;需要安装一些依赖包 sudo apt-get install git wget flex bison gperf python3\python3-pip python3-venv cmake ninja-build ccache\libffi-dev libssl-dev dfu…

nginx-虚拟主机配置笔记

目录 nginx的安装可以查看nginx安装https://blog.csdn.net/m0_68472908/article/details/144609023?spm1001.2014.3001.5501 一、 基于域名 二、 基于IP 三、 基于端口 nginx的安装可以查看nginx安装https://blog.csdn.net/m0_68472908/article/details/144609023?spm100…

AlipayHK支付宝HK接入-商户收款(PHP)

一打开支付宝国际版 二、点开商户服务 三、下载源码

Soul Android端稳定性背后的那些事

前言&#xff1a;移动应用的稳定性对于用户体验和产品商业价值都有着至关重要的作用。应用崩溃会导致关键业务中断、用户留存率下降、品牌口碑变差、生命周期价值下降等影响&#xff0c;甚至会导致用户流失。因此&#xff0c;稳定性是APP质量构建体系中最基本和最关键的一环。当…

深度学习模型 DeepSeek-VL2 及其消费级显卡需求分析

DeepSeek-VL2 是由 DeepSeek 团队开发的一款先进的视觉语言模型&#xff0c;采用了混合专家&#xff08;MoE&#xff09;架构&#xff0c;旨在提升多模态理解能力。该模型包括三个版本&#xff1a;DeepSeek-VL2-Tiny、DeepSeek-VL2-Small 和 DeepSeek-VL2。每个版本具有不同的模…

首批|云轴科技ZStack成为开放智算产业联盟首批会员单位

近日 &#xff0c;在Linux基金会AI & Data及中国开源软件推进联盟的指导之下&#xff0c;开放智算产业联盟成立大会在北京成功召开。在大会上&#xff0c;联盟首次公布了组织架构并颁发了首批会员单位证书。凭借ZStack AIOS平台智塔和在智算领域的技术创新&#xff0c;云轴…

word实现两栏格式公式居中,编号右对齐

1、确定分栏的宽度 选定一段文字 点击分栏&#xff1a;如本文的宽度为22.08字符 2、将公式设置为 两端对齐&#xff0c;首行无缩进。 将光标放在 公式前面 点击 格式-->段落-->制表位 在“制表位位置”输入-->11.04字符&#xff08;22.08/211.04字符&#xff09;&…

go语言zero框架中config读取不到.env文件问题排查与解决方案

在Go语言中&#xff0c;如果你使用.env文件来存储环境变量&#xff0c;通常会用到一些第三方库&#xff0c;例如github.com/joho/godotenv&#xff0c;它可以帮助我们从.env文件中读取环境变量。然而&#xff0c;在使用godotenv时&#xff0c;可能会遇到一些问题&#xff0c;导…

修改vscode设置的原理

转载请标明出处&#xff1a;小帆的帆的专栏 修改vscode设置 首先需要理解的是&#xff0c;vscode的系统设置和插件设置都是通过settings.json文件管理的。 vscode中有三个Settings&#xff0c;三个Settings分别对应三个settings.json文件 Default Settings&#xff1a;默认…

Qt之修改窗口标题、图标以及自定义标题栏(九)

Qt开发 系列文章 - titles-icons-titlebars&#xff08;九&#xff09; 目录 前言 一、修改标题 二、添加图标 三、更换标题栏 1.效果演示 2.创建标题栏类 3.定义相关函数 4.使用标题栏类 总结 前言 在我们利用Qt设计软件时&#xff0c;经常需要修改窗口标题、更改软…

环境变量的知识

目录 1. 环境变量的概念 2. 命令行参数 2.1 2.2 创建 code.c 文件 2.3 对比 ./code 执行和直接 code 执行 2.4 怎么可以不带 ./ 2.4.1 把我们的二进制文件拷贝到 usr/bin 路径下&#xff0c;也不用带 ./ 了 2.4.2 把我们自己的路径添加到环境变量里 3. 认识PATH 3.…

【时时三省】(C语言基础)通讯录1

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 通讯录 1 .通讯录中能够存放1000个人的信息 每个人的信息&#xff1a; 名字年龄性别电话地址 2、增加人的信息 3、删除指定人的信息 4、修改指定人的信息 5&#xff0c;查找指定人的信…

Vulhub:Redis[漏洞复现]

4-unacc(Redis未授权代码执行) 启动漏洞环境 docker-compose up -d 阅读vulhub给出的漏洞文档 cat README.zh-cn.md # Redis 4.x/5.x 主从复制导致的命令执行 Redis是著名的开源Key-Value数据库&#xff0c;其具备在沙箱中执行Lua脚本的能力。 Redis未授权访问在4.x/5.0.5以…

【PGCCC】Postgresql Varlena 结构

前言 postgresql 会有一些变长的数据类型&#xff0c;存储都是采用 varlena 格式的&#xff08;除了 cstring 类型&#xff09;&#xff0c;通过语句 SELECT typname FROM pg_type WHERE typlen -1就可以看到所有采用 varlena 格式的数据类型&#xff0c;比如常见的 text &am…

Ubuntu搭建ES8集群+加密通讯+https访问

目录 写在前面 一、前期准备 1. 创建用户和用户组 2. 修改limits.conf文件 3. 关闭操作系统swap功能 4. 调整mmap上限 二、安装ES 1.下载ES 2.配置集群间安全访问证书密钥 3.配置elasticsearch.yml 4.修改jvm.options 5.启动ES服务 6.修改密码 7.启用外部ht…

LeetCode:144.前序遍历

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;144. 二叉树的前序遍历 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#x…

git remote -v(--verbose)显示你的 Git 仓库配置的远程仓库的详细信息

git remote -v 是一个 Git 命令&#xff0c;用于显示你的 Git 仓库配置的远程仓库的详细信息。 当你执行 git remote -v 命令时&#xff0c;你会看到类似以下的输出&#xff1a; origin https://github.com/your-username/your-repo.git (fetch) origin https://github.com…