일반 권한 명령 프롬프트에서 복구 환경 상태를 보려고 reagentc /info 를 쳤더니 두 줄이 나왔다. 관리자 권한 프롬프트에서만 실행할 수 있다는 안내, 그리고 REAGENTC.EXE: Operation failed: 5. 종료 코드도 5였다. 이 5가 Windows 업데이트 화면이나 스토어에서 보는 0x80070005 의 끝자리 5와 같은 숫자다. 앞에 붙은 0x8007 만 읽을 줄 알면 이 계열 오류 코드는 대부분 혼자 풀 수 있다.
0x8007 은 "Win32 오류"라는 표시다
0x80070005 는 HRESULT 라는 32비트 오류 형식이다. 맨 앞 비트는 실패 여부, 그다음 11비트는 오류가 나온 영역(facility), 아래 16비트가 실제 오류 번호다. 0x80070005 를 쪼개면 실패(1), 영역 7, 번호 5가 된다. 영역 7은 FACILITY_WIN32, 곧 "Windows API 가 돌려준 기본 오류 번호를 HRESULT 로 감쌌다"는 뜻이다. 그래서 0x80070005 는 Win32 오류 5번 ERROR_ACCESS_DENIED, 우리말 메시지로 "액세스가 거부되었습니다"다.
직접 풀어 볼 도구는 Windows 에 이미 있다. certutil -error 에 코드를 주면 이름과 메시지를 같이 보여 준다.
certutil -error 0x80070005
0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED) -- 2147942405 (-2147024891)
오류 메시지 텍스트: 액세스가 거부되었습니다.
괄호 안의 -2147024891 을 기억해 두면 좋다. 같은 값을 부호 있는 10진수로 쓴 것인데, 로그나 프로그램에 따라 이 형태로만 남는 경우가 있다. PowerShell 로도 바로 계산된다.
'{0:X8}' -f -2147024891 # 80070005
0x80070005 -band 0xFFFF # 아래 16비트 = Win32 오류 번호 5
주의할 점은 아래 네 자리가 16진수라는 것이다. 0x80070643 의 643 은 10진수로 1603 이고, 0x80073D02 의 3D02 는 15618 이다. net helpmsg 처럼 10진수 번호를 받는 명령에 643 을 그대로 넣으면 엉뚱한 메시지가 나온다. 자주 검색되는 코드 몇 개를 certutil 로 풀어 보면 이렇다.
| 코드 | Win32 번호 | 이름 | 뜻 |
|---|---|---|---|
| 0x80070002 | 2 | ERROR_FILE_NOT_FOUND | 파일을 찾을 수 없음 |
| 0x80070005 | 5 | ERROR_ACCESS_DENIED | 액세스 거부 |
| 0x80070020 | 32 | ERROR_SHARING_VIOLATION | 다른 프로세스가 사용 중 |
| 0x80070070 | 112 | ERROR_DISK_FULL | 디스크 공간 부족 |
| 0x80070643 | 1603 | ERROR_INSTALL_FAILURE | 설치 중 오류 |
| 0x80073D02 | 15618 | ERROR_PACKAGES_IN_USE | 패키지가 사용 중 |
내 PC 로그에 남아 있던 0x80070005
읽기 권한으로 열리는 로그를 훑었다. 시스템, 응용 프로그램, 설치(Setup), 앱 배포(AppXDeploymentServer), 스토어, Windows 업데이트, BITS 로그다. 0x80070005 는 두 군데에서 나왔다.
하나는 앱 배포 로그의 이벤트 404 였다. 11월 1일과 7일, Edge 가 함께 설치하는 보조 패키지의 배포가 0x80070005 로 실패했다. 바로 앞 기록을 보니 패키지를 내려받아 펼치는 Stage 단계는 375ms 만에 성공했고, 그다음 ProvisionPackageOperation, 즉 이 패키지를 PC 의 모든 사용자용으로 등록하는 단계가 시작되자마자 실패했다. 모든 사용자에게 걸치는 등록은 관리자 권한이 필요한 작업으로 알려져 있어서, 사용자 권한으로 돌던 작업이 이 단계에서 막힌 것으로 보인다. Edge 사용에는 아무 문제가 없었다.
다른 하나는 스토어 로그의 이벤트 2006 이었다. 로그가 남아 있던 11월 4일부터 8일까지 매일 아침 한 번씩, 다섯 번 EndFrameworkUpdate ... HResult = -2147024891 이 찍혀 있었다. 메시지 어디에도 "0x80070005" 라는 글자가 없고 10진수만 있다. 이 로그에서 0x80070005 로만 검색했다면 놓쳤을 것이다. 스토어가 공용 프레임워크 패키지를 갱신하는 과정의 내부 기록으로 보이고, 화면에 오류가 뜬 적은 없었다. 로그의 코드가 늘 눈에 보이는 문제는 아니다.
두 형태를 같이 찾는 명령은 이렇게 쓰면 된다.
$logs = 'System','Application','Microsoft-Windows-AppXDeploymentServer/Operational','Microsoft-Windows-Store/Operational'
foreach ($l in $logs) {
Get-WinEvent -LogName $l -MaxEvents 20000 -ErrorAction SilentlyContinue |
Where-Object { $_.Message -match '0x80070005|-2147024891' } |
Select-Object @{n='Log';e={$l}}, TimeCreated, Id,
@{n='Msg';e={$_.Message.Substring(0, [Math]::Min(120, $_.Message.Length))}}
}
예약 작업의 마지막 실행 결과(Get-ScheduledTaskInfo 의 LastTaskResult)도 같은 형식이다. 215개 작업 중 0x80070005 는 없었고, Windows 기본 작업 세 개에 0x800710E0(4320, ERROR_REQUEST_REFUSED)이 있었다. 읽는 방법은 같다.
사본 파일로 일부러 띄워 보기
원인을 좁히기 전에 0x80070005 가 어떤 상황을 뭉뚱그린 코드인지 직접 보고 싶었다. %TEMP% 아래 시험용 폴더에 텍스트 파일을 하나 만들고, 그 사본에만 Everyone 쓰기 거부를 걸어 덧붙여 쓰기를 시도했다.
$f = "$env:TEMP\acltest\test.txt"
icacls $f /deny "*S-1-1-0:(W)"
try { 'x' | Add-Content $f -ErrorAction Stop } catch { '0x{0:X8}' -f $_.Exception.HResult }
icacls $f /remove:d "*S-1-1-0"
결과는 System.UnauthorizedAccessException, HResult 0x80070005 였다. 확인한 뒤 거부 항목을 지우고 폴더째 삭제했다.
여기서 알 수 있는 건 코드 자체가 원인을 말해 주지 않는다는 점이다. 파일 권한이 막은 것, 관리자 권한이 없던 것, 보안 프로그램이 쓰기를 가로챈 것이 모두 같은 "접근 거부" 하나로 접힌다. 1편의 0xc0000022는 같은 거부를 커널 쪽 NTSTATUS 로 본 것이고, 0x80070005 는 그걸 Win32 오류 5로 바꿔 HRESULT 로 감싼 모습이라고 보면 된다. 그래서 확인할 것은 늘 "무엇이, 누구 권한으로, 무엇에 쓰려다 막혔나"다.
업데이트·스토어·설치에서 볼 때
설치 프로그램이나 관리 명령에서 나왔다면 먼저 권한을 의심한다. 앞의 reagentc 처럼 관리자 권한이 필요한 작업을 일반 권한으로 실행한 경우가 가장 흔하다. 설치 파일을 오른쪽 클릭해 "관리자 권한으로 실행"으로 다시 해 보고, 회사 PC 처럼 관리자 계정이 따로 있다면 그 계정으로 설치를 요청하는 것이 맞다.
Windows 업데이트에서 나왔다면 다시 시작한 뒤 한 번 더 시도하고, 설정의 Windows 업데이트 문제 해결사를 돌려 본다. 업데이트를 내려받아 두는 폴더나 관련 서비스의 권한이 꼬였거나, 보안 프로그램이 업데이트 파일 쓰기를 막는 경우가 흔한 원인으로 알려져 있다. 시스템 파일 손상이 의심되면 관리자 권한 명령 프롬프트에서 아래 두 명령으로 점검할 수 있다. 둘 다 관리자 권한이 필요하고 시간이 꽤 걸린다.
sfc /scannow
DISM /Online /Cleanup-Image /RestoreHealth
문서·사진 폴더에 저장할 때만 거부된다면 Defender 의 "제어된 폴더 액세스"가 켜져 있는지 본다. 이 기능은 허용되지 않은 프로그램의 쓰기를 막아 액세스 거부로 보이게 만든다. (Get-MpPreference).EnableControlledFolderAccess 가 1이면 켜진 상태이고, 내 PC 는 0이었다. 켜져 있다면 기능을 끌 게 아니라 보호 기록에서 막힌 프로그램을 확인하고 그 프로그램만 허용하는 편이 맞다. Defender 동작은 MsMpEng 편에 정리해 두었다.
System32 나 WindowsApps 폴더의 소유권을 가져와 권한을 통째로 바꾸라는 글도 있지만, 이 폴더들은 TrustedInstaller 소유를 전제로 업데이트가 동작해서 바꾸면 다음 업데이트가 더 꼬이는 것으로 알려져 있다. Defender 를 끄는 것도 권하지 않는다. 막힌 지점을 로그에서 찾아 그 지점만 푸는 것이 결국 빠르다.
다음 편은 업데이트 기록에서 더 자주 보이는 0x80070643 이다. 같은 방법으로 풀면 Win32 오류 1603, 설치 실패인데, 이 코드가 왜 그렇게 막연한지와 이 PC 업데이트 기록에서 확인할 수 있는 것을 정리하며 연재를 마무리한다.