zed.0xff.me

demangle MSVC, Delphi & C++Builder mangled function names with pure Ruby

Ever wanted to convert @afunc$qxzcupi or ??3@YAXPAX@Z to something more human-readable?

(ofcourse you wanted, and you know about tdump.exe and undname.exe :)

And now you can do it using pure ruby, thanks to unmangler gem:

Unmangling Borland mangled names

1
2
3
4
5
6
7
8
require 'unmangler'

puts Unmangler.unmangle "@afunc$qxzcupi"
puts Unmangler.unmangle "@Forms@TApplication@SetTitle$qqrx17System@AnsiString"

# output:
# afunc(const signed char, int *)
# __fastcall Forms::TApplication::SetTitle(const System::AnsiString)

Unmangling MSVC mangled names

1
2
3
4
5
6
7
8
require 'unmangler'

puts Unmangler.unmangle "??3@YAXPAX@Z"
puts Unmangler.unmangle "?AFXSetTopLevelFrame@@YAXPAVCFrameWnd@@@Z"

# output:
# void __cdecl operator delete(void *)
# void __cdecl AFXSetTopLevelFrame(class CFrameWnd *)

And now w/o arguments

1
2
3
4
5
6
7
8
9
require 'unmangler'

puts Unmangler.unmangle "@Forms@TApplication@SetTitle$qqrx17System@AnsiString", :args => false

# outputs "Forms::TApplication::SetTitle"

puts Unmangler.unmangle "?AFXSetTopLevelFrame@@YAXPAVCFrameWnd@@@Z", :args => false

# outputs "AFXSetTopLevelFrame"

Links

  1. unmangler gem on rubygems.org
  2. unmangler sources on github.com

TBD: GCC support

Advanced Ruby: percent-literals

most used %-literals:

% code result description
%q %q(all `quotes` 'are' "ok") "all `quotes` 'are' \"ok\"" creates String
%r %r(i_am/a/regexp) /i_am\/a\/regexp/ creates Regexp
%w %w(abc def ghi) ["abc", "def", "ghi"] splits string into Array
%x %x(ls -a /tmp) ".\n..\nfile1\nfile2\n" alias of `cmd`

highlighted

1
2
3
4
5
6
7
%q(all `quotes` 'are' "ok") => "all `quotes` 'are' \"ok\""

%r(i_am/a/regexp)           => /i_am\/a\/regexp/

%w(abc def ghi)             => ["abc", "def", "ghi"]

%x(ls -a /tmp)              => ".\n..\nfile1\nfile2\n"

interpolation

1
2
3
4
5
6
7
8
9
10
11
# lowercase 'w': as-is
%w'a #{2+2} b' => ["a", "\#{2+2}", "b"] 

# uppercase 'W': interpolate
%W'a #{2+2} b' => ["a", "4", "b"] 

# lowercase 'q': as-is
%q'a #{2+2} b' => "a \#{2+2} b" 

# uppercase 'Q': interpolate
%Q'a #{2+2} b' => "a 4 b" 

other %-literals

1
2
3
4
5
6
7
8
# %s: convert to symbol
%s'foo' => :foo 

# %i: convert to array of symbols - not released yet, will be in Ruby 2.0 ?
%i'foo bar baz' => [:foo, :bar, :baz]

# '%' w/o any letter - alias for %Q
%'a #{2+2} b' => "a 4 b"

Advanced Ruby: break(value) & next(value)

1. break(value)

break accepts a value that supplies the result of the expression it is “breaking” out of:

1
2
3
4
5
  result = [1, 2, 3].each do |value|
    break value * 2 if value.even?
  end

  p result # prints 4

2. next(value)

next accepts an argument that can be used the result of the current block iteration:

1
2
3
4
5
6
7
  result = [1, 2, 3].map do |value|
    next value if value.even?

    value * 2
  end

  p result # prints [2, 2, 6]

Ruby: fastest way of converting string into array of characters

