精华内容
下载资源
问答
  • was文件解析协议

    2014-12-17 15:22:53
    自己总结了一下从was文件中读取动态图片内容的协议
  • 最新:https://gitee.com/ulxy/librowser 支持直读迭代资源 自动扫描wdf文件was文件夹
  • 关于梦幻西游was\tcp素材文件的分析

    千次阅读 2013-10-07 13:01:32
    王大理的文章因为年代久远,轻易也找不到了,所以现在重新写一遍WAS文件格式的博客。  直接进入正题,was文件格式应该是王大理第一次提出来的。网易内部应该用的是tcp的扩展名,但是这一点对数据影响不大,其中的...

        其实一年多之前写过一篇类似的文章,用的是之前的CSDN的账号,但是因为当时其实并没有很用心,于是文章中还是有一些小错误。王大理的文章因为年代久远,轻易也找不到了,所以现在重新写一遍WAS文件格式的博客。

        直接进入正题,was文件格式应该是王大理第一次提出来的。网易内部应该用的是tcp的扩展名,但是这一点对数据影响不大,其中的编码方式还是相同的。

        文件头部分:16个字节。

        第1.2字节:文件标志,所有was文件这里都是'SP',如果不是,说明编码有错误。

        第3.4字节:文件头部分的长度,这里没有包含前4个字节。对于was或者说tcp文件,这个值应该是12。(注1)

        第5.6字节:文件中包含图像的方向数量。

        第7.8字节:文件中每个方向包含的图像数量,也就是常说的帧数量。

        第9.10字节:图片的总宽度。

        第11.12字节:图片的总高度。

        第13.14字节:图片的关键点的X坐标。

        第15.16字节:图片的关键点的Y坐标。

        以上是文件头部分。紧跟着文件头是512个字节的调色板部分。每个颜色占用两个字节,16位,565模式,共有256个颜色。对于图片的绘制,所有颜色都是取自调色板中的定义。

        调色板之后是每张图片的起始位置索引。这个索引是从调色板头部作为0值计算的,文件头并没有算在内。这部分的大小是方向数量*每个方向图片数量*4个字节。每个方向的图片索引都在一起。

        图片索引之后是每张图片的内容。其中每个图片头部有4个4字节的信息值,分别表示图片的关键点X,关键点Y,图片的宽度,图片的高度。(注2)

        之后是每一行像素信息的起始位置索引。这段索引应该是将对应图片数据的开始位置(图片关键点X)作为0值计算。所占用的大小是图片的高度*4个字节。

        之后是真正的图片颜色信息,每个单元具体长度取决于数据的内容。

        第一个字节中  如果前两个比特为00时,若其余6个比特也为0,则表示该行的图像数据结束(注3),否则表示带有alpha层的像素。(注4)

        若第3个比特为1 剩下的5个比特为alpha值 且下一个字节为调色板索引。

        若第3个比特为0 则表示将要重复alpha像素,剩下的5个比特为重复的次数。之后的一个字节为alpha值,下一个字节为调色板索引。

        第一个字节中   如果前两个比特为01时,则表示普通像素,剩下的5个比特表示数据段的长度。之后的每一个在长度中的字节都表示调色板索引。

        第一个字节中   如果前两个比特为10时,表示重复像素,剩下的5个比特为重复的次数,之后的一个字节为调色板索引。

        第一个字节中   如果前两个比特位11时,表示跳过像素,剩下的5个比特表示跳过像素的数量。

     

        注1:所有was\tcp文件的前4个字节都是相同的。之所以存在这4个字节,应该是因为网易的素材包WDF文件中并没有文件名这个概念,也就没有扩展名的概念。这4个字节可以将tcp文件和其他类型(如png,mp3格式)文件区分开。

        注2:在文件头部已经存在关键点X,Y和图片的规格,在每张图片的头部仍然有这4个元素。具体程序绘制图片的时候应该是这两部分都要参与计算。

        注3:作者无法确定是否有某一行结束时,并没有将一行的所有像素点的数据全部给出。

        注4:was\tcp文件中的alpha的取值范围是0~31。

     

        写得比较笼统,具体的只要用十六进制编辑器打开一个具体的文件就能弄明白了。

    展开全文
  • linux core文件解析

    千次阅读 2019-07-05 17:08:52
    * If it was able to dump core, this kills all * other threads in the group and synchronizes with * their demise. If we lost the race with another * thread getting here, it set group_exit_code * ...

    Linux ELF core files

    A core file is essentially a snapshot of the process and its state right before it cored
    (crashed or dumped). A core file is a type of ELF file that is primarily made up of program
    headers and memory segments. They also contain a fair amount of notes in the PT_NOTE
    segment that describe file mappings, shared library paths, and other information.

    Core file program headers

    In a core file, there are many program headers. All of them except one are of the PT_LOAD
    type. There is a PT_LOAD program header for every single memory segment in the process,
    with the exception of special devices (that is /dev/mem). Everything from shared libraries
    and anonymous mappings to the stack, the heap, text, and data segments is represented by
    a program header.
    Then, there is one program header of the PT_NOTE type; it contains the most useful and
    descriptive information in the entire core file.

    The PT_NOTE segment

    The eu-readelf -n output that is shown next shows the parsing of the core file notes
    segment.

    $ eu-readelf -n core
    Note segment of 4200 bytes at offset 0x900:
    Owner Data size Type
    CORE 336 PRSTATUS
    info.si_signo: 11, info.si_code: 0, info.si_errno: 0, cursig: 11
    sigpend: <>
    sighold: <>
    pid: 9875, ppid: 7669, pgrp: 9875, sid: 5781
    utime: 5.292000, stime: 0.004000, cutime: 0.000000, cstime: 0.000000
    orig_rax: -1, fpvalid: 1
    r15: 0 r14: 0
    r13: 140736185205120 r12: 4195616
    rbp: 0x00007fffb25380a0 rbx: 0
    r11: 582 r10: 140736185204304
    r9: 15699984 r8: 1886848000
    rax: -1 rcx: -160
    rdx: 140674792738928 rsi: 4294967295
    rdi: 4196093 rip: 0x000000000040064f
    rflags: 0x0000000000000286 rsp: 0x00007fffb2538090
    CORE 1812 FILE
    30 files:
    00400000-00401000 00000000 4096 /home/user/git/azazel/host
    00600000-00601000 00000000 4096 /home/user/git/azazel/host
    00601000-00602000 00001000 4096 /home/user/git/azazel/host
    3001000000-3001019000 00000000 102400 /lib/x86_64-linux-
    gnu/libaudit.so.1.0.0
    3001019000-3001218000 00019000 2093056 /lib/x86_64-linux-
    gnu/libaudit.so.1.0.0
    3001218000-3001219000 00018000 4096 /lib/x86_64-linux-
    gnu/libaudit.so.1.0.0
    3001219000-300121a000 00019000 4096 /lib/x86_64-linux-
    gnu/libaudit.so.1.0.0
    3003400000-300340d000 00000000 53248 /lib/x86_64-linux-
    gnu/libpam.so.0.83.1
    300340d000-300360c000 0000d000 2093056 /lib/x86_64-linux-
    gnu/libpam.so.0.83.1
    300360c000-300360d000 0000c000 4096 /lib/x86_64-linux-
    gnu/libpam.so.0.83.1
    300360d000-300360e000 0000d000 4096 /lib/x86_64-linux-
    gnu/libpam.so.0.83.1
    7ff166bd9000-7ff166bdb000 00000000 8192 /lib/x86_64-linux-gnu/libutil-
    2.19.so
    7ff166bdb000-7ff166dda000 00002000 2093056 /lib/x86_64-linux-gnu/libutil-
    2.19.so
    7ff166dda000-7ff166ddb000 00001000 4096 /lib/x86_64-linux-gnu/libutil-
    2.19.so
    

    Being able to view the register state, auxiliary vector, signal information, and file
    mappings is not bad news at all, but they are not enough by themselves to analyze a
    process for malware infection.

    PT_LOAD segments and the downfalls of core files for forensics purposes

    Each memory segment contains a program header that describes the offset, address, and
    size of the segment it represents. This would almost suggest that you can access every part
    of a process image through the program segments, but this is only partially true. The text
    image of the executable and every shared library that is mapped to the process get only the
    first 4,096 bytes of themselves dumped into a segment.

    This is for saving space and because the Linux kernel developers figured that the text
    segment will not be modified in memory. So, it suffices to reference the original
    executable file and shared libraries when accessing the text areas from a debugger. If a
    core file were to dump the complete text segment for every shared library, then for a large
    program such as Wireshark or Firefox, the output core dump files would be enormous.

    So for debugging reasons, it is usually okay to assume that the text segments have not
    changed in memory, and to just reference the executable and shared library files
    themselves to get the text. But what about runtime malware analysis and process memory
    forensics? In many cases, the text segments have been marked as writeable and contain
    polymorphic engines for code mutation, and in these instances, core files may be useless
    for viewing the code segments.

    Also, what if the core file is the only artifact available for analysis and the original
    executable and shared libraries are no longer accessible? This further demonstrates why
    core files are not particularly good for process memory forensics; nor were they ever
    meant to be.

    源码分析

    源码位置:

    //fs/binfmt_elf.c
      #ifdef CONFIG_ELF_CORE
      static int elf_core_dump(struct coredump_params *cprm);
      #else
      #define elf_core_dump   NULL
      #endif
      
      static struct linux_binfmt elf_format = {
          .module     = THIS_MODULE,
          .load_binary    = load_elf_binary,
          .load_shlib = load_elf_library,
          .core_dump  = elf_core_dump,                                                                                                                                       
          .min_coredump   = ELF_EXEC_PAGESIZE,
      };
      //fs/coredump.c
      void do_coredump(const siginfo_t *siginfo)
      {
      	  ...
           audit_core_dumps(siginfo->si_signo);
      
          binfmt = mm->binfmt;
          if (!binfmt || !binfmt->core_dump)
              goto fail;
          if (!__get_dumpable(cprm.mm_flags))
              goto fail;
         ...
               if (!dump_interrupted()) {
              file_start_write(cprm.file);
              core_dumped = binfmt->core_dump(&cprm);                                                                                                                        
              file_end_write(cprm.file);
          }  
       ...       
    }
    //kernel/signal.c
    int get_signal(struct ksignal *ksig)
    {
    	...
              if (sig_kernel_coredump(signr)) {
                  if (print_fatal_signals)
                      print_fatal_signal(ksig->info.si_signo);
                  proc_coredump_connector(current);
                  /*   
                   * If it was able to dump core, this kills all
                   * other threads in the group and synchronizes with
                   * their demise.  If we lost the race with another
                   * thread getting here, it set group_exit_code
                   * first and our do_group_exit call below will use
                   * that value and ignore the one we pass it.
                   */
                  do_coredump(&ksig->info);
              } 
    	...
    }
    

    下面具体来分析写elf core dump的动作:

    //fs/binfmt_elf.c
      /*
       * Actual dumper
       *
       * This is a two-pass process; first we find the offsets of the bits,
       * and then they are actually written out.  If we run out of core limit
       * we just truncate.
       */
      static int elf_core_dump(struct coredump_params *cprm)
    {
    	elf = kmalloc(sizeof(*elf), GFP_KERNEL);
          segs = current->mm->map_count;     //segment的个数从vma中得到
          /* for notes section */
          segs++;                     //另外一个segment是note
          e_phnum = segs > PN_XNUM ? PN_XNUM : segs;
          /*   
           * Collect all the non-memory information about the process for the
           * notes.  This also sets up the file header.
           */
          if (!fill_note_info(elf, e_phnum, &info, cprm->siginfo, cprm->regs))  //构造note segment的内容,放在info中,主要是寄存器的值和file map,感兴趣的可以自己去研究这个函数
              goto cleanup;
              
        offset += sizeof(*elf);             /* Elf header */ 
         offset += segs * sizeof(struct elf_phdr);   /* Program headers */
         
               /* Write notes phdr entry */
          {
              size_t sz = get_note_info_size(&info);
      
              sz += elf_coredump_extra_notes_size();
      
              phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL);
              if (!phdr4note)
                  goto end_coredump;
      
              fill_elf_note_phdr(phdr4note, sz, offset);    //构造phdr4note, 也就是note segment的phdr
              offset += sz;
          } 
    	
    	  vma_filesz = kmalloc_array(segs - 1, sizeof(*vma_filesz), GFP_KERNEL);
          if (!vma_filesz)
              goto end_coredump;
      
          for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
                  vma = next_vma(vma, gate_vma)) {
              unsigned long dump_size;
      
              dump_size = vma_dump_size(vma, cprm->mm_flags);
              vma_filesz[i++] = dump_size;
              vma_data_size += dump_size;
          }
          
          if (!dump_emit(cprm, elf, sizeof(*elf)))   //真正写elf头
              goto end_coredump;
      
          if (!dump_emit(cprm, phdr4note, sizeof(*phdr4note)))   //真正写note segment的phdr,从phdr4note中写
              goto end_coredump;
              
          /* Write program headers for segments dump */
          for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
                  vma = next_vma(vma, gate_vma)) {
              struct elf_phdr phdr;
      
              phdr.p_type = PT_LOAD;
              phdr.p_offset = offset;
              phdr.p_vaddr = vma->vm_start;     //虚拟地址
              phdr.p_paddr = 0;                          //物理地址总是为0
              phdr.p_filesz = vma_filesz[i++];     //segment的文件大小
              phdr.p_memsz = vma->vm_end - vma->vm_start;   //segment的内存大小
              offset += phdr.p_filesz;
              phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
              if (vma->vm_flags & VM_WRITE)
                  phdr.p_flags |= PF_W;
              if (vma->vm_flags & VM_EXEC)
                  phdr.p_flags |= PF_X;
              phdr.p_align = ELF_EXEC_PAGESIZE;
      
              if (!dump_emit(cprm, &phdr, sizeof(phdr)))    //真正写vma segment的phdr, 从phdr中写
                  goto end_coredump;
          }
          
    	 /* write out the notes section */
          if (!write_note_info(&info, cprm))
              goto end_coredump;
    
          for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
                  vma = next_vma(vma, gate_vma)) {
              unsigned long addr;
              unsigned long end;
      
              end = vma->vm_start + vma_filesz[i++];
      
              for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) {
                  struct page *page;
                  int stop;
      
                  page = get_dump_page(addr);
                  if (page) {
                      void *kaddr = kmap(page);
                      stop = !dump_emit(cprm, kaddr, PAGE_SIZE);    //真正写vma segment,每次写一页的内容
                      kunmap(page);
                      put_page(page);
                  } else
                      stop = !dump_skip(cprm, PAGE_SIZE);
                  if (stop)
                      goto end_coredump;
              }
          }
          dump_truncate(cprm);     //根据core文件大小的限制进行截断
    }
    // note segment phdr的构造
      static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, loff_t offset)                                                                                           
      {
          phdr->p_type = PT_NOTE;
          phdr->p_offset = offset;
          phdr->p_vaddr = 0;
          phdr->p_paddr = 0;
          phdr->p_filesz = sz;
          phdr->p_memsz = 0;   //内存中不存在
          phdr->p_flags = 0;
          phdr->p_align = 0;
          return; 
      }
      
      /*
       * Decide what to dump of a segment, part, all or none.
       */
      static unsigned long vma_dump_size(struct vm_area_struct *vma,
                         unsigned long mm_flags) //决定vma是否dump,已经dump的大小
    {
      #define FILTER(type)    (mm_flags & (1UL << MMF_DUMP_##type))
      
          /* always dump the vdso and vsyscall sections */
          if (always_dump_vma(vma))
              goto whole;
      
          if (vma->vm_flags & VM_DONTDUMP)
              return 0;
          ...
    	 /* Do not dump I/O mapped devices or special mappings */
          if (vma->vm_flags & VM_IO)
              return 0;
          /* By default, dump shared memory if mapped from an anonymous file. */
          if (vma->vm_flags & VM_SHARED) {
              if (file_inode(vma->vm_file)->i_nlink == 0 ?
                  FILTER(ANON_SHARED) : FILTER(MAPPED_SHARED))
                  goto whole;
              return 0;
          }
      
          /* Dump segments that have been written to.  */
          if (vma->anon_vma && FILTER(ANON_PRIVATE))
              goto whole;
          if (vma->vm_file == NULL)
              return 0;
      
          if (FILTER(MAPPED_PRIVATE))
              goto whole;
          /*
           * If this looks like the beginning of a DSO or executable mapping,
           * check for an ELF header.  If we find one, dump the first page to
           * aid in determining what was mapped here.
           */
          if (FILTER(ELF_HEADERS) &&
              vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ)) {          
              u32 __user *header = (u32 __user *) vma->vm_start;
              u32 word;
              mm_segment_t fs = get_fs();
              /*
               * Doing it this way gets the constant folded by GCC.
               */
              union {
                  u32 cmp;
                  char elfmag[SELFMAG];
              } magic;
              BUILD_BUG_ON(SELFMAG != sizeof word);
              magic.elfmag[EI_MAG0] = ELFMAG0;
              magic.elfmag[EI_MAG1] = ELFMAG1;
              magic.elfmag[EI_MAG2] = ELFMAG2;
              magic.elfmag[EI_MAG3] = ELFMAG3;
              /*
               * Switch to the user "segment" for get_user(),
               * then put back what elf_core_dump() had in place.
               */
              set_fs(USER_DS);
              if (unlikely(get_user(word, header)))
                  word = 0;
              set_fs(fs);
              if (word == magic.cmp)
                  return PAGE_SIZE;    //可执行segment, 只dump一个page的大小
      #undef  FILTER
      
          return 0;
      
      whole:
          return vma->vm_end - vma->vm_start;
    }
      /*
       * Core dumping helper functions.  These are the only things you should
       * do on a core-file: use only these functions to write out all the
       * necessary info.
       */
      int dump_emit(struct coredump_params *cprm, const void *addr, int nr) 
      {
          struct file *file = cprm->file;
          loff_t pos = file->f_pos;
          ssize_t n;
          if (cprm->written + nr > cprm->limit)  //判断是否超出core文件的大小
              return 0;
          while (nr) {
              if (dump_interrupted())
                  return 0;
              n = __kernel_write(file, addr, nr, &pos);
              if (n <= 0)
                  return 0;
              file->f_pos = pos;
              cprm->written += n;
              cprm->pos += n;
              nr -= n;
          }   
          return 1;
      }
    

    从上面可以看出,core文件也是一个elf文件,只包含program header(也就是segment header),没有section header,segment包括:
    一个PT_NOTE,其他的都是PT_LOAD
    PT_NOTE segment保存的主要是寄存器,file map等信息
    PT_LOAD segment保存的是vma,由filter决定一个vma是否dump,对于text等可执行段,只保存一个page的内容,因为一般认为text是不可写的,从原始可执行文件中可以得到
    至于filter,可以通过设置/proc//coredump_filter来完成,主要是设置哪些vma需要coredump,具体的可以man core 来查看:

               bit 0  Dump anonymous private mappings.
               bit 1  Dump anonymous shared mappings.
               bit 2  Dump file-backed private mappings.
               bit 3  Dump file-backed shared mappings.
               bit 4 (since Linux 2.6.24)
                      Dump ELF headers.
               bit 5 (since Linux 2.6.28)
                      Dump private huge pages.
               bit 6 (since Linux 2.6.28)
                      Dump shared huge pages.
               bit 7 (since Linux 4.4)
                      Dump private DAX pages.
               bit 8 (since Linux 4.4)
                      Dump shared DAX pages.
    
           By default, the following bits are set: 0, 1, 4 (if the CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS kernel configuration option is enabled), and  5.   This  default
           can be modified at boot time using the coredump_filter boot option.
    
           The value of this file is displayed in hexadecimal.  (The default value is thus displayed as 33.)
    
           Memory-mapped I/O pages such as frame buffer are never dumped, and virtual DSO pages are always dumped, regardless of the coredump_filter value.
    
           A child process created via fork(2) inherits its parent's coredump_filter value; the coredump_filter value is preserved across an execve(2).
    

    参考文档:
    linux内核源码
    Learning Linux Binary Analysis

    展开全文
  • binlog二进制文件解析

    千次阅读 2019-04-07 14:02:13
    本文主要介绍MySQL的binlog二进制文件解析,目的是更好的了解binlog文件的构成并做相应的二次开发,并帮助对主从复制机制有更多理解。 以下内容基于row的日志格式。操作系统redhat7,MySQL版本5.7.17,开启GTID。...

    本文主要介绍MySQL的binlog二进制文件的解析,目的是更好的了解binlog文件的构成并做相应的二次开发,并帮助对主从复制机制有更多理解。

    以下内容基于row的日志格式。操作系统redhat7,MySQL版本5.7.17,开启GTID。

     

    一 Binlog生成

    在MySQL中执行flush logs,并执行以下操作。

    mysql> create database abcd;
    Query OK, 1 row affected (0.01 sec)
    
    mysql> create table test (a1 int primary key not null auto_increment, a2 double not null, a3 timestamp, a4 datetime, a5 char(10), a6 varchar(4000), a7 text);
    Query OK, 0 rows affected (0.21 sec)
    
    mysql> insert into test values(1, 2.222222222, now(), now(), 'abc', 'abcdefghasdasdasd', 'qwetrhyokxocm3479thcms9q25hdr9ker8thcfisdrhoc');
    Query OK, 1 row affected (0.08 sec)
    
    mysql> update test set a1=10, a2=3.33333, a3=now(), a4=now(), a5='abcde', a6='a', a7='s' where a1=1;
    Query OK, 1 row affected (0.06 sec)
    Rows matched:1  Changed: 1  Warnings: 0
    
    mysql> delete from test;
    Query OK, 1 row affected (0.04 sec)
    
    mysql> drop table test;
    Query OK, 0 rows affected (0.22 sec)
    
    mysql> flush logs;
    Query OK, 0 rows affected (0.17 sec)

    可以得到文件 mysql-bin.000006

    hexdump –C mysql-bin.000006

    生成十六进制的解析文件。文件较长,之后会逐层分析。

     

    二 Binlog文件构成

    对于该binlog而言,主要包括以下几种event。

    • FORMAT_DESCRIPTION_EVENT
    • GTID_LOG_EVENT/ANONYMOUS_GTID_LOG_EVENT
    • QUERY_EVENT
    • TABLE_MAP_EVENT
    • WRITE_ROW_EVENT/UPDATE_ROW_EVENT/DELETE_ROW_EVENT
    • XID_EVENT
    • ROTATE_EVENT

    除这些外还有许多其他类型event,但多数不常见或随着binlog版本的升级而弃用。

    每个event包括Binlog event header,Post-Header和Body三部分。

    其中对于所有event而言,Binlog event header的长度与格式是相同的,之后会在

    FORMAT_DESCRIPTION_EVENT部分做统一介绍。

    Post-Header对于同类型的event是长度相同的,部分类型的event并没有Post-Header。

    Body则为event中的变量部分,主要表示Query event中的变量部分。

    下面将详细介绍每一种event在binlog中的解析方式。

     

    三 FORMAT_DESCRIPTION_EVENT

    该部分位于整个文件的头部,每个binlog文件都必定会有唯一一个该event。

    二进制文件内容

    解析的binlog
    /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
    /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
    DELIMITER /*!*/;
    # at 4
    #180321 18:00:32 server id 1813309  end_log_pos 123 CRC32 0x77f16c93  Start: binlog v 4, server v 5.7.17-log created 180321 18:00:32
    BINLOG '
    QC2yWg89qxsAdwAAAHsAAAAAAAQANS43LjE3LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAXwAEGggAAAAICAgCAAAACgoKKioAEjQA
    AZNs8Xc=
    '/*!*/;

    Binlog event header部分:

    fe 62 69 6e:文件前四位非Binlog event header部分,而是magic number,在这里指代.bin,代表文件的格式。

    后面的Header总长固定为19个字节。

    40 2d b2 5a:事件发生的时间戳。应用服务器是X86架构,在存储的时候为小端模式,即低位字节排放在内存的低地址端,也即实际是5a b2 2d 40代表一个数值,解析成时间为2018/3/21 18:00:32。请务必注意,后面的绝大部分数值的表示都将是这样反向的。

    0f:type_code,代表该事件的类型,几个重要的事件类型为:

    • 0x02 QUERY_EVENT
    • 0x04 ROTATE_EVENT
    • 0x0f FORMAT_DESCRIPTION_EVENT
    • 0x10 XID_EVENT
    • 0x13 TABLE_MAP_EVENT
    • 0x1d ROWS_QUERY_EVENT
    • 0x1e WRITE_ROWS_EVENTv2
    • 0x1f UPDATE_ROWS_EVENTv2
    • 0x20 DELETE_ROWS_EVENTv2
    • 0x21 GTID_EVENT
    • 0x22 ANONYMOUS_GTID_EVENT
    • 0x23 PREVIOUS_GTIDS_EVENT

    3d ab 1b 00:server_id,就是MySQL配置cnf文件中的server_id。这里是00 1b ab 3d,代表1813309。

    77 00 00 00:代表整个event的长度,长度为119。

    7b 00 00 00:下一个event开始的位置,可以看到与前一个值刚好差4,也就是魔数的长度。

    00 00:flags,每个events中flags代表的意义大致相同。在此处 00 00 代表binlog已经关闭, 00 01代表binlog仍开启,具体详见https://dev.mysql.com/doc/internals/en/binlog-event-flag.html。

    以上便是所有event的Binlog event header的固定形式,之后便不再解析。

    Post-Header 对于每一类型的event都是相同的。

    FORMAT_DESCRIPTION_EVENT有Post-Header,并没有Body部分,因为并没有变量。

    04 00:binlog的版本,mysql从5.0后日志都是v4版本的。

    之后50位代表mysql-server的版本,5.7.17-log。在此处存的是字符串所以不须反过来看。

    00 00 00 00:代表create timestamp,官网上解释为seconds since Unix epoch when the binlog was created。

    13:代表Binlog event heade的长度,固定长度是19。

    之后39位代表了39种类型事件的Post-Header的长度,从0x01开始,与上面所介绍的时间类型是对应的。其中FORMAT_DESCRIPTION_EVENT为0x0f,即长度对应5f。

    详细的关系在 https://dev.mysql.com/doc/internals/en/format-description-event.html 上有部分的对应,虽然不全但是可以对应主要的event的Post-Header长度。

    可以看到WRITE_ROWS_EVENT、UPDATE_ROWS_EVENT、DELETE_ROWS_EVENT这三种主要event长度都是0x0a,因为他们同属于ROWS_EVENT。

    后面四种可以在源码中查到,分别为

    TRANSACTION_CONTEXT_HEADER_LEN,VIEW_CHANGE_HEADER_LEN,XA_PREPARE_HEADER_LEN,ROWS_HEADER_LEN_V2。

    93 6c f1 77:最后四位为循环冗余校验码,与解析出的CRC32 0x77f16c93是一致的。注意,每一个event末尾都会有这样四个字节,之后也不再赘述。

     

    四 PREVIOUS_GTIDS_EVENT

    # at 123

    #180321 18:00:32 server id 1813309  end_log_pos 194 CRC32 0xfeb5737a  Previous-GTIDs

    # c5f7f863-1b95-11e8-9e24-0024e8629bab:1-4

    Event type是23,紧接着FORMAT_DESCRIPTION_EVENT。

    这个也是单个binlog文件中唯一的event,无论是否开关GTID都存在的。主要用来表示上一个binlog的最后一个GTID的位置。

    Binlog event header中与前面解析唯一不同之处为flags的值是80,代表LOG_EVENT_IGNORABLE_F,在8.0官网写着是代表这事件可以被忽略 (emm..

    该event只有Body变量部分,没有Post-Header,这个的解析官网和源码上给的都不清晰,只能暂且做简单的判断。

    c5 f7 f8 63 1b 95 11 e8 9e 24 00 24 e8 62 9b ab:中间十六字节代表server_uuid。

    05 00 00 00 00 00 00 00:最后8位代表最新的GTID的编号。

     

    五 GTID_EVENT/ANONYMOUS_GTID_EVENT

    # at 194

    #180321 18:00:45 server id 1813309  end_log_pos 259 CRC32 0x606aa948  GTID last_committed=0 sequence_number=1

    SET @@SESSION.GTID_NEXT= 'c5f7f863-1b95-11e8-9e24-0024e8629bab:5'/*!*/;

    event type是21,即GTID_EVENT。如果没有开启GTID模式,事件类型会是ANONYMOUS_GTID_EVENT,这两个都属于Gtid_log_event。

    01:commit flag,代表事务提交情况。

    c5 f7 f8 63 1b 95 11 e8 9e 24 00 24 e8 62 9b ab:server_uuid

    05 00 00 00 00 00 00 00:该GTID号,与上文相同。

    02:TS_TYPE,官网解释 TS_TYPE is from {G_COMMIT_TS2} singleton set of values。

    00 00 00 00 00 00 00 00:代表last_committed号 0

    01 00 00 00 00 00 00 00:代表sequence_number号 1。

    每一个Query事务前都会有这样的一个GTID_EVENT,如果未开启则是ANONYMOUS_GTID_EVENT。

    其中ANONYMOUS_GTID_EVENT只有last_committed和sequence_number号,因为没有GTID号生成因此其他位皆为0。

     

    六 QUERY_EVENT

    SET TIMESTAMP=1521626445/*!*/;
    SET @@session.pseudo_thread_id=95906/*!*/;
    SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
    SET @@session.sql_mode=1437073442/*!*/;
    SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
    /*!\C utf8mb4 *//*!*/;
    SET @@session.character_set_client=45,@@session.collation_connection=45,@@session.collation_server=45/*!*/;
    SET @@session.lc_time_names=0/*!*/;
    SET @@session.collation_database=DEFAULT/*!*/;
    create database abcd
    /*!*/;

    # at 353

    #180321 18:04:24 server id 1813309  end_log_pos 418 CRC32 0x8f20b94b  GTID last_committed=1 sequence_number=2

    在row格式的binlog中,所有的建库建表操作都是QUERY_EVENT。此处取出较短的建库操作做分析,建表操作与建库的binlog格式是完全相同的。

    Post-Header的长度为0x0d。

    00 01 76 a2:slave_proxy_id,代指一个用于临时表的进程号,为了避免创建临时表时重复。

    00 00 00 00:表示语句执行时间,单位为秒。

    04 :代表当前数据库名字的长度,在这里为abcd。

    00 00 :两位错误码,若非0则代表有误,如果从库在复制时收到错误码将停止复制进程。

    00 21 :v4新特性,为status_vars的长度,这个之后会有介绍。

    Body的长度根据DDL语句的长度而定。

    注意,此处的数据解析并不是连续不断的形式,而是类似于key-value的形式,即一个单字节键名,对应数个字节的值。

    00 00 00 00 00:键是00 代表Q_FLAGS2_CODE,以下是对应关系详情

    • 0x00004000 OPTION_AUTO_IS_NULL SQL_AUTO_IS_NULL
    • 0x00080000 OPTION_NOT_AUTOCOMMIT FOREIGN_KEY_CHECKS
    • 0x04000000 OPTION_NO_FOREIGN_KEY_CHECKS UNIQUE_CHECKS
    • 0x08000000 OPTION_RELAXED_UNIQUE_CHECKS AUTOCOMMIT

    每一项都属于叠加的形式,即0x00084000 代表数据库有前两项属性,之后的情况类似。

    此处值为00 00 00 00,即不满足上述条件。

    01 22 00 a8 55 00 00 00 00:键是01 代表Q_SQL_MODE_CODE,值为 00 00 00 00 55 a8 00 22。具体的转换内容详见https://dev.mysql.com/doc/internals/en/query-event.html#q-flags2-code。在这里代表sql_mode的配置,跟上面一样都属于叠加的模式。

    通过文档可以解析出sql_mode为

    PIPES_AS_CONCAT,ONLY_FULL_GROUP_BY,NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION。

    06 03 73 74 64:键是06 代表Q_CATALOG_NZ_CODE,值为 03 73 74 64

    03代表字符串长度,73 74 64指代std字符串。

    04 2d 00 2d 00 2d 00:键是04 代表Q_CHARSET_CODE,后面为三个双字节数字,都为0x002d。分别是指@session.character_set_client=45,@@session.collation_connection=45,@@session.collation_server=45。

    0c 01 61 62 63 64 00:键是0x0c 代表Q_UPDATED_DB_NAMES,后面跟为单字节的01,代表存储访问数据从库的总数,测试环境中仅有一个从库。之后跟的便是对应的数据库的名称,61 62 63 64对应abcd库名,其中字符串需要以00来结尾字符串。

    建表的QUERY_EVENT与建库时一致的,大家可以手动分析下。

     

    七 TABLE_MAP_EVENT

    SET TIMESTAMP=1521626714/*!*/;
    SET @@session.time_zone='SYSTEM'/*!*/;
    BEGIN
    /*!*/;

    # at 782

    #180321 18:05:14 server id 1813309  end_log_pos 843 CRC32 0x98b87aed  Table_map: `abcd`.`test` mapped to number 224

    每个DML事务之前,都会有一个TABLE_MAP_EVENT,记录操作对应的表的信息。

    Post-Header的长度为0x08。

    e0 00 00 00 00 00:代表库的ID 224。

    01 00 :保留位数,Reserved for future use。

    Body部分:

    04:库名的长度,之后是库名的字符串61 62 63 64 00 abcd,与上文相同。

    04:表名的长度,和对应的表名74 65 73 74 00 test。

    07:代表字段的个数,我们建表时设立了7种不同类型的字段。

    可以看出,binlog是完全不记录表的字段名称的,只记录对应的编号和类型。

    让我们重新看一下建表的内容。

    create table test (a1 int primary key not null auto_increment, a2 double not null, a3 timestamp, a4 datetime, a5 char(10), a6 varchar(4000), a7 text);

    03 05 11 12 fe 0f fc 08 08 00 00 fe 28 80 3e 02 78

    其中每个字节都有对应的含义。

    03代表MYSQL_TYPE_LONG。

    05代表MYSQL_TYPE_DOUBLE,后面会跟一位metadata。

    11代表MYSQL_TYPE_TIME,这个官网写后面没有metadata,但是其实是有一个00站位的,而且跟是否有默认值无关。

    12代表MYSQL_TYPE_DATETIME,跟MYSQL_TYPE_TIME情况相同。

    Fe代表MYSQL_TYPE_STRING,后面会跟两位metadata

    0f代表MYSQL_TYPE_VARCHAR,后面会跟两位metadata

    Fc代表MYSQL_TYPE_BLOB,后面会跟一位metadata

    之后的08代表metadata length:08 00 00 fe 28 80 3e 02

    首先08对应的MYSQL_TYPE_DOUBLE的metadata,代表sizeof(double),也就是8

    之后00 00就跟上文说的一样,是MYSQL_TYPE_TIME和MYSQL_TYPE_DATETIME的metadata。

    fe 28:在之后的insert语句的binlog中,可以看到有@5='abc' /* STRING(40) meta=65064 nullable=1 is_null=0 */的内容,其中的meta值便是0xfe28。

    80 3e: 与第六列中@6='abcdefghasdasdasd' /* VARSTRING(16000) meta=16000 nullable=1 is_null=0 */中的meta值一致。

    02:与第七列中@7='qwetrhyokxocm3479thcms9q25hdr9ker8thcfisdrhoc' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */中的meta值一致。

    78这一字节比较特殊,我们需要把它拆为01111000,后七位分别对应表的七个字段,其中1代表非空,0代表可为空。如果表的字段个数超过8的话就会多字节来表示。

    详细的对应关系可以到

    https://dev.mysql.com/doc/dev/mysql-server/8.0.0/classbinary__log_1_1Table__map__event.html

    上查看。

     

    八 WRITE_ROW_EVENT/DELETE_ROW_EVENT

    # at 843

    #180321 18:05:14 server id 1813309  end_log_pos 970 CRC32 0x6c6819d3  Write_rows: table id 224 flags: STMT_END_F

    MYSQL_TYPE_LONG

    BINLOG '

    Wi6yWhM9qxsAPQAAAEsDAAAAAOAAAAAAAAEABGFiY2QABHRlc3QABwMFERL+D/wICAAA/iiAPgJ4

    7Xq4mA==

    Wi6yWh49qxsAfwAAAMoDAAAAAOAAAAAAAAEAAgAH/4ABAAAAwnm/cRzHAUBasi5amZ9rIU4DYWJj

    EQBhYmNkZWZnaGFzZGFzZGFzZC0AcXdldHJoeW9reG9jbTM0Nzl0aGNtczlxMjVoZHI5a2VyOHRo

    Y2Zpc2RyaG9j0xlobA==

    '/*!*/;

    ### INSERT INTO `abcd`.`test`

    ### SET

    ###   @1=1 /* INT meta=0 nullable=0 is_null=0 */

    ###   @2=2.2222222220000000803 /* DOUBLE meta=8 nullable=0 is_null=0 */

    ###   @3=1521626714 /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */

    ###   @4='2018-03-21 18:05:14' /* DATETIME(0) meta=0 nullable=1 is_null=0 */

    ###   @5='abc' /* STRING(40) meta=65064 nullable=1 is_null=0 */

    ###   @6='abcdefghasdasdasd' /* VARSTRING(16000) meta=16000 nullable=1 is_null=0 */

    ###   @7='qwetrhyokxocm3479thcms9q25hdr9ker8thcfisdrhoc' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */

    在这里将插入和删除event放在一起说明,主要是两者除了Header中event type不同外,其余的内容完全一致(WRITE_ROW_EVENTv2的event type为0x1e,DELETE_ROW_EVENT v2的event type为0x20)。也就是说,只要替换下事件类型,就可以从insert变为delete。

    这类event MySQL 5.1.0 至5.1.15是v0版本,MySQL 5.1.15 至5.6.x为v1版本,MySQL 5.6.x之后为v2版本。

    Post-Header的长度为0x0a。

    e0 00 00 00 00 00 01 00 02 00

    e0 00 00 00 00 00:table id 224。

    01 00:flags,代表end of statement。

    0x0001 end of statement

    0x0002 no foreign key checks

    0x0004 no unique key checks

    0x0008 row has a columns

    02 00:extra_data_len,需要大于等于2,官网上写为extra row data的长度。

    Body部分:

    07:表有7个字段。

    ff:官网上解释为m_cols。

    80:1000000,跟TABLE_MAP_EVENT上面一致,只看后七位,表示哪些字段有写入null的情况。其中0代表写入,1代表null。该字段的长度为INT((N + 7) / 8) bytes。N代表列数。

    执行的语句是insert into test

    values(1,2.222222222,now(),now(),'abc','abcdefghasdasdasd','qwetrhyokxocm3479thcms9q25hdr9ker8thcfisdrhoc');

    01 00 00 00:首列是int,插入的值为1。

    c2 79 bf 71 1c c7 01 40:第二列是double,上面已经看到sizeof(double)为8。

    5a  b2 2e 5a :第三列值为1521626714,代表写入的now()的时间戳。

    99 9f 6b 21 4e:此处代表'2018-03-21 18:05:14',是datetime类型字段。

    03 61 62 63:第五列,首先03代表位数,之后便是abc字符串。

    11 00 61 62 63 64 65 66 67 68 61 73 64 61 73 64 61 73 64:第六列,首先0x11代表位数17位。之后出现了00,此处的00代表上一个字符串结束的标识,即将开始17位新的字符串。之后表示的便是abcdefghasdasdasd。

    第七列的字符串较长,就不再赘述。

    最后4位仍为校验位。

     

    九 UPDATE_ROW_EVENT

    # at 1207

    #180321 18:06:16 server id 1813309  end_log_pos 1369 CRC32 0x3cbdcd8d  Update_rows: table id 224 flags: STMT_END_F

    BINLOG '

    mC6yWhM9qxsAPQAAALcEAAAAAOAAAAAAAAEABGFiY2QABHRlc3QABwMFERL+D/wICAAA/iiAPgJ4

    5WTVNA==

    mC6yWh89qxsAogAAAFkFAAAAAOAAAAAAAAEAAgAH//+AAQAAAMJ5v3EcxwFAWrIuWpmfayFOA2Fi

    YxEAYWJjZGVmZ2hhc2Rhc2Rhc2QtAHF3ZXRyaHlva3hvY20zNDc5dGhjbXM5cTI1aGRyOWtlcjh0

    aGNmaXNkcmhvY4AKAAAAmDRG66iqCkBasi6YmZ9rIZAFYWJjZGUBAGEBAHONzb08

    '/*!*/;

    ### UPDATE `abcd`.`test`

    ### WHERE

    ###   @1=1 /* INT meta=0 nullable=0 is_null=0 */

    ###   @2=2.2222222220000000803 /* DOUBLE meta=8 nullable=0 is_null=0 */

    ###   @3=1521626714 /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */

    ###   @4='2018-03-21 18:05:14' /* DATETIME(0) meta=0 nullable=1 is_null=0 */

    ###   @5='abc' /* STRING(40) meta=65064 nullable=1 is_null=0 */

    ###   @6='abcdefghasdasdasd' /* VARSTRING(16000) meta=16000 nullable=1 is_null=0 */

    ###   @7='qwetrhyokxocm3479thcms9q25hdr9ker8thcfisdrhoc' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */

    ### SET

    ###   @1=10 /* INT meta=0 nullable=0 is_null=0 */

    ###   @2=3.3333300000000001262 /* DOUBLE meta=8 nullable=0 is_null=0 */

    ###   @3=1521626776 /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */

    ###   @4='2018-03-21 18:06:16' /* DATETIME(0) meta=0 nullable=1 is_null=0 */

    ###   @5='abcde' /* STRING(40) meta=65064 nullable=1 is_null=0 */

    ###   @6='a' /* VARSTRING(16000) meta=16000 nullable=1 is_null=0 */

    ###   @7='s' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */

    执行的sql为update test set a1=10,a2=3.33333,a3=now(),a4=now(),a5='abcde',a6='a',a7='s' where a1=1

    在解析的binlog文件中可以看出,binlog记载的是一条记录的完整的变化情况,即从前量变为后量的过程,并没有记录执行语句的条件等信息。

    Fixed data部分与上面所述完全一致

    Variable data部分:

    第二位ff后又接了一位ff,与上面一致,为前量后量的m_cols。

    之后的解析便与WRITE_ROW_EVENT中完全一致,唯一区别是记录了旧值和新值的两部分内容,此处请自行解析。

     

    十 XID_EVENT

    # at 970

    #180321 18:05:14 server id 1813309  end_log_pos 1001 CRC32 0xb73f8e76  Xid = 8096890

    COMMIT/*!*/;

    Xid event比较短,主要是事务提交的时候回在最后生成一个xid号,有这个便代表事务已经成功提交了。

    只有Body且一共就八位。

    7a 8c 7b 00 00 00 00 00,为8096890,代表xid号。

     

    十一 ROTATE_EVENT

    # at 1880

    #180321 18:06:37 server id 1813309  end_log_pos 1927 CRC32 0xe42f7923  Rotate to mysql-bin.000007  pos: 4

    SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;

    DELIMITER ;

    # End of log file

    /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;

    /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

    Binlog结束时的事件,与FORMAT_DESCRIPTION_EVENT一样仅有一个。

    Post-Header的长度为8位:

    04 00 00 00 00 00 00 00:代表第一个事件开始的位置,为4.

    Body:

    6d 79 73 71 6c 2d 62 69 6e 2e 30 30 30 30 30 37 代表mysql-bin.000007,为下个binlog的文件名。

    至此全部事件解析结束。

     

    在之后的文章中,我们会逐渐介绍binlog主从复制机制和相关的二次开发工作。

    展开全文
  • pcap文件格式及文件解析

    万次阅读 2017-09-19 17:01:11
    第一部分:PCAP包文件格式 一 基本格式:  文件头 数据包头数据报数据包头数据报...... 二、文件头:    文件头结构体  sturct pcap_file_header  {  DWORD magic;  DWORD version_major;  DWORD ve
    第一部分:PCAP包文件格式

    一 基本格式:

       文件头 数据包头数据报数据包头数据报......

    二、文件头:

       

       文件头结构体
     sturct pcap_file_header
     {
          DWORD           magic;
          DWORD           version_major;
          DWORD           version_minor;
          DWORD           thiszone;
          DWORD           sigfigs;
          DWORD           snaplen;
          DWORD           linktype;
     }
     
    说明:
     1、标识位:32位的,这个标识位的值是16进制的 0xa1b2c3d4。
    a 32-bit        magic number ,The magic number has the value hex a1b2c3d4.
    2、主版本号:16位, 默认值为0x2。
    a 16-bit          major version number,The major version number should have the value 2.
    3、副版本号:16位,默认值为0x04。
    a 16-bit          minor version number,The minor version number should have the value 4.
    4、区域时间:32位,实际上该值并未使用,因此可以将该位设置为0。
    a 32-bit          time zone offset field that actually not used, so you can (and probably should) just make it 0;
    5、精确时间戳:32位,实际上该值并未使用,因此可以将该值设置为0。
    a 32-bit          time stamp accuracy field tha not actually used,so you can (and probably should) just make it 0;
    6、数据包最大长度:32位,该值设置所抓获的数据包的最大长度,如果所有数据包都要抓获,将该值设置为65535;例如:想获取数据包的前64字节,可将该值设置为64。
    a 32-bit          snapshot length" field;The snapshot length field should be the maximum number of bytes perpacket that will be captured. If the entire packet is captured, make it 65535; if you only capture, for example, the first 64 bytes of the packet, make it 64.
    7、链路层类型:32位, 数据包的链路层包头决定了链路层的类型。
    a 32-bit link layer type field.The link-layer type depends on the type of link-layer header that the
    packets in the capture file have:
     
    以下是数据值与链路层类型的对应表
    0            BSD       loopback devices, except for later OpenBSD
    1            Ethernet, and Linux loopback devices   以太网类型,大多数的数据包为这种类型。
    6            802.5 Token Ring
    7            ARCnet
    8            SLIP
    9            PPP
    10          FDDI
    100        LLC/SNAP-encapsulated ATM
    101        raw IP, with no link
    102        BSD/OS SLIP
    103        BSD/OS PPP
    104        Cisco HDLC
    105        802.11
    108        later OpenBSD loopback devices (with the AF_value in network byte order)
    113               special Linux cooked capture
    114               LocalTalk

    三 packet数据包头:

     

    struct pcap_pkthdr
    {
    struct tim         ts;
          DWORD              caplen;
          DWORD              len;
    }
     
    struct tim
    {
    DWORD       GMTtime;
    DWORD       microTime
    }
    说明:
     
    1、时间戳,包括:
    秒计时:32位,一个UNIX格式的精确到秒时间值,用来记录数据包抓获的时间,记录方式是记录从格林尼治时间的1970年1月1日 00:00:00 到抓包时经过的秒数;
    微秒计时:32位, 抓取数据包时的微秒值。
    a time stamp, consisting of:
    a UNIX-format time-in-seconds when the packet was captured, i.e. the number of seconds since January 1,1970, 00:00:00 GMT (that GMT, *NOT* local time!);  
    the number of microseconds since that second when the packet was captured;

    Timestamp:时间戳高位,精确到seconds(值是自从January 1, 1970 00:00:00 GMT以来的秒数来记)
    Timestamp:时间戳低位,精确到microseconds (数据包被捕获时候的微秒(microseconds)数,是自ts-sec的偏移量)

     
    2、数据包长度:32位 ,标识所抓获的数据包保存在pcap文件中的实际长度,以字节为单位。
    a 32-bit value giving the number of bytes of packet data that were captured;

    Caplen:当前数据区的长度,即抓取到的数据帧长度,由此可以得到下一个数据帧的位置。

     
    3、数据包实际长度: 所抓获的数据包的真实长度,如果文件中保存不是完整的数据包,那么这个值可能要比前面的数据包长度的值大。
    a 32-bit value giving the actual length of the packet, in bytes (which may be greater than the previous number, if you are not saving the entire packet).

    Len:离线数据长度:网络中实际数据帧的长度,一般不大于caplen,多数情况下和Caplen数值相等。
    (例如,实际上有一个包长度是1500 bytes(Len=1500),但是因为在Global Header的snaplen=1300有限制,所以只能抓取这个包的前1300个字节,这个时候,Caplen = 1300 )

    四:packet数据:

      即Packet(通常就是链路层的数据帧)具体内容,长度就是Caplen,这个长度的后面,就是当前PCAP文件中存放的下一个Packet数据包,也就是说:PCAP文件里面并没有规定捕获的Packet数据包之间有什么间隔字符串,下一组数据在文件中的起始位置。我们需要靠第一个Packet包确定。最后,Packet数据部分的格式其实就是标准的网路协议格式了可以任何网络教材上找得到。

     

     

    五:举例分析

     

    图中最开始的绿色部分就是24 Bytes的Pcap Header,接下来红色的16 Bytes是第一个消息的Pcap Header。后面的红色的16 Bytes是第二个消息的Pcap Header。两块蓝色的部分分别是两个消息从链路层开始的完整内容。在网络上实际传输的数据包在数据链路层上每一个Packet开始都会有7个用于同步的字节和一个用于标识该Packet开始的字节,最后还会有四个CRC校验字节;而PCAP文件中会把前8个字节和最后4个校验自己去掉,因为这些信息对于协议分析是没有用的。

    用Wireshark打开一个PCAP数据包,每条消息的所有field会被解析出来并会按照协议层次折叠起来。第一层显示的是FrameXXX,这一级别没有对应某层具体的协议,而是对本条消息的一个概括性总结,描述了一些有用的概括性信息,比如从里面我们可以看到本条消息各种协议的层次关系,展开其它协议层之后对应的是该协议的各个域,如下图所示:

     

    第二部分:PCAP文件解析

    1、 pcap解析工具 Xplico

    Xplico 是一个从 pcap 文件中解析出IP流量数据的工具,可解析每个邮箱 (POP, IMAP, 和 SMTP 协议), 所有 HTTP 内容, VoIP calls (SIP) 等等

     

    2、 C语言实现PCAP文件分析

    实例一、

    实现步骤:
    1)用Wireshark软件抓包得到test.pcap文件
    2)程序:分析pcap文件头 -> 分析pcap_pkt头 -> 分析帧头 -> 分析ip头 -> 分析tcp头 -> 分析http信息

    #include<stdio.h>

    #include<string.h>

    #include<stdlib.h>

    #include<netinet/in.h>

    #include<time.h>

    #define BUFSIZE 10240

    #define STRSIZE 1024

    typedef long bpf_int32;

    typedef unsigned long bpf_u_int32;

    typedef unsigned short  u_short;

    typedef unsigned long u_int32;

    typedef unsigned short u_int16;

    typedef unsigned char u_int8;

    //pacp文件头结构体

    struct pcap_file_header

    {

    bpf_u_int32 magic;       /* 0xa1b2c3d4 */

    u_short version_major;   /* magjor Version 2 */

    u_short version_minor;   /* magjor Version 4 */

    bpf_int32 thiszone;      /* gmt to local correction */

    bpf_u_int32 sigfigs;     /* accuracy of timestamps */

    bpf_u_int32 snaplen;     /* max length saved portion of each pkt */

    bpf_u_int32 linktype;    /* data link type (LINKTYPE_*) */

    };

    //时间戳

    struct time_val

    {

    long tv_sec;         /* seconds 含义同 time_t 对象的值 */

    long tv_usec;        /* and microseconds */

    };

    //pcap数据包头结构体

    struct pcap_pkthdr

    {

    struct time_val ts;  /* time stamp */

    bpf_u_int32 caplen; /* length of portion present */

    bpf_u_int32 len;    /* length this packet (off wire) */

    };

    //数据帧头

    typedef struct FramHeader_t

    { //Pcap捕获的数据帧头

    u_int8 DstMAC[6]; //目的MAC地址

    u_int8 SrcMAC[6]; //MAC地址

    u_short FrameType;    //帧类型

    } FramHeader_t;

    //IP数据报头

    typedef struct IPHeader_t

    { //IP数据报头

    u_int8 Ver_HLen;       //版本+报头长度

    u_int8 TOS;            //服务类型

    u_int16 TotalLen;       //总长度

    u_int16 ID; //标识

    u_int16 Flag_Segment;   //标志+片偏移

    u_int8 TTL;            //生存周期

    u_int8 Protocol;       //协议类型

    u_int16 Checksum;       //头部校验和

    u_int32 SrcIP; //IP地址

    u_int32 DstIP; //目的IP地址

    } IPHeader_t;

    //TCP数据报头

    typedef struct TCPHeader_t

    { //TCP数据报头

    u_int16 SrcPort; //源端口

    u_int16 DstPort; //目的端口

    u_int32 SeqNO; //序号

    u_int32 AckNO; //确认号

    u_int8 HeaderLen; //数据报头的长度(4 bit) + 保留(4 bit)

    u_int8 Flags; //标识TCP不同的控制消息

    u_int16 Window; //窗口大小

    u_int16 Checksum; //校验和

    u_int16 UrgentPointer;  //紧急指针

    }TCPHeader_t;

    //

    void match_http(FILE *fp, char *head_str, char *tail_str, char *buf, int total_len); //查找 http 信息函数

    //

    int main()

    {

    struct pcap_file_header *file_header;

    struct pcap_pkthdr *ptk_header;

    IPHeader_t *ip_header;

    TCPHeader_t *tcp_header;

    FILE *fp, *output;

    int   pkt_offset, i=0;

    int ip_len, http_len, ip_proto;

    int src_port, dst_port, tcp_flags;

    char buf[BUFSIZE], my_time[STRSIZE];

    char src_ip[STRSIZE], dst_ip[STRSIZE];

    char  host[STRSIZE], uri[BUFSIZE];

    //初始化

    file_header = (struct pcap_file_header *)malloc(sizeof(struct pcap_file_header));

    ptk_header  = (struct pcap_pkthdr *)malloc(sizeof(struct pcap_pkthdr));

    ip_header = (IPHeader_t *)malloc(sizeof(IPHeader_t));

    tcp_header = (TCPHeader_t *)malloc(sizeof(TCPHeader_t));

    memset(buf, 0, sizeof(buf));

    //

    if((fp = fopen(“test.pcap”,”r”)) == NULL)

    {

    printf(“error: can not open pcap file\n”);

    exit(0);

    }

    if((output = fopen(“output.txt”,”w+”)) == NULL)

    {

    printf(“error: can not open output file\n”);

    exit(0);

    }

    //开始读数据包

    pkt_offset = 24; //pcap文件头结构 24个字节

    while(fseek(fp, pkt_offset, SEEK_SET) == 0) //遍历数据包

    {

    i++;

    //pcap_pkt_header 16 byte

    if(fread(ptk_header, 16, 1, fp) != 1) //pcap数据包头结构

    {

    printf(“\nread end of pcap file\n”);

    break;

    }

    pkt_offset += 16 + ptk_header->caplen;   //下一个数据包的偏移值

    strftime(my_time, sizeof(my_time), “%Y-%m-%d %T”, localtime(&(ptk_header->ts.tv_sec))); //获取时间

    // printf(“%d: %s\n”, i, my_time);

    //数据帧头 14字节

    fseek(fp, 14, SEEK_CUR); //忽略数据帧头

    //IP数据报头 20字节

    if(fread(ip_header, sizeof(IPHeader_t), 1, fp) != 1)

    {

    printf(“%d: can not read ip_header\n”, i);

    break;

    }

    inet_ntop(AF_INET, (void *)&(ip_header->SrcIP), src_ip, 16);

    inet_ntop(AF_INET, (void *)&(ip_header->DstIP), dst_ip, 16);

    ip_proto = ip_header->Protocol;

    ip_len = ip_header->TotalLen; //IP数据报总长度

    // printf(“%d:  src=%s\n”, i, src_ip);

    if(ip_proto != 0×06) //判断是否是 TCP 协议

    {

    continue;

    }

    //TCP头 20字节

    if(fread(tcp_header, sizeof(TCPHeader_t), 1, fp) != 1)

    {

    printf(“%d: can not read ip_header\n”, i);

    break;

    }

    src_port = ntohs(tcp_header->SrcPort);

    dst_port = ntohs(tcp_header->DstPort);

    tcp_flags = tcp_header->Flags;

    // printf(“%d:  src=%x\n”, i, tcp_flags);

    if(tcp_flags == 0×18) // (PSH, ACK) 3路握手成功后

    {

    if(dst_port == 80) // HTTP GET请求

    {

    http_len = ip_len – 40; //http 报文长度

    match_http(fp, “Host: “, “\r\n”, host, http_len); //查找 host 

    match_http(fp, “GET “, “HTTP”, uri, http_len); //查找 uri 

    sprintf(buf, “%d:  %s  src=%s:%d  dst=%s:%d  %s%s\r\n”, i, my_time, src_ip, src_port, dst_ip, dst_port, host, uri);

    //printf(“%s”, buf);

    if(fwrite(buf, strlen(buf), 1, output) != 1)

    {

    printf(“output file can not write”);

    break;

    }

    }

    }

    } // end while

    fclose(fp);

    fclose(output);

    return 0;

    }

    //查找 HTTP 信息

    void match_http(FILE *fp, char *head_str, char *tail_str, char *buf, int total_len)

    {

    int i;

    int http_offset;

    int head_len, tail_len, val_len;

    char head_tmp[STRSIZE], tail_tmp[STRSIZE];

    //初始化

    memset(head_tmp, 0, sizeof(head_tmp));

    memset(tail_tmp, 0, sizeof(tail_tmp));

    head_len = strlen(head_str);

    tail_len = strlen(tail_str);

    //查找 head_str

    http_offset = ftell(fp); //记录下HTTP报文初始文件偏移

    while((head_tmp[0] = fgetc(fp)) != EOF) //逐个字节遍历

    {

    if((ftell(fp) – http_offset) > total_len) //遍历完成

    {

    sprintf(buf, “can not find %s \r\n”, head_str);

    exit(0);

    }

    if(head_tmp[0] == *head_str) //匹配到第一个字符

    {

    for(i=1; i<head_len; i++) //匹配 head_str 的其他字符

    {

    head_tmp[i]=fgetc(fp);

    if(head_tmp[i] != *(head_str+i))

    break;

    }

    if(i == head_len) //匹配 head_str 成功,停止遍历

    break;

    }

    }

    // printf(“head_tmp=%s \n”, head_tmp);

    //查找 tail_str

    val_len = 0;

    while((tail_tmp[0] = fgetc(fp)) != EOF) //遍历

    {

    if((ftell(fp) – http_offset) > total_len) //遍历完成

    {

    sprintf(buf, “can not find %s \r\n”, tail_str);

    exit(0);

    }

    buf[val_len++] = tail_tmp[0]; //buf 存储 value 直到查找到 tail_str

    if(tail_tmp[0] == *tail_str) //匹配到第一个字符

    {

    for(i=1; i<tail_len; i++) //匹配 head_str 的其他字符

    {

    tail_tmp[i]=fgetc(fp);

    if(tail_tmp[i] != *(tail_str+i))

    break;

    }

    if(i == tail_len) //匹配 head_str 成功,停止遍历

    {

    buf[val_len-1] = 0; //清除多余的一个字符

    break;

    }

    }

    }

    // printf(“val=%s\n”, buf);

    fseek(fp, http_offset, SEEK_SET); //将文件指针 回到初始偏移

    }




    实例二

    //
    //  pcap.h
    //  pcaptest
    //
    //  Created by zc on 12-1-24.
    //  Copyright 2012年 __MyCompanyName__. All rights reserved.
    //
     
    #ifndef pcaptest_pcap_h
    #define pcaptest_pcap_h
     
    typedef unsigned int  bpf_u_int32;
    typedef unsigned short  u_short;
    typedef int bpf_int32;
     
    /*
     Pcap文件头24B各字段说明:
     Magic:4B:0x1A 2B 3C 4D:用来标示文件的开始
     Major:2B,0x02 00:当前文件主要的版本号     
     Minor:2B,0x04 00当前文件次要的版本号
     ThisZone:4B当地的标准时间;全零
     SigFigs:4B时间戳的精度;全零
     SnapLen:4B最大的存储长度    
     LinkType:4B链路类型
     常用类型:
      0            BSD loopback devices, except for later OpenBSD
     1            Ethernet, and Linux loopback devices
     6            802.5 Token Ring
     7            ARCnet
     8            SLIP
     9            PPP
     */
    typedef struct pcap_file_header {
    	bpf_u_int32 magic;
    	u_short version_major;
    	u_short version_minor;
    	bpf_int32 thiszone;    
    	bpf_u_int32 sigfigs;   
    	bpf_u_int32 snaplen;   
    	bpf_u_int32 linktype;  
    }pcap_file_header;
     
    /*
     Packet 包头和Packet数据组成
     字段说明:
     Timestamp:时间戳高位,精确到seconds     
     Timestamp:时间戳低位,精确到microseconds
     Caplen:当前数据区的长度,即抓取到的数据帧长度,由此可以得到下一个数据帧的位置。
     Len:离线数据长度:网络中实际数据帧的长度,一般不大于caplen,多数情况下和Caplen数值相等。
     Packet 数据:即 Packet(通常就是链路层的数据帧)具体内容,长度就是Caplen,这个长度的后面,就是当前PCAP文件中存放的下一个Packet数据包,也就 是说:PCAP文件里面并没有规定捕获的Packet数据包之间有什么间隔字符串,下一组数据在文件中的起始位置。我们需要靠第一个Packet包确定。
     */
     
    typedef struct  timestamp{
    	bpf_u_int32 timestamp_s;
    	bpf_u_int32 timestamp_ms;
    }timestamp;
     
    typedef struct pcap_header{
    	timestamp ts;
    	bpf_u_int32 capture_len;
    	bpf_u_int32 len;
     
    }pcap_header;
     
     
    void prinfPcapFileHeader(pcap_file_header *pfh);
    void printfPcapHeader(pcap_header *ph);
    void printPcap(void * data,size_t size);
     
    #endif
    

    //
    //  pcap.c
    //  pcaptest
    //
    //  Created by zc on 12-1-24.
    //  Copyright 2012年 __MyCompanyName__. All rights reserved.
    //
     
    #include <stdio.h>
    #include "pcap.h"
     
    void prinfPcapFileHeader(pcap_file_header *pfh){
    	if (pfh==NULL) {
    		return;
    	}
    	printf("=====================\n"
    		   "magic:0x%0x\n"
    		   "version_major:%u\n"
    		   "version_minor:%u\n"
    		   "thiszone:%d\n"
    		   "sigfigs:%u\n"
    		   "snaplen:%u\n"
    		   "linktype:%u\n"
    		   "=====================\n",
    		   pfh->magic,
    		   pfh->version_major,
    		   pfh->version_minor,
    		   pfh->thiszone,
    		   pfh->sigfigs,
    		   pfh->snaplen,
    		   pfh->linktype);
    }
     
    void printfPcapHeader(pcap_header *ph){
    	if (ph==NULL) {
    		return;
    	}
    	printf("=====================\n"
    		   "ts.timestamp_s:%u\n"
    		   "ts.timestamp_ms:%u\n"
    		   "capture_len:%u\n"
    		   "len:%d\n"
    		   "=====================\n",
    		   ph->ts.timestamp_s,
    		   ph->ts.timestamp_ms,
    		   ph->capture_len,
    		   ph->len);
     
     
    }
     
    void printPcap(void * data,size_t size){
    	unsigned  short iPos = 0;
    	//int * p = (int *)data;
    	//unsigned short* p = (unsigned short *)data;
    	if (data==NULL) {
    		return;
    	}
     
    	printf("\n==data:0x%x,len:%lu=========",data,size);
     
    	for (iPos=0; iPos < size/sizeof(unsigned short); iPos++) {
    		//printf(" %x ",(int)( * (p+iPos) ));
    		//unsigned short a = ntohs(p[iPos]);
     
    		unsigned short a = ntohs( *((unsigned short *)data + iPos ) );
    		if (iPos%8==0) printf("\n");
    		if (iPos%4==0) printf(" ");
     
    		printf("%04x",a);
     
     
    	}
    	/*
    	 for (iPos=0; iPos <= size/sizeof(int); iPos++) {
    		//printf(" %x ",(int)( * (p+iPos) ));
    		int a = ntohl(p[iPos]);
     
    		//int a = ntohl( *((int *)data + iPos ) );
    		if (iPos %4==0) printf("\n");
     
    		printf("%08x ",a);
     
     
    	}
    	 */
    	printf("\n============\n");

    //
    //  main.c
    //  pcaptest
    //
    //  Created by zc on 12-1-24.
    //  Copyright 2012年 __MyCompanyName__. All rights reserved.
    //
     
    #include <stdio.h>
    #include <arpa/inet.h>
    #include "pcap.h"
     
    #define PCAP_FILE "ping.pcap"
    #define MAX_ETH_FRAME 1514
    #define ERROR_FILE_OPEN_FAILED -1
    #define ERROR_MEM_ALLOC_FAILED -2
    #define ERROR_PCAP_PARSE_FAILED -3
     
     
    int main (int argc, const char * argv[])
    {
     
    	printf("sizeof:int %lu,unsigned int %lu,char %lu,unsigned char %lu,short:%lu,unsigned short:%lu\n",
    		    sizeof(int),sizeof(unsigned int),sizeof(char),sizeof(unsigned char),sizeof(short),sizeof(unsigned short));
     
    	pcap_file_header  pfh;
    	pcap_header  ph;
    	int count=0;
    	void * buff = NULL;
    	int readSize=0;
    	int ret = 0;
     
    	FILE *fp = fopen(PCAP_FILE, "rw");
     
    	if (fp==NULL) {
    		fprintf(stderr, "Open file %s error.",PCAP_FILE);
    		ret = ERROR_FILE_OPEN_FAILED;
    		goto ERROR;
    	}
     
    	fread(&pfh, sizeof(pcap_file_header), 1, fp);	
    	prinfPcapFileHeader(&pfh);
    	//fseek(fp, 0, sizeof(pcap_file_header));
     
    	buff = (void *)malloc(MAX_ETH_FRAME);
    	for (count=1; ; count++) {
    		memset(buff,0,MAX_ETH_FRAME);
    		//read pcap header to get a packet
    		//get only a pcap head count .
    		readSize=fread(&ph, sizeof(pcap_header), 1, fp);
    		if (readSize<=0) {
    			break;
    		}
    		printfPcapHeader(&ph);
     
     
    		if (buff==NULL) {
    			fprintf(stderr, "malloc memory failed.\n");
    			ret = ERROR_MEM_ALLOC_FAILED;
    			goto ERROR;
    		}
     
    		//get a packet contents.
    		//read ph.capture_len bytes.
    		readSize=fread(buff,1,ph.capture_len, fp);
    		if (readSize != ph.capture_len) {
    			free(buff);
    			fprintf(stderr, "pcap file parse error.\n");
    			ret = ERROR_PCAP_PARSE_FAILED;
    			goto ERROR;
    		}
    		printPcap(buff, ph.capture_len);
     
     
    		printf("===count:%d,readSize:%d===\n",count,readSize);
     
    		if (feof(fp) || readSize <=0 ) { 
    			break;
    		}
    	}
     
    ERROR:
    	//free
    	if (buff) {
    		free(buff);
    		buff=NULL;
    	} 
    	if (fp) {
    		fclose(fp);
    		fp=NULL;
    	}	
     
        return ret;
    }

    objects = main.o pcap.o
     
    pcaptest : $(objects)
    	gcc -o pcaptest  $(objects)
     
    main.o:pcap.h
    pcap.o:pcap.h
     
    .PHONY : clean
    clean :
    	rm pcaptest  $(objects)



    展开全文
  • Properties文件解析的坑

    千次阅读 2017-03-31 22:48:56
    记录最近遇到的jdk解析properties的坑
  • BVH文件格式解析

    万次阅读 多人点赞 2016-03-24 16:44:40
    BVH文件解析 简介简介
  • CAN通讯进阶-基于Python使用DBC文件解析CAN数据配置环境1.环境配置1.1安装CAN通讯需要的包1.2 安装kvaser_drivers以及Kvaser CanKing2.使用DBC文件解析CAN帧2.1DBC文件2.2本博客布局3.DBC文件的创建3.1创建DBC文件...
  • WAV文件格式解析

    万次阅读 2017-04-16 15:31:02
    本文通过翻译分析了WAV的文件格式。WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频信息资源,被Windows平台及其应用程序所...
  • pyc文件解析

    千次阅读 2013-08-23 17:29:24
    1. PyCodeObject与Pyc文件 通常认为,Python是一种解释性的语言,但是这种说法是不正确的,实际上,Python在执行时,首先会将.py文件中的源代码编译成Python的byte code(字节码),然后再由Python Virtual ...
  • 使用GLib提供的API解析类ini形式的配置文件。 本文转自https://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html 业余时间进行翻译,欢迎批评指正。   Functions GKeyFile * g_key_...
  • Nginx源码实现有一个很好的优点就是模块化,有点像面向对象语言的设计模式,实现“高内聚,松耦合”,这篇博客主要讲解模块的... 共有流程开始于解析 nginx 配置,这个过程集中在 ngx_init_cycle 函数中。 ngx_init_cy
  • 声明:博客代码只是实际项目的一部分,项目是前后端...在项目pom.xml文件中添加依赖(版本自选,注意不同的版本有可能会抱错,需根据自己的项目导入合适的版本): <!-- 解析excel --> <dependency> ...
  • 文件解析004-解析常量池元素

    万次阅读 2019-05-29 14:53:08
    我们在上篇文章介绍了创建constantPoolOop的过程,本文我们就来看看解析常量池元素的过程.这里涉及的方法为:ClassFileParser::parse_constant_pool_entries。代码如下: void ClassFileParser::parse_constant_pool_...
  • 2、Spring加载Dubbo配置文件解析过程 在 AbstractXmlApplicationContext 的loadBeanDefinitions方法中使用XmlBeanDefinitionReader. loadBeanDefinitions加载applicationProvider.xml文件 protected void ...
  • 然后新建db.abc.rangotec.com-v6.rev 文件,内容如下: $TTL 604800 @ IN SOA abc.rangotec.com. root.localhost. ( 1 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ...
  • was缓存jsp文件

    千次阅读 2016-03-04 17:47:02
    一个jsp文件叫a include了另外一个jsp文件b,这时候如果b文件改变了,重新部署到was时候,发现没有变化,还是保留着修改前的b。但是a文件本身的改变却是可以看到效果的。于是怀疑was缓存问题, 找到部署该项目文件...
  • Linux删除文件过程解析

    千次阅读 2019-03-16 15:29:35
    当我们执行rm命令删除一个文件的时候,在操作系统底层究竟会发生些什么事情呢,带着这个疑问,我们在Linux-3.10.104内核下对ext4文件系统下的rm操作进行分析。rm命令本身比较简单,但其在内核底层涉及到VFS操作、ext...
  • 1. 记录到普通文件或设备文件:: *.* /var/log/file.log # 绝对路径 *.* /dev/pts/0 测试 : logger -p local3.info ‘KadeFor is testing the rsyslog and logger ‘ logger 命令用于产生日志 2. 转发到远程...
  • core dump文件 core dump 又叫核心转储,是一个程序运行时的环境一个集合包,包含崩溃时的堆栈信息,是一个二进制文件,无法使用记事本打开,一般会在指定目录下生成一个core文件。core文件仅仅是一个内存映象,主要...
  • RTF 文件格式解析

    万次阅读 2018-01-31 12:44:37
    RTF 格式解析 例子内容 代码如下 {\rtf1\ansi\ansicpg936\deff0\deflang1033\deflangfe2052{\fonttbl{\f0\fnil\fcharset134 \'cb\'ce\'cc\'e5;}} {\colortbl ;\red255\green255\blue0;\red255\green0\blue0;...
  • 歌曲文件格式的 ruby​​ 解析器。 它可以像这样转换 chordpro 格式的歌曲...... {title: You Are My Sunshine} {c:Verse 1} [G]The other night dear as I lay sleeping [G7]I dreamed I [C]held you in my [G]...
  • return "You failed to upload " + i + " because the file was empty."; } } return "upload successful"; } (3)上传解析excel a、控制层 @PostMapping(value = "/batchImport") @...
  • gzip文件格式解析

    千次阅读 2014-03-14 15:47:21
    在windows下使用UltraEdit打开后缀的.gz的文件显示的数据格式如下: 2 bytes GZIP标志字节:0x1f, 0x8b (\037 \213) 1 byte 压缩方法: (0..7 reserved, 8 = deflate) 1 byte 标志位:  bit 0 set: 文件...
  • Jprofile解析dump文件使用详解

    千次阅读 2020-02-29 03:25:17
    需要调试的时候才需要加这个参数 注意:通过WAS生成的PHD文件dump不能分析出出问题的模板,因为PHD文件不包含对象的值内容,无法根据PHD文件找到出问题的模板,所以PHD文件没有太大的参考价值 3.2 dump文件分析 dump...
  • BVH文件 Java解析

    千次阅读 2016-03-25 14:49:24
    BVH文件解析器 简介 解析器 源码 谢谢简介对于BVH文件的格式,可以参考:BVH文件格式解析解析器使用java编写,具体使用方法: 导入google的gson包,用来生成json:如果是eclipse,下载jar导入到项目中:gson jar下载...
  • 设置的headers信息 发起请求后服务器端报错 ...出现了空指针异常的错误,咨询了开发应该是没有获取到文件信息,不太清楚具体错误的原因 在网上看到篇博客里面说不要在headers里面加自己的content-t...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 57,614
精华内容 23,045
关键字:

was文件解析