黑客技巧谈之创建高权限进程的方法

写这个初衷是为了让 Windows 任务管理器可以结束掉一些服务和僵死进程,用 pslist/pskill 之类工具无法获得象任务管理器那样丰富的信息,还得来回切换,麻烦的很。最初想写个驱动监视任务管理器运行,使用 SYSTEM 进程 TOKEN 替换来达到目的。

后来觉得通用性不好,就改用了这种方法。此方法还可使Regedit查看、编辑 SAM 等注册表键,何乐而不为。






         
         
          
          // wssrun taskmgr.exe 
// wssrun regedit.exe 
// 


#include 
#include 
#include 
#include 
#include 
#include 
#include 

#pragma comment(lib,"Shlwapi.lib") 


/ 
// 函数类型 :自定义工具函数 
// 函数模块 : 
 
// 功能 :提升当前进程权限 
// 注意 : 
/ 
// 作者 : sinister 
// 发布版本 : 1.00.00 
// 发布日期 : 2006.2.09 
/ 
// 重   大   修   改   历   史 
 
// 修改者 : 
// 修改日期 : 
// 修改内容 : 
/ 

BOOL 
EnableDebugPriv( LPCTSTR szPrivilege ) 
{ 
HANDLE hToken; 
LUID sedebugnameValue; 
TOKEN_PRIVILEGES tkp; 

if ( !OpenProcessToken( GetCurrentProcess(), 
                TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
                &hToken ) ) 
{ 
  return FALSE; 
} 
if ( !LookupPrivilegeValue( NULL, szPrivilege, &sedebugnameValue ) ) 
{ 
  CloseHandle( hToken ); 
  return FALSE; 
} 

tkp.PrivilegeCount = 1; 
tkp.Privileges[0].Luid = sedebugnameValue; 
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 

if ( !AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL ) ) 
{ 
  CloseHandle( hToken ); 
  return FALSE; 
} 

return TRUE; 
} 

/ 
// 函数类型 :自定义工具函数 
// 函数模块 : 
 
// 功能 :通过指定进程名得到其进程 ID 
// 注意 : 
/ 
// 作者 : sinister 
// 发布版本 : 1.00.00 
// 发布日期 : 2006.2.09 
/ 
// 重   大   修   改   历   史 
 
// 修改者 : 
// 修改日期 : 
// 修改内容 : 
/ 

DWORD 
GetProcessId( LPCTSTR szProcName ) 
{ 
PROCESSENTRY32 pe;   
DWORD dwPid; 
DWORD dwRet; 
BOOL bFound = FALSE; 

// 
// 通过 TOOHLP32 函数枚举进程 
// 

HANDLE hSP = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); 
if ( hSP ) 
{ 
  pe.dwSize = sizeof( pe ); 

  for ( dwRet = Process32First( hSP, &pe ); 
      dwRet; 
      dwRet = Process32Next( hSP, &pe ) ) 
  { 
    // 
    // 使用 StrCmpNI 比较字符传,可忽略大小写 
    // 
    if ( StrCmpNI( szProcName, pe.szExeFile, strlen( szProcName ) ) == 0 ) 
    { 
    dwPid = pe.th32ProcessID; 
    bFound = TRUE; 
    break; 
    } 
  } 

  CloseHandle( hSP ); 

  if ( bFound == TRUE ) 
  { 
    return dwPid; 
  } 
} 

return NULL; 
} 

/ 
// 函数类型 :自定义工具函数 
// 函数模块 : 
 
// 功能 : 创建具有高权限的进程 
// 注意 : 
/ 
// 作者 : sinister 
// 发布版本 : 1.00.00 
// 发布日期 : 2006.2.09 
/ 
// 重   大   修   改   历   史 
 
// 修改者 : 
// 修改日期 : 
// 修改内容 : 
/ 

