반응형


<사진 출처 : UPack 제작자 dwing's homepage>


그 동안 총 5 회에 걸쳐서 UPack 의 PE Header 분석과 Debugging 에 대해서 연재를 하였습니다.

UPack 상세 분석 – PE Header 완전 정복 (1)
UPack 상세 분석 – PE Header 완전 정복 (2)
UPack 상세 분석 – PE Header 완전 정복 (3)
UPack 상세 분석 – PE Header 완전 정복 (4)
UPack 디버깅 - OEP 찾기


다른 Packer 도 많은데 굳이 이 UPack 에 이렇게 많은 공(?)을 들인 까닭은 제 개인적인 추억(경험) 때문입니다.

예전에 PE 공부를 마치고 PE File Format 에 자신 있던 그때에, 전혀 새로운 PE 세계가 있다는 걸 알려준 소중한 Packer 입니다. 또한 PE 스펙은 그냥 스펙일 뿐이고, 실제 구현은 PE Loader 개발자에 의해 좌우되기 때문에 OS 별로 실제 테스트를 해봐야 한다는 깨우침(?)을 주었습니다.

제 블로그를 방문하시는 여러분들께서도 저와 같은 경험과 느낌을 가져보시라는 뜻으로 UPack 을 상세히 소개하였습니다.

물론 UPack 에서 소개된 내용이 PE Header Patch 의 전부는 아닙니다.

하지만 제가 분명히 장담할 수 있는 것은 "UPack 을 정복한 사람에겐 앞으로 어떤 PE Header 변형이 나타나더라도 두렵지 않다." 는 것입니다. PE Header 에서 실제 사용되는 값들과 사용되지 않는 값들을 잘 숙지 하고 있다면 어떤 변형도 무리 없이 분석이 가능합니다. (제 경험입니다. ^^)

감사합니다.


ReverseCore


반응형
  1. 신화창조 2009.09.21 23:25 신고

    열심히 써주신 글들 RSS로 정말 잘 읽었습니다. ^^

    확실히 국내 몇 안되게 체계적이고 내용도 쉬운 것(!) 같습니다. 고맙습니다!

    • reversecore 2009.09.22 02:01

      신화창조님, 칭찬 감사합니다.
      더 열심히 해야 겠다는 생각이 드네요. ^^

  2. Sun2Day 2009.09.22 10:01

    좌절의 UPack..;;

    주말에 가지고 놀아보다가.. 안되면 문의 드리러 올께요 (+__)ㅋ

    • reversecore 2009.09.22 10:11

      Sun2Day님, 재밌으실 겁니다.
      잘 안되면 질문 남겨주세요~

  3. 2009.09.24 01:03

    비밀댓글입니다

  4. 2010.12.09 20:47

    비밀댓글입니다

    • reversecore 2010.12.03 16:23 신고

      안녕하세요.

      사실 합법/불법을 저조차도 딱부러지게 100% 판단하기는 어렵습니다.

      다만 제 기준은 이렇습니다.

      누군가에게 피해를 끼쳤느냐 아니냐로 구분하는 것이죠.

      가령 리버싱을 해서 패커의 고유 압축 알고리즘을 공개해버리면 소중한 지식을 공개한 셈이 되지 않을까요? 제작자 입장에서는 피해를 보는 상황이라고 생각합니다.

      저는 법에 대해 문외한 이므로 참고만 하시기 바랍니다. ^^

      감사합니다.

  5. プラダ トートバッグ 2013.04.25 16:28

    누구냐고 물어보면 윤동주라고

  6. netinfil 2013.10.08 14:38

    안녕하세요. 책을 구매한 후에 Upack의 OEP 찾는 부분을 진행하고있습니다.

    그런데 압축되어있는 실습예제의 notepad.exe가 언패킹이 되지 않은 것 같습니다.

    참고해주셨으면 합니다!

    또한 책 전반에 걸쳐서 16진수와 10진수의 구분이 애매하게 되어있어서

    PE구조를 분석할때 자주 헷갈리네요. 혹시 다음에 개정판을 내실 의향이 있으시다면

    '0x'와 같이 분류를 해주셨으면 합니다^^

  7. 2019.06.08 20:22

    비밀댓글입니다

반응형


리버스 엔지니어링 분야에 대해 질문이 있으시면 아래의 댓글로 올려주세요. 역시 댓글로 답변을 올려드리겠습니다. 공개하기 어려운 내용은 '비밀글' 에 체크해주세요.

"비도덕적, 불법적" 인 내용은 답변 드릴 수 없음을 이해해 주시기 바랍니다.


댓글, 방명록, 이메일 등으로 많은 분들께서 여러 가지 질문들을 해주십니다.
그중에 정말 좋은 질문들이 많아서 여러분들과 공유하면 좋겠다고 생각하였습니다.

이제부터 저에게 들어오는 모든 질문과 답변들을 이곳으로 모을 것입니다.
앞으로 질문과 답변은 이곳에서 해주세요~

질문에 대한 답변은 저 뿐만 아니라 제 블로그에 오시는 모든 분들께서 하실 수 있습니다.
문제 해결을 위한 방법은 다양합니다. 저 말고도 다른 분들의 답변은 언제나 환영입니다.

"질문은 좋은 것 입니다. 많이 해주세요. ^^ "

* 댓글로 질문하기 어려운 내용들(긴내용, 첨부파일 등)은 제 이메일(reversecore@gmail.com)로 문의해 주세요.

* 댓글이 너무 많이 달리면 제가 질문/답변 빈 포스트를 또 올릴 겁니다. 그쪽으로 계속 질문 댓글 달아주시면 됩니다.

감사합니다.


ReverseCore



반응형

'q&a' 카테고리의 다른 글