timecodecomment
0.147 s.bytes.to_amost fastest, but returns ASCII codes instead of chars
0.242 s.chars.to_a FASTEST
0.257 Array(s.chars)
0.265 a=[]; s.size.times{ |i| a<<s[i] }
0.268 a=[]; s.chars.each{ |c| a<<c }
0.278 s.bytes.map(&:chr)
0.513 s.scan(/./)
0.775 s.split(//)
0.795 s.split('') SLOWEST

first column is time of 100.000 iterations on Core i5 1.7GHz

code: bench-split.rb

bReader - Читалка цитатников, которая экономит ваше время и трафик

Особенные уникальности:

  • Поддержка картинок (xkcd, bash, что-то еще)
  • Цитаты обновляются через выделенный сервер обновлений, что:

    • гораздо быстрее, чем парсить HTML/XML на устройстве
    • экономит трафик, сжимая его и не загружая повторно то, что уже загружено
    • ВСЕ цитатники обновляются одним запросом, 2-3 секунды – и у вас уже 200 новых цитат

  • Все цитаты образуют единую ленту, но при желании можно читать любой цитатник отдельно.

  • Интеграция с твиттером (ну как без него) + все урлы жмутся через bit.ly, так что в один твит влезет больше полезного текста.

  • Читалка изначально заряжена 512-ю цитатами, так что даже в глуши без Интернета будет что почитать.

Изначально решил писать свою читалку потому что:

  1. надоело ждать когда уже одна небезызвестная читалка распарсит все свои источники
  2. при этом она еще и модальным окном загрузки все закрывает, хотя можно было бы и дать юзеру почитать то что есть, пока новое грузится
  3. в случае зависания сети/сайта/хз чего – висла вся программа, целиком и полностью
  4. некоторые цитатники (sramu.net) в RSS и на главной цитаты отдают не целиком, а половинку со ссылкой “читать дальше” – хотелось все-таки такие цитатники тоже читать нормально и без лишних кликов
  5. ну и комиксы тоже интересно было чтоб были

Немного технических фактов

  • итого на написание ушло полгода
  • сам читаю цитатники каждый день
  • AppStore аналогичное мое приложение отклонил, сволочь
  • backend: nginx, ruby, Sinatra, memcached
  • хостинг: Amazon EC2 small instance
  • IDE: VIM, rake

Все цитатники: (38шт)

QDBbash.org (English)
bash.altlinux.orgALT Linux Fortunator
bash.org.byЦитатник Байнета
bash.org.ruЦитатник Рунета
bash.org.ru комикскомиксы по мотивам цитат
besit2.ruесли накипело…
bezdna.suЛучшие цитаты из Бездны
comicsbook.ruFFFuuu, trollface, forever alone, okay и другие комиксы.
delonevtebe.ruДело не в тебе | Неудачные свидания
det.org.ruговорят дети
greatwords.ruВеликие слова — цитаты, афоризмы, высказывания
hatewall.ruстена ненависти
ibash.org.ruНовый цитатник Рунета
inwebwetrust комиксКомиксы об Интернете
ipfw.ruЦитатник канала #FreeBSD@RusNET
ithappens.ruКлиент и саппорт. Разработчик и заказчик. Программист и программа.
killmepls.ruКилл Ми Плз: Жизнь. Вид сзади.
kstatida.ruКороткие полезные советы, основанные на личном жизненном опыте, иногда печальном.
la2bash.ruLineage 2 беседка
lorquotes.ruLatest fortunes from Linux.Org.Ru.
nefart.ruВот такой не фарт!
nextjoke.netСамые смешные шутки
pinator.orgКраткие мотивирующие высказывания
pip.ecСегодня был ППЦ
polniyp.meПолный П!
psyhumor.ruПсихологи шутят
shortiki.comкороткие и смешные
sramu.netпостыдные истории, рассказанные реальными людьми
ukrbash.orgУкрБаш — український цитатник
wgbash.orgWorld of Tanks
wumocomicstrip.comWumo by Wulff & Morgenthaler – a commentary on life
xkcd.comA webcomic of romance, sarcasm, math, and language.
xkcd.ruxkcd по-русски
zadolba.liзадолба!ли
жж ellustratorЖЖ: Картинки здесь!
жж pirozhki-ruЖЖ: Пирожковая
жж poroshkiЖЖ: Порошки: кристаллизованная поэзия
жж ru-comicstripЖЖ: комиксы на русском

Скриншотики

Photobucket Photobucket Photobucket Photobucket Photobucket

Скачать

bReader в Google.Play

grub2 simple hardware boot switch

1. take any old unused USB stick (any size, 16M is more than enough, mine was 128M)

#fdisk -l /dev/sdc
Disk /dev/sdc: 126 MB, 126353408 bytes
16 heads, 32 sectors/track, 482 cylinders, total 246784 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x59d30b2a

   Device Boot      Start         End      Blocks   Id  System
/dev/sdc1   *          32      246271      123120   83  Linux

2. [optional] format it to ext2

3. find out its UUID

#blkid /dev/sdc*
/dev/sdc1: UUID="3a5ef90f-0640-42c3-97c2-8743c822ba60" TYPE="ext2"

4. do some simple grub2 scripting:

menuentry "auto" {
        set foo_uuid=3a5ef90f-0640-42c3-97c2-8743c822ba60
        set foo=empty

        insmod part_gpt
        insmod ext2
        insmod search_fs_uuid

        search --fs-uuid --no-floppy --set=foo $foo_uuid

        if [ $foo = "empty" ]; then
                # boot windows
                insmod fat
                insmod chain
                search --fs-uuid --no-floppy --set=root B0D7-DA71
                chainloader (${root})/efi/Microsoft/Boot/bootmgfw.efi
        else
                # boot linux
                set root='(hd0,gpt1)'
                search --no-floppy --fs-uuid --set=root 22c0603d-670e-4097-83d2-539b520fc75a
                linux /boot/vmlinuz root=/dev/sda1
        fi
}

So, if this (and exactly this, with unique UUID) USB stick is plugged in while computer boots – it will boot Linux, otherwise Windows will boot.

Dwarf Fortress

Первая цивилизация погибла от (пары) злобных гоблинов.

Вторая тоже =).

Третья научилась строить ДВЕРЬ(!!!) на входе в поселение. Погибла от злобного тролля, который умел ломать двери.

Четвертая погибла от жажды. Потому что все речки и озера зимой замерзли, до подземных источников она докопаться не успела, а как из льда добыть воду не дотумкала.

Пятая сейчас погибает от.. обновления 34.07, после которого дети вдруг стали паниковать по поводу своей одежды (ее отсутствия или поношенности, а новую надевать не хотят) и сходить с ума..

раскодируем Trojan.Siggen3.35000

лечение

обнаружение

Попался мне как-то в руки троянчик.
Вот его анализ на pedump.me
Вот так он обнаруживается различными антивирусами:

AntiVir: TR/Offend.6610086.8
BitDefender: Trojan.Generic.6610086
ClamAV: Trojan.Dropper-31300
DrWeb: Trojan.Siggen3.35000
Emsisoft: Trojan.SuspectCRC!IK
F-Secure: Trojan.Generic.6610086
Fortinet: W32/Filecoder.AA
GData: Trojan.Generic.6610086
Ikarus: Trojan.SuspectCRC
K7AntiVirus: Riskware
Kaspersky: UDS:DangerousObject.Multi.Generic
McAfee: Artemis!2A8242105FED
NOD32: Win32/Filecoder.AA
Norman: W32/Malware.WVNX
Panda: Trj/CI.A
TheHacker: Trojan/Filecoder.ab

полный отчет на virustotal.com

анализ

троян портит JPG, DOC, XLS файлы. Обнаружить испорченные файлы можно визуально по следующему куску данных в конце файла:

1
2
3
4
5
6
7
8
9
10
001a2cfb:  34 36 34 36 34 34 33 38  34 36 34 36 34 35 33 31  |4646443846464531|
001a2d0b:  33 32 33 35 34 36 34 36  34 36 34 36 33 30 33 31  |3235464646463031|
001a2d1b:  33 30 33 30 33 30 33 30  33 30 33 30 33 30 33 30  |3030303030303030|
001a2d2b:  33 30 33 30 33 30 33 30  33 30 33 30 33 30 33 30  |3030303030303030|
001a2d3b:  33 30 33 30 33 30 33 30  33 30 33 30 33 30 33 30  |3030303030303030|
001a2d4b:  33 30 33 30 33 30 33 30  33 30 33 30 33 30 33 30  |3030303030303030|
001a2d5b:  33 30 33 30 14 00 00 00  00 fb 2c 1a 00 14 00 04  |3030......,.....|
001a2d6b:  00 00 0f 2d 1a 00 14 00  08 00 00 23 2d 1a 00 14  |...-.......#-...|
001a2d7b:  00 0c 00 00 37 2d 1a 00  14 00 10 00 00 4b 2d 1a  |....7-.......K-.|
001a2d8b:  00 05 

(конкретные данные различны в зависимости от содержимого файла)