BOOL 
CreateSystemProcess( LPTSTR szProcessName ) 
{ 
HANDLE hProcess; 
HANDLE hToken, hNewToken; 
DWORD dwPid; 

PACL pOldDAcl = NULL; 
PACL pNewDAcl = NULL; 
BOOL bDAcl; 
BOOL bDefDAcl; 
DWORD dwRet; 

PACL pSacl = NULL; 
PSID pSidOwner = NULL; 
PSID pSidPrimary = NULL; 
DWORD dwAclSize = 0; 
DWORD dwSaclSize = 0; 
DWORD dwSidOwnLen = 0; 
DWORD dwSidPrimLen = 0; 

DWORD dwSDLen; 
EXPLICIT_ACCESS ea; 
PSECURITY_DESCRIPTOR pOrigSd = NULL; 
PSECURITY_DESCRIPTOR pNewSd = NULL; 

STARTUPINFO si; 
PROCESS_INFORMATION pi; 

BOOL bError; 

if ( !EnableDebugPriv( "SeDebugPrivilege" ) ) 
{ 
  printf( "EnableDebugPriv() to failed!/n" ); 

  bError = TRUE; 
  goto Cleanup; 
} 

// 
// 选择 WINLOGON 进程 
// 
if ( ( dwPid = GetProcessId( "WINLOGON.EXE" ) ) == NULL ) 
{ 
  printf( "GetProcessId() to failed!/n" );   

  bError = TRUE; 
  goto Cleanup; 
} 

hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, dwPid ); 
if ( hProcess == NULL ) 
{ 
  printf( "OpenProcess() = %d/n", GetLastError() );   

  bError = TRUE; 
  goto Cleanup; 
} 

if ( !OpenProcessToken( hProcess, READ_CONTROL | WRITE_DAC, &hToken ) ) 
{ 
  printf( "OpenProcessToken() = %d/n", GetLastError() ); 

  bError = TRUE; 
  goto Cleanup; 
} 

// 
// 设置 ACE 具有所有访问权限 
// 
ZeroMemory( &ea, sizeof( EXPLICIT_ACCESS ) ); 
BuildExplicitAccessWithName( &ea, 
                    "Everyone", 
                    TOKEN_ALL_ACCESS, 
                    GRANT_ACCESS, 
                    0 ); 

if ( !GetKernelObjectSecurity( hToken, 
                      DACL_SECURITY_INFORMATION, 
                      pOrigSd, 
                      0, 
                      &dwSDLen ) ) 
{ 
  // 
  // 第一次调用给出的参数肯定返回这个错误,这样做的目的是 
  // 为了得到原安全描述符 pOrigSd 的长度 
  // 
  if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) 
  { 
    pOrigSd = ( PSECURITY_DESCRIPTOR ) HeapAlloc( GetProcessHeap(), 
                                  HEAP_ZERO_MEMORY, 
                                  dwSDLen ); 
    if ( pOrigSd == NULL ) 
    { 
    printf( "Allocate pSd memory to failed!/n" ); 

    bError = TRUE; 
    goto Cleanup; 
    } 

    // 
    // 再次调用才正确得到安全描述符 pOrigSd 
    // 
    if ( !GetKernelObjectSecurity( hToken, 
                        DACL_SECURITY_INFORMATION, 
                        pOrigSd, 
                        dwSDLen, 
                        &dwSDLen ) ) 
    { 
    printf( "GetKernelObjectSecurity() = %d/n", GetLastError() ); 
    bError = TRUE; 
    goto Cleanup; 
    } 
  } 
  else 
  { 
    printf( "GetKernelObjectSecurity() = %d/n", GetLastError() ); 
    bError = TRUE; 
    goto Cleanup; 
  } 
} 

// 
// 得到原安全描述符的访问控制列表 ACL 
// 
if ( !GetSecurityDescriptorDacl( pOrigSd, &bDAcl, &pOldDAcl, &bDefDAcl ) ) 
{ 
  printf( "GetSecurityDescriptorDacl() = %d/n", GetLastError() ); 

  bError = TRUE; 
  goto Cleanup; 
} 

// 
// 生成新 ACE 权限的访问控制列表 ACL 
// 
dwRet = SetEntriesInAcl( 1, &ea, pOldDAcl, &pNewDAcl ); 
if ( dwRet != ERROR_SUCCESS ) 
{ 
  printf( "SetEntriesInAcl() = %d/n", GetLastError() ); 
  pNewDAcl = NULL; 

  bError = TRUE; 
  goto Cleanup; 
} 