질문/답변 코너입니다. (3)  (381) 2011.04.13
질문/답변 코너입니다. (2)  (223) 2010.01.28
질문/답변 코너입니다.  (295) 2009.09.21
  1. 이전 댓글 더보기
  2. RCE 2013.12.31 09:57

    안녕하세요
    리버싱 공부 중인 학생입니다.
    인라인 패치를 하고 있는데 책에 나와 있는데로가 아닌
    rdata 영역 또는 data 영역을 이용해서 패치하려는데 패치가 잘 안 되서 질문드립니다.
    401083의 JMP 영역은 XOR 7로 잘 바꾸어 주었는데
    아마도 패치 영역이 문제인 것 같습니다. 바꾸고 저장해도 다시 디버깅을 해보면
    패치했던 부분이 그대로 0000 으로 되어 있더군요
    제가 패치한 부분은
    rdata 영역일 때 메모리상 pointer to offset은 402000을 시작으로 402B38까지 rdata 내용, 그 뒤로
    402B38~403000을 Null 영역으로 계산하였고 그 중 패치는 402B38~402200사이에 했습니다.
    (Pointer To Rawdata : 800 / VirutalSize : 138 / SizeOfRawdata : 200)
    그런데 제대로 패치가 되지 않아 저의 계산이 틀린 것 같은데 어떻게 계산을 해서 Null영역에
    패치를 해야 하는지 궁금합니다.
    <data 영역일 경우 403000을 시작으로 40302C 까지 data 내용, 그 뒤로 40302C부터 404000까지
    Null 영역으로 잡고 패치했었습니다.>

  3. James 2014.01.05 22:36

    처음 공부시작하는학생입니다...
    올리디버거 글자크기가 너무 작아서요... 눈이 아픈데요
    Option의 Appearence의 Fonts를 들어가서 Font parameters의 change를 눌러 폰트사이즈를 변경해도 바뀌지가 않습니다..
    글자를 좀 크게볼수있는 방법이 있나요?

  4. choi 2014.01.19 11:20

    처음 리버싱을 공부하는 학생인데 공부하던 도중 IA-32 매뉴얼은 어디에서 얻을 수 있나요?

  5. galois 2014.02.02 13:59

    음 올리디버그로 헬로월드 디버깅 하는 예제를 책을보고 따라해볼려고 올리디버그를 다운받고 예제 HelloWorld.exe도 다운받아서 올디에서 파일을 여는데 이런 에러가 뜨네요.

    Unable locate to file " 파일 경로"

    이거는 어떻게 해결하나요??

    그리고 올리디버그를 관리자 계정에서 사용하는데 그냥 실행하면
    you do not have administrative rights on this computer ~~ 라는 메시지 박스가 뜨길래
    관리자 권한으로 실행을 해본 결과 메시지박스가 안뜨는 걸 알게 됬는데
    이렇게 사용하는게 맞는지요??

    질문 읽어주셔서 감사합니다.

  6. 2014.04.24 13:22

    비밀댓글입니다

  7. shiri 2014.06.25 12:34

    안녕하세요

    Hello World! 리버싱을 해보려고 Ollydbg 110으로 실행을 했는데 CALL 0040270C는 안보이고

    774A01B8 895C24 08 MOV DWORD PTR SS:[ESP+8],EBX 이런게 먼저 나오네요...

    200버전으로 다시 실행해도 책에 있는 것처럼 나오지 않아서 책 따라하기가 힘들어요

    제 운영체제가 64비트인데 혹시 이것 때문인가요?

  8. ㅇㄹㄴㅇㅁㄹ 2014.07.12 22:33

    datadirectory export값이요.
    NT Header - optional header의 멤버를 설명하는 부분에선
    datadirectory[0]의 값, 그러니까 Export의 Size값과 RVA값이 모두 00 00 00 00, 00 00 00 00
    이라고 나오는데

    EAT를 설명하는 부분에선 Size=00 00 6d 19, RVA=00 00 26 2c라고 값이 바뀌어 있습니다.
    이유가 뭔가요.

  9. ㅇㄹㄴㅇㅁㄹ 2014.07.13 19:17

    아 위의 질문 취소입니다. 파일이 다른거였네여 ㅈㅅㅈㅅ

  10. 전국수석 2014.07.16 02:31

    안녕하세요
    올해 5년차 Windows 개발자 입니다
    주로 C/C++ 사용해서 개발은 하고 있습니다 ~ 응용프로그램개발, 시스템 프로그래밍을 했습니다
    지금 분야를 보안개발이나 정보보안전문가로 가고 싶은데요
    현재 리버싱책을 사서 공부중인데요 IDA Pro 정품을 구매해서 제대로 공부해보고 싶습니다
    정품구매 요령하고 어디서 구매하는지 패키지가 얼마정도 하는지 좀 알려주세요
    감사합니다 ~

  11. 한숨만나온다 2014.07.22 07:53

    저자분 블로그 관리 너무 안하시는것 같아요.
    리버싱이란게 생소하고, 변수가 많은 분야이기 때문에
    당연히 수많은 독자들이 수많은 질문을 할텐데, 그 부분을 생각지 못하고
    저자분 혼자 답변하겠다는 생각으로 메일과, 블로그에 QA게시판을 만들면...
    지금 저자분처럼 일일이 답변해주는거 포기하고, 블로그도 뜸하게 들어오게 되고
    덕분에 독자들만 개고생하는 결과가 나는거죠.
    윤성우님처럼 저자의 개입없이 독자들끼리 정보공유해서 해답을 찾을 수 있게끔
    커뮤니티 카페를 만들었다면 좋았을텐데 말입니다.
    다시 한번 윤성우님을 찬양하게 되는 부분입니다.

  12. 한숨만나온다 2014.07.22 07:53

    저자분 블로그 관리 너무 안하시는것 같아요.
    리버싱이란게 생소하고, 변수가 많은 분야이기 때문에
    당연히 수많은 독자들이 수많은 질문을 할텐데, 그 부분을 생각지 못하고
    저자분 혼자 답변하겠다는 생각으로 메일과, 블로그에 QA게시판을 만들면...
    지금 저자분처럼 일일이 답변해주는거 포기하고, 블로그도 뜸하게 들어오게 되고
    덕분에 독자들만 개고생하는 결과가 나는거죠.
    윤성우님처럼 저자의 개입없이 독자들끼리 정보공유해서 해답을 찾을 수 있게끔
    커뮤니티 카페를 만들었다면 좋았을텐데 말입니다.
    다시 한번 윤성우님을 찬양하게 되는 부분입니다.

  13. 2014.08.12 12:56

    비밀댓글입니다

  14. 애독자 2014.09.29 19:00

    안녕하세요. 애독자이며, 정말 많은 도움을 받고있습니다. 감사합니다.
    다름 아니오라 한가지 궁금하게 있어서 질문하게 되었습니다.

    a.dll 에서 aaa라는 함수를 실행하면 그 함수안에서 c:\ccc 라를 폴더에서 ccc.ini 불러오고있습니다. 그런데 a.dll 은 제가 LoadLibrary로 실행중에 로드 하는 상황입니다.
    a.dll 의 aaa라는 함수에서 c:\ccc 라는 폴더 말고 c:\ccc\sub 라는 폴더로 수정하고싶은데,
    책에서 보면 별도의 exe를 만들어서 만들어서 pid를 얻고 그 다음 실행중에인젝션하는 것 같습니다.

    a.dll 자체를 수정해서 인젝션 따로 필요없이 하는 처리하는 방법은 없을까요?


  15. 좋아요 2014.10.28 22:31

    안녕하십니까, 저자님의 ollydbg.ini 파일을 적용시킨 후, 실행하고 F7나 F8을 누르면
    다른 경고창으로 HelloWorld가 작동하지 않습니다 또는 중지되었습니다 따위의 오류가 발생합니다. 어떻게 해야됩니까??

  16. 이리x 2014.11.18 13:59 신고

    리버싱의 핵심원리를 공부하다가 궁금증이 생겨서 문의 드립니다.
    어떻게 보면 아주 간단한거고, 어떻게 보면 아주 간단하지 않은 질문이지만요^^
    제가 궁금한것은 hooking의 기초, SetWindowsHookEx 함수입니다.
    (리버싱의 핵심원리 21장, 294~297페이지 소스를 같이 봐주시면 이해가 더 빠르실지도.. ㅎㅎ)
    소스 내용은 대충 이러합니다.
    콜백함수를 통해 메시지 발생 시(키보드 누르는 메시지 발생 시) notepad일 경우 메시지를 후킹하여 전달하지 않아 키보드 입력이 안되게 하는 알고리즘으로 되어 있습니다.

    자 여기서 질문입니다.
    1. 부모프로세스가 explorer인 notepad는 정상적으로 후킹이 됩니다. 후킹이 되어 키보드 입력이 되지 않는데요.. 하지만 부모프로세스가 ollydbg인 notepad에는 후킹이 되질 않습니다.(키보드 입력이 정상적으로 됩니다.) 또한 다른 컴퓨터에서 진행해도 똑같이 정상적으로 입력이 됩니다. 이유가 무엇일까요?
    2. process explorer를 보면 ollydbg의 자식 프로세스 notepad에는 dll을 클릭해도 아무것도 출력되지 않습니다. 그 이유는 무엇일까요?
    3. 정확하게 dll이 붙는 곳은 어디일까요?(이 질문을 하는 이유가 뭐냐면 hookmain 프로그램과 메모장, 그리고 스티커 메모장 실행 시 키보드 메시지를 발생시키자 keyhook.dll이 붙는 것을 확인할 수 있었습니다. -> 이 말은 콜백함수가 우선적으로 호출된 후 injection 된다는 얘기입니다. dll injection 코드가 정확하게 어딘지 모르겠네요.. 올리디버거로 분석하려고 해도 붙질 않으니.. ㅠㅠ) 콜백 함수가 실행되고 dllmain이 실행되는 걸까요..? 흠..
    그래서 제가 고민끝에 내린 추측은
    콜백함수나, dll injection 과정을 거칠때 ppid가 같아야한다?
    아니면 애초에 keyhook.dll이 로드되지 않았다??(process explorer를 볼 시 아무것도 출력이 되지 않아서..)
    dll이 정상적으로 injection 되지 않았기 때문에..?? 그 이유는..??
    이정도 입니다..
    혹여 이 부분을 실습하다가 안되신 분들 계시면 답변 부탁드리고, 아시는 내용이 있으면 댓글 부탁드립니다.

  17. 쉬리 2015.02.08 23:12

    안녕하세요. 지금 코드 인젝션을 공부하고 있습니다. InjectCode() 함수에서
    dwSize = (DWORD)InjectionCode - (DWORD)ThreadProc; 이 부분에서 질문이 있습니다.
    위의 코드가 MS Visual C++에서 사용되는 코드라면 다른 컴파일러를 사용했을땐 어떤식으로
    함수의 크기를 구하는지 알고싶습니다. 답변해주시면 감사하겠습니다.

  18. 2015.07.13 16:24

    비밀댓글입니다

  19. 2015.07.13 16:24

    비밀댓글입니다

  20. 디디딛 2015.07.21 17:03

    ollydbg 하다가 비정상 종료 되었는데요.. 그 이후부터 실행하면 바로

    "OllyDbg, 32-bit analysing debugger의 작동이 중지되었습니다."

    라는 윈도우 창이 뜨면서 실행이 안됩니다. 이를 디버그 해보니

    "0x004942D4에(OLLYDBG.EXE의) 처리되지 않은 예외가 있습니다. 0xC000041D: 사용자 콜백 중 처리되지 않은 예외가 발생했습니다."

    라고 뜹니다. 언능 고처서 공부하고 싶네요.. 답변 기다립니다..

  21. 김준영 2015.09.25 22:46

    안녕하세요. 리버싱 핵심원리 책 덕분에 즐겁게 리버싱 공부하고 있는 한 학생입니다. 책을 읽다가 여쭤보고 싶은 내용이 생겨서요. 15장에서 notepad_upx.exe를 디버깅할 때
    "이것은 원본 코드의 Call/jmp 명 령 어 (op code : E8/E9)의 destination 주소를
    복원시 켜주는코드입 니다. 이 루프도 0 1015436 주소에 BP를설치하여 탈출할수
    있습니다."
    이 부분에서 궁금한 점이 생겨서요. 위에서 dest 주소를 구한다는 것을 어떻게 알아내셨는지 궁금합니다. 제가 코드에서 알아낼 수 있는 힌트는 4바이트 값을 넣는다는 것 밖에는 없는 거 같아요. OEP로 돌아가 call하는 주소를 보고 알게 된 건가요 아니면 다른 방법이 있었는지 궁급합니다. 올리디버거에서 캡처한 링크 드립니다. http://postfiles11.naver.net/20150925_10/kimjun136_1443188714453wLYni_JPEG/%C4%B8%C3%B3.JPG?type=w1
    바쁘셔서 못 보실 수도 있을텐데 좋은 책 써주셔서 항상 감사한 마음 가지고 있습니다. 감사드립니다. ^^

반응형

지금까지 우리가 원하는 DLL 을 "실행중인" 프로세스에 강제로 Injection 시키는 방법에 대해서 알아봤습니다.

이번에는 접근 방법을 바꿔서 아예 대상 프로그램 "파일을 직접 수정"하여 DLL 을 강제로 로딩하도록 해보겠습니다.
이 방법은 한 번 적용해 놓으면 (별도의 Injection 과정없이) 프로세스가 시작할 때마다 원하는 DLL 을 로딩하게 만들 수 있습니다. 일종의 크랙이라고 생각하시면 됩니다.

목표는 notepad.exe 파일을 직접 수정하여 실행 시 myhack3.dll 을 로딩하도록 만드는 것입니다.
이를 위해서는 PE Header 를 자유자재로 다룰 수 있는 실력이 필요합니다.

IDT(Import Directory Table)와 기타 PE File Format 에 대해서는 아래의 글을 참조하시기 바랍니다.

- PE(Portable Executable) File Format (6) - PE Header



myhack3.dll


먼저 로딩 시킬 DLL 의 소스를 보겠습니다.

// myhack2.cpp
// ReverseCore

include "windows.h"

