how to write a module dumper & disassembler

elfutils交叉编译 编译版本elfutils-0.150 阅读详情
 

0 - 前言
1 - Linux 2.6 Kernel Module的加载过程
 1.1 加载流程简介
 1.2 测试模块
 1.3 关键点分析
 1.4 可行性结论
 1.5 补充几点
2 - 模块的提取
 2.1 简单的module dumper
 2.2 程序的功能
3 - 模块的反汇编
 3.1 BFD简介
 3.2 Intel的机器指令的基本格式
 3.3 利用binutils包编写一个简单的反编译器
 3.4 需要注意的两点
4 - 结尾
5 - 参考

--------------------------------------------------------------------------
- ---[ 0 前言

Linux系统下的LKM Trojan已经出现了不少,包括Knark,Adore,Adore-ng…,在应急响
应过程中,我们可以使用Madsys的方法[1]来查找隐藏的LKM Trojan,即使如此,对于取证
过程来说,这是不够的,我们还需要获得木马模块的样本。如果攻击者足够狡猾,可以安
全删除.o/.ko文件,因此单凭模块的名称,我们并不能知道这一可疑的模块到底做了些什
么。

因此,我们需要一种方法,将这些可疑模块提取出来,并对模块的功能进行分析。那么这
一想法的可行性如何呢?

本文正题分三部分
第一部分,分析一下Module加载的过程,看模块的提取与分析是否可行。
第二部分,编写一个简单的模块,将指定的模块内容提取出来。
第三部分,简单介绍反汇编的基本知识,包括机器指令的结构,BFD,然后利用binutils
源码写一个简单的反汇编器。
结尾,会提出一些可以改进的想法。

- ---[ 1 Linux 2.6 Kernel Module的加载过程

- ---[ 1.1 加载流程简介

当程序执行insmod abc.ko时,会调用sys_init_module系统调用[2],将整个文件的内容传
入内核,由内核实现程序重定位、符号解析和模块相关函数的运行。
sys_init_module首先进行一些检查,然后
 mod = load_module(umod, len, uargs);
load_module做了大部分的工作,然后将modules插入链表中,如果定义了初始化函数,会
在加载的最后调用该函数(也就是module_init指定的函数),然后释放mod->module_init
的空间。注意:前面的module_init是指在模块编程时指定的模块初始化函数,而后边的
则是指struct module的一个指针,该指针指向只运行一次就可以释放的空间。

下面转入load_module, 该函数会首先检查ELF文件的合法性,接着读取ELF文件头的信息,
然后按照各个节的标志计算需要分配的内存空间,之后,将相应的节复制过来,最后完成
符号的重定位过程。

为了便于理解,先给出一个测试模块

- ---[ 1.2 测试模块

先给出一个测试程序 test.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/string.h>

static char *mod_name = "module";
//module_param(mod_name, charp, 0);
static int remove_init(void)
{
 struct module *mod_head, *mod_counter;
 struct list_head *p;

 mod_head = &__this_module;
 list_for_each(p, &mod_head->list){
  mod_counter = list_entry(p, struct module, list);
  if(strcmp(mod_counter->name, mod_name) == 0){
   list_del(p);
   printk("remove module %s successfully./n", mod_name);
   return 0;
  }
 }
 printk("Can't find module %s./n", mod_name);
 return 0;
}
static void remove_exit(void)
{
 
}
module_init(remove_init);
module_exit(remove_exit);

MODULE_LICENSE("GPL")

- ---[ 1.3 关键点分析

由于.o/.ko文件调入内存之后,ELF的头文件信息不复存在,因此必须了解模块空间的具体
内存布局,才能确定某个模块究竟能不能提取出来、代码段的开始与结尾(是否可以反汇编
)。

我们先看看生成的test.ko的ELF部局:

#readelf -S test.ko

There are 19 section headers, starting at offset 0x620:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 000080 00  AX  0   0  4
  [ 2] .rel.text         REL             00000000 000918 000048 08     17   1  4
  [ 3] .altinstr_replace PROGBITS        00000000 0000b4 000006 00  AX  0   0  1
  [ 4] .rodata.str1.1    PROGBITS        00000000 0000ba 00003e 01 AMS  0   0  1
  [ 5] .altinstructions  PROGBITS        00000000 0000f8 000017 00   A  0   0  4
  [ 6] .rel.altinstructi REL             00000000 000960 000020 08     17   5  4
  [ 7] .modinfo          PROGBITS        00000000 000120 00005b 00   A  0   0 32
  [ 8] __versions        PROGBITS        00000000 000180 000100 00   A  0   0 32
  [ 9] .data             PROGBITS        00000000 000280 000004 00  WA  0   0  4
  [10] .rel.data         REL             00000000 000980 000008 08     17   9  4
  [11] .gnu.linkonce.thi PROGBITS        00000000 000300 000200 00  WA  0   0 128
  [12] .rel.gnu.linkonce REL             00000000 000988 000010 08     17   b  4
  [13] .bss              NOBITS          00000000 000500 000000 00  WA  0   0  4
  [14] .comment          PROGBITS        00000000 000500 000066 00      0   0  1
  [15] .note.GNU-stack   NOTE            00000000 000566 000000 00      0   0  1
  [16] .shstrtab         STRTAB          00000000 000566 0000b9 00      0   0  1
  [17] .symtab           SYMTAB          00000000 000998 000200 10     18  1c  4
  [18] .strtab           STRTAB          00000000 000b98 0000a6 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

然后我们来看看模块所占用的空间到底是怎么分配的:

分配空间的大小主要是由layout_sections函数来实现的:
/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
   might -- code, read-only data, read-write data, small data.  Tally
   sizes, and place the offsets into sh_entsize fields: high bit means it
   belongs in init. */
static void layout_sections(struct module *mod,
       const Elf_Ehdr *hdr,
       Elf_Shdr *sechdrs,
       const char *secstrings)
{
 static unsigned long const masks[][2] = {
  /* NOTE: all executable code must be the first section
   * in this array; otherwise modify the text_size
   * finder in the two loops below */
  { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL },
  { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL },
  { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL },
  { ARCH_SHF_SMALL | SHF_ALLOC, 0 }
 };
 unsigned int m, i;

 for (i = 0; i < hdr->e_shnum; i++)
  sechdrs[i].sh_entsize = ~0UL;

 DEBUGP("Core section allocation order:/n");
 for (m = 0; m < ARRAY_SIZE(masks); ++m) {
  for (i = 0; i < hdr->e_shnum; ++i) {
   Elf_Shdr *s = &sechdrs[i];

   if ((s->sh_flags & masks[m][0]) != masks[m][0]
       || (s->sh_flags & masks[m][1])
       || s->sh_entsize != ~0UL
       || strncmp(secstrings + s->sh_name,
           ".init", 5) == 0)
    continue;
   s->sh_entsize = get_offset(&mod->core_size, s);
   DEBUGP("/t%s/n", secstrings + s->sh_name);
  }
  if (m == 0)
   mod->core_text_size = mod->core_size;
 }

 DEBUGP("Init section allocation order:/n");
 for (m = 0; m < ARRAY_SIZE(masks); ++m) {
  for (i = 0; i < hdr->e_shnum; ++i) {
   Elf_Shdr *s = &sechdrs[i];

   if ((s->sh_flags & masks[m][0]) != masks[m][0]
       || (s->sh_flags & masks[m][1])
       || s->sh_entsize != ~0UL
       || strncmp(secstrings + s->sh_name,
           ".init", 5) != 0)
    continue;
   s->sh_entsize = (get_offset(&mod->init_size, s)
      | INIT_OFFSET_MASK);
   DEBUGP("/t%s/n", secstrings + s->sh_name);
  }
  if (m == 0)
   mod->init_text_size = mod->init_size;
 }
}

因此,对于标志为AX,AMS,A,WA且不是".init"的节,都会将大小累计到mod->core_size中
(包含一定的对齐),并且将AX的大小保存到mod->core_text_size中;对于含有A(X)的
".init"节,会将大小累计到mod->init_size中,并将AX的大小保存到mod->init_text_
size中.而对于那些没有A的节,不分配空间.

对于各个节的复制,代码如下:
for (i = 0; i < hdr->e_shnum; i++) {
 void *dest;

 if (!(sechdrs[i].sh_flags & SHF_ALLOC))
  continue;

 if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK)
  dest = mod->module_init
   + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK);
 else
  dest = mod->module_core + sechdrs[i].sh_entsize;

 if (sechdrs[i].sh_type != SHT_NOBITS)
  memcpy(dest, (void *)sechdrs[i].sh_addr,
         sechdrs[i].sh_size);
 /* Update sh_addr to point to copy in image. */
 sechdrs[i].sh_addr = (unsigned long)dest;
 DEBUGP("/t0x%lx %s/n", sechdrs[i].sh_addr, secstrings +
      sechdrs[i].sh_name);
}
可见,节的复制是按照原来ELF的顺序,将所有标志包含A的节都复制到相应的分配空间
(module_core/module_init),例外的情况是SHT_NOBITS,也就是BSS段,文件中没有分配
空间,因此不需要复制。

- ---[ 1.4 可行性结论

现在我们就能得出结论了,我们完全可以通过struct module结构中的字段,将内核模块
的大部分信息都抽取出来。由于我们节的复制是按顺序的,而.text节是第一个节,因此
mod->module_core实际上指向的就是.text段。而mod->core_text_size中也包含了.text
节的大小,因此我们的反编译代码段,范围也是明确的。

- ---[ 1.5 补充几点

!!!  现在我们来看一下.symtab和.strtab节,程序中有这么几行:
 #ifdef CONFIG_KALLSYMS
  /* Keep symbol and string tables for decoding later. */
  sechdrs[symindex].sh_flags |= SHF_ALLOC;
  sechdrs[strindex].sh_flags |= SHF_ALLOC;
 #endif 
 这说明,只有定义了CONFIG_KALLSYMS时,这两个节才会被分配空间.

!!! 我们的程序中好像并没有.init节,因此module_init,init_size,init_text_size
 都为0,那么什么时候会存在.init节呢?我们试着把
 static int remove_init(void) -> static int __init remove_init(void)
 再看看readelf的结果:

  [2] .init.text        PROGBITS        00000000 000038 00007e 00  AX  0   0  1
 看来这下remove_init就变成.init.text段了。

- ---[ 2 模块的提取

按照模块内存分配的过程,我们很容易就能写出一个简易的module dumper.

- ---[ 2.1 简单的module dumper

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/list.h>
#include <linux/string.h>
#include <asm/uaccess.h>

#define EOF             (-1)
#define SEEK_SET        0
#define SEEK_CUR        1
#define SEEK_END        2

struct file *klib_fopen(const char *filename, int flags, int mode);
void klib_fclose(struct file *filp);
int klib_fwrite(char *buf, int len, struct file *filp);

static struct module  *mod;
static char buffer[256];
static char *mod_name;

module_param(mod_name, charp, 0);

ssize_t show_mod_read(struct file *fp, char *buf, size_t len, loff_t *off)
{
 struct file  *filep;

 filep = klib_fopen("./dump.dat", O_WRONLY | O_CREAT | O_TRUNC,
     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 if(filep == NULL){
  printk("Error open files./n");
  return 0;
 }
 klib_fwrite(mod->module_core, mod->core_size, filep);
 klib_fclose(filep);
 
 filep = klib_fopen("./dump.info", O_WRONLY | O_CREAT, O_TRUNC,
     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 if(filep == NULL){
  printk("Error open files./n");
  return 0;
 }
 sprintf(buffer, "mod->module_init = 0x%p/n"
   "mod->module_core = 0x%p/n"
   "mod->init_size = %ld/n"
   "mod->core_size = %ld/n"
   "mod->init_text_size = %ld/n"
   "mod->core_text_size = %ld/n",
   mod->module_init, mod->module_core,
   mod->init_size, mod->core_size,
   mod->init_text_size, mod->core_text_size);
 klib_fwrite(buffer, strlen(buffer), filep);
 klib_fclose(filep);
 
 return 0;
}
static struct file_operations show_mod_fops = {

 .read = show_mod_read,
};

static int dummy_init(void)
{
 struct proc_dir_entry  *entry;
 struct list_head  *p;
 struct module  *head, *counter;
 
 mod = NULL;
 if(!mod_name)
  mod = THIS_MODULE;
 else{
  head = THIS_MODULE;
  list_for_each(p, head->list.prev){
   counter = list_entry(p, struct module, list);
   if(strcmp(counter->name, mod_name) == 0){
    mod = counter;
    break;
  }
 }
 if(!mod){
  printk("Can't find module named %s/n", mod_name);
  return -1;
 }
 entry = create_proc_entry("show_mod", S_IRUSR, &proc_root);
 entry->proc_fops = &show_mod_fops;
 
 return 0;
}

static void dummy_exit(void)
{
 remove_proc_entry("show_mod", &proc_root);
 return;
}

struct file *klib_fopen(const char *filename, int flags, int mode)
{
    struct file *filp = filp_open(filename, flags, mode);
    return (IS_ERR(filp)) ? NULL : filp;
}

void klib_fclose(struct file *filp)
{
    if (filp)
        fput(filp);
}

int klib_fwrite(char *buf, int len, struct file *filp)

{

 int writelen;
 mm_segment_t oldfs;

 if (filp == NULL)
  return -ENOENT;
 if (filp->f_op->write == NULL)
  return -ENOSYS;
 if (((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) == 0)
  return -EACCES;
 oldfs = get_fs();
 set_fs(KERNEL_DS);
 writelen = filp->f_op->write(filp, buf, len, &filp->f_pos);
 set_fs(oldfs);

 return writelen;

}

module_init(dummy_init);
module_exit(dummy_exit);

MODULE_LICENSE("GPL");

- ---[ 2.2 程序的功能

这其实是一个很简单的小程序,将某些struct module的描述信息保存到./dump.info中,
将module装载以后的内容保存到./dump.dat中,由于dump.info中含有程序基址,代码段
长度等信息,有助于对dump.dat的反汇编。不过,对于法律取证来说,该程序还是不满
足要求的,我们在文章的最后会进行讨论。

- ---[ 3 模块的反汇编

现在既然我们获得了模块的内存镜像,现在该是分析模块具体作用的时候了,模块到底是
一个正常模块,还是一个LKM的木马呢?

- ---[ 3.1 BFD简介

BFD, Binary File Descriptor, 是一个支持多架构,多类型的二进制平台.有了这个平台,
你可以写出支持各种文件格式(ELF, COFF, A.OUT...)的工具,objdump就是一个例子。
BFD打开文件的时候,通过Magic来确定文件的类型,然后读取相应的文件头,并建立若干
canonical object,向调用的上层提供统一的接口。
可以看出,BFD对于编写多种文件格式的工具时,是个很好的起点,不过BFD并不是十全十美
的,由于严格遵循了规范,因此对某些畸形的文件支持不好,这种情况下,我们可能需要别的
工具,例如Fenris[3].

- ---[ 3.2 Intel的机器指令的基本格式

Intel x86的机器指令型为[4]
 [Prefix] Opcode [ModR/M] [SIB] [Displacment] [Immediate]

其中Opcode是必须的,其它的都是可选的,其中Prefix为0-4字节的组合,可能的情况有:

The following are the allowable instruction prefix codes:
 F3H REP prefix (used only with string instructions)
 F3H REPE/REPZ prefix (used only with string instructions
 F2H REPNE/REPNZ prefix (used only with string instructions)
 F0H LOCK prefix
The following are the segment override prefixes:
 2EH CS segment override prefix
 36H SS segment override prefix
 3EH DS segment override prefix
 26H ES segment override prefix
 64H FS segment override prefix
 65H GS segment override prefix
 66H Operand-size override
 67H Address-size override

因此,当我们对一段机器码进行反汇编时,首先从第一个字节开始,把所有的Prefix跳过,
之后就是1-2字节的Opcode(2字节的Opcode第一个字节为特定字符,例如0x0F),对于某些
Opcode指令,需要相应的[ModR/M] [SIB] [Displacment] [Immediate],具体的情况
请参见Intel的手册。

- ---[ 3.3 利用binutils包编写一个简单的反编译器

如果自己从头写一个反编译器,Intel的指令很多,需要的工作量很大,我们可以利用现成
的开源代码,稍作修改,来满足我们的需要.Open Source的目的就是Never invent wheel
again, never solve the same problem twice.

我们不能使用标准的BFD Library,因为我们dump出来的镜像已经丢失了ELF的头信息,
因此需要调用BFD底层的函数来反汇编指令,同时还要自己处理符号。

对于反汇编指令,我们可以使用binutils/opcodes/i386-dis.c[5]中的print_insn函数,
至于符号解析,我们可以使用系统/boot/System.map文件的内容。

由于print_insn函数是BFD的底层函数,使用了许多BFD相关的数据结构,因此如果我们
使用,需要自己构造一些结构,通过对print_insn的分析,我们只需要构造以下结构即可:

struct disassemble_info myinfo;
static void info_init(void)
{
 myinfo.mach = bfd_mach_i386_i386;
 myinfo.disassembler_options = "i386,att,addr32,data32";
 myinfo.fprintf_func = fprintf;
 myinfo.stream = stdout;
 myinfo.read_memory_func = my_read_func;
 myinfo.memory_error_func = my_error_func;
 myinfo.print_address_func = my_address_func;
 myinfo.buffer_vma = base_addr;
 myinfo.buffer_length = dis_size;
 myinfo.buffer = malloc(dis_size);
}
其中my_read_func是自定义的读取指令的函数,my_error_func是出错处理函数,
my_address_func是打印地址函数,在这里我们可以查找符号表,得到该地址的
相应符号。base_addr就是mod->module_core。

简单的my_address_func实现:
void my_address_func(bfd_vma memaddr,
  struct disassemble_info *myinfo)
{
 char  *p;

 p = NULL;
 myinfo->fprintf_func(myinfo->stream, "0x%x", memaddr);
 p = find_symbol(root, memaddr);
 if(p)
  myinfo->fprintf_func(myinfo->stream, " <%s>", p);
 return;
}

简单的my_read_func实现:
int my_read_func(bfd_vma memaddr,
  bfd_byte *myaddr,
  unsigned int length,
  struct disassemble_info *myinfo)
{
 unsigned long bytes;
 
 bytes = memaddr - myinfo->buffer_vma;
 
 memcpy(myaddr, myinfo->buffer + bytes, length);
 return 0;
}

我们对test.ko进行反汇编,得到的结果如下:(片段)

...
<8881000+59> mov    %edx,(%eax)
<8881000+61> movl   $0x200200,0x4(%ecx)
<8881000+68> movl   $0x100100,(%ecx)
<8881000+74> pushl  0x8881488
<8881000+80> push   $0x888108d
<8881000+85> jmp    0x8881072
<8881000+87> mov    %edx,%ecx
<8881000+89> mov    (%edx),%eax
<8881000+91> prefetchnta (%eax)
<8881000+94> nop   
<8881000+95> cmp    $0x8881504,%edx
<8881000+101> jne    0x8881016
<8881000+103> pushl  0x8881488
<8881000+109> push   $0x88810ad
<8881000+114> call   0x21188c7 <printk>
<8881000+119> pop    %eax
<8881000+120> xor    %eax,%eax
<8881000+122> pop    %edx
<8881000+123> pop    %esi
<8881000+124> pop    %edi
<8881000+125> ret   
...

- ---[ 3.4 需要注意的两点

1. i386-dis.c中需要的bfd.h在源代码包中并不存在,需要运行./configure && make
才能生成,只是运行./configure是不够的.
2. 前面提到的__init修饰,以及.init开头的节,在模块插入完毕运行之后,这一部分
的内存空间会被释放,因此dump的时候这些信息是无法得到的。

-----------------------------------------------------------------------------

- ---[ 4 结尾

至此,我们已经实现了基本的模块提取和反编译功能,但仍有很多不足及值得改进的地方

o  模块dump的结果默认放到了磁盘上,这是法律取证中应该避免的(尽量少的磁盘访问)。
   解决方法是通过内核向网上的另一台主机发包,内容就是原来的文件。

o  反编译的过程还是依赖BFD的底层,对于畸形的指令是没有免疫力的,简单的花指令就
   会使反编译的结果出错。

o  如果symtab和strtab也保存到模块镜像中,这些信息对于反编译也很有帮助,我们没有
   使用。

大家可以根据自己的要求,适当增加该反编译器的功能。

qtcreater 调试时进入 disassembler 汇编界面--项目无中文路径 qtcreater 调试时进入 disassembler 汇编界面 () 在项目进行调试,进入不了断点位置,而是进入了一个disassembler 界面,查询资料后,发现是汇编界面,解释大同小异,都是说是项目路径有中文,排查后并未发现中文路径,所以我更换了构建目录,发现可以了,生成新的qmake文件替换后发现可以使用。 我觉得是qmake的原因吧。 ... 阅读详情

相关推荐

linux netfilter 实例

linux netfilter 实例 实现了一个用于阻断给定ip的数据包的功能。

linux内核空间访问文件系统代码实现

本文代码是从互联网收集而来,我记录下来主要为学习方便,版权归原作者所有。 以下代码实现了在linux内核空间打开、读、写文件等相关函数,有兴趣的可以研究一下并在此基础上扩展。   #include   #include   #include   #include   #include   #include   #include   /*    file I/O in ke

fh400的专栏 1072

openwrt编译及第一个安装包教程

Date: 2017.03.14 Made:SuperDeverloper Email: na1206@live.com Target: For mt7688 based board说明:本人在学习过程中走了不少弯路,希望本文档能为后来者提供便利。欢迎复制和使用本文档。本文档记录了搭建openwrt编译环境以及快速创建ipk包(openwrt上的安装程序)。本文档在实际应用中做过几次修改,以帮

SuperDevloper的博客 1万+

linux内核空间 打开、读、写等文件操作实现

#include #include #include #include #include #include #include   /*   file I/O in kernel module */ #define EOF (-1) #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_

curry1990的专栏 932

项目管理——14规划绩效域论文套路

规划绩效域论文套路

Flee Elf 1153

DGX Spark上PyTorch GPU环境配置实战:CUDA 13.0与ARM架构全流程

在深度学习中,CUDA作为GPU加速计算的底层基础,其版本与深度学习框架(如PyTorch)的匹配直接影响模型训练的效率与稳定性。GPU环境配置不仅涉及驱动、CUDA工具链与框架运行时的协同,还依赖于硬件架构与预构建包的兼容性。在AI开发机Ubuntu系统上,正确对齐CUDA版本、计算能力架构及安装源,是避免环境故障、发挥硬件算力的关键。无论是面向个人AI超算还是通用GPU服务器,掌握版本对齐、路径隔离与验证方法,都能大幅提升开发效率。本文以DGX Spark为例,详解从环境摸底到PyTorch GPU版本

weixin_29483277的博客 222

vue+django 返回的数据中内含外键关联组成的列表

如题,返回的数据中内含外键关联组成的列表,比如一个部门有多个人,将所有人和部门组成键值对,形成字典,返回数据以后,渲染出现问题, 决绝方案: 在el-table-column里添加遍历,如下 &lt;el-table-column prop="Dis_Size" label="磁盘大小" &gt; &lt;template slot-scope="scope"&gt; &lt;div&gt; &lt;li v-for="item in scope.row.Dis_Size"&g

ctrl_z_x_c的博客 617

桁架 拓扑优化 matlab程序_拓扑优化在结构轻量化及板壳加筋肋最优布置的应用...

1 拓扑优化简介材料的有效利用一直都是人类追求的目标,也是许多研究领域不变的话题,并伴随着结构优化理论和方法的产生而发展。结构优化设计通常分为三个层次:(1)拓扑优化:在一个确定的连续区域内寻求结构内部非实体区域位置和数量的最佳配置,寻求结构中的构件布局及节点联结方式最优化,使结构能在满足应力、位移等约束条件下,将外载荷传递到结构支撑位置,同时使结构的某种性态指标达到最优;(2)形状优化:指在保持...

weixin_39525355的博客 1878

ubuntu+systemtap进行Linux内核和用户空间开发测试

【摘要】本文主要介绍在ubuntu平台 + 自定义内核上如何安装systemtap工具包及解决编译加载运行过程中的一些问题,如何利用systemtap工具监测分析内核函数,内核模块及用户态程序。 【关键字】ubuntu, systemtap, utrace, backtrace, dtrace, callstack

Sailor_forever 1万+

【Linux命令大全】006.网络通讯之netstat命令(实操篇)

本文详细介绍了Linux系统中netstat网络诊断命令的实用技巧。主要内容包括:1) 基本功能与参数详解,展示如何查看网络连接、路由表和接口统计;2) 安装方法及基础用法,如查看TCP/UDP连接、显示进程信息和路由表;3) 高级应用技巧,包括监听端口检查、连接状态统计和实时监控。文章通过表格和代码示例,系统性地呈现了netstat在系统管理中的核心应用场景,是网络故障排查和性能监控的实用指南。

yuhuasheng1997的博客 708

【华为OD技术面试手撕真题】1、长度最小的子数组 | 手撕真题+思路参考+代码解析(C & C++ & Java & Python & JS)(三种解法)

【华为OD面试手撕真题】长度最小的子数组,本专栏更新每年华为OD机试的高频手撕代码题,每个题目都会使用五种语言进行解答(C & C++& Java & Python & JS),思路分析都非常详细, 实现最低的时间复杂度和高通过率,每个解题思路超过百字,内含各个编程语言的八股文面试题,欢迎大家订阅学习, 代码可以直接运行使用,全网限时最低价,持续更新

KJ.JK 1万+

Qt:万能的Debug大神

▂▃▅▆█ 初识 刚学习Qt不久。最近学习了几个简单的实例,于是想实现网络通信,在GUI下,对一些大牛来说很简单了,别笑我哦,。 废话不说了,程序遇到了问题。 问题:⊙0⊙ 经过几次编译(一直

钱国正的专栏 6179

matlab 一维相位解缠

一维相位解缠算法的matlab代码实现。

点云侠的博客 373

用Dumpbin命令的Headers查看一下.dll文件的各节信息列表

用Dumpbin命令的Headers查看一下.dll文件的各节信息列表:若.dll文件名为Hook,假设Hook.dll文件所在目录为C:\Users\Hook\Debug则在cmd命令中输入C:\Users\Hook\Debug>dumpbin -headers Hook.dll,则会显示: Microsoft (R) COFF Binary File Dumper Version 6.0

u014023993的专栏 2467

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

qt creater 调试时进入disassembler 汇编界面

原文地址:https://blog.youkuaiyun.com/weixin_40020455/article/details/80715568 问题描述:某个项目进入调试时,进入disassembler 反汇编的界面。 解决方法:根据其他人提供的办法,首先将项目右键 clean后,run qmake 再调试时恢复代码界面。 敬请指正。 ...

Hi, Boy 1万+

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

神经网络调试经验汇编

1.没有归一化数据; 2.没有检查结果; 3.没有预处理数据; 4.没有使用任何调整; 5.使用了太大的batch size; 6. 使用了不正确的学习率; 7.最后一层使用了不正确的学习率; 8.网络包含错误的梯度; 9.初始化的网络权重不对; 10.网络太深; 11.使用了错误的隐藏单元;

twinkle_star1314的博客 463

QT调试常见问题

1. the cdb process terminated 错误原因:生成的exe目录下缺少运行所需的dll

JIN JI 2013.12.24 549

汇编调试实战:DEBUG、Turbo Debugger、GDB 与 IDA

本文系统讲解汇编调试技巧,涵盖 DEBUG.exe 基础使用(寄存器查看、单步执行、内存操作)、Turbo Debugger 图形化调试,以及 GDB(32位Linux)和逆向工具(IDA/Ghidra)的高级应用。重点解析寄存器管理、断点设置、栈平衡检查与常见错误排查,如死循环、内存越界、栈崩溃等,强调静态分析识别函数边界、动态分析验证逻辑流程。掌握这些方法可高效定位底层缺陷,提升汇编开发能力。

软件工程师文艺 247

ARM 汇编调试入门:MOV、LDR 和 STR

掌握,最重要的是看清数据移动的方向。

ccddvvv的博客 203
上一篇: linux僵尸进程
下一篇: Dr.Watson使用技巧摘要
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值