if ( !MakeAbsoluteSD( pOrigSd, 
                pNewSd, 
                &dwSDLen, 
                pOldDAcl, 
                &dwAclSize, 
                pSacl, 
                &dwSaclSize, 
                pSidOwner, 
                &dwSidOwnLen, 
                pSidPrimary, 
                &dwSidPrimLen ) ) 
{ 
  // 
  // 第一次调用给出的参数肯定返回这个错误,这样做的目的是 
  // 为了创建新的安全描述符 pNewSd 而得到各项的长度 
  // 
  if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) 
  { 
    pOldDAcl = ( PACL ) HeapAlloc( GetProcessHeap(), 
                        HEAP_ZERO_MEMORY, 
                        dwAclSize ); 
    pSacl = ( PACL ) HeapAlloc( GetProcessHeap(), 
                      HEAP_ZERO_MEMORY, 
                      dwSaclSize ); 
    pSidOwner = ( PSID ) HeapAlloc( GetProcessHeap(), 
                        HEAP_ZERO_MEMORY, 
                        dwSidOwnLen ); 
    pSidPrimary = ( PSID ) HeapAlloc( GetProcessHeap(), 
                          HEAP_ZERO_MEMORY, 
                          dwSidPrimLen ); 
    pNewSd = ( PSECURITY_DESCRIPTOR ) HeapAlloc( GetProcessHeap(), 
                                  HEAP_ZERO_MEMORY, 
                                  dwSDLen ); 

    if ( pOldDAcl == NULL || 
      pSacl == NULL || 
      pSidOwner == NULL || 
      pSidPrimary == NULL || 
      pNewSd == NULL ) 
    { 
    printf( "Allocate SID or ACL to failed!/n" ); 

    bError = TRUE; 
    goto Cleanup; 
    } 

    // 
    // 再次调用才可以成功创建新的安全描述符 pNewSd 
    // 但新的安全描述符仍然是原访问控制列表 ACL 
    // 
    if ( !MakeAbsoluteSD( pOrigSd, 
                  pNewSd, 
                  &dwSDLen, 
                  pOldDAcl, 
                  &dwAclSize, 
                  pSacl, 
                  &dwSaclSize, 
                  pSidOwner, 
                  &dwSidOwnLen, 
                  pSidPrimary, 
                  &dwSidPrimLen ) ) 
    { 
    printf( "MakeAbsoluteSD() = %d/n", GetLastError() ); 

    bError = TRUE; 
    goto Cleanup; 
    } 
  } 
  else 
  { 
    printf( "MakeAbsoluteSD() = %d/n", GetLastError() ); 

    bError = TRUE; 
    goto Cleanup; 
  } 
} 

// 
// 将具有所有访问权限的访问控制列表 pNewDAcl 加入到新的 
// 安全描述符 pNewSd 中 
// 
if ( !SetSecurityDescriptorDacl( pNewSd, bDAcl, pNewDAcl, bDefDAcl ) ) 
{ 
  printf( "SetSecurityDescriptorDacl() = %d/n", GetLastError() ); 

  bError = TRUE; 
  goto Cleanup; 
} 

// 
// 将新的安全描述符加到 TOKEN 中 
// 
if ( !SetKernelObjectSecurity( hToken, DACL_SECURITY_INFORMATION, pNewSd ) ) 
{ 
  printf( "SetKernelObjectSecurity() = %d/n", GetLastError() ); 

  bError = TRUE; 
  goto Cleanup; 
} 

// 
// 再次打开 WINLOGON 进程的 TOKEN,这时已经具有所有访问权限 
// 
if ( !OpenProcessToken( hProcess, TOKEN_ALL_ACCESS, &hToken ) ) 
{ 
  printf( "OpenProcessToken() = %d/n", GetLastError() );   

  bError = TRUE; 
  goto Cleanup; 
} 