результат бинарного сравнения нормального (слева) и испорченного (справа) файла:

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
00000000: ff 54
00000001: d8 2f
00000002: ff 54
00000003: e1 38
00000004: 25 7a
00000400: ff 54
00000401: ff 56
00000402: 01 56
00000403: 00 57
00000404: 00 55
00000800: 00 54
00000801: 00 51
00000802: 00 54
00000803: 00 51
00000804: 00 54
00000c00: 00 54
00000c01: 00 51
00000c02: 00 54
00000c03: 00 51
00000c04: 00 54
00001000: 00 54
00001001: 00 51
00001002: 00 54
00001003: 00 51
00001004: 00 54

[!]           168_ok.jpg is  1715451 bytes long
[!]          168_bad.jpg is  1715597 bytes long

фактически троян портит по 5 байт в файле с интервалом в 1024 байта. но не просто портит, а записывает оригинал в конец испорченного файла. вот наглядный вид чего он там понаписал:

1
2
3
4
5
6
001a2d5f:  14  00 00 00 00  fb 2c 1a 00  |......,..|
001a2d68:  14  00 04 00 00  0f 2d 1a 00  |......-..|
001a2d71:  14  00 08 00 00  23 2d 1a 00  |.....#-..|
001a2d7a:  14  00 0c 00 00  37 2d 1a 00  |.....7-..|
001a2d83:  14  00 10 00 00  4b 2d 1a 00  |.....K-..|
001a2d8c:  05         

radare2 0.8.x unnecessary memory zeroing fix

radare2 bug

the bug only appears when debugging 32-bit binary on a 64-bit host

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
diff -r e96275c214b5 libr/io/p/io_ptrace.c
--- a/libr/io/p/io_ptrace.c        Mon Oct 24 04:35:42 2011 +0200
+++ b/libr/io/p/io_ptrace.c        Mon Oct 24 16:48:31 2011 +0300
@@ -34,9 +34,11 @@
 #if __OpenBSD__ || __KFBSD__
 #define debug_read_raw(x,y) ptrace(PTRACE_PEEKTEXT, (pid_t)(x), (caddr_t)(y), 0)
 #define debug_write_raw(x,y,z) ptrace(PTRACE_POKEDATA, (pid_t)(x), (caddr_t)(y), (int)(size_t)(z))
+typedef int ptrace_word;   // int ptrace(int request, pid_t pid, caddr_t addr, int data);
 #else
 #define debug_read_raw(x,y) ptrace(PTRACE_PEEKTEXT, x, y, 0)
 #define debug_write_raw(x,y,z) ptrace(PTRACE_POKEDATA, x, y, z)
+typedef void* ptrace_word; // long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
 #endif
 
 static int debug_os_read_at(int pid, ut32 *buf, int sz, ut64 addr) {
@@ -63,10 +65,11 @@
 }
 
 static int ptrace_write_at(int pid, const ut8 *pbuf, int sz, ut64 addr) {
-        ut32 *buf = (ut32*)pbuf;
-        ut32 words = sz / sizeof (ut32);
-        ut32 last = sz % sizeof (ut32);
-        ut32 x, lr, *at = (ut32*)(size_t)addr;
+        ptrace_word *buf = (ptrace_word*)pbuf;
+        ut32 words = sz / sizeof (ptrace_word);
+        ut32 last = sz % sizeof (ptrace_word);
+        ut32 x, *at = (ptrace_word*)(size_t)addr;
+        ptrace_word lr;
         if (sz<1 || addr==UT64_MAX)
                 return -1;
         for (x=0; x<words; x++)

printing from OSX & iOS to Canon MF-4018 shared via WNDR3700

0. Reason for this all

HP Photosmart 5510

Just bought an HP Photosmart 5510 with wifi & ePrint and my MacBook Air found and installed it via wifi without the need of any drivers or anything.

Canon MF4018

But I also have older Canon MF4018, connected directly to WNDR3700 via usb, and which I failed to set up for printing from my Macs via wifi.

1. Drivers

Netgear WNDR3700

The main reason why I failed is Canon’s drivers for MF4018 (or MF4010, their printing functions are the same). They only have more or less usable drivers for Windows and Linux, closed-source and bad designed, with a lot of proprietary stuff.

There is a way to make your OpenWRT router act like a print server for Windows & Linux clients, but it refuses to work with OSX client.

And even if we’ll somehow force it to work with OSX client, it will still need a lot of Canon drivers installed on Mac and carefully configured. And definitely will not work with iOS

2. Workaround

You can use one simple workaround if you have an x86 or x86_64 computer running 24/7 at your place. Or if you want to manually turn it on every time when you need to print something.

You can then install CUPS on your device, or use a Windows Printer Sharing.

Unacceptable for me. I only have WNDR3700 on ar71xx platform, and AppleTV2 on ARM Cortex A8 running nearly 24/7 at my home.

3. An Insight

If I can’t run an x86 PC 24/7 at my place — who or what can stop me to run it somewhere on Internet?!

(I already have a pair of servers on Internet, so let’s increase their load average a little :)

IMPORTANT: You’ll need to have a Real IP address at your place, or some other way of receiving data from your Internet server, like a SSH tunnel.

4. Step-by-step guide

  1. WNDR3700 (or other OpenWrt-based router, or DD-WRT one, or wl500g)
    1. Connect MF4018 via usb
    2. Set up p910nd
    3. Open port 9100 in firewall:
      iptables -A INPUT -j ACCEPT -p tcp -s your.internet.server.real.ip --dport 9100
  2. Internet server
    1. install CUPS
    2. install Canon Proprietary drivers: for example UFRII/UFRII LT Printer Driver for Linux v2.20
    3. Add a printer:
      lpadmin -p mf4010 -P CNCUPSMF4010ZK.ppd -v socket://my.home.real.ip:9100 -o printer-is-shared=true
    4. enable CUPS Internet sharing:
    5. set up CUPS and/or firewall to only allow connections from YOUR home/office network
  3. Optional steps, needed only for seamless printer autoconfiguration for OSX/iOS devices
    1. Configure
      1. Internet server: use airprint-generate to generate a .service file for your printer
      2. edit generated file: add <host-name>my.internet.server.real.ip</host-name>
      3. WNDR3700: install avahi-daemon, feed it with previously generated file, start it
    2. Now in OSX “Add Printers” Dialog just click “+” and it should see your printer, iOS should see your printer when you’ll try to print some photo

my autogenerated & edited mf4010.service file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0"?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
  <name replace-wildcards="yes">Canon MF4018</name>
  <service>
    <type>_ipp._tcp</type>
    <subtype>_universal._sub._ipp._tcp</subtype>
    <port>631</port>
    <host-name>my.internet.server.real.ip</host-name>       <!-- EDIT THIS -->
    <txt-record>txtvers=1</txt-record>
    <txt-record>qtotal=1</txt-record>
    <txt-record>Transparent=T</txt-record>
    <txt-record>URF=none</txt-record>
    <txt-record>rp=printers/mf4010</txt-record>
    <txt-record>note=laser</txt-record>
    <txt-record>product=(GPL Ghostscript)</txt-record>
    <txt-record>printer-state=3</txt-record>
    <txt-record>printer-type=0x821484</txt-record>
    <txt-record>pdl=application/octet-stream,application/pdf,application/postscript,image/gif,image/jpeg,image/png,image/tiff,text/html,text/plain,application/vnd.cups-banner,application/vnd.cups-postscript,application/vnd.cups-raw,application/vnd.hp-hpgl,image/x-bitmap</txt-record>
  </service>
</service-group>

hack.lu 2011 CTF -- Unknown Planet -- writeup

[ Lobotomy ]

0. original image file

1. analyzing image

all JPEGs have special EOF marker FF D9 and no data must be after this marker.

1
2
3
4
5
[zed@zmac 200.unknown.planet+]#irb -E binary
ruby-1.9.2-p290 :004 > data=File.read '0_8c4f14e28155a2c3cf4b2538c1e0958b.jpg'; data.size
 => 194420 
ruby-1.9.2-p290 :005 > data.split("\xff\xd9").map(&:size)
 => [192405, 2013] 

so, we can see that there’s 2013 spare bytes after EOF marker.

1
2
3
4
5
6
File.open('foo','w'){ |f| f<< data.split("\xff\xd9").last }
 => #<File:foo (closed)> 
ruby-1.9.2-p290 :007 > ^D

[zed@zmac 200.unknown.planet+]#file foo
foo: Zip archive data, at least v2.0 to extract

AHA! It’s a zip! :)

