솔직히 이번게 ㄹㅇ 젤 이해 안됨. 포인터가 많아서.
상대방 프로세스에 독립된 실행 코드를 삽입시키고 그걸 싱행시키는 것이라고 한다.
CreateRemoteThread( ) API를 이용한 원격 스레드방식이라서 Thread Injection이라고도 한다.
타깃 프로세스에 코드랑 데이터를 삽입한다. 코드랑 데이터를 어떻게 삽입하는지를 이해하자.
코드: Thread Procedure 형식으로 전달
데이터: Thread의 파라미터로 전달한다.
핵심은 코드랑 데이터를 각각 인젝션 해주는 것이다. 이걸 이해하려면 DLL 인젝션이랑 비교하면 된다.
인젝션을 할 때 인젝션 시킬 함수에 쓰이는 데이터들이 있겠지. 그 데이터가 어디에 위치하는지를 설명해보면
DLL 인젝션 : DLL 파일의 data영역에 위치한다.
즉 dll 인젝션에선 dll을 통째로 타깃 프로세스 메모리에 삽입시키면 코드랑 데이터가 모두 한꺼번에 메모리에 존재하게 된다.
Code 인젝션: 얘는 데이터가 어디에 위치한다는 걸까. 뭐 얘의 데이터는 인젝터하는 프로세스에 데이터 영역에 있겠지. 코드 인젝션 할 때는 그 데이터의 주소를 코드에서 알 수 있게 하기 위한 복잡한 관계의 코딩을 한다는 거지 뭐.
DLL 인젝션은 dll 파일 따로, 그리고 그 dll을 넣어주는 인젝터 프로세스 따로 이렇게 필요하다.
코드 인젝션은 별도의 dll 파일 없이 code injector 프로세스 하나만 있으면 된다. 물론 이게 위에 말한 두개를 합친 것 같은 느낌이긴 하다.
만약 타깃에다가 인젝션할 코드랑 데이터가 아주 작으면 굳이 dll로 만들 필요가 없다. 그냥 code로 만든 다음에 코드 인젝션으로 구현하면 메모리를 덜 차지한다.
사실상 코드인젝션의 가장 큰 장점이 아닐까. dll 인젝션은 타깃 프로세스의 메모리에 흔적을 남기기 때문에 인젝션이 금방 감지가 된다. 하지만 코드 인젝션은 그보다는 흔적이 덜 남는다. 물론 알아내는 고급스러운 기법이 있겠지.
dll 인젝션과 비교하면 기능은 비슷한데 고려해야 할 게 더 많아서 뒤지게 불편하다.
그래서 코드랑 데이터 규모가 작고 간단하면 code 인젝션을 쓰고 규모가 크고 복잡하면 dll 인젝션을 쓴다.
우선 code injector를 쓰기 위해선 타깃으l pid를 알아야 한다. 그래서 관리자 커맨드로
[code injector 프로세스 이름] [pid] 명령을 하면 되지.
그럼 이렇게 메모장에 메시지박스가 출력된다.
하나씩 까보자.
Codeinjection.exe의 codeinjection.cpp를 열어보자.
근데 이게 코드가 길어서 하나씩 볼거야.
메인의 역할은 타깃의 dwPID를 받아서 InjectCode를 실행하는 것이다.
injectcode( )는 상대프로세스에게 어떻게 코드를 삽입하는지를 담고 있겠지. 그럼 대체 이게 넣고 싶어하는 코드는 뭘까를 먼저 알아보자.
타깃에 인젝션할 코드가 바로 스레드 함수이다.
얘가 원하는건 뭐냐.
- hMod=LoadLibrary("user32.dll")
user32.dl을 로드시키고
- pFunc=GetProcAddress(hMod, "MessageBoxA")
메시지박스 주소를 알아내고
pFunc(NULL, "www.reversecore.com", "ReverseCore",MB_OK);
메시지 박스에 들어갈 데이터들 담아내.
이런 것이다. 중요한 것만 짚자.
첫번째로는 코드와 데이터를 같이 인젝션 하고 있다.
두번째로 중요한 건 ThreadProc( )은 직접 API를 호출하거나 문자열을 직접 정의하거나 하는 하드코딩이 되어있지 않다. 전부 Thread Parameter로 넘어온 THREAD_PARAM 구조체에서 가져다 사용한다.
그래서 포인터를 많이 써서 코드가 복잡해 보이는 것이다.
ThreadProc( )은 2개의 API 주소와 4개의 문자열 데이터를 받아들인다고 한다.
2개의 API 주소는 LoadLibraryA( )랑 GetProcAddress( )란 거 알겠어. 문자열 4개 중 2개는 "www.reversecore.com"랑 "ReverseCore"란 거 알겠어.
나머지 문자열 두개는 뭐지?
"LoadLibraryA"랑 "GetProcAddress" 이 API 자체 문자열인가?
그럼 이 코드랑 데이터를 어떻게 타깃에 인젝션하는 걸까. 코드가 길어 힝.
그래도 그중에서
요건 좀 이해해보자.
이게 뭐냐면 ThreadProc( )이 들어갈 공간을 Virtual Allocation 하기 위해서 ThreadProc( )의 크기를 구하는 것이다.
InjectCode의 시작주소에서 ThreadProc의 시작주소를 빼고 있지.
각 함수의 시작주소를 빼면 왜 ThreadProc의 크기냐? 설명할게.
지금 InjectCode.cpp에서 코드가 작성된 순서를 보면 ThreadProc이 작성되고, 그 다음 InjectCode가 작성되었다.
Visual C++를 이용해서 Release 모드로 코드를 빌드하게 되면 소스코드에 적힌 순서대로 파일에서 바이너리가 위치한다.
그래서 파일에서도 ThreadProc 바이너리가 있고 바로 그 다음에 InjectCode가 있다. 그래서 InjectCode 시작 주소에서 ThreadProc 시작 주소를 빼면 ThreadProc 크기가 나온다.
마찬가지로 만약에 InjectCode 크기를 알고 싶으면 SetPrivilege 시작주소에서 InjectCode 시작주소를 빼면 된다.
SetPrivilege 얘기 나와서 말인데 권한에 대해서 공부를 하면 좋다.
Windows LUID 값과 Token을 이용한 Privilege 변경
이런거 참고하면 좋다.
디버깅은 참고로 New Thread에서 break하게 설정하면 된다.
디버거로 보면 ThreadProc이 파라미터를 계속 THREAD_PARAM 구조체를 가리켜서 받아오는 것을 볼 수 있다. [EPP+8]을 계속 쓰거든.