배열 과 함수 포인터 형변환에 관해서..

oprsystem의 이미지

안녕하세요..
함수를 배열로 복사한뒤에 배열을 함수 포인터의 주소로 옴겨서 실행하려고 합니다.

테스트 해 보기위해서 VC6.0에서 작성하였습니다.
작성한 파일의 확장자는 CPP입니다.
제가 무엇을 잘못 하였는지 지적 부탁드리겠습니다.

int (*ramfunction)();
unsigned long RAM_FUNC_AREA[1000];

int RamFunc();
int EndRamFunc();


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

	memcpy((char *) RAM_FUNC_AREA,(char *) RamFunc,
			(int)((unsigned long)EndRamFunc - (unsigned long) RamFunc));
	
	ramfunction = (int(*)())RAM_FUNC_AREA;

	ramfunction();

	return 1;
}

int RamFunc()
{
	printf("HI RAM AREA");
	return 1;
}

int EndRamFunc()
{
#if 0
	DUMMY
#endif
	return 1;
}
[/b]
cinsk의 이미지

기발한 아이디어입니다.

안타깝게도.. 실행 불가능합니다.

함수의 크기를 계산할 수 있는 방법은 존재하지 않으며,
함수의 내용을 변수에 복사하는 방법도 존재하지 않으며, 복사를 했다고 하더라도 그 코드를 실행할 수 있는 방법도 존재하지 않습니다.

이런 아이디어가 필요하시다면 C 대신에 lisp이나 scheme을 쓰시기 바랍니다.

moonzoo의 이미지

함수 포인터의 개념에 대해 잘못 생각하신듯 합니다.

일단 간단히 님의 소스를 변경해 보았는데.

이해를 도와줄 것입니다.

typedef int (*FUNC_PTR)(void);

FUNC_PTR RAM_FUNC_AREA[1000];

int RamFunc();
int EndRamFunc();


int main(int argc, char* argv[])
{
   
   RAM_FUNC_AREA[0] = &RamFunc;

   RAM_FUNC_AREA[0]();


   return 1;
}

int RamFunc()
{
   printf("HI RAM AREA");
   return 1;
}

int EndRamFunc()
{
#if 0
   DUMMY
#endif
   return 1;
}
ㅡ,.ㅡ;;의 이미지

cinsk wrote:
기발한 아이디어입니다.

안타깝게도.. 실행 불가능합니다.

함수의 크기를 계산할 수 있는 방법은 존재하지 않으며,
함수의 내용을 변수에 복사하는 방법도 존재하지 않으며, 복사를 했다고 하더라도 그 코드를 실행할 수 있는 방법도 존재하지 않습니다.

이런 아이디어가 필요하시다면 C 대신에 lisp이나 scheme을 쓰시기 바랍니다.

음.. 저도 좀의문을 가졌었는데..
함수포인터의 주소는 실제 코드가 저장된위치가 아닌모양이지요..


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

oprsystem의 이미지

안녕하세요..
벌써 답글이 3개나 달렸네요.. 관심 감사 합니다.

제가 미리 밝혔어야 하는데.. 저 코드는 저의 아이디어가 아니라 "Firmware Demystified" 라는 책에서 발췌한 내용 입니다. 제 불찰 입니다. 죄송합니다.

그리고.. 아직 해결 하지 못했습니다. :roll:

황혼보다 어두운 자여
내 몸에 흐르는 피보다 더 붉은 자여
시간의 흐름 속에 파뭍힌 위대한 그대의 이름을 걸고 나 여기서 어둠에 맹세하노라
우리 앞을 가로막고 있는 모든 어리석은 자 들에게
나와 그대의 힘을
위대한 파멸의 힘을 보여줄 것을

cedar의 이미지

함수 포인터 대신에 함수 객체(function object)를 쓰면 가능하지요.
검색해보시길...

pynoos의 이미지

재밌는 기능인데, 제 생각은 함수 내의 내용에 따라 실행 되는 함수도 있을 것 같고 그렇지 않은 함수도 있을 것 같습니다.

즉, OS나 컴파일러등 여러 환경에 따라 특정 상태에서만 실행될 것이라는 얘기 입니다.

data segment가 실행가능한 경우가 보장되어야하고 (2004년 현재 대부분 그렇죠?)
이름이 뭐하지만, 대충 지어본다면 User level re-location 으로도 안전한 코드여야하고..

몇몇 조건만 만족하면 실행가능한 코드도 찾아 볼 수 있을 것 같은데요?

faye의 이미지

가능하지 않을까요?

buffer overflow 코드들도 어셈으로 작성되어 stack 메모리에서 실행하는 구조이니..

어셈으로 원하는 기능을 모두 구현하려면 꽤 힘들것 같기는합니다.

pynoos의 이미지

#include <stdio.h>
int (*ramfunction)();
unsigned long RAM_FUNC_AREA[1000];

int RamFunc();
int EndRamFunc();


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

   memcpy((char *) RAM_FUNC_AREA,(char *) RamFunc,
         (int)((unsigned long)EndRamFunc - (unsigned long) RamFunc));

   ramfunction = (int(*)())RAM_FUNC_AREA;

   ramfunction();

   return 1;
}

int RamFunc()
{
   int (*print)(const char *,...) = printf;
   (*print)("HI RAM AREA");
   return 1;
}

int EndRamFunc()
{
#if 0 
   DUMMY 
#endif
   return 1;
}

이렇게 하면 잘되는군요..! 왜 그런지는 assembly code를 잘 살펴보면 됩니다...

힌트는 메모리 이동이 일어나는 함수 안에는 상대 점프가 일어나지 않아야합니다.

oprsystem의 이미지

아. .그렇군요..

황혼보다 어두운 자여
내 몸에 흐르는 피보다 더 붉은 자여
시간의 흐름 속에 파뭍힌 위대한 그대의 이름을 걸고 나 여기서 어둠에 맹세하노라
우리 앞을 가로막고 있는 모든 어리석은 자 들에게
나와 그대의 힘을
위대한 파멸의 힘을 보여줄 것을

oprsystem의 이미지

혹시 아래의 에러에 대해 솔류션을 가지신분 없으신가요? (VC++6.0)

C:\Program Files\Microsoft Visual Studio\MyProjects\aaasss\aaasss.cpp(22) : error C2440: 'type cast' : cannot convert from 'unsigned long [1000]' to 'int (__cdecl *)(void)'
There is no context in which this conversion is possible
Error executing cl.exe.

황혼보다 어두운 자여
내 몸에 흐르는 피보다 더 붉은 자여
시간의 흐름 속에 파뭍힌 위대한 그대의 이름을 걸고 나 여기서 어둠에 맹세하노라
우리 앞을 가로막고 있는 모든 어리석은 자 들에게
나와 그대의 힘을
위대한 파멸의 힘을 보여줄 것을

pynoos의 이미지

암시적 형변환이 일어나게 했나보죠?

oprsystem의 이미지

ramfunction = (int(*)())RAM_FUNC_AREA;
이런식으로 했습니다. VC++에서 나오는 에러네요
다른컴파일러는 그냥 넘어 가는데..

황혼보다 어두운 자여
내 몸에 흐르는 피보다 더 붉은 자여
시간의 흐름 속에 파뭍힌 위대한 그대의 이름을 걸고 나 여기서 어둠에 맹세하노라
우리 앞을 가로막고 있는 모든 어리석은 자 들에게
나와 그대의 힘을
위대한 파멸의 힘을 보여줄 것을

pynoos의 이미지

제가 지금 VC가 없어서... 확인을 못하겠군요.. :)

oprsystem의 이미지

:D

황혼보다 어두운 자여
내 몸에 흐르는 피보다 더 붉은 자여
시간의 흐름 속에 파뭍힌 위대한 그대의 이름을 걸고 나 여기서 어둠에 맹세하노라
우리 앞을 가로막고 있는 모든 어리석은 자 들에게
나와 그대의 힘을
위대한 파멸의 힘을 보여줄 것을

barmi의 이미지

바람돌이 wrote:
ramfunction = (int(*)())RAM_FUNC_AREA;
이런식으로 했습니다. VC++에서 나오는 에러네요
다른컴파일러는 그냥 넘어 가는데..

에러메시지처럼 'unsigned long [1000]'를 바로 'int (__cdecl *)(void)'로 바꾸지는 못합니다.
다음처럼 중간에 void*를 경유하게 해 보세요.

ramfunction = (int(*)())(void*)RAM_FUNC_AREA;

일단 에러는 나지 않을 겁니다.

그리고, 위의 코드를 windows상에서 실행시켰더니, 실행이 되질 않습니다.
문제를 찾아보니, 실제 코드영역으로부터 데이터영역으로의 복사가 수행되질 않습니다.

   memcpy((char *) RAM_FUNC_AREA,(char *) RamFunc, 
         (int)((unsigned long)EndRamFunc - (unsigned long) RamFunc)); 

아무래도 OS에 따라 동작하는 내용이 달라질 듯한 내용인 것 같습니다.

제 테스트 환경은 WindowsXP Prof + VC++6.0 SP5 입니다.

oprsystem의 이미지

저도 동작하지 않는군요.

windows2000 server , VC++6.0 입니다.

황혼보다 어두운 자여
내 몸에 흐르는 피보다 더 붉은 자여
시간의 흐름 속에 파뭍힌 위대한 그대의 이름을 걸고 나 여기서 어둠에 맹세하노라
우리 앞을 가로막고 있는 모든 어리석은 자 들에게
나와 그대의 힘을
위대한 파멸의 힘을 보여줄 것을

barmi의 이미지

흠.. 좀더 살펴보니 디버깅정보에 보여지는 함수포인터의 값과 런타임에 읽어오는 함수포인터의 값이 다르더군요.
그래서, 어셈블코드를 보니, 런타임에는 함수포인터를 모두 @ILT 를 통해서 접근하더군요... (ILT-Inline Table)
그러니, 복사가 제대로 안됐던 거였구요....
ILT접근을 조금 변경하면 될 것도 같긴 하네요....

14:      memcpy((char *) RAM_FUNC_AREA,(char *) RamFunc,
15:            (int)((unsigned long)EndRamFunc - (unsigned long) RamFunc));
00401048 B8 0F 10 40 00       mov         eax,offset @ILT+10(EndRamFunc) (0040100f)
0040104D 2D 05 10 40 00       sub         eax,offset @ILT+0(RamFunc) (00401005)
00401052 50                   push        eax
00401053 68 05 10 40 00       push        offset @ILT+0(RamFunc) (00401005)
00401058 68 48 7C 42 00       push        offset RAM_FUNC_AREA (00427c48)
0040105D E8 BE 01 00 00       call        memcpy (00401220)
00401062 83 C4 0C             add         esp,0Ch

....

@ILT+0(?RamFunc@@YAHXZ):
00401005 E9 F6 00 00 00       jmp         RamFunc (00401100)
@ILT+5(_main):
0040100A E9 21 00 00 00       jmp         main (00401030)
@ILT+10(?EndRamFunc@@YAHXZ):
0040100F E9 5C 01 00 00       jmp         EndRamFunc (00401170)

....

--- c:\program files\html help workshop\ttt.cpp  -----------------------------------------------------------------------------------------------------
31:
32:   int RamFunc()
33:   {
00401100 55                   push        ebp
00401101 8B EC                mov         ebp,esp
00401103 83 EC 44             sub         esp,44h
00401106 53                   push        ebx
00401107 56                   push        esi
00401108 57                   push        edi
00401109 8D 7D BC             lea         edi,[ebp-44h]
0040110C B9 11 00 00 00       mov         ecx,11h
00401111 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401116 F3 AB                rep stos    dword ptr [edi]
xster의 이미지

플래시 메모리에 있는 코드를 플래시 내용을 갱신시키기 위해서 사용할 경우 코드를 램으로 옮겨서 실행하는 경우가 필요한데 그러한 경우에 위의 방법처럼 사용하더군요. 임베디드 기기를 업그레이드 할 때 많이 쓰는 방식으로 알고 있는데 아마도 머신이나 OS, 컴파일러마다 방식이 다르게 나오지 않을까 하는 생각입니다.

pynoos의 이미지

저는 VC에서 잘 되는 걸요?

제가 바꾼 것이라곤 memcpy 될 크기입니다.

그리고, 확장자는 .c 로 하여 c compiler를 사용하였습니다.

#include <stdio.h>
int (*ramfunction)(); 
unsigned long RAM_FUNC_AREA[1000]; 

int RamFunc(); 
int EndRamFunc(); 


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

   memcpy((char *) RAM_FUNC_AREA,(char *) RamFunc, 
         sizeof RAM_FUNC_AREA ); 

   ramfunction = (int(*)())RAM_FUNC_AREA; 

   ramfunction(); 

   return 1; 
} 

int RamFunc() 
{ 
   int (*print)(const char *,...) = printf; 
   (*print)("HI RAM AREA"); 
   return 1; 
} 

int EndRamFunc() 
{ 
#if 0 
   DUMMY 
#endif 
   return 1; 
}
eplus2의 이미지

저도 잘되는 데요...
Visual C++ 6.0 & window 2000 SP3
확장자는 .cpp,

#include <memory.h>
#include <stdio.h> 
int (*ramfunction)(); 
unsigned long RAM_FUNC_AREA[1000]; 

int RamFunc(); 
int EndRamFunc(); 


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

   memcpy((char *) RAM_FUNC_AREA,(char *) RamFunc, 
         sizeof RAM_FUNC_AREA ); 
//(int)((unsigned long)EndRamFunc - (unsigned long) RamFunc)); 
// 이렇게 하니까 안되네요..


   ramfunction = (int(*)())(void *)RAM_FUNC_AREA; 

   ramfunction(); 

   return 1; 
} 

int RamFunc() 
{ 
   int (*print)(const char *,...) = printf; 
   (*print)("HI RAM AREA\n"); 
   return 1; 
} 

int EndRamFunc() 
{ 
#if 0 
   DUMMY 
#endif 
   return 1; 
}

로 했습니다.
oprsystem의 이미지

코드 메모리의 영역을 결정하는 부분에서 문제였군요.
감사 합니다.

황혼보다 어두운 자여
내 몸에 흐르는 피보다 더 붉은 자여
시간의 흐름 속에 파뭍힌 위대한 그대의 이름을 걸고 나 여기서 어둠에 맹세하노라
우리 앞을 가로막고 있는 모든 어리석은 자 들에게
나와 그대의 힘을
위대한 파멸의 힘을 보여줄 것을

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.