#define DEF_CMD  "c:\\Program Files\\Internet Explorer\\iexplore.exe"
#define DEF_ADDR "www.naver.com"

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) BOOL start()
{
    char szCmd[MAX_PATH] = {0,};
    STARTUPINFO si = {0,};
    PROCESS_INFORMATION pi = {0,};

    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    // IE 를 hidden window 로 실행시켜 naver 에 접속합니다.
    wsprintf(szCmd, "%s %s", DEF_CMD, DEF_ADDR);
    if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd, 
                       NULL, NULL, FALSE,
                       NORMAL_PRIORITY_CLASS,
                       NULL, NULL, &si, &pi) )
        return FALSE;

    if( pi.hProcess != NULL )
        CloseHandle(pi.hProcess);

    return TRUE;
}
#ifdef __cplusplus
}
#endif

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH :
            start();
            break;
    }
  
    return TRUE;
}

myhack3.dll


기능은 지난번과 마찬가지로 IE 를 hidden 속성으로 실행시켜 Naver 초기화면에 접속시키는 것입니다.

가장 눈에 띄는 것은 start() EXPORT 함수입니다.

PE 파일에서 어떤 DLL 을 IMPORT 한다는 것은 코드내에서 그 DLL 의 EXPORT 함수를 호출한다는 의미입니다. 그래서 myhack3.dll 파일은 형식적인 완전성을 위해 EXPORT 함수를 제공해야 합니다. (실제로 notepad.exe 내부에는 myhack3.dll!start() 함수를 호출하는 코드 같은건 존재하지 않습니다.)

프로그래밍에서 DLL 의 IMPORT 는 컴파일러/링커가 해주지만, 저는 이 작업을 PE View 와 Hex Editor 만을 가지고 수동으로 해보겠습니다. (제 환경은 WinXP SP3 KOR 버전입니다. OS 와 SP 버전에 따라서 방법이 약간 틀려질 수 있습니다.)



파일 변경 아이디어 

notepad.exe 파일이 실행될 때 자동으로 myhack3.dll 파일을 로딩하기 위해서는 PE Header 의 IMPORT DIRECTORY TABLE 에 myhack3.dll 을 (IMPORT 하도록) 추가해야 합니다.

notepad.exe 원본과 제가 수정한 notepad_hack.exe 를 첨부합니다.

notepad.exe

notepad_hack.exe


PE View 를 이용하여 notepad.exe 의 IDT 의 주소를 확인해 보겠습니다. IMAGE_OPTIONAL_HEADER 의 IMPORT Table RVA 값이 바로 IDT 의 RVA 입니다.


<Fig. 1>

위 그림에서 IDT 주소는 RVA 로 7604 입니다. 이를 PE View 로 따라가 보겠습니다. (PE View 의 주소 보기 옵션을 "RVA" 로 해주세요.)



<Fig. 2>

IDT 는 <Fig. 2> 와 같이 IMAGE_IMPORT_DESCRIPTOR(이하 IID) 구조체 배열로 되어 있으며, 마지막은 NULL 구조체로 끝납니다. IMPORT 하는 DLL 파일 하나당 IID 구조체 하나가 필요합니다. (IID 구조체 하나의 크기는 14h bytes)

PE View 의 주소 보기 옵션을 "File Offset" 으로 변환하면 IDT 의 file offset 은 6A04 입니다.
Hex editor 를 이용해 6A04 주소를 찾으면 아래 그림과 같습니다.


<Fig. 3>

IDT 는 file offset 으로 6A04 ~ 6ACC 범위에 있으며, 전체 크기는 C8 입니다.
마지막 NULL 구조체를 포함하여 총 10개의 IID 구조체가 있습니다. 

처음에 전 IDT 마지막에 myhack3.dll 을 위한 IID 구조체를 추가하려 했습니다만, 더 이상 추가할 공간이 없습니다. (마지막 NULL 구조체는 꼭 필요하거든요.)

그래서 Hex editor 로 notepad.exe 의 빈 영역(사용되지 않는 영역)을 찾아 보았습니다.
<Fig. 4> 에서 보이는 것처럼 마침 파일 끝부분에 알맞은 크기가 비어 있네요.

* 만약 파일에 빈 영역이 보이지 않는다면 파일 끝에 새로운 섹션을 추가하거나, 또는 마지막 섹션의 크기를 늘리는 방법을 사용해야 합니다.


<Fig. 4>

바로 이 위치(file offset = 10710, RVA = 13310)에 새로운 IID 를 만들것입니다.

주의 하실 점은 저 영역(file:10710 ~ 107FF, RVA:13310 ~ 133FF)이 과연 메모리에 로딩되는 영역인지 확인하는 것입니다.
파일에 존재한다고 해서 반드시 메모리에 로딩되는 것은 아닙니다. 섹션 헤더에 명시된 영역만 메모리에 로딩됩니다.


notepad 의 마지막 섹션 헤더(".rsrc") 를 살펴보겠습니다.


<Fig. 5>

RVA = B000 이고 VirtualSize = 8304 이므로 메모리에 로딩되는 영역은 RVA = 13304 까지입니다.
하지만 <Fig. 4> 의 파란색 영역(RVA:13310 ~ 133FF)은 포함되지 않는 영역입니다.

그렇다면 이부분은 메모리에 로딩되지 않을까요?
실제로는 메모리에 로딩됩니다. 이유는 notepad 에서 IMAGE_OPTIONAL_HEADER 의 SectionAlignment 값이 1000 이기 때문에 마지막 섹션(".rsrc")헤더에 명시된 위치(RVA=13304)는 실제로 14000 까지 확장됩니다. (기본 단위의 배수)

디버거에서 확인해 보겠습니다.

<Fig. 6>

위 그림을 보시면 notepad 프로세스 메모리에서 마지막 섹션(".rsrc")의 VirtualSize 값이 9000 으로 확장된 것을 보실 수 있습니다. <Fig. 5> 의 실제 VirtualSize 값은 8304 였으나 SectionAlignment (1000) 의 배수인 9000 으로 자동 확장된 것입니다.

따라서 RVA = 13310 위치에 새로운 IID 를 만드는 것은 문제가 없습니다.



IDT(Import Directory Table) 변경


notepad.exe 를 복사하셔서 이름을 notepad_hack.exe 로 변경해주세요.
이제부터 notepad_hack.exe 파일을 가지고 변경 작업을 진행합니다.

#1. IMPORT Table 의 RVA 값 변경

PE View 를 기준으로 설명드리겠습니다.
IMAGE_OPTIONAL_HEADER 의 IMPORT Table 구조체 멤버는 IDT 의 위치와 크기를 알려줍니다. <Fig. 1> 을 보시면 현재 IMPORT Table 의 RVA 값은 7604 입니다. Hex editor 에서 이 값을 새로운 IDT 의 RVA 값인 13310 으로 변경합니다. 그리고 Size 값을 기존 C8 에 14 (IID 구조체 크기) 를 더한 값인 DC 로 변경합니다. (Size 멤버는 수정 하지 않아도 됩니다.)


<Fig. 7>

PE View 로 확인하면 아래 그림과 같습니다.

<Fig. 8>

이제 notepad 실행 시 PE Loader 는 IDT 가 RVA = 13310 에 존재한다고 간주합니다.

#2. BOUND IMPORT TABLE 사용 안함


PE HEADER 에서 BOUND IMPORT TABLE 은 DLL 로딩 속도를 조금 향상시킬 수 있는 기법입니다. 이 값을 그냥 놔둬도 문제 없는 경우가 많지만, 제가 테스트할 때는 myhack3.dll 이 정상동작하지 않았습니다. (편하게 작업하시려면 이 값을 무조건 0 으로 밀어버리시기 바랍니다.)

Hex editor 를 이용해서 BOUND IMPORT Table 영역(RVA, Size)을 0 으로 밀어버립니다.

<Fig. 9>

PE View 로 확인해 볼까요?

<Fig. 10>

#3. 새로운 IDT 생성

기존 IDT (file = 6A04 ~ 6ACB, RVA = 7604 ~ 76CB) 를 복사하여 새로운 위치(file = 10710, RVA = 13310)에 붙여넣고, 나머지 지저분한 데이타들은 깔끔하게 NULL 로 밀어버립니다.

<Fig. 11>

이 상태에서 아래와 같이 myhack3.dll 을 위한 IID 를 구성하여 새로운 IDT 끝에 추가시킵니다.
(각 멤버의 데이타에 대해서는 아래에서 따로 설명드리겠습니다.)

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk;       // INT(Import Name Table) => 8750
    };

    DWORD   TimeDateStamp;                // => FFFFFFFF

    DWORD   ForwarderChain;               // => FFFFFFFF
    DWORD   Name;                         // library name => 8760
    DWORD   FirstThunk;                   // IAT(Import Address Table) => 1348
} IMAGE_IMPORT_DESCRIPTOR;


아래는 Hex editor 로 작업한 그림입니다. (IDT 마지막에 myhack3.dll 을 위한 IID 를 추가)

<Fig. 12>

#4. IID(IMAGE_IMPORT_DESCRIPTOR) 구성

앞 단계에서 추가한 IID 구조체 멤버들이 정상적으로 동작하기 위해서 적절한 세팅이 필요합니다.

먼저 INT(Import Name Table) 와 Name 멤버 설명입니다. 
이 값들은 각각 RVA = 8750 (file = 7B50), RVA = 8760 (file = 7B60) 입니다.

참고로 이 위치는 첫번째 섹션(".text")의 끝부분으로써 빈 영역입니다. 제가 편의상 이 영역을 선정한 것이며 다른 위치에 선정하셔도 상관 없습니다.

일단 Hex Editor 로 RVA = 8750 (file = 7B50) 위치로 가서 아래 그림과 같이 값을 입력합니다.

<Fig. 13>

위 그림에 나타난 값들의 의미를 설명드리겠습니다.

INT 는 쉽게 말해서 RVA 배열인데요, 배열의 각 원소는 Import 하는 함수의 Ordinal(2 bytes) + Func Name String 구조체의 주소(RVA) 를 나타내며 배열의 끝은 NULL 입니다. 

<Fig. 13> 에서 INT 에는 1 개의 원소가 있고 그 값은 RVA 8770 입니다. RVA 8770 으로 가보면 Import 하려는 함수의 Ordinal (2 bytes) 와 함수 이름 문자열이 나타납니다.

Name 은 Import 하는 함수를 제공하는 DLL 파일의 이름 문자열 입니다. "myhack3.dll" 이라고 보이시죠?