2. unzipping

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
32
33
34
[zed@zmac 200.unknown.planet+]#unzip foo 
Archive:  foo
  inflating: 5IIUED7GheR             
  inflating: 6JXtwsTTh9k             
  inflating: 87F1s5POUJc             
  inflating: BPiIOASG_Z6             
  inflating: nLPA8X0UJqf             
  inflating: rySOWi4fZkA             
  inflating: uvlSlG3Tgow             
  inflating: Uw105aD3qYh             
  inflating: Yui5oq58hlx    


[zed@zmac 200.unknown.planet+]#ls -la
-rw-r--r--@  1 zed  staff  20000 Apr 25 16:45 5IIUED7GheR
-rw-r--r--@  1 zed  staff  20000 Apr 25 16:45 6JXtwsTTh9k
-rw-r--r--@  1 zed  staff  20000 Apr 25 16:45 87F1s5POUJc
-rw-r--r--@  1 zed  staff  20000 Apr 25 16:45 BPiIOASG_Z6
-rw-r--r--@  1 zed  staff  20000 Apr 25 16:45 Uw105aD3qYh
-rw-r--r--@  1 zed  staff  20000 Apr 25 16:45 Yui5oq58hlx
-rw-r--r--@  1 zed  staff   1324 Apr 25 16:45 nLPA8X0UJqf
-rw-r--r--@  1 zed  staff  20000 Apr 25 16:45 rySOWi4fZkA
-rw-r--r--@  1 zed  staff  20000 Apr 25 16:45 uvlSlG3Tgow

[zed@zmac 200.unknown.planet+]#file *
5IIUED7GheR: data
6JXtwsTTh9k: data
87F1s5POUJc: 8086 relocatable (Microsoft)
BPiIOASG_Z6: data
Uw105aD3qYh: data
Yui5oq58hlx: data
nLPA8X0UJqf: 8086 relocatable (Microsoft)
rySOWi4fZkA: data
uvlSlG3Tgow: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 8 bit, mono 8000 Hz

Looks like audio file, that was split in chunks of 20000 bytes each.
uvlSlG3Tgow is a first chunk b/c it has a RIFF WAVE header.
nLPA8X0UJqf is a last tail chunk b/c it’s size less than 20000.

3. gluing waves

importing files in Audacity (or any other sound editor) discovers that source file is supposed to be a Morse – coded message. But we must find a correct order of chunks.
So, morse code consists of dots and dashes. Each kind must have fixed length.
We suppose that source file was generated programmatically, not recorder from line or mic. So, it’s timings must be perfect.
Following tool helps to manually find a correct chunks order.

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
32
33
34
35
#!/usr/bin/env ruby
STDOUT.sync = true

if ARGV.size == 0
  raise "gimme at least one chunk filename"
end

b0 = "\x80"*8
b1 = "\x27\x01\x27\x80\xd9\xff\xd9\x80"

data = ARGV.map{ |x| File.read(x) }.join.force_encoding('binary')
if data[0,4] == 'RIFF'
  data = data[44..-1]
end

N=120

b0 = b0*N
b1 = b1*N

r = ''
0.step(data.size-1,b0.size) do |i|
  case (d=data[i,b0.size])
  when b0
    print "."
    r << '0'
  when b1
    print "#"
    r << '1'
  else
    raise "SYNC ERROR" if d.size == b0.size
    raise "NOT ENOUGH DATA #{d.size}/#{b0.size}"
    raise "unknown #{d.size} (normal: #{b0.size}) bytes of data #{d.split('').map{|x| "%02x " % x.ord}.join}"
  end
end

calling with a single chunk – script says that it needs more data (more chunks):

1
2
[zed@zmac 1]#./2_manually_guess_chunk_order.rb uvlSlG3Tgow 
##..######..######.../2_manually_guess_chunk_order.rb:32:in `block in <main>': NOT ENOUGH DATA 756/960

calling with wrong 2nd chunk => SYNC ERROR:

1
2
[zed@zmac 1]#./2_manually_guess_chunk_order.rb uvlSlG3Tgow 6JXtwsTTh9k 
##..######..######..###./2_manually_guess_chunk_order.rb:31:in `block in <main>': SYNC ERROR 

two chunks in correct order, script says it needs more chunks:

1
2
[zed@zmac 1]#./2_manually_guess_chunk_order.rb uvlSlG3Tgow 5IIUED7GheR 
##..######..######..##......##..##..##..#./2_manually_guess_chunk_order.rb:32:in `block in <main>': NOT ENOUGH DATA 596/960 

all chunks in correct order:

1
2
[zed@zmac 1]#./2_manually_guess_chunk_order.rb uvlSlG3Tgow 5IIUED7GheR rySOWi4fZkA 87F1s5POUJc 6JXtwsTTh9k Uw105aD3qYh BPiIOASG_Z6 Yui5oq58hlx nLPA8X0UJqf 
##..######..######..##......##..##..##..##......##......##..##......######..##..######......######..##..######..######......######..######..######......##..##..##......

4. decoding Morse

we’ll need a ruby morse gem. install it with “gem install morse

1
2
3
4
5
6
[zed@zmac 200.unknown.planet+]#irb
ruby-1.9.2-p290 :001 > r='##..######..######..##......##..##..##..##......##......##..##......######..##..######......######..##..######..######......######..######..######......##..##..##......'
ruby-1.9.2-p290 :002 > require 'morse'
 => true 
ruby-1.9.2-p290 :005 > puts Morse.decode(r.gsub('......'," ").gsub('######','-').gsub('.','').gsub('##','.'))
PHEIKYOS

Voila! “Pheikyos” is the answer. Case-sensitive.

PS: all source & data files are available at my ctf github repo.

iphone glossy buttons howto

sample images

one big sample image33 individual button images











ObjC code

Following ObjC code uses iphone Private API (UIGlassButton class), so it may be rejected from AppStore.
BUT you can generate a lot of iphone-style glossy button images with it, save to PNG, and use in your projects.

IMPORTANT: this code must be run on iPhone simulator. it saves PNG images to /tmp folder on your computer

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
#define UIColorFromRGBA(rgbValue,a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
  green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
  blue:((float)(rgbValue & 0xFF))/255.0 alpha:a]

Class theClass = NSClassFromString(@"UIGlassButton");

for(int j=0;j<3;j++){
    for(int i=0;i<=10;i++){
        UIButton *theButton = [[[theClass alloc] initWithFrame:CGRectMake(5+120*j, 5+i*44, 120, 44)] autorelease];
        [theButton setValue:UIColorFromRGBA(0xff<<(j*8),0.1*i) forKey:@"tintColor"];
        //[theButton setTitle:@"Accept" forState:UIControlStateNormal];
        [self.view addSubview:theButton];
        
        UIGraphicsBeginImageContext(theButton.frame.size);
        CGContextRef theContext = UIGraphicsGetCurrentContext();
        [theButton.layer renderInContext:theContext];
        
        UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
        NSData *theData = UIImagePNGRepresentation(theImage);
        [theData writeToFile:[NSString stringWithFormat:
                              @"/tmp/btn%@%02i.png",
                              j==0 ? @"B" : (j==1 ? @"G" : @"R"),
                              i] atomically:NO];
        
        UIGraphicsEndImageContext();
    }
}

and now make them stretch

automatically stretching to
(oops, button lost a shadow during screen capture, not important here though)

note that stretchableImageWithLeftCapWidth method:

1
2
3
4
            UIButton* button = [[UIButton alloc] init];
            [button setTitle:@"  looong long long very long text  " forState:UIControlStateNormal];
            [button setBackgroundImage:[[UIImage imageNamed:@"btnB10.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:10] forState:UIControlStateNormal];
            [button sizeToFit];

NB: Don’t forget to release a button ! :)

Наноисследование твиттера :)

1. Очевидный факт – минимальная длина юзернейма в твиттере = 1 символ

примеры: @a @b @0
естественно, на данный момент они уже все давно заняты :)

2. Не всякий @lol это LOL и не всякий @nike это Nike

примеры:

@nike 1K followers 0 tweets ни разу не найк
@adidas 4K followers 170 tweets вроде как адидас
@puma 21K followers 1200 tweets аутентичная пума
@sela 46 followers 4 tweets какой-то мужик
@mcdonalds 163K followers 5500 tweets аутентичный макдак
 
@lol 800 followers 41 tweets совсем не смешно
@humor 1200 followers 13 tweets аналогично
 
@yandex 65K followers 800 tweets аутентичный яндекс
@google 3.5M followers 2800 tweets аутентичный гугл
@apple 2K followers 2 tweets не эппл ни разу

Вывод №1: популярный юзернейм вроде @apple или @humor может принести пару тысяч лишних фолловеров

Вывод №2: что следует сделать твиттеру