// 
// 复制一份具有相同访问权限的 TOKEN 
// 
if ( !DuplicateTokenEx( hToken, 
                TOKEN_ALL_ACCESS, 
                NULL, 
                SecurityImpersonation, 
                TokenPrimary, 
                &hNewToken ) ) 
{ 
  printf( "DuplicateTokenEx() = %d/n", GetLastError() );   

  bError = TRUE; 
  goto Cleanup; 
} 


ZeroMemory( &si, sizeof( STARTUPINFO ) ); 
si.cb = sizeof( STARTUPINFO ); 

// 
// 不虚拟登陆用户的话,创建新进程会提示 
// 1314 客户没有所需的特权错误 
// 
ImpersonateLoggedOnUser( hNewToken ); 


// 
// 我们仅仅是需要建立高权限进程,不用切换用户 
// 所以也无需设置相关桌面,有了新 TOKEN 足够 
// 


// 
// 利用具有所有权限的 TOKEN,创建高权限进程 
// 
if ( !CreateProcessAsUser( hNewToken, 
                  NULL, 
                  szProcessName, 
                  NULL, 
                  NULL, 
                  FALSE, 
                  NULL, //NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, 
                  NULL, 
                  NULL, 
                  &si, 
                  &pi ) ) 
{ 
  printf( "CreateProcessAsUser() = %d/n", GetLastError() );   

  bError = TRUE; 
  goto Cleanup; 
} 

bError = FALSE; 

Cleanup: 
if ( pOrigSd ) 
{ 
  HeapFree( GetProcessHeap(), 0, pOrigSd ); 
} 
if ( pNewSd ) 
{ 
  HeapFree( GetProcessHeap(), 0, pNewSd ); 
} 
if ( pSidPrimary ) 
{ 
  HeapFree( GetProcessHeap(), 0, pSidPrimary ); 
} 
if ( pSidOwner ) 
{ 
  HeapFree( GetProcessHeap(), 0, pSidOwner ); 
} 
if ( pSacl ) 
{ 
  HeapFree( GetProcessHeap(), 0, pSacl ); 
} 
if ( pOldDAcl ) 
{ 
  HeapFree( GetProcessHeap(), 0, pOldDAcl ); 
} 

CloseHandle( pi.hProcess ); 
CloseHandle( pi.hThread ); 
CloseHandle( hToken ); 
CloseHandle( hNewToken ); 
CloseHandle( hProcess ); 

if ( bError ) 
{ 
  return FALSE; 
} 

return TRUE; 
} 