마지막으로 IID 의 IAT(Import Address Table) 멤버에 대해 설명드리겠습니다.

#3 에서 IAT 값을 RVA = 1348 (file = 748) 로 세팅하였습니다. 참고로 이 위치는 원래 notepad 의 IAT 영역의 끝 부분입니다.
(INT 데이타와 IAT 데이타는 각각 뭉쳐서 존재합니다.)


<Fig. 14>

IAT 역시 RVA 배열입니다. 각 원소는 INT 와 같은 값을 가져도 좋고 NULL 을 가져도 좋습니다. INT 가 정확하다면 IAT 는 다른 값을 가져도 상관없습니다. 어차피 실행 시에 PE Loader 에 의해 메모리 상에서 실제 함수 주소로 덮어써집니다. (간혹 NULL 을 넣었을때 에러는 발생하지 않지만 DLL 이 정상 동작하지 않을 때가 있습니다. 확실한 건 INT 와 같은 값을 써주면 정확히 동작합니다.)

* 반드시 IAT 를 기존 IAT 영역에 쓸 필요는 없습니다만, 프로세스에 따라서 혹은 DLL 에 따라서 정상 동작하지 않는 경우가 있습니다. 세심한 테스트가 필요한 부분입니다.

여기까지 제대로 따라 하셨으면 작업끝입니다.



검증 (Test)


새로 만든 notepad_hack.exe 파일을 PE View 로 열어보겠습니다.


<Fig. 15>

마지막 섹션(".rsrc")로 IDT 가 옮겨졌으며, myhack3.dll 을 IMPORT 시키기 위한 IID 구조체가 정상적으로 세팅된 것을 확인 하실 수 있습니다.


<Fig. 16>

<Fig. 16> 에서 추가된 INT 도 확인 하실 수 있습니다.

notepad_hack.exe 와 myhack3.dll 파일을 같은 폴더에 넣고 notepad_hack.exe 를 실행해 보겠습니다.
Process Explorer 로 확인하면 아래 그림과 같습니다.


<Fig. 17>

notepad_hack.exe 에 myhack3.dll 이 로딩되어서 IE 가 정확하게 실행되는 화면을 보실 수 있습니다.

성공입니다!



Epilogue


설명이 길어서 자칫 복잡하게 느끼실 수 있습니다.

Import Directory Table 에 내가 원하는 dll 을 추가시켜서 실행 시 자동으로 로딩하게 한다는 기본 원리만 확실히 이해하시고, 관련된 PE Header 를 참고하시면 쉽게 하실 수 있으실 겁니다.

단, OS 와 SP 버전에 따라서 PE Loader 의 구현이 조금씩 다를 수 있으니 처음 하시는 분들께서는 약간의 시행 착오를 거치셔야 합니다. 또한 대상 프로세스와 DLL 파일의 프로그래밍에 따라서 약간씩 방법을 달리 적용해야 하는 경우도 있습니다.

그러나 위의 원리를 이해하신다면 어렵지 않게 해내실 걸로 생각합니다.


+---+


다음번에는 프로세스에 Injection 된 DLL 을 강제로 꺼내는 기법인 DLL Ejection 에 대해서 알아보도록 하겠습니다.


ReverseCore



반응형
  1. NIKA 2009.08.14 14:27

    멋진글 잘보고 있습니다 ^^

    • reversecore 2009.08.15 10:03

      NIKA님, 방문 감사합니다.
      좋은 하루 되세요~

  2. lscpjyoon 2009.09.17 21:52

    정말로 명 강의다.

    이 글을 읽으면서 문득 한가지 궁금한것이 있다면

    무슨일을 하시는분 인지 갑자기 궁금해 지네요 ;

    • reversecore 2009.09.17 22:46

      lscpjyoon님, 칭찬 감사합니다. ^^
      저는 Reverse Code Engineer 입니다.
      국내의 리버싱 기술 향상, 교류, 전파에 관심이 많습니다.

  3. juhens 2010.01.20 23:10

    아우..제가 궁금해했던것입니다...
    고등학생때 거진 3년정도......전공이 이쪽계열이 아니라
    네이버에 질문을해봐도 대답을 해주는사람은 없고........
    우연히 여기서 궁금증을 해결하네요... 정말 감사합니다.

    • reversecore 2010.01.21 01:28

      juhens님, 안녕하세요.

      고등학교 때부터 벌써 리버싱에 관심이 있으셨군요.

      도움이 되셨다니 제가 기쁘네요~ ^^

      감사합니다.

  4. LHS 2010.04.04 17:51

    와... 현재 리버싱에 관심이 많은 고2 학생에게는 단비와 같은 곳이네요...

    그런데 질문 하나 드려도 될까요?? 제가 이 글을 읽고 적당한 프로그램을 고르고 직접 시도해 보려고 했는데 위의 사진과 좀 다른 점이 있어서요...

    제가 DLL Injection을 시도한 프로그램에서는 모든 DLL의 IMPORT Directory Table에서 Import Name Table RVA, Time Data Stamp, Forwarder Chain이 모두 00000000이네요... 이 글을 읽고 따라해 봤지만 계속 오류 메시지만 뜨고... 어떻게 해야 하나요??
    (아, 또 이상한 점이 있네요... 제가 시도한 프로그램에서는 IMPORT Directory Table, IMPORT Address Table, IMPORT DLL Names, IMPORTS Hints/Names 항목(?)이 모두 rdata 섹션에 있네요... 원래 rdata섹션은 리소스가 있는 곳 아닌가요??

    • reversecore 2010.04.05 23:04 신고

      LHS님, 안녕하세요.
      반갑습니다. ^^

      PE 파일에 따라서는 지적하신 3 가지 항목이 0 인 파일도 있을 수 있습니다. 보통 packer 류에 많지요.

      하지만 그런 파일도 Import Address Table RVA 항목은 값이 있을테니, 그걸 따라가면 Import Name Table RVA 와 같은 역할을 합니다.

      그리고 섹션 이름은 개발 도구에 따라서 달라지고요,
      packer 를 사용하면 변경할 수 있으며, 사용자가 수동으로 조작할 수 도 있습니다. 그리 중요한 의미는 없지요.

      또한 Import Directory Table 이 딱히 어느 섹션에 있어야 한다는 규칙같은건 없습니다. 그리고 그 섹션의 이름도 정해진건 없구요.

      리소스는 보통 .rsrc 섹션에 있는 경우도 많지만 위에서 설명드린바와 같이 다른 섹션 이름을 가지기도 합니다.

      PE File Format 은 상당히 느슨한 편이라서 어떤 절대적인 규칙이라고 할 만한 내용은 몇 개 되지 않습니다. 또한 그 규칙들 또한 OS 버전에 따라서 조금씩 다르게 적용되는 현실입니다.

      다른 질문 있으시면 또 올려주세요~

      감사합니다.

  5. gamjadory 2010.04.11 00:56

    잘 보고 갑니다. 혹 dll 파일을 이용한 메모리 영역 변환에 대한 강좌는 없는지요... ㅠㅠ

    • reversecore 2010.04.11 23:04 신고

      gamjadory님, 안녕하세요.

      메모리 영역 변환이란게 어떤 의미인지요?

      일단 프로세스에 침투하고 나면 프로세스 메모리의 내용 혹은 메모리 영역의 속성등은 전부 변경 가능해 집니다.

      혹시 질문하신 내용이 이와 다른 것인가요?

      감사합니다.

  6. 리버싱나그네 2010.08.06 12:58

    정말 열심히 강좌를 따라가고 있는데..ㅡㅜ 그림처럼 되지 않는 부분이 있어서요 ㅜㅜ 노트패드의 빈영역 그러니까 사용하지 않는 영역을 어떻게 찾죠? ㅜㅜ 그냥 00000 ~~ 이렇게 쭉~ 되어있는 곳에 복사해서 넣고 import table 값을 복사해서 넣은 주소로 바꿧는데, 자꾸 오류가 나구.. 그림처럼 되지를 않아요 ㅜㅜ 설명을 보면, DINGPADDINGXXPAD 라고 써져있는 곳이 있던데 저는 헥사로 봐두 그런 영역은 없어요 ㅜㅜ 문제가 뭘까요 리버스 코어님 ㅜㅜ(환경 : 윈도우 7)

    • reversecore 2010.08.09 11:35 신고

      안녕하세요.

      제가 Windows 7 의 노트패드를 본 후 다시 답변 달아드릴께요~

  7. 리버싱나그네 2010.08.09 20:36

    감사합니다 ^^

    • reversecore 2010.08.10 12:39 신고

      안녕하세요.

      제가 마침 예전에 월간 마소에 기고한 글이 있는데,
      바로 위 글의 내용을 Windows 7 으로 변경한 것입니다.

      아마 원하시는 내용이 이곳에 있을 것 같네요.

      제가 댓글로 따로 써드릴려고 보니... 아무래도 그림과 같이 보시는게 이해가 쉬우실것 같습니다...

      메일 주소를 알려주시면 해당 문서를 보내드리겠습니다. (잡지사와 협업한 문서라 원본 전체를 보내드릴 수 없음을 양해해 주시기 바랍니다.)

      감사합니다.

  8. 2010.08.10 21:02

    비밀댓글입니다

  9. BlueH4G 2010.08.13 21:52

    헛,, 이번 글 보고 괜찮은 워게임이 떠올라서, 하나 만들어보네요 ㅋㅋ

    항상 리버스코어에서 많은 정보 얻어갑니다. ㅋ 앞으로도 좋은 글 많이 부탁드려요 xD

  10. binish 2010.08.17 11:47

    정말 모든 글에 정성이 깃들여져 있고 내공이 느껴집니다.
    훌륭한 강의에 감사드립니다.

  11. jaew 2011.02.15 16:50

    우연히 들어와서 그동안 궁금했던 것들이 해소되네요. 좋은 강의 잘 보았습니다. 감사합니다. !

  12. LinkC 2011.02.16 09:02

    IDT를 다른 section 으로 옮길 때 간혹 기존에 IDT가 있던 section 에

    쓰기 속성이 없으면 Access Violation 이 나는 경우가 있더군요.

    좀 더 조사해보면 나올거 같긴 한데 혹시 이 점에 관해 알고 계신가요?

    • reversecore 2011.02.18 22:41 신고

      안녕하세요.

      수작업으로 IDT 를 변경하고 계신가요?

      ACCESS_VIOLATION 은 아마 IAT 때문일 것으로 생각됩니다. IAT 를 수작업으로 재작성 할때는 쓰기 속성을 주시는 것이 안전합니다. (IAT 는 로딩시에 로더가 실제 주소값을 채워주기 때문입니다.)

      혹시 다른 이유 때문이라면 해당 파일을 간략한 설명과 함께 저에게 보내주시면 제가 한번 봐드리겠습니다.

      reversecore@gmail.com

      감사합니다.

  13. Rony 2011.03.30 11:17

    글읽다가 질문이 있어서 글 남깁니다..

    optional_hearer의 import_table 주소를 7604에서 13310으로 변경 한 후에

    왜 size값을 dc로 변경하는지 모르겠습니다. 7606 rva가 가르키는 곳은 import table이고 이곳은

    IDT, 즉 IID구조체의 배열일 탠데요.. 새로 IID를 하나 더 작성한 것도 아닌데 14h를 왜 더해주는거죠?

    • reversecore 2011.04.13 00:11 신고

      안녕하세요.

      myhack3.dll 을 import 시키기 위해서 실제로 IID 구조체 하나를 추가하였습니다. 그래서 구조체 크기(0x14) 만큼 늘려준 것입니다.

      감사합니다.

  14. SmartSnake 2011.10.27 17:42 신고

    위에 virtualAddress는 어디에 명시되있어서 b0000 - (virtualaddress) + 8400 해야되는데
    버추얼 어드레스는어떻게 아셧나요.. ㅠㅠ;;

  15. 알려주세요 2012.06.30 19:06

    myhack.dll을 보면은 CreateThread를 통해 ThreadProc을

    호출했는데 이런거를 제외하면 전부 export를 해줘야 하나

    요?

  16. louboutin uk 2013.04.21 00:11

    착한 아내와 건강은 남자의 가장 훌륭 한재산이다.

  17. chlehd 2014.01.10 18:17

    안녕하세요. 내주신 책 읽고 열심히 공부하는 한 뉴비입니다.
    제가 어떤 패커로 packing된 프로그램을 뚫어보려고 하는데요,
    ollydbg로 하나하나 진행하려다 안되서 dll injection으로 뚫을 수 없을까 하는데,
    아무래도 그 프로그램이 CreateRemoteThread() 함수를 감지하는 것 같아서 이전 방법들은 아예
    쓸 수가 없어서 이 방법으로 시도해보려고 합니다.
    만약 패킹이 다 풀린 그 시점에서 jmp하고 난 바로 그때를 감지하는 방법이 있을까요?

  18. yoo870701 2014.08.16 16:15

    안녕하세요
    잘 안되는 것이 있어 질문드립니다.
    책 내용대로 INT, IAT 모두 셋팅해서 PeView 로 올바르게 셋팅된것을 확인후
    TestView_Patched.exe 를 실행하니
    프로시저 시작지점 을 DLL myhack3.dll 에서 찾을수 없습니다.
    라는 에러를 뿜어냅니다.

    환경은 xp sp3, visual c++ 2010 express입니다.
    myhack3.dll과 TestView_Patched.exe은 당연히 같은폴더에 있습니다.
    뭐가 문제일까요

  19. ㅇㅇ 2015.05.25 02:39

    좋은글 감사합니다.

  20. 리버싱스터디 2017.03.31 18:43

    리버스코어님이 올려주신 걸 이용해서 타 다른 프로그램에서 응용 공부를 하고있는데요 #3에서 .txt 값에 널 값이 존재하지 않을 경우는
    마지막 부분의 널값에 적용시켜도 되나요? 또한 주소값이 예를들어 7ED80 의 값이로 5자리인경우 50 87 00 00 FF.....등의 값을 어떻게 셋팅해야 하나요?

반응형

 

앞에서 DLL Injection 의 개념에 대해서 알아보았고 CreateRemoteThread() API 를 이용하여 DLL Injection 을 실제로 구현 해보았습니다.

이번에는 DLL Injection 의 또 다른 구현 방법들에 대해서 알아보도록 하겠습니다.




AppInit_DLLs


Windows 운영체제에서 기본으로 제공하는 레지스트리 키 중에서 AppInit_DLLs 란 것이 있습니다.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
  AppInit_DLLs


<Fig. 1>

AppInit_DLLs 값에 인젝션을 원하는 DLL 경로를 써준 후 재부팅하면, 이후 Windows 운영체제는 재부팅하면서 실행되는 모든 프로세스에 해당 DLL 을 인젝션 시켜줍니다. 너무 간단하면서도 너무 강력한 기능이지요.

간단한 실습을 해보겠습니다. 일단 아래 myhack2.cpp 소스를 보시죠.

// myhack2.cpp

#include "windows.h"

#define DEF_CMD  "c:\\Program Files\\Internet Explorer\\iexplore.exe"
#define DEF_ADDR "www.naver.com"
#define DEF_DST_PROC "notepad.exe"

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    char szCmd[MAX_PATH]  = {0,};
    char szPath[MAX_PATH] = {0,};
    char *szProcess = NULL;
    STARTUPINFO si = {0,};
    PROCESS_INFORMATION pi = {0,};

    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH :
            if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )
                break;
   
            if( !(szProcess = strrchr(szPath, '\\')) )
                break;

            szProcess++;
            if( stricmp(szProcess, DEF_DST_PROC) )
                break;

            wsprintf(szCmd, "%s %s", DEF_CMD, DEF_ADDR);
            if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd,
                               NULL, NULL, FALSE, 
                               NORMAL_PRIORITY_CLASS,
                               NULL, NULL, &si, &pi) )
                break;

            if( pi.hProcess != NULL )
                CloseHandle(pi.hProcess);

            break;
    }
  
    return TRUE;
}

소스 코드 내용은 간단합니다.
현재 자신을 로딩한 프로세스 이름이 "notepad" 라면 IE(Internet Explorer) 를 숨김모드로 실행시켜 Naver 사이트에 접속하게 됩니다. 목적에 따라서 다양한 업무를 수행할 수 있겠죠?

차례대로 따라해보겠습니다.

#1. 파일 복사

myhack2.dll


첨부된 파일을 적절한 위치에 복사합니다. (제 경우엔 C:\work\myhack2.dll)

#2. 레지스트리 값(AppInit_DLLs) 입력

regedit.exe 를 실행하여 아래와 같이 입력합니다. (myhack2.dll 의 전체경로를 입력해주세요.)

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows

<Fig. 2>

#3. 재부팅

재부팅이 완료되었으면 Process Explorer 를 이용해서 과연 myhack.dll 이 모든 프로세스에 인젝션 되었는지 확인해 보겠습니다.


<Fig. 3>

myhack2.dll 이 모든 프로세스에 성공적으로 인젝션 되었습니다.
인젝션된 myhack2.dll 은 아무 동작을 하고 있지 않습니다. (notepad 프로세스만 대상으로 한다는 걸 기억하세요.)

notepad 를 실행하면 아래 그림과 같이 IE 가 (숨김 속성으로) 실행되는걸 확인할 수 있습니다.


<Fig. 4>


* 주의!
AppInit_DLLs 레지스트리 키는 너무나 강력해서 모든 프로세스에 DLL 을 인젝션 시켜버립니다.
만약 인젝션되는 DLL 에 문제(버그)가 있다면 자칫 Windows 부팅이 안되는 상황이 발생할 수 있습니다.
이 경우 Window Preinstalled Environment 부팅 CD (예:BartPE)가 없다면 복구할 수 없습니다.
따라서 AppInit_Dlls 를 사용할 때는 사전에 철저한 테스트가 필요합니다.




SetWindowsHookEx() - message hook


SetWindowsHookEx() API 를 이용하여 메시지 훅을 설치하면 OS 에서 hook procedure 를 담고 있는 DLL 을 (윈도우를 가진) 프로세스에 강제로 인젝션 시켜줍니다.

이 또한 DLL Injection 의 한 기법입니다.

자세한 설명은 제 글을 참고하시기 바랍니다.

- Windows Message Hooking (윈도우 메시지 후킹)


다음번에는 실행파일의 PE Header 정보를 조작하여 원하는 DLL 파일을 로딩시키는 방법에 대해서 알아보겠습니다. (엄밀히 말하면 DLL Injection 이라고 할 수 는 없습니다만, 리버싱에서 즐겨 사용되는 방법이기에 같이 소개합니다.)


Dll Injection - 다른 프로세스에 침투하기 (4)


ReverseCore


반응형
  1. dsds1994@naver.com 2010.06.10 21:48

    죄송한데 AppInit_DLLs 의 레지를 수정하면 모든 프로세서에 dll 이 인젝션된다는거잖아요

    일정 프로세서를 지정해서 인젝션되게는 할수없나요?

    • reversecore 2010.06.14 23:56

      안녕하세요.

      AppInit_Dlls 는 원래부터 global injection 을 목표로 만들어진 것입니다.

      특정 프로세스만을 원하신다면 CreateRemoteThread()/ZwCreateThreadEx() 등을 이용하시면 됩니다.

      감사합니다.

  2. ky 2010.07.02 18:56

    저기 .. DLL작동하나요?,, 안하는거같은데?,

    저 위에 인터넷실행 부분 지우고 메시지박스로 대체했는데 안되는군요.

    • reversecore 2010.07.03 14:55

      안녕하세요.

      위 myhack2.dll 말씀이시죠?
      직접 코드를 수정한 후 테스트했는데 안되셨다는 말씀같네요.

      혹시 myhack2.dll 은 잘 실행되는지요?
      ky 님께서 만든 DLL 이 안되었다면 그 DLL 을 저에게 보내주세요.
      제가 한번 봐드리겠습니다.

      reversecore@gmail.com 주소로 해당 dll 파일의 확장자를 dllx 로 바꾸신후 첨부해서 보내주세요~

      그리고 위 내용에 소개되어 있듯이, APPInit_Dlls 키는 너무나 강력해서 만약 테스트하는 DLL 에 버그가 있거나 논리적인 오류가 있다면 OS 부팅이 안되는 경우가 발생합니다. 매우 주의하시기 바랍니다.

      감사합니다.

  3. eky 2011.04.21 22:07

    appinit_dlls 로 인젝션할때

    보호된 프로세스에도 인젝션이 되는건가요?

    • reversecore 2011.05.03 00:58 신고

      안녕하세요.

      Anti-DLL Injection 말씀이신가요?

      간단한 API Hooking 만으로도 인젝션 기법은 무력화 된답니다.

      감사합니다.

  4. kimmj9512@naver.com 2013.09.28 16:28

    dll 내부에 훅 프로시저를 만들어놓고 dllmain에다가 dll이 로딩될때 setwindowshookex를 호출하도록 dll 을 만들면 이 dll 파일을 dll 인젝터로 특정 프로세스에 인젝션시키면 setwindowshookex함수가 자동으로 호출되면서후킹이 될거같은데 이게 가능한가요?

  5. cyocyo 2014.10.08 00:49

    이젝션 예제를 실행할 때 자꾸
    The token does not have the specified privilege.이 뜹니다 ㅜㅜ 왜 그런지 설명좀 부탁드려도 될까요?ㅜㅜ

    • cyocyo 2014.10.08 17:55

      해결했습니다!! 64bit환경에서 해서 그랬네요 ㅎㅎ

  6. 은시내 2015.08.05 10:20 신고

    AppInit_DLLs 방법을 사용하면 MessageBox같은 함수를 호출하지 못하던데요
    http://www.digipine.com/index.php?mid=programming&search_target=tag&search_keyword=C&page=2&listStyle=webzine&document_srl=615
    여기를 좀 읽어보니까 대충 kernel32.dll만 올라온 다음 즉시 인젝션이 되는 듯한 느낌이던데
    그래서 user32.dll의 함수인 MessageBox같은 API를 불러오지 못하는 것 같고..
    제 생각이 맞는 해석인지 아시는 분이 계신다면 확답을 부탁 드립니다..

반응형

DLL Injection 구현 방법


몇 가지 구현 방법이 있습니다.

그 중에서 가장 유명한 방법이 CreateRemoteThread() API 를 이용하는 방법입니다.

이 방법은 윈도우즈 프로그래밍 서적의 바이블인 Jeffrey Richter Programming Applications for Microsoft Windows 에 소개된 내용입니다.

일단 소스 코드를 보겠습니다. (엔지니어에게는 백 마디 설명 보다는 역시 소스 코드를 한번 보는게 낫죠.)

먼저 Injection 시킬 myhack.dll 소스 코드입니다.

myhack.cpp


// myhack.cpp

#include "stdio.h"
#include "windows.h"

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

#define DEF_NAVER_ADDR ("http://www.naver.com/index.html")
#define DEF_INDEX_PATH ("c:\\index.html")

DWORD WINAPI ThreadProc(LPVOID lParam)
{
    URLDownloadToFile(NULL, DEF_NAVER_ADDR, DEF_INDEX_PATH, 0, NULL);
    return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    HANDLE hThread = NULL;

    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH :
            hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
            CloseHandle(hThread);
            break;
    }
  
    return TRUE;
}

아주 간단한 코드입니다.

DllMain() 을 보시면 DLL 이 로딩(DLL_PROCESS_ATTACH)될 때 스레드(ThreadProc)를 실행합니다.

ThreadProc() 의 내용은 urlmon.dll 의 URLDownloadToFile() 함수를 실행시켜서 네이버 초기화면(index.html)을 다운받습니다.

프로세스에 DLL Injection 이 발생하면 해당 DLL 의 DllMain() 함수가 호출된다고 이전 포스트에서 설명드렸습니다. 따라서 notepad.exe 프로세스에 myhack.dll 이 Injection 되면 결국 URLDownloadToFile() 함수가 실행될 것입니다.

* DLLMain() 에서 직접 URLDownloadToFile() 을 호출하면 간혹 hang 이 걸리는 경우가 있어서, 별도의 스레드를 생성하여 호출하도록 프로그래밍 하였습니다.



이제 myhack.dll 을 notepad.exe 프로세스에 Injection 시켜줄 프로그램(InjectDll.exe)의 소스코드를 보시겠습니다.

InjectDll.cpp


// InjectDll.exe

#include "stdio.h"

#include "windows.h"
#include "tlhelp32.h"

#define DEF_PROC_NAME ("notepad.exe")
#define DEF_DLL_PATH ("c:\\myhack.dll")

DWORD FindProcessID(LPCTSTR szProcessName);
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName);

int main(int argc, char* argv[])
{
    DWORD dwPID = 0xFFFFFFFF;
 
    // find process
    dwPID = FindProcessID(DEF_PROC_NAME);
    if( dwPID == 0xFFFFFFFF )
    {
        printf("There is no <%s> process!\n", DEF_PROC_NAME);
        return 1;
    }

    // inject dll
    InjectDll(dwPID, DEF_DLL_PATH); 

    return 0;
}

DWORD FindProcessID(LPCTSTR szProcessName)
{
    DWORD dwPID = 0xFFFFFFFF;
    HANDLE hSnapShot = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe;

    // Get the snapshot of the system
    pe.dwSize = sizeof( PROCESSENTRY32 );
    hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

    // find process
    Process32First(hSnapShot, &pe);
    do
    {
        if(!_stricmp(szProcessName, pe.szExeFile))
        {
            dwPID = pe.th32ProcessID;
            break;
        }
    }
    while(Process32Next(hSnapShot, &pe));

    CloseHandle(hSnapShot);

    return dwPID;
}

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
{
    HANDLE hProcess, hThread;
    HMODULE hMod;
    LPVOID pRemoteBuf;
    DWORD dwBufSize = lstrlen(szDllName) + 1;
    LPTHREAD_START_ROUTINE pThreadProc;

    // #1. dwPID 를 이용하여 대상 프로세스(notepad.exe)의 HANDLE을 구함
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
        return FALSE;

    // #2. 대상 프로세스(notepad.exe) 메모리에 szDllName 크기만큼 메모리를 할당
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

    // #3. 할당 받은 메모리에 myhack.dll 경로("c:\\myhack.dll")를 씀
    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);

    // #4. LoadLibraryA() API 주소를 구함
    hMod = GetModuleHandle("kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA");

    // #5. notepad.exe 프로세스에 스레드를 실행
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
    WaitForSingleObject(hThread, INFINITE); 

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

역시 코드가 간결합니다. (편의상 예외처리, 리턴값 체크 등의 코드는 생략하였습니다.)

main() 함수에서는 2개의 서브 함수를 호출하고 있습니다.

FindProcessID(DEF_PROC_NAME) 함수는 프로세스 이름으로 PID(Process ID) 를 구해주는 함수입니다. (설명은 생략합니다.) 그리고 InjectDll(dwPID, DEF_DLL_PATH) 함수가 바로 DLL Injection 을 해주는 핵심 함수입니다.

InjectDll() 함수를 자세히 살펴보겠습니다.

InjectDll() 함수는 대상 프로세스(notepad.exe)로 하여금 스스로 LoadLibrary("myhack.dll") API 를 호출하도록 명령하는 기능을 가지고 있습니다.

#1. 대상 프로세스 핸들 구하기

hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))

OpenProcess() API 를 이용해서 notepad.exe 의 프로세스 핸들을 구합니다. (이때 미리 구해놓은 PID 를 사용함)
이 프로세스 핸들(hProcess)을 이용해서 해당 프로세스(notepad.exe)를 제어할 수 있습니다.

#2-3. 대상 프로세스 메모리에 Injection 시킬 DLL 경로를 써주기

pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

대상 프로세스(notepad.exe)에게 로딩할 DLL 파일의 경로(문자열)를 알려줘야 합니다.
아무 메모리 공간에 쓸 수 없으므로 VirtualAllocEx() API 를 이용하여 대상 프로세스(notepad.exe) 메모리 공간에 버퍼를 할당합니다. 버퍼 크기는 DLL 경로 문자열 길이(NULL 포함)입니다. 

* 주의! 
VirtualAllocEx() 함수의 리턴값(pRemoteBuf)은 할당된 버퍼 주소입니다. 이 주소는 내 프로세스(Inject.exe)의 메모리 주소가 아니라 hProcess 핸들이 가리키는 대상 프로세스(notepad.exe)의 메모리 주소라는것을 꼭 기억하시기 바랍니다.


WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);

할당 받은 버퍼 주소(pRemoteBuf)에 WriteProcessMemory() API 를 이용하여 DLL 경로 문자열("C:\\myhack.dll")을 써줍니다.

이로써 대상 프로세스(notepad.exe) 메모리 공간에 Injection 시킬 DLL 파일의 경로가 생겼습니다.

* 참고
Win32 프로그래밍을 처음 배울 때 분명 다른 프로세스의 메모리에 읽고 쓰는 일이 어렵다고(혹은 불가능하다고) 배웠습니다. 하지만 실제로는 다른 프로세스의 메모리 공간에 접근을 못하면 운영체제도 답답해 집니다. (예를 들어 다른 프로세스 메모리에 접근 할 수 없다면 디버거 제작이 불가능해지지요.) 그래서 Windows 운영체제는 Debug API 를 제공하여 다른 프로세스 메모리 공간에 접근 할 수 있도록 하였습니다. 대표적인 Debug API 가 바로 위에서 소개해 드린 VirtualAllocEx(), VirtualFreeEx(), WriteProcessMemory(), ReadProcessMemory() 등이 있습니다.

#4. LoadLibraryA() API 주소를 구하기

hMod = GetModuleHandle("kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA");


LoadLibrary() API 를 호출시키기 위해 그 주소가 필요합니다.
(LoadLibraryA() 는 LoadLibrary() 의 ASCII 문자열 버전입니다.)

위 코드의 의미를 잘 생각해봐야 합니다.
우리는 분명 notepad.exe 에 로딩된 kernel32.dll 의 LoadLibraryA() API 의 시작 주소를 알아내야 합니다.
하지만 위 코드는 InjectDll.exe 에 로딩된 kernel32.dll 의 LoadLibraryA() API 의 시작 주소를 얻어내고 있습니다.

notepad.exe 에 로딩된 kernel32.dll 과 InjectDll.exe 에 로딩된 kernel32.dll 의 메모리 시작 위치(ImageBase)가 동일 하다면 위 코드는 문제가 없습니다.

일반적인 DLL 파일의 ImageBase 는 0x10000000 으로 설정되기 때문에 a.dll 과 b.dll 을 차례대로 로딩하면 a.dll 은 정상적으로 0x1000000 주소에 로딩이 되겠지만 b.dll 은 자신이 원하는 0x10000000 주소에 로딩되지 못하고 다른 비어 있는 주소 공간에 로딩됩니다. 즉, DLL Relocation 이 발생하는 것입니다. (a.dll 이 같은 주소에 이미 로딩되어 있기 때문입니다.)

만약 kernel32.dll 이 프로세스마다 다른 주소에 로딩된다면 위 코드는 잘못된 것입니다.
하지만 실제 Winodows 운영체제에서 kernel32.dll 은 프로세스마다 같은 주소에 로딩됩니다.

어째서 그런걸까요?

PE View 를 통해서 Windows 운영체제의 핵심 DLL 파일들의 ImageBase 값을 조사해 봤습니다.
(Windows XP SP3 KOR 버전입니다. Windows 업데이트 상태에 따라서 아래 값들은 달라질 수 있습니다.)

DLL file        ImageBase       SizeOfImage
--------------------------------------------
msvcrt.dll      77BC0000        00058000
user32.dll      77CF0000        00090000
gdi32.dll       77E20000        00049000
advapi32.dll    77F50000        000A8000
kernel32.dll    7C7D0000        00130000
shell32.dll     7D5A0000        007FD000
...


Microsoft 에서 친절하게 OS 핵심 DLL 파일들의 ImageBase 값을 이쁘게 정리해놨습니다.
즉, 자신들끼리 절대로 겹치지 않고 따라서 DLL Relocation 이 발생하지 않습니다.

Dll Injection 기법은 위와같이 OS 핵심 DLL 들은 자신만의 고유한 주소에 로딩된는 것을 보장해주는 Windows 특성을 이용한 것입니다. (이 특성이 Windows 보안 취약점으로 이용되기도 합니다.)

따라서 InjectDll.exe 프로세스에 import 된 LoadLibraryA() 주소와 notepad.exe 프로세스에 import 된 LoadLibraryA() 주소는 동일합니다.

* 참고!
모든 Windows 프로세스는 kernel32.dll 을 로딩합니다.
PE Header 를 조작하여 IAT 에서 kernel32.dll 항목을 제거해버려도 loader 가 강제로 kernel32.dll 을 로딩시켜버립니다. (XP 부터 해당됨. 2000 에서는 실행불가.)


#5. 대상 프로세스에 스레드를 실행 시킴

모든 준비는 끝났고 마지막으로 notepad.exe 로 하여금 LoadLibraryA() API 를 호출하도록 명령만 내리면 됩니다. 하지만 Windows 에서는 그런 API 를 제공하지 않습니다.

그래서 편법(?)으로 CreateRemoteThread() API 를 사용합니다.
(편법이라기 보다는 DLL Injection 의 정석이라고 말 할 수 있지요.)

CreateRemoteThread() API 는 다른 프로세스에게 스레드를 실행시켜주는 함수입니다.

HANDLE WINAPI CreateRemoteThread(
  __in   HANDLE                   hProcess,             // 프로세스 핸들
  __in   LPSECURITY_ATTRIBUTES    lpThreadAttributes,
  __in   SIZE_T                   dwStackSize,
  __in   LPTHREAD_START_ROUTINE   lpStartAddress,       // 스레드 함수 주소
  __in   LPVOID                   lpParameter,          // 스레드 파라미터 주소

  __in   DWORD                    dwCreationFlags,
  __out  LPDWORD                  lpThreadId
);


첫번째 파라미터인 hProcess 만 빼면 일반적으로 사용되는 CreateThread() 함수와 다 똑같습니다.

hProcess 파라미터가 바로 스레드를 실행시킬 프로세스의 핸들입니다.
lpStartAddress 와 lpParameter 파라미터는 각각 스레드 함수 주소와 스레드 파라미터 주소입니다.
중요한건 이 주소들이 대상 프로세스의 가상 메모리 공간의 주소이어야 한다는 것입니다. (그래야 그 프로세스에서 인식을 할 수 있겠죠.)

좀 어리둥절 하시죠?
다른 프로세스에 DLL 을 injection 시키는데 스레드가 무슨 상관일까요?

스레드 함수 ThreadProc() 과 LoadLibrary() API 를 보시면 힌트를 얻을 수 있습니다.

DWORD WINAPI ThreadProc(
  __in  LPVOID           lpParameter
);

HMODULE WINAPI LoadLibrary(
  __in  LPCTSTR          lpFileName
);


두 함수 모두 4 byte 파라미터를 받고, 4 byte 값을 리턴하지요.
바로 여기서 아이디어를 얻은 것입니다.

CreateRemoteThread() 를 호출해서 4 번째 파라미터 lpStartAddress 에 "LoadLibrary() 주소"를 주고, 5 번째 파라미터 lpParameter 에 원하는 "DLL 의 경로" 문자열을 주면 됩니다. (반드시 대상 프로세스의 가상 메모리 공간에의 주소이여야 합니다.)

우린 이미 위에서 다 준비해놨지요. 편안하게 호출해주면 됩니다.

hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);

  pThreadProc = notepad.exe 의 LoadLibraryA() 주소
  pRemoteBuf = notepad.exe 의 "c:\\myhack.dll" 문자열 주소


CreateRemoteThread() 는 스레드를 생성하는 것이 아니라 실제로는 LoadLibraryA() 를 호출 시키는 것입니다.



+---+

CreateRemoteThread() 를 이용한 Dll Injection 기법에 대한 설명을 마치겠습니다.

처음에는 잘 이해가 되지 않을 수 있습니다. 설명을 다시 차근차근 읽어보시고 직접 실습해 보세요.

다음 번에는 다른 Dll Injection 기법들과 한번 Injection 된 Dll 을 꺼내는 (Ejection) 방법에 대해서 설명드리도록 하겠습니다.


Dll Injection - 다른 프로세스에 침투하기 (3)


ReverseCore

반응형
  1. 이전 댓글 더보기
  2. 2011.09.09 21:47

    비밀댓글입니다

    • reversecore 2011.09.19 20:17 신고

      안녕하세요.

      혹시 Vista/7 계열의 OS 를 사용하시는지요?

      Vista/7 OS 의 특정 프로세스들에서는 다른 방법을 사용해야 합니다. 제 블로그에 관련 글이 있으니 참고하시기 바랍니다.

      혹은 64bit 환경에서도 그 환경에 맞는 DLL Injection 방법을 쓰셔야 합니다. (이건 블로그에 아직 없네요...)

      감사합니다.

  3. 2011.09.10 21:31

    비밀댓글입니다

    • reversecore 2011.09.19 20:14 신고

      안녕하세요.

      ㅎㅎㅎ 죄송합니다. 원격으로 봐드리는 건 좀 부담스럽군요.

      감사합니다.

  4. 2011.10.14 23:24

    비밀댓글입니다

  5. 고니파 2011.11.08 16:52

    책이 따로 없네요. 정말 잘 봤습니다. ^^

    그리고 혹시 Detours를 이용한 DLL injection에 관해 포스팅 하실 계획은 없으신지요... ^^

    Detours를 이용한 API hook 관련 글은 있지만 dll injection 부분을 Detours로 처리한 글은 찾을 수가 없네요..
    Detours API 매뉴얼도 없고 관련 정보를 찾기가 꽤 힘들군요..

    • reversecore 2011.11.18 20:42 신고

      안녕하세요.

      Detour 후킹 라이브러리를 말씀하시는 것이지요?

      매뉴얼은 없구요. 그 내부에 있는 예제 샘플을 보시고 따라 만드시면 됩니다.

      Detour 를 이용한 API 후킹도 좋은 포스팅 주제가 되겠네요. 책이 완료되고 본격적인 블로그 활동이 재개되면 글을 써보도록 하겠습니다.

      감사합니다.

  6. 김정훈 2011.11.14 23:55


    혹시 VC++ 6 프로젝트 파일 2가지를 받을수 있을까요??

    phontest01@nate.com

    좋은 자료 감사합니다

    • reversecore 2011.11.18 20:39 신고

      안녕하세요.

      VS++ 2010 프로젝트이구요...

      제 책이 출시되는 시점에 같이 공개될 예정입니다.

      감사합니다.

  7. 탱그 2011.12.09 14:54

    CreateRemoteThread() 함수가 64bit컴퓨터에서는 안되네요 ㅜㅜ
    GetLastError()함수로 봤더니 5번 에러(ERROR_ACCESS_DENIED)
    가 나네요 ㅜㅜ

    • reversecore 2011.12.21 01:39 신고

      안녕하세요.

      64bit 에서 무조건 안되지는 않습니다.
      세션이 다른 경우에 실패하지요.

      관련 설명은 http://www.reversecore.com/73 를 참고하시면 될 것 같습니다.

      문의하신 에러는 좀 다른 상황인데요...
      OpenProcess() API 호출에서 실패한 것 같은데요.
      적절한 권한이 없는 상태에서 타겟 프로세스를 Open 하려고 할때 그런 현상이 발생합니다.

      역시 알려드린 링크의 InjectDll.cpp 파일내의 SetPrivilege() 함수를 먼저 호출하시면 자신의 권한을 상승 시킬 수 있으며 그런 에러가 발생하지 않을 것입니다.

      감사합니다.

  8. 배우는중 2012.02.24 21:56

    맨날 버퍼오버플로우,포멧스트링 이런 리눅스에서 배운거 쓰다보면서 API만 보면 덜덜 떨었는데
    쉽게 잘 설명하셧네요

  9. 알려주세요 2012.06.05 02:48

    안녕하세요 Injectdll.exe 의 소스를 그대로 베껴서 해보니 몇가지 오류가 나서 고쳤더니 dll이 로딩되지 않는 것 같습니다. win7,winxp 전부 안되구여.. 왜 그럴까요?


    #include "stdio.h"
    #include "windows.h"
    #include "tlhelp32.h"

    #define DEF_PROC_NAME TEXT("notepad.exe")
    #define DEF_DLL_PATH TEXT("c:\\myhack.dll")

    DWORD FindProcessID(LPCTSTR szProcessName);
    BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName);

    int main(int argc, char* argv[])
    {
    DWORD dwPID = 0xFFFFFFFF;

    // find process
    dwPID = FindProcessID(DEF_PROC_NAME);
    if( dwPID == 0xFFFFFFFF )
    {
    printf("There is no <%s> process!\n", DEF_PROC_NAME);
    return 1;
    }

    // inject dll
    InjectDll(dwPID, DEF_DLL_PATH);

    return 0;
    }

    DWORD FindProcessID(LPCTSTR szProcessName)
    {
    DWORD dwPID = 0xFFFFFFFF;
    HANDLE hSnapShot = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe;

    // Get the snapshot of the system
    pe.dwSize = sizeof( PROCESSENTRY32 );
    hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

    // find process
    Process32First(hSnapShot, &pe);
    do
    {
    if(!wcsicmp(szProcessName, pe.szExeFile))
    {
    dwPID = pe.th32ProcessID;
    break;
    }
    }
    while(Process32Next(hSnapShot, &pe));

    CloseHandle(hSnapShot);

    return dwPID;
    }

    BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
    {
    HANDLE hProcess, hThread;
    HMODULE hMod;
    LPVOID pRemoteBuf;
    DWORD dwBufSize = lstrlen(szDllName) + 1;
    LPTHREAD_START_ROUTINE pThreadProc;

    // #1. dwPID 를 이용하여 대상 프로세스(notepad.exe)의 HANDLE을 구함
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    return FALSE;

    // #2. 대상 프로세스(notepad.exe) 메모리에 szDllName 크기만큼 메모리를 할당
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

    // #3. 할당 받은 메모리에 myhack.dll 경로("c:\\myhack.dll")를 씀
    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);

    // #4. LoadLibraryA() API 주소를 구함
    hMod = GetModuleHandle(TEXT("kernel32.dll"));
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA");

    // #5. notepad.exe 프로세스에 스레드를 실행
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
    }

  10. 마리오 2012.06.18 23:53

    hMod = GetModuleHandle("kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod,"LoadlibraryA");
    이부분 이해가 잘안가는대
    hMod = GetModuleHandle("kernel32.dll"); -> 이부분 Inject_DLL의 커널32.dll 핸들을 얻어서
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod,"LoadlibraryA");
    └>이 부분은 커널32.dll의 LoadlibraryA 를 호출 한다고 보면될까요?

    그리고.. 이두 줄의 용도가 먼가요?
    이 2줄로 구해진 loadLibraryA 의 커널 핸들로 노트페드의 스레드를 실행시키는건가요?

    • reversecore 2012.06.23 01:28 신고

      안녕하세요.

      아직 Win32 API 에 익숙치 않으셔서 그러실 겁니다.

      GetModuleHandle("kernel32.dll") API 는 프로세스에 로딩된 kernel32.dll 의 로딩 주소를 리턴합니다.

      GetProcAddress(hMod,"LoadlibraryA") API 는 kernel32.dll 에서 제공하는 LoadLibraryA 라는 API 코드 주소를 리턴합니다. 즉 pThreadProc 에는 LoadLibraryA API 주소가 저장되며, LoadLibraryA() 를 호출하는 것과 pThreadProc 을 호출하는 것은 같은 코드를 실행하게 됩니다. 이러한 pThreadProc 을 함수포인터라고 얘기합니다.

      MSDN 도움말에서 API 설명을 읽어보시면 더 자세히 설명되어 있습니다.

      감사합니다.

  11. gilyong 2012.06.26 16:16

    글을 읽고 실습을 하던 중 궁굼한점이 있어 글을 올립니다.
    혹시 notepad.exe의 IAT(Import Address Table)에 LoadLibraryA가 없으면, DLLinjection.exe에서
    LoadLibraryA의 주소를 가지고 오더라두 인젝션이 되는지 궁굼합니다.
    이렇게 생각한 이유는 위 코드를 유니코드로 작성했는데 인젝션이 안되서 자료를 찾던 중 유니코드로 작성되면 LoadLibraryW에 주소를 가지고 온다고 봤습니다. 그래서 notepad.exe에 해당"LoadLibraryW"가 있는지 확인 해봤는데 IAT에는 없었습니다.. 그런데 이건 추측으로 부터 시작해서 정확히 알고 싶습니다.....

  12. 익명 2012.07.27 08:46

    죄송하지만 ㅠㅠ visual 6.0에서 위의예제 BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
    함수에서 이런 에러가 나는데 왜그런건가요 ㅠㅠ 도저히 해결을 못하겟네요

    injdectdll.obj : error LNK2001: unresolved external symbol "int __cdecl InjdectDll(unsigned long,char const *)" (?InjdectDll@@YAHKPBD@Z)

  13. 귀찮니즘 2012.07.29 15:51

    안녕하세요.
    제가 메일 보냈는데 확인좀 해주세요.

  14. 귀찮니즘 2012.07.30 22:17

    안녕하세요.
    여기 댓글들 보니까 UNICODE하면 실패하는것 같군요.
    저도 UNICODE로 해서 실패한것 같습니다.
    UNICODE로 할려면 어떻게 해야 되나요?

    • saeglo 2012.07.31 10:06

      // 귀찮니즘
      "string"의 형태는 유니코드에서는 char *로 인식하므로
      1. _T("string")
      2. _TEXT("string")
      3. L"string"
      의 세 가지 방법 중 하나로 하시면 됩니다.

  15. 2012.08.08 18:05

    비밀댓글입니다

  16. 삼철 2012.12.05 11:42

    포스팅이 매우 간결하고 내용 이해가 너무 쉽워서 정말 많은 도움이 되었습니다.
    샘플코드를 수정해보면서 확인한 정보에 대해서, 본 포스트에 추가하면 좋을 것 같아 공유해봅니다.

    결론부터 말씀드리면 32bit, 64bit Windows의 차이점이 되겠네요.
    설명하신 내용 중에 DLL 파일의 Image Base 값이 Process 마다 거의 동일하다는 전제가 있었습니다.
    그런데 테스트 해본 결과 Windows 7 32bit 환경에서는 정상 동작하였으나,
    Windows 7 64bit 환경에서는 정상 동작하지 않았습니다.

    이유는, 64bit Windows가
    64bit Process에 대해서는 [ C:\\Windows\\System32\\kernel32.dll ] 을 사용하는 반면
    32bit Process에 대해서는 [ C:\\Windows\\SysWOW64\\kernel32.dll ] 을 사용하는 것에 있었습니다.
    이때, notepad.exe는 64bit Process이고
    예제코드로 만든 Process는 32bit 입니다.

    따라서 Injection 하려는 메모리 영역이 다른 곳에 존재하여 CreateRemoteThread를 호출 해봐야 별 의미가 없었습니다.

    • reversecore 2012.12.13 00:00 신고

      안녕하세요.

      WOW64 에 대한 좋은 설명 감사합니다. ^^

      추가적으로 32bit DLL 은 64bit 프로세스에 인젝션 시킬 수 없습니다.
      그 반대도 마찬가지구요.

      참고하시기 바랍니다.

      감사합니다.

  17. 똥낀도너츠 2013.01.14 01:44

    안녕하세요.
    DLL 인젝터 프로그램을 만들어야하는데.
    아는 지식이 너무 없어 어찌하다보니 여기까지 들어오게됬네요 ^^;
    최근에 책도 구입해서 잘보고있답니다.

    개발환경이 Windows 7 / VC 2010 사용하신다고하셨는데요.
    2010이있긴한데 프로젝트 생성에서 DLL 파일을 만들때 사용하는 프로젝트가
    MFC DLL이 맞나요?
    VC 6에서는 Dynamic Linked Library를 사용하는 걸로 알고있는데.
    위의 코드를 그대로 입력해서 제대로 동작하려면
    어떤 프로젝트를 생성하고 c++ 소스파일을 생성해서 타이핑해야하나요?

    너무 기본적인것을 질문해서 죄송합니다. 구글링으로 알아보려했지만...
    제 검색실력에 한계가있는것같습니다 ㅠㅠ

    바쁘실텐데 이런 질문남겨 죄송합니다.

    • reversecore 2013.01.18 01:27 신고

      안녕하세요.

      그냥 Win32 DLL 을 선택하시면 됩니다.

      제 책의 예제 소스 코드를 그대로 열어 보시면 참고가 되실 것입니다.

      질문은 좋은 것이니 부담갖지 말고 올려주세요~ ^^

      감사합니다.

  18. 라마르틴 2013.02.24 16:20

    깔끔합니다. 완벽한 소스네요. 감사합니다

  19. 낙군 2013.07.24 17:19 신고

    안녕하세요.
    리버싱 핵심원리 책을 구매해서 공부 하던 중 궁금한 점이 있습니다.
    Windows 7 에서 실습을 하던 중 Dll Injection 보안정책 때문에 제대로 되지 않을 수 있다라고 책에 씌여져 있는데 이러한 경우 보안 정책을 우회하여 Dll Injection 가능하게 할 수 있는 기법이 있는가요?? ?

  20. 주영이ミ☆ 2013.09.09 18:29 신고

    와.. 진짜 성인군자시네여..
    일일히 다 설명해주시네.. ㄷㄷ;;

  21. 김응두 2014.09.27 05:32

    하하 정말 감사합니다. 예전에 이 기법을 가지고 이런저런 해킹툴을 만들었던적이
    있었는데 해놓고도 의문이 가는게 한둘이 아니였거든요... 속 쉬원하게 해결이 되네요.
    사실 댓글은 잘 남가지 않는데 너무 제게 도움이 되어서... 남기게 되었어요 두번 감사합니다.

+ Recent posts