Твиттеру следует сделать возможность покупки уже существующих имен. Не принудительной естественно. Просто при заходе в чужой профиль кнопочка “I want to buy this username” и как на ебее – ставки, минимальная цена, аукцион (опционально) и т.п.
А может и сам владелец профиля у себя в настройках выставлять “I would sell this username for at least $5000”.
Соответственно если кто-то на эту кнопочку нажал, и сумму указал, он подтверждает что он не просто так нажал, а этим взял на себя обязательство в случае согласия владельца юзернейма купить его (юзернейм, а не владельца :) за указанную сумму.

Android Alchemy 2.0 new features & elements

Features

  • 10 new elements (380 total)
  • optimizations for tablet devices
  • profiles support
  • workspace contents is now saved between game sessions
  • performance improvements

New elements

chocolate
hot chocolate
e-book
beetroot
borscht
crop circles
woman
mermaid
baby
Transformers

Setting up a scan server on OpenWrt & NetGear WNDR3700

1. Install OpenWrt trunk for ar71xx

Follow openwrt wiki WNDR3700 page for detailed instructions.
I used openwrt-ar71xx-generic-wndr3700-squashfs-factory.img and it worked like a charm. Your firmware image may be different, especially if you have WNDR3700 V2. Read wiki carefully.

Note that trunk build does not have HTTP GUI by default. If you want GUI you have to login with telnet and do:

1
2
3
4
opkg update
opkg install luci
/etc/init.d/uhttpd start
/etc/init.d/uhttpd enable

Or just don’t use trunk. stable branch has the GUI by default.

As for me, I’m happy with CLI (no GUI at all) because it saves precious kilobytes of internal flash (5Mb total is available for user)

2. Configure your wired/wireless networks.

Use HTTP GUI or CLI, see WNDR3700 page or other pages in OpenWrt wiki

2½. [optional] set up extroot

Or you may exhaust all your router free “disk” space very fast.

Or you may be smart enough to fit everything in your router’s default flash memory.

Or your router may have much more that 5Mb of flash available :)

at this step I used a small 2-port unpowered USB hub with integrated microSD card reader + 256Mb microSD card

3. Connect your USB scanner & install USB support packages

1
2
opkg update
opkg install kmod-usb2 usbutils kmod-leds-wndr3700-usb

repeating opkg update is not necessary if you did update before and did not rebooted your device.

Ensure your scanner is visible as USB device (mine is Canon MF4018):

1
2
3
4
root@OpenWrt:~# lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 003: ID 04a9:26b4 Canon, Inc. MF4010 series

4. Install SANE frontends & backends

1
opkg install sane-frontends sane-backends

Ensure your scanner is recognized by SANE:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@OpenWrt:~# sane-find-scanner 

  # sane-find-scanner will now attempt to detect your scanner. If the
  # result is different from what you expected, first make sure your
  # scanner is powered up and properly connected to your computer.

  # No SCSI scanners found. If you expected something different, make sure that
  # you have loaded a kernel SCSI driver for your SCSI adapter.
  # Also you need support for SCSI Generic (sg) in your operating system.
  # If using Linux, try "modprobe sg".

found USB scanner (vendor=0x04a9 [Canon Inc.], product=0x26b4 [MF4010]) at libusb:001:003
  # Your USB scanner was (probably) detected. It may or may not be supported by
  # SANE. Try scanimage -L and read the backend's manpage.

  # Not checking for parallel port scanners.

  # Most Scanners connected to the parallel port or other proprietary ports
  # can't be detected by this program.

note that “found USB scanner” string.

Try to scan something:

1
2
3
4
5
6
7
root@OpenWrt:~# scanimage
P6
# SANE data follows
640 877
255

***SKIPPING A LOT OF GARBAGE***

you should see a lot of garbage (raw scan data) on your screen, it can be interrupted with Ctrl+C.

5. Set up HTTP

1
opkg install uhttpd jpeg-tools

now edit /etc/config/uhttpd, especially necessary if you have GUI installed. As for me, I only commented out listening on port 443.

grab small HTML/JS/CGI code from my openwrt-scan-server github repo (free hint: look for “Downloads” button at right ;)
and put it to /www dir on your router.

Note that current OpenWrt repos do not have ImageMagick tools available, so if you want a nice image resolution + file size info – you have to install imagemagick-jpeg package and use my identify binary (only for ar71xx platform, see at bin/ar71xx/identify in my github repo, put it in /usr/bin on your router)

6. Test

Navigate to your router’s address: http://192.168.1.1 (you may have another one).
You should see something like this: