일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- pthread
- initial-exec
- 안티디버깅
- Linux packer
- 난독화
- LLVM
- so inject
- uftrace
- thread local storage
- on-stack replacement
- tracerpid
- Linux custom packer
- Android
- Obfuscator
- v8 tracing
- android inject
- LLVM Obfuscator
- OSR
- anti debugging
- tracing
- TLS
- linux thread
- pinpoint
- v8 optimizing
- custom packer
- linux debugging
- on stack replacement
- LLVM 난독화
- Injection
- apm
- Today
- Total
Why should I know this?
[C] 다른 문자 위치 찾기 본문
C에 존재하는 함수 strcmp, strncmp는 String Compare 기능을 제공한다.
이 함수들은
1) 두 문자열의 동일 여부를 확인할 수 있게
2) 두 문자열을 하나씩 비교해 다른 문자가 있을 경우 그 차를 반환
한다.
그러므로 프로그래머는 오직 이 함수들을 통해 두 문자가 다른지 여부만 확인할 수 있다.
이를 조금 손봐서,
1) 두 문자열 간의 다른 문자가 존재하는 최초 위치를 제공하며
2) 동시에 탐색시간을 조금 단축시켜보는 트윅을 해보려고 한다.
int
glibc_strncmp (const char *s1, const char *s2, size_t n)
{
uintptr_t s1_ori = (uintptr_t)s1;
unsigned char c1 = '\0';
unsigned char c2 = '\0';
if (n >= 4)
{
size_t n4 = n >> 2;
do
{
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == '\0' || c1 != c2)
goto out;
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == '\0' || c1 != c2)
goto out;
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == '\0' || c1 != c2)
goto out;
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == '\0' || c1 != c2)
goto out;
} while (--n4 > 0);
n &= 3;
}
while (n > 0)
{
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == '\0' || c1 != c2)
goto out;
n--;
}
return n;
out:
return (uintptr_t)s1 - s1_ori;
}
먼저 glibc에서 긁어온 strncmp 함수를 조금 변형해서, 다른 문자의 차를 반환하는게 아닌,
위치를 반환하게 하도록 변경했다.
다음은 위의 코드를 조금 변경해서 비트연산을 통해 속도를 조금 향상시킨 버전이다.
int
bitcomp_strncmp(const char *s1, const char *s2, size_t n)
{
uintptr_t s1_ori = (uintptr_t)s1;
const int MAX_LEN = 8; // maximum size can be compared at once
int chunk = 0;
unsigned char c1 = '\0';
unsigned char c2 = '\0';
if (n >= MAX_LEN)
{
size_t n8 = n >> 3;
do {
uint64_t s1_val = *((uint64_t *)s1);
uint64_t s2_val = *((uint64_t *)s2);
uint64_t res = s1_val ^ s2_val;
int pos = 0;
if (res > 0) {
do {
res <<= 8;
pos++;
} while (res > 0);
s1 += (8 - pos) +1;
goto out;
}
s1 = s1 + MAX_LEN; s2 = s2 + MAX_LEN;
} while(--n8 > 0);
n &= 7;
}
while (n > 0)
{
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == '0' || c1 != c2)
goto out;
n--;
}
return n;
out:
return (uintptr_t)s1 - s1_ori;
}
아주 간단한 트윅이므로, 쉽게 이해할 수 있을거라 생각한다.
다음은 이를 테스트하기 위해 만든 코드들이다.
double get_time()
{
struct timeval t;
struct timezone tzp;
gettimeofday(&t, &tzp);
return t.tv_sec + t.tv_usec*1e-6;
}
char *
getRandomText(int min, int max)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
srand((time_t)ts.tv_nsec);
int len = rand() % max;
if (len < min)
len = min;
char *res = malloc(len);
for (int i = 0 ; i < len; i ++)
{
clock_gettime(CLOCK_MONOTONIC, &ts);
srand((time_t)ts.tv_nsec);
res[i] = (rand() % 0x19) + 0x61; // a start at 0x61
}
res[len] = '\0';
return res;
}
char *
genText(char *target, int pos)
{
char *temp = strdup(target);
temp[pos] = '@';
return temp;
}
int
main()
{
struct timespec ts;
int count = 0;
int expected = 0;
int glibc_res = 0;
int bitcomp_res = 0;
double time = 0.0;
double glibc_running = 0.0;
double bitcomp_running = 0.0;
#define min 5
#define max 25
double glibc_table[max + min];
double bitcomp_table[max + min];
do {
char *text = getRandomText(min, max);
int len = strlen(text);
clock_gettime(CLOCK_MONOTONIC, &ts);
srand((time_t)ts.tv_nsec);
int diff_pos = (rand() % len);
char *test = genText(text, diff_pos);
expected = diff_pos;
time = get_time();
glibc_res = glibc_strncmp(text, test, len);
time = get_time() - time;
glibc_table[len] += time;
time = get_time();
bitcomp_res = bitcomp_strncmp(text, test, len);
time = get_time() - time;
bitcomp_table[len] += time;
if ((count % 10000) == 0) {
printf("[%08d] %s %s : %d %d %d\n", count,
text, test, expected, glibc_res, bitcomp_res);
}
count++;
free(text);
// free(test);
} while (count < 200000 && expected+1 == glibc_res && expected+1 == bitcomp_res);
printf("Running time Table \n");
for (int i = 0 ; i < max; i++) {
printf("[%02d] GLIBC : %f\t BIT : %f \n", i,
glibc_table[i], bitcomp_table[i]);
}
}
그 결과는 다음과 같다.
glibc에서 긁어온 strncmp를 일부 변경한 함수와 bitcomp_strncmp를 비교하면 bitcomp가 '대부분'의 경우 10~20% 좋은 성능을 나타내는 것을 확인 할 수 있다. 특히 bitcomp의 경우 같은 문자의 경우 한번에 8 글자를 비교할 수 있으므로 문자열의 길이에 영향을 '덜' 받을 수 있다는 장점이 있다.
# 성능 비교 - glibc의 strncmp 변경 vs bitcomp $ gcc -O2 main.c -o test_1 $ ./test_1 |
그렇다면 실제 glibc에 존재하는 strncmp와의 성능비교는 어떻게 나올까?
# 성능 비교 - glibc의 strncmp(순정) vs bitcomp $ gcc -O2 main_strncmp.c -o test_2 $ ./test_2 $ ./test_2
|
순정과 비교하면 '일부'를 제외하고는 거의 대부분 성능이 안좋게 나오는 것을 확인할 수 있다...
BITCOMP의 변을 하자면, Glibc의 Strncmp는 두 문자열을 비교해 첫 번째 다른 문자를 찾아내면 그 '차'를 반환한다는 것이다. 예를 들어 'abc', 'abC'를 비교하면 99-67 = 32을 반환할 것이다. 만약 'abC'와 'abc'를 비교하면 67-99 = -32를 반환하게 된다.
또한 순정 strncmp는 사실 strncmp.c에 들어있는 내용과 많이 다르다.
이에 대한 내용은 strncmp.S를 참고하면 된다. 실제 코드는 훨씬 고성능을 낼 수 있게 되어 있다.
하지만 bitcomp 역시 아직 최적화 할 요소가 조금 남아있다고~??
'Study' 카테고리의 다른 글
Control Flow Integrity for COTS Binaries (0) | 2023.02.04 |
---|---|
Class linking 메커니즘 - jvm (0) | 2022.09.30 |
역공학(=Reverse Engineering)을 위한 컴퓨터 기본 원리 (0) | 2022.02.15 |
git 복구 (0) | 2019.09.25 |
git 계정 설정 (0) | 2017.12.17 |