Skip to content

Instantly share code, notes, and snippets.

@ddevault
Created May 20, 2018 01:07
Show Gist options
  • Save ddevault/c8a7404403d0499db01c248b944ba942 to your computer and use it in GitHub Desktop.
Save ddevault/c8a7404403d0499db01c248b944ba942 to your computer and use it in GitHub Desktop.
IOCCC 2018 entry
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#define S "/>\x10v1"
#define F "8\x1E~\"\x1C"
#define O "HR&K\bS\x15\tMs|\7x\x1b"
struct passwd *pw; struct group *gr; struct stat st;
extern char **environ; char b[512];
char h(char*s){char _=*s;while(*s)_=*s+++_+1;return _;}
char*N(char*_){return _&&strchr(_,S[0])?strrchr(_,S[0])+1:_?_:"";}
char*D(char*_){char*n=strrchr(_?_:"",0[S]);if(n)*n=0;return _;}
void d(int A,FILE**B){
long c=**(char**)B, C=c==0x38, x[11]={0}, y=0, d;
if(c==0x1C||(C&&A!=3)) return;
++B,A-=C+1;
FILE*P=C?B[1]:stdout,*I=A?B[0]:stdin;
C=0;
while(I){
while(!feof(I)){
C+=(d=fgetc(I))==10&&(x[y++%11]=ftell(I));
if(d<0&&c=='"'&&!fseek(I,x[y%11],SEEK_SET)&&(c='~')&&!(C=0))
continue;
if(d>0&&c!='"'&&fputc(d,P)&&C>=10&&c=='~')
break;}
fclose(I);
I=--A>0?*++B:NULL;}
fclose(P);}
void L(char*p,int r){
if(r&&S_ISDIR(st.st_mode)){
DIR*d=opendir(p);
struct dirent*e;
while((e=readdir(d))){
strcpy(b,p);
strcat(b,"/");
strcat(b,e->d_name);
stat(b,&st);
L(b,0);}
closedir(d);
}else{
pw=getpwuid(st.st_uid);
gr=getgrgid(st.st_gid);
printf("%04o %lu %s %s %lu %s\n",st.st_mode&07777,
st.st_nlink,pw->pw_name,gr->gr_name,st.st_size,N(p));}}
void e(int A,char**B){
int s=A>1&&!!strchr("\240\237",B[1][0]+B[1][1]);
int _=s+**B;
B+=s+1;
long a=-1;
while(--A){
char*s=NULL;
int(*f)(const char*,const char*)=NULL;
stat(*B,&st);
switch(_){
case'\7':if(a<0){pw=getpwnam(*B);a=pw->pw_uid;}else chown(*B,a,st.st_gid);break;
case'|':if(a<0){gr=getgrnam(*B);a=gr->gr_gid;}else chown(*B,st.st_uid,a);break;
case'\x1b':kill(atoi(B[1]),atoi(&(*B)[1]));return;
case'H':f=link;break;
case'I':f=symlink;break;
case'&':s=N(*B);break;
case'K':s=D(*B);break;
case'\b':s=*B;break;
case'S':unlink(*B);break;
case'M':L(*B,1);break;
case'R':f=rename;break;
case'T':remove(*B);break;
case'\x15':rmdir(*B);break;
case'\t':mkdir(*B,0755);
case's':if(a<0)a=strtol(*B,NULL,8);else chmod(*B,a);break;
case'x':chroot(*B);chdir("/");execvp(B[1],B+1);break;}
if(f){
f(B[0],B[1]);
return;}
if(s)puts(s);++B;}}
int main(int A,char**B){
char _=**B=h(N(*B))&0x7F;
struct utsname u;int i;
uname(&u);
switch(!!strchr(S,_)+!!strchr(S F,_)+!!strchr(S F O,_)+!!(h(N(*B))==-063)*2){
case 3:while(_=='1'&&*environ)puts(*environ++);
while(_=='\x4D'){putchar(_^'4');putchar('\n');}
return _=='v'||_=='1'||!puts(_=='>'?getcwd(b,512):u.sysname);
case 2:for(i=1;i<A;++i){
char*_=B[i];
B[i]=(char*)fopen(_,access(_,F_OK)!=-1?"r+":"w+");
if(!B[i])B[i]=(char*)fopen(_,"r");}
d(A,(FILE**)B);break;
case 1:e(A,B);}return 0;
}
Eat your heart out, busybox!
At least some of the features of this tool are probably apparent with a little
examination. Once you understand the basic concept, you can discover many of the
features by simply looking for which syscalls are used and knocking a couple of
brain cells together. However, this little box contains more subtlety than you
think! The trick is finding out not some of the things it does, but all of the
things it does.
Some of the obsfucations I use here are:
- Hashing argv[0] to avoid having to explicitly name all of the features
- Using the same code to implement several features at once via obtuse
conditional logic
- Using variables in misleading ways and exploiting the side effects
- Replacing argv with file pointers to make some things easier but also confuse
anyone researching it with their debugger. Note: this is, strictly speaking,
not abusing writable strings as the guidelines bemoan. argv is an array of
_pointers_ to strings!
- Just because the same number shows up several times doesn't mean it has to
have a consistent representation
- Bad symbol names and centering all of the code, I guess
Note: I recommend naming this file ln.c. The name of the binary is important,
and "prog" will not work.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment