这个总结的很好,从前一个项目也用到这中技术
转自:http://blog.csdn.net/myarrow/article/details/9630377

1. 简介

使用ptrace向已运行进程中注入.so并执行相关函数,其中的“注入”二字的真正含义为:此.so被link到已运行进程(以下简称为:目标进程)空间中,从而.so中的函数在目标进程空间中有对应的地址,然后通过此地址便可在目标进程中进行调用。

到底是如何注入的呢?

本文实现方案为:在目标进程中,通过dlopen把需要注入的.so加载到目标进程的空间中。

2. 如何让目标进程执行dlopen加载.so?

显然,目标进程本来是没有实现通过dlopen来加载我们想注入的.so,为了实现此功能,我们需要目标进程执行一段我们实现的代码,此段代码的功能为通过dlopen来加载一个.so。

3. 【加载.so的实现代码】

加载需要注入的.so的实现代码如下所示:

  1. .global _dlopen_addr_s       @dlopen函数在目标进程中的地址     注:以下全局变化在C中可读写
  2. .global _dlopen_param1_s     @dlopen参数1<.so>在目标进程中的地址
  3. .global _dlopen_param2_s     @dlopen参数2在目标进程中的地址
  4. .global _dlsym_addr_s        @dlsym函数在目标进程中的地址
  5. .global _dlsym_param2_s      @dlsym参数2在目标进程中的地址,其实为函数名
  6. .global _dlclose_addr_s      @dlcose在目标进程中的地址
  7. .global _inject_start_s      @汇编代码段的起始地址
  8. .global _inject_end_s        @汇编代码段的结束地址
  9. .global _inject_function_param_s  @hook_init参数在目标进程中的地址
  10. .global _saved_cpsr_s        @保存CPSR,以便执行完hook_init之后恢复环境
  11. .global _saved_r0_pc_s       @保存r0-r15,以便执行完hook_init之后恢复环境
  12. .data
  13. _inject_start_s:
  14. @ debug loop
  15. 3:
  16. @sub r1, r1, #0
  17. @B 3b
  18. @ dlopen
  19. ldr r1, _dlopen_param2_s        @设置dlopen第二个参数, flag
  20. ldr r0, _dlopen_param1_s        @设置dlopen第一个参数 .so
  21. ldr r3, _dlopen_addr_s          @设置dlopen函数
  22. blx r3                          @执行dlopen函数,返回值位于r0中
  23. subs r4, r0, #0                 @把dlopen的返回值soinfo保存在r4中,以方便后面dlclose使用
  24. beq 2f
  25. @dlsym
  26. ldr r1, _dlsym_param2_s        @设置dlsym第二个参数,第一个参数已经在r0中了
  27. ldr r3, _dlsym_addr_s          @设置dlsym函数
  28. blx r3                         @执行dlsym函数,返回值位于r0中
  29. subs r3, r0, #0                @把返回值<hook_init在目标进程中的地址>保存在r3中
  30. beq 1f
  31. @call our function
  32. ldr r0, _inject_function_param_s  @设置hook_init第一个参数
  33. blx r3                            @执行hook_init
  34. subs r0, r0, #0
  35. beq 2f
  36. 1:
  37. @dlclose
  38. mov r0, r4                        @把dlopen的返回值设为dlcose的第一个参数
  39. ldr r3, _dlclose_addr_s           @设置dlclose函数
  40. blx r3                            @执行dlclose函数
  41. 2:
  42. @restore context
  43. ldr r1, _saved_cpsr_s             @恢复CPSR
  44. msr cpsr_cf, r1
  45. ldr sp, _saved_r0_pc_s            @恢复寄存器r0-r15
  46. ldmfd sp, {r0-pc}
  47. _dlopen_addr_s:                           @初始化_dlopen_addr_s
  48. .word 0x11111111
  49. _dlopen_param1_s:
  50. .word 0x11111111
  51. _dlopen_param2_s:
  52. .word 0x2                                 @RTLD_GLOBAL
  53. _dlsym_addr_s:
  54. .word 0x11111111
  55. _dlsym_param2_s:
  56. .word 0x11111111
  57. _dlclose_addr_s:
  58. .word 0x11111111
  59. _inject_function_param_s:
  60. .word 0x11111111
  61. _saved_cpsr_s:
  62. .word 0x11111111
  63. _saved_r0_pc_s:
  64. .word 0x11111111
  65. _inject_end_s:                     @代码结束地址
  66. .space 0x400, 0                    @代码段空间大小
  67. .end

4. 如何把【加载.so的实现代码】写入目标进程并启动执行?

为了把【加载.so的实现代码】写入目标进程,主要有以下两步操作:

1) 在目标进程中找到存放【加载.so的实现代码】的空间(通过mmap实现)

2) 把【加载.so的实现代码】写入目标进程指定的空间

3) 启动执行

4.1 在目标进程中找到存放【加载.so的实现代码】的空间

通过mmap来实现,其实现步骤如下:

1) 获取目标进程中mmap地址
   2) 把mmap参数据放入r0-r3,另外两个写入目标进程sp 
   3) pc设置为mmap地址,lr设置为0
   4) 把准备好的寄存器写入目标进程(PTRACE_SETREGS),并启动目标进程运行(PTRACE_CONT)
   5) 分配的内存首地址位于r0 (PTRACE_GETREGS)

4.2 为【加载.so的实现代码】中的全局变量赋值

1) 获取目标进程中dlopen地址并赋值给_dlopen_addr_s

2) 获取目标进程中dlsym地址并赋值给_dlsym_addr_s

3) 获取目标进程中dlclose地址并赋值给_dlclose_addr_s

4) 把需要加载的.so的路径放入 汇编代码中,并获取此路径在目标进程中的地址然后赋值给_dlopen_param1_s

5) 把需要加载的.so中的hook_init放入 汇编代码中,并获取此路径在目标进程中的地址然后赋值给_dlsym_param2_s

6) 把目标进程中的cpsr保存在_saved_cpsr_s中

7) 把目标进程中的r0-r15存入汇编代码中,并获取此变量在目标进程中的地址然后赋值给_saved_r0_pc_s

8) 通过ptrace( PTRACE_POKETEXT,...)把汇编代码写入目标进程中,起始地址由前面的mmap所分配

9) 把目标进程的pc设置为汇编代码的起始地址,然后调用ptrace(PTRACE_DETACH,...)以启动目标进程执行

5. 把汇编代码写入目标进程并执行的实现代码

5.1 主函数 writecode_to_targetproc

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <asm/ptrace.h>
  4. #include <asm/user.h>
  5. #include <asm/ptrace.h>
  6. #include <sys/wait.h>
  7. #include <sys/mman.h>
  8. #include <dlfcn.h>
  9. #include <dirent.h>
  10. #include <unistd.h>
  11. #include <string.h>
  12. #include <android/log.h>
  13. #include <sys/types.h>
  14. #include <sys/socket.h>
  15. #include <netinet/in.h>
  16. #include <sys/stat.h>
  17. #define MAX_PATH 0x100
  18. #define REMOTE_ADDR( addr, local_base, remote_base ) ( (uint32_t)(addr) + (uint32_t)(remote_base) - (uint32_t)(local_base) )
  19. /* write the assembler code into target proc,
  20. * and invoke it to execute
  21. */
  22. int writecode_to_targetproc(
  23. pid_t target_pid, // target process pid
  24. const char *library_path, // the path of .so that will be
  25. // upload to target process
  26. const char *function_name, // .so init fucntion e.g. hook_init
  27. void *param, // the parameters of init function
  28. size_t param_size ) // number of parameters
  29. {
  30. int ret = -1;
  31. void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr;
  32. void *local_handle, *remote_handle, *dlhandle;
  33. uint8_t *map_base;
  34. uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr;
  35. struct pt_regs regs, original_regs;
  36. // extern global variable in the assembler code
  37. extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, \
  38. _dlsym_addr_s, _dlsym_param2_s, _dlclose_addr_s, \
  39. _inject_start_s, _inject_end_s, _inject_function_param_s, \
  40. _saved_cpsr_s, _saved_r0_pc_s;
  41. uint32_t code_length;
  42. long parameters[10];
  43. // make target_pid as its child process and stop
  44. if ( ptrace_attach( target_pid ) == -1 )
  45. return -1;
  46. // get the values of 18 registers from target_pid
  47. if ( ptrace_getregs( target_pid, ®s ) == -1 )
  48. goto exit;
  49. // save original registers
  50. memcpy( &original_regs, ®s, sizeof(regs) );
  51. // get mmap address from target_pid
  52. // the mmap is the address of mmap in the cur process
  53. mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (void *)mmap );
  54. // set mmap parameters
  55. parameters[0] = 0;  // addr
  56. parameters[1] = 0x4000; // size
  57. parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC;  // prot
  58. parameters[3] =  MAP_ANONYMOUS | MAP_PRIVATE; // flags
  59. parameters[4] = 0; //fd
  60. parameters[5] = 0; //offset
  61. // execute the mmap in target_pid
  62. if ( ptrace_call( target_pid, (uint32_t)mmap_addr, parameters, 6, ®s ) == -1 )
  63. goto exit;
  64. // get the return values of mmap <in r0>
  65. if ( ptrace_getregs( target_pid, ®s ) == -1 )
  66. goto exit;
  67. // get the start address for assembler code
  68. map_base = (uint8_t *)regs.ARM_r0;
  69. // get the address of dlopen, dlsym and dlclose in target process
  70. dlopen_addr = get_remote_addr( target_pid, "/system/bin/linker", (void *)dlopen );
  71. dlsym_addr = get_remote_addr( target_pid, "/system/bin/linker", (void *)dlsym );
  72. dlclose_addr = get_remote_addr( target_pid, "/system/bin/linker", (void *)dlclose );
  73. // set the start address for assembler code in target process
  74. remote_code_ptr = map_base + 0x3C00;
  75. // set the start address for assembler code in cur process
  76. local_code_ptr = (uint8_t *)&_inject_start_s;
  77. // set global variable of assembler code
  78. // and these address is in the target process
  79. _dlopen_addr_s = (uint32_t)dlopen_addr;
  80. _dlsym_addr_s = (uint32_t)dlsym_addr;
  81. _dlclose_addr_s = (uint32_t)dlclose_addr;
  82. code_length = (uint32_t)&_inject_end_s - (uint32_t)&_inject_start_s;
  83. dlopen_param1_ptr = local_code_ptr + code_length + 0x20;
  84. dlsym_param2_ptr = dlopen_param1_ptr + MAX_PATH;
  85. saved_r0_pc_ptr = dlsym_param2_ptr + MAX_PATH;
  86. inject_param_ptr = saved_r0_pc_ptr + MAX_PATH;
  87. // save library path to assembler code global variable
  88. strcpy( dlopen_param1_ptr, library_path );
  89. _dlopen_param1_s = REMOTE_ADDR( dlopen_param1_ptr, local_code_ptr, remote_code_ptr );
  90. // save function name to assembler code global variable
  91. strcpy( dlsym_param2_ptr, function_name );
  92. _dlsym_param2_s = REMOTE_ADDR( dlsym_param2_ptr, local_code_ptr, remote_code_ptr );
  93. // save cpsr to assembler code global variable
  94. _saved_cpsr_s = original_regs.ARM_cpsr;
  95. // save r0-r15 to assembler code global variable
  96. memcpy( saved_r0_pc_ptr, &(original_regs.ARM_r0), 16 * 4 ); // r0 ~ r15
  97. _saved_r0_pc_s = REMOTE_ADDR( saved_r0_pc_ptr, local_code_ptr, remote_code_ptr );
  98. // save function parameters to assembler code global variable
  99. memcpy( inject_param_ptr, param, param_size );
  100. _inject_function_param_s = REMOTE_ADDR( inject_param_ptr, local_code_ptr, remote_code_ptr );
  101. // write the assembler code into target process
  102. // now the values of global variable is in the target process space
  103. ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 );
  104. memcpy( ®s, &original_regs, sizeof(regs) );
  105. // set sp and pc to the start address of assembler code
  106. regs.ARM_sp = (long)remote_code_ptr;
  107. regs.ARM_pc = (long)remote_code_ptr;
  108. // set registers for target process
  109. ptrace_setregs( target_pid, ®s );
  110. // make the target_pid is not a child process of cur process
  111. // and make target_pid continue to running
  112. ptrace_detach( target_pid );
  113. // now finish it successfully
  114. ret = 0;
  115. exit:
  116. return ret;
  117. }

5.2 attach目标进程ptrace_attach

  1. int ptrace_attach( pid_t pid )
  2. {
  3. // after PTRACE_ATTACH, the proc<pid> will stop
  4. if ( ptrace( PTRACE_ATTACH, pid, NULL, 0  ) < 0 )
  5. {
  6. perror( "ptrace_attach" );
  7. return -1;
  8. }
  9. // wait proc<pid> stop
  10. waitpid( pid, NULL, WUNTRACED );
  11. // after PTRACE_SYSCALL, the proc<pid> will continue,
  12. // but when exectue sys call function, proc<pid> will stop
  13. if ( ptrace( PTRACE_SYSCALL, pid, NULL, 0  ) < 0 )
  14. {
  15. perror( "ptrace_syscall" );
  16. return -1;
  17. }
  18. // wait proc<pid> stop
  19. waitpid( pid, NULL, WUNTRACED );
  20. return 0;
  21. }

5.3 获取目标进程寄存器值ptrace_getregs

  1. int ptrace_getregs( pid_t pid, struct pt_regs* regs )
  2. {
  3. if ( ptrace( PTRACE_GETREGS, pid, NULL, regs ) < 0 )
  4. {
  5. perror( "ptrace_getregs: Can not get register values" );
  6. return -1;
  7. }
  8. return 0;
  9. }

5.4 获取目标进程中指定模块中指定函数的地址get_remote_addr

  1. /* find the start address of module whose name is module_name
  2. * in the designated process
  3. */
  4. void* get_module_base( pid_t pid, const char* module_name )
  5. {
  6. FILE *fp;
  7. long addr = 0;
  8. char *pch;
  9. char filename[32];
  10. char line[1024];
  11. if ( pid < 0 )
  12. {
  13. /* self process */
  14. snprintf( filename, sizeof(filename), "/proc/self/maps", pid );
  15. }
  16. else
  17. {
  18. snprintf( filename, sizeof(filename), "/proc/%d/maps", pid );
  19. }
  20. fp = fopen( filename, "r" );
  21. if ( fp != NULL )
  22. {
  23. while ( fgets( line, sizeof(line), fp ) )
  24. {
  25. if ( strstr( line, module_name ) )
  26. {
  27. pch = strtok( line, "-" );
  28. addr = strtoul( pch, NULL, 16 );
  29. if ( addr == 0x8000 )
  30. addr = 0;
  31. break;
  32. }
  33. }
  34. fclose( fp ) ;
  35. }
  36. return (void *)addr;
  37. }
  38. void* get_remote_addr( pid_t target_pid, const char* module_name, void* local_addr )
  39. {
  40. void* local_handle, *remote_handle;
  41. local_handle = get_module_base( -1, module_name );
  42. remote_handle = get_module_base( target_pid, module_name );
  43. return (void *)( (uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle );
  44. }

5.5 在目标进程中执行指定函数ptrace_call

  1. int ptrace_call( pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs )
  2. {
  3. uint32_t i;
  4. // put the first 4 parameters into r0-r3
  5. for ( i = 0; i < num_params && i < 4; i ++ )
  6. {
  7. regs->uregs[i] = params[i];
  8. }
  9. // push remained params into stack
  10. if ( i < num_params )
  11. {
  12. regs->ARM_sp -= (num_params - i) * sizeof(long) ;
  13. ptrace_writedata( pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long) );
  14. }
  15. // set the pc to func <e.g: mmap> that will be executed
  16. regs->ARM_pc = addr;
  17. if ( regs->ARM_pc & 1 )
  18. {
  19. /* thumb */
  20. regs->ARM_pc &= (~1u);
  21. regs->ARM_cpsr |= CPSR_T_MASK;
  22. }
  23. else
  24. {
  25. /* arm */
  26. regs->ARM_cpsr &= ~CPSR_T_MASK;
  27. }
  28. // when finish this func, pid will stop
  29. regs->ARM_lr = 0;
  30. // set the regsister and start to execute
  31. if ( ptrace_setregs( pid, regs ) == -1
  32. || ptrace_continue( pid ) == -1 )
  33. {
  34. return -1;
  35. }
  36. // wait pid finish work and stop
  37. waitpid( pid, NULL, WUNTRACED );
  38. return 0;
  39. }

5.6 把代码写入目标进程指定地址ptrace_writedata

  1. int ptrace_writedata( pid_t pid, uint8_t *dest, uint8_t *data, size_t size )
  2. {
  3. uint32_t i, j, remain;
  4. uint8_t *laddr;
  5. union u {
  6. long val;
  7. char chars[sizeof(long)];
  8. } d;
  9. j = size / 4;
  10. remain = size % 4;
  11. laddr = data;
  12. for ( i = 0; i < j; i ++ )
  13. {
  14. memcpy( d.chars, laddr, 4 );
  15. ptrace( PTRACE_POKETEXT, pid, dest, d.val );
  16. dest  += 4;
  17. laddr += 4;
  18. }
  19. if ( remain > 0 )
  20. {
  21. d.val = ptrace( PTRACE_PEEKTEXT, pid, dest, 0 );
  22. for ( i = 0; i < remain; i ++ )
  23. {
  24. d.chars[i] = *laddr ++;
  25. }
  26. ptrace( PTRACE_POKETEXT, pid, dest, d.val );
  27. }
  28. return 0;
  29. }

5.7 设置目标进程寄存器ptrace_setregs

  1. int ptrace_setregs( pid_t pid, struct pt_regs* regs )
  2. {
  3. if ( ptrace( PTRACE_SETREGS, pid, NULL, regs ) < 0 )
  4. {
  5. perror( "ptrace_setregs: Can not set register values" );
  6. return -1;
  7. }
  8. return 0;
  9. }

5.8 detach目标进程ptrace_detach

  1. int ptrace_detach( pid_t pid )
  2. {
  3. if ( ptrace( PTRACE_DETACH, pid, NULL, 0 ) < 0 )
  4. {
  5. perror( "ptrace_detach" );
  6. return -1;
  7. }
  8. return 0;
  9. }

6.  需要被加载的.so

需要被加载的.so例子程序如下,其目的是替换目标进程libapp.so中的strlen函数。其主要实现见hook_init。

  1. int g_isInit = 0;
  2. pthread_t g_hThread;
  3. __attribute__((visibility("default"))) void hook_init( char *args )
  4. {
  5. if( g_isInit == 1 )
  6. {
  7. printf("i am already in!");
  8. return;
  9. }
  10. void* soHandle = NULL;
  11. // the libapp.so is a .so of target process, and it call strcmp
  12. soHandle  = dlopen( "libapp.so", RTLD_GLOBAL );
  13. if( soHandle != NULL )
  14. {
  15. g_realstrcmp = NULL;
  16. replaceFunc( soHandle, "strcmp", my_strcmp, (void**)&g_realstrcmp );
  17. int ret = pthread_create( &g_hThread, NULL, my_thread, NULL );
  18. if( ret != 0 )
  19. {
  20. printf("create thread error:%d", ret );
  21. }
  22. g_isInit = 1;
  23. }
  24. }

6.1 替换函数replaceFunc

  1. // replace function of libapp.so
  2. // e.g: replace strcmp of libapp.so with my_strcmp
  3. void replaceFunc(void *handle,const char *name, void* pNewFun, void** pOldFun )
  4. {
  5. if(!handle)
  6. return;
  7. soinfo *si = (soinfo*)handle;
  8. Elf32_Sym *symtab = si->symtab;
  9. const char *strtab = si->strtab;
  10. Elf32_Rel *rel = si->plt_rel;
  11. unsigned count = si->plt_rel_count;
  12. unsigned idx;
  13. // these external functions that are called by libapp.so
  14. // is in the plt_rel
  15. for(idx=0; idx<count; idx++)
  16. {
  17. unsigned type = ELF32_R_TYPE(rel->r_info);
  18. unsigned sym = ELF32_R_SYM(rel->r_info);
  19. unsigned reloc = (unsigned)(rel->r_offset + si->base);
  20. char *sym_name = (char *)(strtab + symtab[sym].st_name);
  21. if(strcmp(sym_name, name)==0)
  22. {
  23. *pOldFun = (void *)*((unsigned*)reloc);
  24. *((unsigned*)reloc)= pNewFun;
  25. break;
  26. }
  27. rel++;
  28. }
  29. }

6.2 新函数及其它函数

  1. // global function variable, save the address of strcmp of libapp.so
  2. int (*g_realstrcmp)(const char *s1, const char *s2);
  3. // my strcmp function
  4. int my_strcmp(const char *s1, const char *s2)
  5. {
  6. if( g_realstrcmp != NULL )
  7. {
  8. int nRet = g_realstrcmp( s1, s2 );
  9. printf("***%s: s1=%s, s2=%s\n",__FUNCTION__, s1, s2 );
  10. return nRet;
  11. }
  12. return -1;
  13. }
  14. // create a thread
  15. void* my_thread( void* pVoid )
  16. {
  17. int sock;
  18. sock = socket(AF_INET, SOCK_DGRAM, 0);
  19. if( sock < -1 )
  20. {
  21. printf("create socket failed!\n");
  22. return 0;
  23. }
  24. struct sockaddr_in addr_serv;
  25. int len;
  26. memset(&addr_serv, 0, sizeof(struct sockaddr_in));
  27. addr_serv.sin_family = AF_INET;
  28. addr_serv.sin_port = htons(9999);
  29. addr_serv.sin_addr.s_addr = inet_addr("127.0.0.1");
  30. len = sizeof(addr_serv);
  31. int flags = fcntl( sock, F_GETFL, 0);
  32. fcntl( sock, F_SETFL, flags | O_NONBLOCK);
  33. int nPreState = -1;
  34. unsigned char data=0;
  35. while( 1 )
  36. {
  37. data++;
  38. sendto( sock, &data,  sizeof( data ), 0, (struct sockaddr *)&addr_serv, sizeof( addr_serv ) );
  39. usleep( 30000 );
  40. }
  41. }