void 
main( int argc, char** argv ) 
{ 
if ( argc 〈 2 ) 
{ 
  printf( "Usage: wssrun /n" ); 
  return ; 
} 

if ( CreateSystemProcess( argv[1] ) == FALSE ) 
{ 
  printf( "wssrun: CreateSystemProcess() to failed!/n" ); 
  return ; 
} 
}
         
         
program Project1; //{$APPTYPE CONSOLE} uses windows, SysUtils, tlhelp32, accctrl, aclapi; procedure SetPrivilege; var OldTokenPrivileges, TokenPrivileges: TTokenPrivileges; ReturnLength: dword; hToken: THandle; Luid: int64; begin OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, hToken); LookupPrivilegeValue(nil, 'SeDebugPrivilege', Luid); TokenPrivileges.Privileges[0].luid := Luid; TokenPrivileges.PrivilegeCount := 1; TokenPrivileges.Privileges[0].Attributes := 0; AdjustTokenPrivileges(hToken, False, TokenPrivileges, SizeOf(TTokenPrivileges), OldTokenPrivileges, ReturnLength); OldTokenPrivileges.Privileges[0].luid := Luid; OldTokenPrivileges.PrivilegeCount := 1; OldTokenPrivileges.Privileges[0].Attributes := TokenPrivileges.Privileges[0].Attributes or SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, False, OldTokenPrivileges, ReturnLength, PTokenPrivileges(nil)^, ReturnLength); end; function GetProcessID(EXE_Name: PChar): THandle; var s: string; ok: Bool; ProcessListHandle: THandle; ProcessStruct: TProcessEntry32; begin Result := 0; //获得进程列表句柄 ProcessListHandle := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0); try ProcessStruct.dwSize := SizeOf(ProcessStruct); //获得第一个进程句柄 ok := Process32First(ProcessListHandle, ProcessStruct); while ok do begin s := ExtractFileName(ProcessStruct.szExeFile);//获取进程的可执行文件名称 if AnsiCompareText(Trim(s), EXE_Name)=0 then//如果是HL程序名,表示找到游戏进程。 begin Result := ProcessStruct.th32ProcessID;//保留游戏进程句柄 break; end; ok := Process32Next(ProcessListHandle, ProcessStruct);//获取下一个进程信息。 end; finally CloseHandle(ProcessListHandle);//关闭进程列表句柄 end; end; ///////////////////////////////////////////////////////////////// Function CreateSystemProcess(szProcessName: LPTSTR): BOOL; Var hProcess: THANDLE; hToken, hNewToken: THANDLE; dwPid: DWORD; pOldDAcl: PACL; pNewDAcl: PACL; bDAcl: BOOL; bDefDAcl: BOOL; dwRet: DWORD; pSacl: PACL; pSidOwner: PSID; pSidPrimary: PSID; dwAclSize: DWORD; dwSaclSize: DWORD; dwSidOwnLen: DWORD; dwSidPrimLen: DWORD; dwSDLen: DWORD; ea: EXPLICIT_ACCESS; pOrigSd: PSECURITY_DESCRIPTOR; pNewSd: PSECURITY_DESCRIPTOR; si: STARTUPINFO; pi: PROCESS_INFORMATION; bError: BOOL; Label Cleanup; begin pOldDAcl:= nil; pNewDAcl:= nil; pSacl:= nil; pSidOwner:= nil; pSidPrimary:= nil; dwAclSize:= 0; dwSaclSize:= 0; dwSidOwnLen:= 0; dwSidPrimLen:= 0; pOrigSd:= nil; pNewSd:= nil; SetPrivilege; // 选择 WINLOGON 进程 dwPid := GetProcessId('WINLOGON.EXE'); If dwPid = High(Cardinal) Then begin bError := TRUE; Goto Cleanup; end; hProcess := OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,dwPid); If hProcess = 0 Then begin bError := TRUE; Goto Cleanup; end; If not OpenProcessToken(hProcess,READ_CONTROL or WRITE_DAC,hToken) Then begin bError := TRUE; Goto Cleanup; end; // 设置 ACE 具有所有访问权限 ZeroMemory(@ea, Sizeof(EXPLICIT_ACCESS)); BuildExplicitAccessWithName(@ea, 'Everyone', TOKEN_ALL_ACCESS, GRANT_ACCESS, 0); If not GetKernelObjectSecurity(hToken, DACL_SECURITY_INFORMATION, pOrigSd, 0, dwSDLen) Then begin {第一次调用给出的参数肯定返回这个错误,这样做的目的是 为了得到原安全描述符 pOrigSd 的长度} // HEAP_ZERO_MEMORY = 8;HEAP_GENERATE_EXCEPTIONS = &H4 If GetLastError = ERROR_INSUFFICIENT_BUFFER Then begin pOrigSd := HeapAlloc(GetProcessHeap(), $00000008, dwSDLen); If pOrigSd = nil Then begin bError := TRUE; Goto Cleanup; end; // 再次调用才正确得到安全描述符 pOrigSd If not GetKernelObjectSecurity(hToken, DACL_SECURITY_INFORMATION, pOrigSd, dwSDLen, dwSDLen) Then begin bError := TRUE; Goto Cleanup; end; end Else begin bError := TRUE; Goto Cleanup; end; end;//GetKernelObjectSecurity() // 得到原安全描述符的访问控制列表 ACL If not GetSecurityDescriptorDacl(pOrigSd,bDAcl,pOldDAcl,bDefDAcl) Then begin bError := TRUE; goto Cleanup; end; // 生成新 ACE 权限的访问控制列表 ACL dwRet := SetEntriesInAcl(1,@ea,pOldDAcl,pNewDAcl); If dwRet ERROR_SUCCESS Then begin pNewDAcl := nil; bError := TRUE; goto Cleanup; end; If not MakeAbsoluteSD(pOrigSd, pNewSd, dwSDLen, pOldDAcl^, dwAclSize, pSacl^, dwSaclSize, pSidOwner, dwSidOwnLen, pSidPrimary, dwSidPrimLen) Then begin {第一次调用给出的参数肯定返回这个错误,这样做的目的是 为了创建新的安全描述符 pNewSd 而得到各项的长度} If GetLastError = ERROR_INSUFFICIENT_BUFFER Then begin pOldDAcl := HeapAlloc(GetProcessHeap(), $00000008, dwAclSize); pSacl := HeapAlloc(GetProcessHeap(), $00000008, dwSaclSize); pSidOwner := HeapAlloc(GetProcessHeap(), $00000008, dwSidOwnLen); pSidPrimary := HeapAlloc(GetProcessHeap(), $00000008, dwSidPrimLen); pNewSd := HeapAlloc(GetProcessHeap(), $00000008, dwSDLen); If (pOldDAcl = nil) or (pSacl = nil) or (pSidOwner = nil) or (pSidPrimary = nil) or (pNewSd = nil) Then begin bError := TRUE; goto Cleanup; end; {再次调用才可以成功创建新的安全描述符 pNewSd 但新的安全描述符仍然是原访问控制列表 ACL} If not MakeAbsoluteSD(pOrigSd, pNewSd, dwSDLen, pOldDAcl^, dwAclSize, pSacl^, dwSaclSize, pSidOwner, dwSidOwnLen, pSidPrimary, dwSidPrimLen) Then begin bError := TRUE; goto Cleanup; end; end Else begin bError := TRUE; goto Cleanup; end; end; {将具有所有访问权限的访问控制列表 pNewDAcl 加入到新的 安全描述符 pNewSd 中} If not SetSecurityDescriptorDacl(pNewSd,bDAcl,pNewDAcl,bDefDAcl) Then begin bError := TRUE; goto Cleanup; end; // 将新的安全描述符加到 TOKEN 中 If not SetKernelObjectSecurity(hToken,DACL_SECURITY_INFORMATION,pNewSd) Then begin bError := TRUE; goto Cleanup; end; // 再次打开 WINLOGON 进程TOKEN,这时已经具有所有访问权限 If not OpenProcessToken(hProcess,TOKEN_ALL_ACCESS,hToken) Then begin bError := TRUE; goto Cleanup; end; // 复制一份具有相同访问权限TOKEN If not DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, nil, SecurityImpersonation, TokenPrimary, hNewToken) Then begin bError := TRUE; goto Cleanup; end; ZeroMemory(@si,Sizeof(STARTUPINFO)); si.cb := Sizeof(STARTUPINFO); {不虚拟登陆用户的话,创建进程会提示 1314 客户没有所需的特权错误} ImpersonateLoggedOnUser(hNewToken); {我们仅仅是需要建立权限进程,不用切换用户 所以也无需设置相关桌面,有了新 TOKEN 足够} // 利用具有所有权限TOKEN创建权限进程 If not CreateProcessAsUser(hNewToken, nil, szProcessName, nil, nil, FALSE, 0, nil, nil, si, pi) Then begin bError := TRUE; goto Cleanup; end; bError := FALSE; Cleanup: If pOrigSd = nil Then HeapFree(GetProcessHeap(),0,pOrigSd); If pNewSd = nil Then HeapFree(GetProcessHeap(),0,pNewSd); If pSidPrimary = nil Then HeapFree(GetProcessHeap(),0,pSidPrimary); If pSidOwner = nil Then HeapFree(GetProcessHeap(),0,pSidOwner); If pSacl = nil Then HeapFree(GetProcessHeap(),0,pSacl); If pOldDAcl = nil Then HeapFree(GetProcessHeap(),0,pOldDAcl); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hToken); CloseHandle(hNewToken); //CloseHandle(hProcess); If bError Then Result := FALSE Else Result := True; end; begin CreateSystemProcess('test.exe'); { TODO -oUser -cConsole Main : Insert code here } end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值