使用ptrace向已运行进程中注入.so并执行相关函数的更多相关文章

  1. 使用ptrace向已运行进程中注入.so并执行相关函数(转)

    1. 简介 使用ptrace向已运行进程中注入.so并执行相关函数,其中的“注入”二字的真正含义为:此.so被link到已运行进程(以下简称为:目标进程)空间中,从而.so中的函数在目标进程空间中有对 ...

  2. Linux查询已开启文件或已运行进程开启之文件fuser,lsof,pidof

    fuser:藉由文件(或文件系统)找出正在使用该文件的程序 [root@www ~]# fuser [-umv] [-k [i] [-signal]] file/dir 选项与参数: -u :除了进程 ...

  3. 2.添加键盘钩子。向进程中注入dll

    学习笔记 1.首先要建立mfc的动态链接库.在def文件中放入要导出的函数名. 2.添加函数如下 //安装钩子 //HHOOK SetWindowsHookEx( // int idHook,//钩子 ...

  4. 将dll文件注入到其他进程中的一种新方法

    http://www.45it.com/windowszh/201212/33946.htm http://www.hx95.cn/Article/OS/201212/65095.html 我们知道将 ...

  5. INNO setup安装卸载钱判断进程中是否在运行总结

    1.安装前判断进程中是否有程序在运行. [files] ; 安装前判断进程,dll文件放在inno的安装目录中Source: compiler:psvince.dll; Flags: dontcopy ...

  6. linux中应用程序main函数中没有开辟进程的,它应该在那个进程中运行呢?

    1.main函数是一个进程还是一个线程? 不知道你是用c创建的,还是用java创建的. 因为它们都是以main()做为入口开始运行的. 是一个线程,同时还是一个进程. 在现在的操作系统中,都是多线程的 ...

  7. 错误 : 资产文件“项目\obj\project.assets.json”没有“.NETCoreApp,Version=v2.0”的目标。确保已运行还原,且“netcoreapp2.0”已包含在项目的 TargetFrameworks 中。

    升级 vs201715.6.3之后发布出现 错误 : 资产文件“项目\obj\project.assets.json”没有“.NETCoreApp,Version=v2.0”的目标.确保已运行还原,且 ...

  8. pgrep---以名称为依据从运行进程队列中查找进程

    pgrep命令以名称为依据从运行进程队列中查找进程,并显示查找到的进程id.每一个进程ID以一个十进制数表示,通过一个分割字符串和下一个ID分开,默认的分割字符串是一个新行.对于每个属性选项,用户可以 ...

  9. 在Linux中通过Top运行进程查找最高内存和CPU使用率

    按内存使用情况查找前15个进程,在批处理模式下为"top" 使用top命令查看有关当前状态,系统使用情况的更详细信息:正常运行时间,负载平均值和进程总数. 分类:Linux命令操作 ...

随机推荐

  1. Android Activity切换动画overridePendingTransition

    Activity在切换或者是退出的时候能够使用渐入,滑动,缩放等动态效果.使用的就是方法overridePendingTransition,能够直在Activity其中直接调用. overridePe ...

  2. 【WinForm】使用NSIS发布程序

    简介 NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序.它提供了安装.卸载.系统设置.文件解压缩等功能 使用 以下是 ...

  3. linux自己带的apache重新启动

    如果是linux自己带的apache的话就使用命令 service httpd start 启动 service httpd stop 关闭 service httpd restart 重新启动 如果 ...

  4. Python2.7.3移除字符串中重复字符(一)

    移除重复字符很简单,这里是最笨,也是最简单的一种.问题关键是理解排序的意义: # coding=utf-8 #learning at jeapedu in 2013/10/26 #移除给定字符串中重复 ...

  5. uva216 c++回溯法

    因为题目要求最多8台电脑,所以可以枚举全排列,然后依次计算距离进行比较,枚举量8!=40320并不大,但这种方法不如回溯法好,当数据再大一些枚举就显得笨拙了,所以这个题我用回溯法做的,回溯有一个好处是 ...

  6. 如何在Ubuntu 13.04中升级到 GNOME 3.8

    如何在Ubuntu 13.04中升级到 GNOME 3.8 添加 GNOME 3 PPA(Personal Package Archives) 在你进一步浏览之前,确认你正在运行的是Ubuntu 13 ...

  7. Metadata Lock原理5

    [MySQL] 之一2015-09-05 15:46:51 分类: MySQL 一 简介 和MySQL打交道比较多的朋友,肯定遇到过 "Waiting for table metadata ...

  8. SparkSQLTest.scala

    /** * Created by root on 9/7/15. */ import org.apache.spark.SparkConf import org.apache.spark.SparkC ...

  9. 判断脚本,图片,CSS,iframe等是否加载完成

    1.图片 <img id="MyImg" src="src"/>jquery实现:$("#MyImg").load(functi ...

  10. How to Setup Replicated LevelDB Persistence in Apache ActiveMQ 5.9--转载

    原文地址:https://simplesassim.wordpress.com/2013/11/03/how-to-setup-replicated-leveldb-persistence-in-ap ...